@opentelemetry/instrumentation-fetch 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,346 @@
1
+ /*
2
+ * Copyright The OpenTelemetry Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as api from '@opentelemetry/api';
17
+ import { isWrapped, InstrumentationBase, safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation';
18
+ import * as core from '@opentelemetry/core';
19
+ import * as web from '@opentelemetry/sdk-trace-web';
20
+ import { AttributeNames } from './enums/AttributeNames';
21
+ import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
22
+ import { VERSION } from './version';
23
+ import { _globalThis } from '@opentelemetry/core';
24
+ function parseUrl(url) {
25
+ const element = document.createElement('a');
26
+ element.href = url;
27
+ return element;
28
+ }
29
+ // how long to wait for observer to collect information about resources
30
+ // this is needed as event "load" is called before observer
31
+ // hard to say how long it should really wait, seems like 300ms is
32
+ // safe enough
33
+ const OBSERVER_WAIT_TIME_MS = 300;
34
+ /**
35
+ * This class represents a fetch plugin for auto instrumentation
36
+ */
37
+ export class FetchInstrumentation extends InstrumentationBase {
38
+ constructor(config) {
39
+ super('@opentelemetry/instrumentation-fetch', VERSION, config);
40
+ this.component = 'fetch';
41
+ this.version = VERSION;
42
+ this.moduleName = this.component;
43
+ this._usedResources = new WeakSet();
44
+ this._tasksCount = 0;
45
+ }
46
+ init() { }
47
+ _getConfig() {
48
+ return this._config;
49
+ }
50
+ /**
51
+ * Add cors pre flight child span
52
+ * @param span
53
+ * @param corsPreFlightRequest
54
+ */
55
+ _addChildSpan(span, corsPreFlightRequest) {
56
+ const childSpan = this.tracer.startSpan('CORS Preflight', {
57
+ startTime: corsPreFlightRequest[web.PerformanceTimingNames.FETCH_START],
58
+ }, api.trace.setSpan(api.context.active(), span));
59
+ web.addSpanNetworkEvents(childSpan, corsPreFlightRequest);
60
+ childSpan.end(corsPreFlightRequest[web.PerformanceTimingNames.RESPONSE_END]);
61
+ }
62
+ /**
63
+ * Adds more attributes to span just before ending it
64
+ * @param span
65
+ * @param response
66
+ */
67
+ _addFinalSpanAttributes(span, response) {
68
+ const parsedUrl = parseUrl(response.url);
69
+ span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status);
70
+ if (response.statusText != null) {
71
+ span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText);
72
+ }
73
+ span.setAttribute(SemanticAttributes.HTTP_HOST, parsedUrl.host);
74
+ span.setAttribute(SemanticAttributes.HTTP_SCHEME, parsedUrl.protocol.replace(':', ''));
75
+ span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent);
76
+ }
77
+ /**
78
+ * Add headers
79
+ * @param options
80
+ * @param spanUrl
81
+ */
82
+ _addHeaders(options, spanUrl) {
83
+ if (!web.shouldPropagateTraceHeaders(spanUrl, this._getConfig().propagateTraceHeaderCorsUrls)) {
84
+ const headers = {};
85
+ api.propagation.inject(api.context.active(), headers);
86
+ if (Object.keys(headers).length > 0) {
87
+ this._diag.debug('headers inject skipped due to CORS policy');
88
+ }
89
+ return;
90
+ }
91
+ if (options instanceof Request) {
92
+ api.propagation.inject(api.context.active(), options.headers, {
93
+ set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),
94
+ });
95
+ }
96
+ else if (options.headers instanceof Headers) {
97
+ api.propagation.inject(api.context.active(), options.headers, {
98
+ set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),
99
+ });
100
+ }
101
+ else {
102
+ const headers = {};
103
+ api.propagation.inject(api.context.active(), headers);
104
+ options.headers = Object.assign({}, headers, options.headers || {});
105
+ }
106
+ }
107
+ /**
108
+ * Clears the resource timings and all resources assigned with spans
109
+ * when {@link FetchPluginConfig.clearTimingResources} is
110
+ * set to true (default false)
111
+ * @private
112
+ */
113
+ _clearResources() {
114
+ if (this._tasksCount === 0 && this._getConfig().clearTimingResources) {
115
+ performance.clearResourceTimings();
116
+ this._usedResources = new WeakSet();
117
+ }
118
+ }
119
+ /**
120
+ * Creates a new span
121
+ * @param url
122
+ * @param options
123
+ */
124
+ _createSpan(url, options = {}) {
125
+ if (core.isUrlIgnored(url, this._getConfig().ignoreUrls)) {
126
+ this._diag.debug('ignoring span as url matches ignored url');
127
+ return;
128
+ }
129
+ const method = (options.method || 'GET').toUpperCase();
130
+ const spanName = `HTTP ${method}`;
131
+ return this.tracer.startSpan(spanName, {
132
+ kind: api.SpanKind.CLIENT,
133
+ attributes: {
134
+ [AttributeNames.COMPONENT]: this.moduleName,
135
+ [SemanticAttributes.HTTP_METHOD]: method,
136
+ [SemanticAttributes.HTTP_URL]: url,
137
+ },
138
+ });
139
+ }
140
+ /**
141
+ * Finds appropriate resource and add network events to the span
142
+ * @param span
143
+ * @param resourcesObserver
144
+ * @param endTime
145
+ */
146
+ _findResourceAndAddNetworkEvents(span, resourcesObserver, endTime) {
147
+ let resources = resourcesObserver.entries;
148
+ if (!resources.length) {
149
+ if (!performance.getEntriesByType) {
150
+ return;
151
+ }
152
+ // fallback - either Observer is not available or it took longer
153
+ // then OBSERVER_WAIT_TIME_MS and observer didn't collect enough
154
+ // information
155
+ resources = performance.getEntriesByType('resource');
156
+ }
157
+ const resource = web.getResource(resourcesObserver.spanUrl, resourcesObserver.startTime, endTime, resources, this._usedResources, 'fetch');
158
+ if (resource.mainRequest) {
159
+ const mainRequest = resource.mainRequest;
160
+ this._markResourceAsUsed(mainRequest);
161
+ const corsPreFlightRequest = resource.corsPreFlightRequest;
162
+ if (corsPreFlightRequest) {
163
+ this._addChildSpan(span, corsPreFlightRequest);
164
+ this._markResourceAsUsed(corsPreFlightRequest);
165
+ }
166
+ web.addSpanNetworkEvents(span, mainRequest);
167
+ }
168
+ }
169
+ /**
170
+ * Marks certain [resource]{@link PerformanceResourceTiming} when information
171
+ * from this is used to add events to span.
172
+ * This is done to avoid reusing the same resource again for next span
173
+ * @param resource
174
+ */
175
+ _markResourceAsUsed(resource) {
176
+ this._usedResources.add(resource);
177
+ }
178
+ /**
179
+ * Finish span, add attributes, network events etc.
180
+ * @param span
181
+ * @param spanData
182
+ * @param response
183
+ */
184
+ _endSpan(span, spanData, response) {
185
+ const endTime = core.hrTime();
186
+ this._addFinalSpanAttributes(span, response);
187
+ setTimeout(() => {
188
+ var _a;
189
+ (_a = spanData.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
190
+ this._findResourceAndAddNetworkEvents(span, spanData, endTime);
191
+ this._tasksCount--;
192
+ this._clearResources();
193
+ span.end(endTime);
194
+ }, OBSERVER_WAIT_TIME_MS);
195
+ }
196
+ /**
197
+ * Patches the constructor of fetch
198
+ */
199
+ _patchConstructor() {
200
+ return original => {
201
+ const plugin = this;
202
+ return function patchConstructor(...args) {
203
+ const self = this;
204
+ const url = parseUrl(args[0] instanceof Request ? args[0].url : args[0]).href;
205
+ const options = args[0] instanceof Request ? args[0] : args[1] || {};
206
+ const createdSpan = plugin._createSpan(url, options);
207
+ if (!createdSpan) {
208
+ return original.apply(this, args);
209
+ }
210
+ const spanData = plugin._prepareSpanData(url);
211
+ function endSpanOnError(span, error) {
212
+ plugin._applyAttributesAfterFetch(span, options, error);
213
+ plugin._endSpan(span, spanData, {
214
+ status: error.status || 0,
215
+ statusText: error.message,
216
+ url,
217
+ });
218
+ }
219
+ function endSpanOnSuccess(span, response) {
220
+ plugin._applyAttributesAfterFetch(span, options, response);
221
+ const spanResponse = {
222
+ status: response.status,
223
+ statusText: response.statusText,
224
+ headers: response.headers,
225
+ url
226
+ };
227
+ if (response.status >= 200 && response.status < 400) {
228
+ if (response.url != null && response.url !== '') {
229
+ spanResponse.url = url;
230
+ }
231
+ }
232
+ plugin._endSpan(span, spanData, {
233
+ status: response.status,
234
+ statusText: response.statusText,
235
+ url,
236
+ });
237
+ }
238
+ function onSuccess(span, resolve, response) {
239
+ try {
240
+ const resClone = response.clone();
241
+ const resClone4Hook = response.clone();
242
+ const body = resClone.body;
243
+ if (body) {
244
+ const reader = body.getReader();
245
+ const read = () => {
246
+ reader.read().then(({ done }) => {
247
+ if (done) {
248
+ endSpanOnSuccess(span, resClone4Hook);
249
+ }
250
+ else {
251
+ read();
252
+ }
253
+ }, error => {
254
+ endSpanOnError(span, error);
255
+ });
256
+ };
257
+ read();
258
+ }
259
+ else {
260
+ // some older browsers don't have .body implemented
261
+ endSpanOnSuccess(span, response);
262
+ }
263
+ }
264
+ finally {
265
+ resolve(response);
266
+ }
267
+ }
268
+ function onError(span, reject, error) {
269
+ try {
270
+ endSpanOnError(span, error);
271
+ }
272
+ finally {
273
+ reject(error);
274
+ }
275
+ }
276
+ return new Promise((resolve, reject) => {
277
+ return api.context.with(api.trace.setSpan(api.context.active(), createdSpan), () => {
278
+ plugin._addHeaders(options, url);
279
+ plugin._tasksCount++;
280
+ // TypeScript complains about arrow function captured a this typed as globalThis
281
+ // ts(7041)
282
+ return original
283
+ .apply(self, options instanceof Request ? [options] : [url, options])
284
+ .then(onSuccess.bind(self, createdSpan, resolve), onError.bind(self, createdSpan, reject));
285
+ });
286
+ });
287
+ };
288
+ };
289
+ }
290
+ _applyAttributesAfterFetch(span, request, result) {
291
+ const applyCustomAttributesOnSpan = this._getConfig()
292
+ .applyCustomAttributesOnSpan;
293
+ if (applyCustomAttributesOnSpan) {
294
+ safeExecuteInTheMiddle(() => applyCustomAttributesOnSpan(span, request, result), error => {
295
+ if (!error) {
296
+ return;
297
+ }
298
+ this._diag.error('applyCustomAttributesOnSpan', error);
299
+ }, true);
300
+ }
301
+ }
302
+ /**
303
+ * Prepares a span data - needed later for matching appropriate network
304
+ * resources
305
+ * @param spanUrl
306
+ */
307
+ _prepareSpanData(spanUrl) {
308
+ const startTime = core.hrTime();
309
+ const entries = [];
310
+ if (PerformanceObserver == null) {
311
+ return { entries, startTime, spanUrl };
312
+ }
313
+ const observer = new PerformanceObserver(list => {
314
+ const perfObsEntries = list.getEntries();
315
+ const parsedUrl = parseUrl(spanUrl);
316
+ perfObsEntries.forEach(entry => {
317
+ if (entry.initiatorType === 'fetch' &&
318
+ entry.name === parsedUrl.href) {
319
+ entries.push(entry);
320
+ }
321
+ });
322
+ });
323
+ observer.observe({
324
+ entryTypes: ['resource'],
325
+ });
326
+ return { entries, observer, startTime, spanUrl };
327
+ }
328
+ /**
329
+ * implements enable function
330
+ */
331
+ enable() {
332
+ if (isWrapped(fetch)) {
333
+ this._unwrap(_globalThis, 'fetch');
334
+ this._diag.debug('removing previous patch for constructor');
335
+ }
336
+ this._wrap(_globalThis, 'fetch', this._patchConstructor());
337
+ }
338
+ /**
339
+ * implements unpatch function
340
+ */
341
+ disable() {
342
+ this._unwrap(_globalThis, 'fetch');
343
+ this._usedResources = new WeakSet();
344
+ }
345
+ }
346
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/fetch.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as api from '@opentelemetry/api';\nimport {\n isWrapped,\n InstrumentationBase,\n InstrumentationConfig,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport * as core from '@opentelemetry/core';\nimport * as web from '@opentelemetry/sdk-trace-web';\nimport { AttributeNames } from './enums/AttributeNames';\nimport { SemanticAttributes } from '@opentelemetry/semantic-conventions';\nimport { FetchError, FetchResponse, SpanData } from './types';\nimport { VERSION } from './version';\nimport { _globalThis } from '@opentelemetry/core';\n\nfunction parseUrl(url: string): web.URLLike {\n const element = document.createElement('a');\n element.href = url;\n return element;\n}\n\n// how long to wait for observer to collect information about resources\n// this is needed as event \"load\" is called before observer\n// hard to say how long it should really wait, seems like 300ms is\n// safe enough\nconst OBSERVER_WAIT_TIME_MS = 300;\n\nexport interface FetchCustomAttributeFunction {\n (\n span: api.Span,\n request: Request | RequestInit,\n result: Response | FetchError\n ): void;\n}\n\n/**\n * FetchPlugin Config\n */\nexport interface FetchInstrumentationConfig extends InstrumentationConfig {\n // the number of timing resources is limited, after the limit\n // (chrome 250, safari 150) the information is not collected anymore\n // the only way to prevent that is to regularly clean the resources\n // whenever it is possible, this is needed only when PerformanceObserver\n // is not available\n clearTimingResources?: boolean;\n // urls which should include trace headers when origin doesn't match\n propagateTraceHeaderCorsUrls?: web.PropagateTraceHeaderCorsUrls;\n /**\n * URLs that partially match any regex in ignoreUrls will not be traced.\n * In addition, URLs that are _exact matches_ of strings in ignoreUrls will\n * also not be traced.\n */\n ignoreUrls?: Array<string | RegExp>;\n /** Function for adding custom attributes on the span */\n applyCustomAttributesOnSpan?: FetchCustomAttributeFunction;\n}\n\n/**\n * This class represents a fetch plugin for auto instrumentation\n */\nexport class FetchInstrumentation extends InstrumentationBase<Promise<Response>> {\n readonly component: string = 'fetch';\n readonly version: string = VERSION;\n moduleName = this.component;\n private _usedResources = new WeakSet<PerformanceResourceTiming>();\n private _tasksCount = 0;\n\n constructor(config?: FetchInstrumentationConfig) {\n super(\n '@opentelemetry/instrumentation-fetch',\n VERSION,\n config\n );\n }\n\n init(): void {}\n\n private _getConfig(): FetchInstrumentationConfig {\n return this._config;\n }\n\n /**\n * Add cors pre flight child span\n * @param span\n * @param corsPreFlightRequest\n */\n private _addChildSpan(\n span: api.Span,\n corsPreFlightRequest: PerformanceResourceTiming\n ): void {\n const childSpan = this.tracer.startSpan(\n 'CORS Preflight',\n {\n startTime: corsPreFlightRequest[web.PerformanceTimingNames.FETCH_START],\n },\n api.trace.setSpan(api.context.active(), span)\n );\n web.addSpanNetworkEvents(childSpan, corsPreFlightRequest);\n childSpan.end(\n corsPreFlightRequest[web.PerformanceTimingNames.RESPONSE_END]\n );\n }\n\n /**\n * Adds more attributes to span just before ending it\n * @param span\n * @param response\n */\n private _addFinalSpanAttributes(\n span: api.Span,\n response: FetchResponse\n ): void {\n const parsedUrl = parseUrl(response.url);\n span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status);\n if (response.statusText != null) {\n span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText);\n }\n span.setAttribute(SemanticAttributes.HTTP_HOST, parsedUrl.host);\n span.setAttribute(\n SemanticAttributes.HTTP_SCHEME,\n parsedUrl.protocol.replace(':', '')\n );\n span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent);\n }\n\n /**\n * Add headers\n * @param options\n * @param spanUrl\n */\n private _addHeaders(options: Request | RequestInit, spanUrl: string): void {\n if (\n !web.shouldPropagateTraceHeaders(\n spanUrl,\n this._getConfig().propagateTraceHeaderCorsUrls\n )\n ) {\n const headers: Partial<Record<string, unknown>> = {};\n api.propagation.inject(api.context.active(), headers);\n if (Object.keys(headers).length > 0) {\n this._diag.debug('headers inject skipped due to CORS policy');\n }\n return;\n }\n\n if (options instanceof Request) {\n api.propagation.inject(api.context.active(), options.headers, {\n set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),\n });\n } else if (options.headers instanceof Headers) {\n api.propagation.inject(api.context.active(), options.headers, {\n set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),\n });\n } else {\n const headers: Partial<Record<string, unknown>> = {};\n api.propagation.inject(api.context.active(), headers);\n options.headers = Object.assign({}, headers, options.headers || {});\n }\n }\n\n /**\n * Clears the resource timings and all resources assigned with spans\n * when {@link FetchPluginConfig.clearTimingResources} is\n * set to true (default false)\n * @private\n */\n private _clearResources() {\n if (this._tasksCount === 0 && this._getConfig().clearTimingResources) {\n performance.clearResourceTimings();\n this._usedResources = new WeakSet<PerformanceResourceTiming>();\n }\n }\n\n /**\n * Creates a new span\n * @param url\n * @param options\n */\n private _createSpan(\n url: string,\n options: Partial<Request | RequestInit> = {}\n ): api.Span | undefined {\n if (core.isUrlIgnored(url, this._getConfig().ignoreUrls)) {\n this._diag.debug('ignoring span as url matches ignored url');\n return;\n }\n const method = (options.method || 'GET').toUpperCase();\n const spanName = `HTTP ${method}`;\n return this.tracer.startSpan(spanName, {\n kind: api.SpanKind.CLIENT,\n attributes: {\n [AttributeNames.COMPONENT]: this.moduleName,\n [SemanticAttributes.HTTP_METHOD]: method,\n [SemanticAttributes.HTTP_URL]: url,\n },\n });\n }\n\n /**\n * Finds appropriate resource and add network events to the span\n * @param span\n * @param resourcesObserver\n * @param endTime\n */\n private _findResourceAndAddNetworkEvents(\n span: api.Span,\n resourcesObserver: SpanData,\n endTime: api.HrTime\n ): void {\n let resources: PerformanceResourceTiming[] = resourcesObserver.entries;\n if (!resources.length) {\n if (!performance.getEntriesByType) {\n return;\n }\n // fallback - either Observer is not available or it took longer\n // then OBSERVER_WAIT_TIME_MS and observer didn't collect enough\n // information\n resources = performance.getEntriesByType(\n 'resource'\n ) as PerformanceResourceTiming[];\n }\n const resource = web.getResource(\n resourcesObserver.spanUrl,\n resourcesObserver.startTime,\n endTime,\n resources,\n this._usedResources,\n 'fetch'\n );\n\n if (resource.mainRequest) {\n const mainRequest = resource.mainRequest;\n this._markResourceAsUsed(mainRequest);\n\n const corsPreFlightRequest = resource.corsPreFlightRequest;\n if (corsPreFlightRequest) {\n this._addChildSpan(span, corsPreFlightRequest);\n this._markResourceAsUsed(corsPreFlightRequest);\n }\n web.addSpanNetworkEvents(span, mainRequest);\n }\n }\n\n /**\n * Marks certain [resource]{@link PerformanceResourceTiming} when information\n * from this is used to add events to span.\n * This is done to avoid reusing the same resource again for next span\n * @param resource\n */\n private _markResourceAsUsed(resource: PerformanceResourceTiming): void {\n this._usedResources.add(resource);\n }\n\n /**\n * Finish span, add attributes, network events etc.\n * @param span\n * @param spanData\n * @param response\n */\n private _endSpan(\n span: api.Span,\n spanData: SpanData,\n response: FetchResponse\n ) {\n const endTime = core.hrTime();\n this._addFinalSpanAttributes(span, response);\n\n setTimeout(() => {\n spanData.observer?.disconnect();\n this._findResourceAndAddNetworkEvents(span, spanData, endTime);\n this._tasksCount--;\n this._clearResources();\n span.end(endTime);\n }, OBSERVER_WAIT_TIME_MS);\n }\n\n /**\n * Patches the constructor of fetch\n */\n private _patchConstructor(): (original: typeof fetch) => typeof fetch {\n return original => {\n const plugin = this;\n return function patchConstructor(\n this: typeof globalThis,\n ...args: Parameters<typeof fetch>\n ): Promise<Response> {\n const self = this;\n const url = parseUrl(args[0] instanceof Request ? args[0].url : args[0]).href;\n\n const options = args[0] instanceof Request ? args[0] : args[1] || {};\n const createdSpan = plugin._createSpan(url, options);\n if (!createdSpan) {\n return original.apply(this, args);\n }\n const spanData = plugin._prepareSpanData(url);\n\n function endSpanOnError(span: api.Span, error: FetchError) {\n plugin._applyAttributesAfterFetch(span, options, error);\n plugin._endSpan(span, spanData, {\n status: error.status || 0,\n statusText: error.message,\n url,\n });\n }\n\n function endSpanOnSuccess(span: api.Span, response: Response) {\n plugin._applyAttributesAfterFetch(span, options, response);\n const spanResponse = {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n url\n };\n if (response.status >= 200 && response.status < 400) {\n if (response.url != null && response.url !== '') {\n spanResponse.url = url;\n }\n }\n plugin._endSpan(span, spanData, {\n status: response.status,\n statusText: response.statusText,\n url,\n });\n }\n\n function onSuccess(\n span: api.Span,\n resolve: (value: Response | PromiseLike<Response>) => void,\n response: Response\n ): void {\n try {\n const resClone = response.clone();\n const resClone4Hook = response.clone();\n const body = resClone.body;\n if (body) {\n const reader = body.getReader();\n const read = (): void => {\n reader.read().then(\n ({ done }) => {\n if (done) {\n endSpanOnSuccess(span, resClone4Hook);\n } else {\n read();\n }\n },\n error => {\n endSpanOnError(span, error);\n }\n );\n };\n read();\n } else {\n // some older browsers don't have .body implemented\n endSpanOnSuccess(span, response);\n }\n } finally {\n resolve(response);\n }\n }\n\n function onError(\n span: api.Span,\n reject: (reason?: unknown) => void,\n error: FetchError\n ) {\n try {\n endSpanOnError(span, error);\n } finally {\n reject(error);\n }\n }\n\n return new Promise((resolve, reject) => {\n return api.context.with(\n api.trace.setSpan(api.context.active(), createdSpan),\n () => {\n plugin._addHeaders(options, url);\n plugin._tasksCount++;\n // TypeScript complains about arrow function captured a this typed as globalThis\n // ts(7041)\n return original\n .apply(self, options instanceof Request ? [options] : [url, options])\n .then(\n onSuccess.bind(self, createdSpan, resolve),\n onError.bind(self, createdSpan, reject)\n );\n }\n );\n });\n };\n };\n }\n\n private _applyAttributesAfterFetch(\n span: api.Span,\n request: Request | RequestInit,\n result: Response | FetchError\n ) {\n const applyCustomAttributesOnSpan = this._getConfig()\n .applyCustomAttributesOnSpan;\n if (applyCustomAttributesOnSpan) {\n safeExecuteInTheMiddle(\n () => applyCustomAttributesOnSpan(span, request, result),\n error => {\n if (!error) {\n return;\n }\n\n this._diag.error('applyCustomAttributesOnSpan', error);\n },\n true\n );\n }\n }\n\n /**\n * Prepares a span data - needed later for matching appropriate network\n * resources\n * @param spanUrl\n */\n private _prepareSpanData(spanUrl: string): SpanData {\n const startTime = core.hrTime();\n const entries: PerformanceResourceTiming[] = [];\n if (PerformanceObserver == null) {\n return { entries, startTime, spanUrl };\n }\n\n const observer: PerformanceObserver = new PerformanceObserver(list => {\n const perfObsEntries = list.getEntries() as PerformanceResourceTiming[];\n const parsedUrl = parseUrl(spanUrl);\n perfObsEntries.forEach(entry => {\n if (\n entry.initiatorType === 'fetch' &&\n entry.name === parsedUrl.href\n ) {\n entries.push(entry);\n }\n });\n });\n observer.observe({\n entryTypes: ['resource'],\n });\n return { entries, observer, startTime, spanUrl };\n }\n\n /**\n * implements enable function\n */\n override enable(): void {\n if (isWrapped(fetch)) {\n this._unwrap(_globalThis, 'fetch');\n this._diag.debug('removing previous patch for constructor');\n }\n this._wrap(_globalThis, 'fetch', this._patchConstructor());\n }\n\n /**\n * implements unpatch function\n */\n override disable(): void {\n this._unwrap(_globalThis, 'fetch');\n this._usedResources = new WeakSet<PerformanceResourceTiming>();\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export * from './fetch';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright The OpenTelemetry Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export * from './fetch';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,SAAS,CAAC","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport * from './fetch';\n"]}
@@ -0,0 +1,27 @@
1
+ import * as api from '@opentelemetry/api';
2
+ /**
3
+ * Interface used to provide information to finish span on fetch response
4
+ */
5
+ export interface FetchResponse {
6
+ status: number;
7
+ statusText?: string;
8
+ url: string;
9
+ }
10
+ /**
11
+ * Interface used to provide information to finish span on fetch error
12
+ */
13
+ export interface FetchError {
14
+ status?: number;
15
+ message: string;
16
+ }
17
+ /**
18
+ * Interface used to keep information about span between creating and
19
+ * ending span
20
+ */
21
+ export interface SpanData {
22
+ entries: PerformanceResourceTiming[];
23
+ observer?: PerformanceObserver;
24
+ spanUrl: string;
25
+ startTime: api.HrTime;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright The OpenTelemetry Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as api from '@opentelemetry/api';\n\n/**\n * Interface used to provide information to finish span on fetch response\n */\nexport interface FetchResponse {\n status: number;\n statusText?: string;\n url: string;\n}\n\n/**\n * Interface used to provide information to finish span on fetch error\n */\nexport interface FetchError {\n status?: number;\n message: string;\n}\n\n/**\n * Interface used to keep information about span between creating and\n * ending span\n */\nexport interface SpanData {\n entries: PerformanceResourceTiming[];\n observer?: PerformanceObserver;\n spanUrl: string;\n startTime: api.HrTime;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const VERSION = "0.28.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1,18 @@
1
+ /*
2
+ * Copyright The OpenTelemetry Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // this is autogenerated file, see scripts/version-update.js
17
+ export const VERSION = '0.28.0';
18
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// this is autogenerated file, see scripts/version-update.js\nexport const VERSION = '0.28.0';\n"]}
@@ -23,6 +23,12 @@ const web = require("@opentelemetry/sdk-trace-web");
23
23
  const AttributeNames_1 = require("./enums/AttributeNames");
24
24
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
25
25
  const version_1 = require("./version");
26
+ const core_1 = require("@opentelemetry/core");
27
+ function parseUrl(url) {
28
+ const element = document.createElement('a');
29
+ element.href = url;
30
+ return element;
31
+ }
26
32
  // how long to wait for observer to collect information about resources
27
33
  // this is needed as event "load" is called before observer
28
34
  // hard to say how long it should really wait, seems like 300ms is
@@ -62,7 +68,7 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
62
68
  * @param response
63
69
  */
64
70
  _addFinalSpanAttributes(span, response) {
65
- const parsedUrl = web.parseUrl(response.url);
71
+ const parsedUrl = parseUrl(response.url);
66
72
  span.setAttribute(semantic_conventions_1.SemanticAttributes.HTTP_STATUS_CODE, response.status);
67
73
  if (response.statusText != null) {
68
74
  span.setAttribute(AttributeNames_1.AttributeNames.HTTP_STATUS_TEXT, response.statusText);
@@ -197,7 +203,8 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
197
203
  return original => {
198
204
  const plugin = this;
199
205
  return function patchConstructor(...args) {
200
- const url = args[0] instanceof Request ? args[0].url : args[0];
206
+ const self = this;
207
+ const url = parseUrl(args[0] instanceof Request ? args[0].url : args[0]).href;
201
208
  const options = args[0] instanceof Request ? args[0] : args[1] || {};
202
209
  const createdSpan = plugin._createSpan(url, options);
203
210
  if (!createdSpan) {
@@ -214,16 +221,22 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
214
221
  }
215
222
  function endSpanOnSuccess(span, response) {
216
223
  plugin._applyAttributesAfterFetch(span, options, response);
224
+ const spanResponse = {
225
+ status: response.status,
226
+ statusText: response.statusText,
227
+ headers: response.headers,
228
+ url
229
+ };
217
230
  if (response.status >= 200 && response.status < 400) {
218
- plugin._endSpan(span, spanData, response);
219
- }
220
- else {
221
- plugin._endSpan(span, spanData, {
222
- status: response.status,
223
- statusText: response.statusText,
224
- url,
225
- });
231
+ if (response.url != null && response.url !== '') {
232
+ spanResponse.url = url;
233
+ }
226
234
  }
235
+ plugin._endSpan(span, spanData, {
236
+ status: response.status,
237
+ statusText: response.statusText,
238
+ url,
239
+ });
227
240
  }
228
241
  function onSuccess(span, resolve, response) {
229
242
  try {
@@ -267,9 +280,11 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
267
280
  return api.context.with(api.trace.setSpan(api.context.active(), createdSpan), () => {
268
281
  plugin._addHeaders(options, url);
269
282
  plugin._tasksCount++;
283
+ // TypeScript complains about arrow function captured a this typed as globalThis
284
+ // ts(7041)
270
285
  return original
271
- .apply(this, options instanceof Request ? [options] : [url, options])
272
- .then(onSuccess.bind(this, createdSpan, resolve), onError.bind(this, createdSpan, reject));
286
+ .apply(self, options instanceof Request ? [options] : [url, options])
287
+ .then(onSuccess.bind(self, createdSpan, resolve), onError.bind(self, createdSpan, reject));
273
288
  });
274
289
  });
275
290
  };
@@ -279,7 +294,7 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
279
294
  const applyCustomAttributesOnSpan = this._getConfig()
280
295
  .applyCustomAttributesOnSpan;
281
296
  if (applyCustomAttributesOnSpan) {
282
- instrumentation_1.safeExecuteInTheMiddle(() => applyCustomAttributesOnSpan(span, request, result), error => {
297
+ (0, instrumentation_1.safeExecuteInTheMiddle)(() => applyCustomAttributesOnSpan(span, request, result), error => {
283
298
  if (!error) {
284
299
  return;
285
300
  }
@@ -295,16 +310,15 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
295
310
  _prepareSpanData(spanUrl) {
296
311
  const startTime = core.hrTime();
297
312
  const entries = [];
298
- if (typeof window.PerformanceObserver === 'undefined') {
313
+ if (PerformanceObserver == null) {
299
314
  return { entries, startTime, spanUrl };
300
315
  }
301
316
  const observer = new PerformanceObserver(list => {
302
317
  const perfObsEntries = list.getEntries();
303
- const urlNormalizingAnchor = web.getUrlNormalizingAnchor();
304
- urlNormalizingAnchor.href = spanUrl;
318
+ const parsedUrl = parseUrl(spanUrl);
305
319
  perfObsEntries.forEach(entry => {
306
320
  if (entry.initiatorType === 'fetch' &&
307
- entry.name === urlNormalizingAnchor.href) {
321
+ entry.name === parsedUrl.href) {
308
322
  entries.push(entry);
309
323
  }
310
324
  });
@@ -318,17 +332,17 @@ class FetchInstrumentation extends instrumentation_1.InstrumentationBase {
318
332
  * implements enable function
319
333
  */
320
334
  enable() {
321
- if (instrumentation_1.isWrapped(window.fetch)) {
322
- this._unwrap(window, 'fetch');
335
+ if ((0, instrumentation_1.isWrapped)(fetch)) {
336
+ this._unwrap(core_1._globalThis, 'fetch');
323
337
  this._diag.debug('removing previous patch for constructor');
324
338
  }
325
- this._wrap(window, 'fetch', this._patchConstructor());
339
+ this._wrap(core_1._globalThis, 'fetch', this._patchConstructor());
326
340
  }
327
341
  /**
328
342
  * implements unpatch function
329
343
  */
330
344
  disable() {
331
- this._unwrap(window, 'fetch');
345
+ this._unwrap(core_1._globalThis, 'fetch');
332
346
  this._usedResources = new WeakSet();
333
347
  }
334
348
  }