@opentelemetry/browser-instrumentation 0.7.0 → 0.8.1

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.
Files changed (50) hide show
  1. package/README.md +93 -0
  2. package/dist/console/instrumentation.d.ts +1 -2
  3. package/dist/console/instrumentation.js.map +1 -1
  4. package/dist/console/types.d.ts +2 -3
  5. package/dist/errors/instrumentation.d.ts +1 -2
  6. package/dist/errors/instrumentation.js.map +1 -1
  7. package/dist/errors/types.d.ts +2 -3
  8. package/dist/fetch/index.d.ts +3 -0
  9. package/dist/fetch/index.js +2 -0
  10. package/dist/fetch/instrumentation.d.ts +35 -0
  11. package/dist/fetch/instrumentation.js +223 -0
  12. package/dist/fetch/instrumentation.js.map +1 -0
  13. package/dist/fetch/semconv.js +13 -0
  14. package/dist/fetch/semconv.js.map +1 -0
  15. package/dist/fetch/types.d.ts +51 -0
  16. package/dist/navigation/instrumentation.d.ts +1 -2
  17. package/dist/navigation/instrumentation.js.map +1 -1
  18. package/dist/navigation/types.d.ts +4 -5
  19. package/dist/navigation-timing/instrumentation.d.ts +1 -2
  20. package/dist/navigation-timing/instrumentation.js.map +1 -1
  21. package/dist/navigation-timing/types.d.ts +1 -2
  22. package/dist/package.js +1 -1
  23. package/dist/resource-timing/instrumentation.d.ts +1 -2
  24. package/dist/resource-timing/instrumentation.js +12 -11
  25. package/dist/resource-timing/instrumentation.js.map +1 -1
  26. package/dist/resource-timing/types.d.ts +1 -2
  27. package/dist/user-action/instrumentation.d.ts +1 -2
  28. package/dist/user-action/instrumentation.js.map +1 -1
  29. package/dist/user-action/types.d.ts +3 -4
  30. package/dist/utils/ContextRegistry.js +77 -0
  31. package/dist/utils/ContextRegistry.js.map +1 -0
  32. package/dist/utils/NetworkContextRegistry.js +23 -0
  33. package/dist/utils/NetworkContextRegistry.js.map +1 -0
  34. package/dist/utils/getElementCSSSelector.js.map +1 -1
  35. package/dist/utils/request.js +131 -0
  36. package/dist/utils/request.js.map +1 -0
  37. package/dist/utils/url.js +36 -0
  38. package/dist/utils/url.js.map +1 -0
  39. package/dist/web-vitals/instrumentation.d.ts +1 -2
  40. package/dist/web-vitals/instrumentation.js.map +1 -1
  41. package/dist/web-vitals/types.d.ts +1 -2
  42. package/dist/xhr/index.d.ts +3 -0
  43. package/dist/xhr/index.js +2 -0
  44. package/dist/xhr/instrumentation.d.ts +38 -0
  45. package/dist/xhr/instrumentation.js +187 -0
  46. package/dist/xhr/instrumentation.js.map +1 -0
  47. package/dist/xhr/semconv.js +13 -0
  48. package/dist/xhr/semconv.js.map +1 -0
  49. package/dist/xhr/types.d.ts +22 -0
  50. package/package.json +21 -8
package/README.md CHANGED
@@ -20,6 +20,7 @@ npm install @opentelemetry/browser-instrumentation
20
20
  - [Web Vitals](#web-vitals) — automatic instrumentation for Core Web Vitals
21
21
  - [Console](#console) — automatic instrumentation for console API calls (log, warn, error, info, debug)
22
22
  - [Errors](#errors) — automatic instrumentation for unhandled errors and promise rejections
23
+ - [Fetch](#fetch) — automatic instrumentation for request using the `fetch` API
23
24
 
24
25
  ## Usage
25
26
 
@@ -300,6 +301,98 @@ Each `exception` event includes:
300
301
  | `exception.message` | The error's `message`, or the thrown string itself. |
301
302
  | `exception.stacktrace` | The error's `stack` (omitted when the thrown value is a string). |
302
303
 
304
+ ---
305
+
306
+ ### Fetch
307
+
308
+ ```typescript
309
+ import { FetchInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/fetch';
310
+ ```
311
+
312
+ Emits a Span for every HTTP request made using the `window.fetch` browser API. This instrumentation also propagates the context for distributed traces.
313
+
314
+ #### Configuration
315
+
316
+ ```typescript
317
+ new FetchInstrumentation({
318
+ ignoreUrls: ['https://example.com/api'],
319
+ propagateTraceHeaderCorsUrls: [/domain.com\/api.*/],
320
+ measureRequestSize: true,
321
+ applyCustomAttributesOnSpan: (span, request, result) => {
322
+ span.setAttribute('foo', 'bar');
323
+ },
324
+ });
325
+ ```
326
+
327
+ | Option | Type | Default | Description |
328
+ |--------|------|---------|-------------|
329
+ | `ignoreUrls` | `Array<string \| RegExp>` | — | List of URLs that we do not want its requests instrumented. You can use strings for a single match or regular expressions to match many. |
330
+ | `propagateTraceHeaderCorsUrls` | `Array<string \| RegExp>` | — | List of cross origin URLs to appens trace context headers. You can use strings for a single match or regular expressions to match many. |
331
+ | `measureRequestSize` | `boolean` | — | Set it to `true` to add the request size as a Span attribute. |
332
+ | `sanitizeUrl` | `(url: string) => string` | — | Returns the given URL with sensitive fields `REDACTED`. |
333
+ | `requestHook` | `(span: Span, request: Request) => void` | — | Function that allows you to interact with the Span and the Request object before the request starts. |
334
+ | `applyCustomAttributesOnSpan` | `(span: Span, request: Request, result: Response \| FetchError) => void` | — | Function that allows you to interact with the Span once the request is finished. |
335
+
336
+ #### Captured Attributes
337
+
338
+ Each `fetch` Span includes:
339
+
340
+ | Attribute | Description |
341
+ |-----------|-------------|
342
+ | `http.request.method` | The method of the request in uppercase (GET, POST, PUT, PATCH, QUERY). |
343
+ | `http.request.method_original` | Original HTTP method sent by the client in the request line.. |
344
+ | `url.full` | Absolute URL describing a network resource according to RFC3986 (sanitized if passing `sanitizeUrl` config option). |
345
+ | `server.address` | The hostmane of the request's URL. |
346
+ | `server.port` | The port of the request's URL. |
347
+ | `http.response.status_code` | HTTP response status code. |
348
+ | `error.type` | If request failed. Describes a class of error the operation ended with. |
349
+
350
+ ---
351
+
352
+ ### XHR (XmlHttpRequest)
353
+
354
+ ```typescript
355
+ import { XhrInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/xhr';
356
+ ```
357
+
358
+ Emits a Span for every HTTP request made using the `window.XmlHttpRequest` browser API. This instrumentation also propagates the context for distributed traces.
359
+
360
+ #### Configuration
361
+
362
+ ```typescript
363
+ new XhrInstrumentation({
364
+ ignoreUrls: ['https://example.com/api'],
365
+ propagateTraceHeaderCorsUrls: [/domain.com\/api.*/],
366
+ measureRequestSize: true,
367
+ applyCustomAttributesOnSpan: (span, xhr) => {
368
+ span.setAttribute('foo', 'bar');
369
+ },
370
+ });
371
+ ```
372
+
373
+ | Option | Type | Default | Description |
374
+ |--------|------|---------|-------------|
375
+ | `ignoreUrls` | `Array<string \| RegExp>` | — | List of URLs that we do not want its requests instrumented. You can use strings for a single match or regular expressions to match many. |
376
+ | `propagateTraceHeaderCorsUrls` | `Array<string \| RegExp>` | — | List of cross origin URLs to appens trace context headers. You can use strings for a single match or regular expressions to match many. |
377
+ | `measureRequestSize` | `boolean` | — | Set it to `true` to add the request size as a Span attribute. |
378
+ | `sanitizeUrl` | `(url: string) => string` | — | Returns the given URL with sensitive fields `REDACTED`. |
379
+ | `applyCustomAttributesOnSpan` | `(span: Span, request: Request, result: Response \| FetchError) => void` | — | Function that allows you to interact with the Span once the request is finished. |
380
+
381
+ #### Captured Attributes
382
+
383
+ Each `fetch` Span includes:
384
+
385
+ | Attribute | Description |
386
+ |-----------|-------------|
387
+ | `http.request.method` | The method of the request in uppercase (GET, POST, PUT, PATCH, QUERY). |
388
+ | `http.request.method_original` | Original HTTP method sent by the client in the request line.. |
389
+ | `url.full` | Absolute URL describing a network resource according to RFC3986 (sanitized if passing `sanitizeUrl` config option). |
390
+ | `server.address` | The hostmane of the request's URL. |
391
+ | `server.port` | The port of the request's URL. |
392
+ | `http.response.status_code` | HTTP response status code. |
393
+ | `error.type` | If request failed. Describes a class of error the operation ended with. |
394
+
395
+
303
396
  ## Useful links
304
397
 
305
398
  - For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
@@ -4,7 +4,7 @@ import { InstrumentationBase } from "@opentelemetry/instrumentation";
4
4
  /**
5
5
  * OpenTelemetry instrumentation that captures console calls and emits them as OpenTelemetry logs.
6
6
  */
7
- declare class ConsoleInstrumentation extends InstrumentationBase<ConsoleInstrumentationConfig> {
7
+ export declare class ConsoleInstrumentation extends InstrumentationBase<ConsoleInstrumentationConfig> {
8
8
  private _isPatched;
9
9
  private _active;
10
10
  constructor(config?: ConsoleInstrumentationConfig);
@@ -16,5 +16,4 @@ declare class ConsoleInstrumentation extends InstrumentationBase<ConsoleInstrume
16
16
  disable(): void;
17
17
  }
18
18
  //#endregion
19
- export { ConsoleInstrumentation };
20
19
  //# sourceMappingURL=instrumentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/console/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { context } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { version } from '../../package.json' with { type: 'json' };\nimport { ATTR_CONSOLE_METHOD, CONSOLE_LOG_EVENT_NAME } from './semconv.ts';\nimport type { ConsoleInstrumentationConfig, ConsoleMethod } from './types.ts';\n\nconst DEFAULT_LOG_METHODS: ConsoleMethod[] = [\n 'log',\n 'warn',\n 'error',\n 'info',\n 'debug',\n];\n\nconst SEVERITY_MAP: Record<ConsoleMethod, SeverityNumber> = {\n debug: SeverityNumber.DEBUG,\n log: SeverityNumber.INFO,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n};\n\n/**\n * Default serializer for console arguments.\n * Joins arguments as strings.\n */\nfunction defaultMessageSerializer(args: unknown[]): string {\n return args\n .map((arg) => {\n if (typeof arg === 'object' && arg !== null) {\n try {\n return JSON.stringify(arg);\n } catch {\n // Circular reference or other error, fallback to String\n return String(arg);\n }\n }\n return String(arg);\n })\n .join(' ');\n}\n\n/**\n * OpenTelemetry instrumentation that captures console calls and emits them as OpenTelemetry logs.\n */\nexport class ConsoleInstrumentation extends InstrumentationBase<ConsoleInstrumentationConfig> {\n private declare _isPatched: boolean;\n private declare _active: boolean;\n\n constructor(config: ConsoleInstrumentationConfig = {}) {\n super('@opentelemetry/browser-instrumentation/console', version, config);\n }\n\n protected override init() {\n return [];\n }\n\n private _getMessageSerializer(): (args: unknown[]) => string {\n return this._config.messageSerializer ?? defaultMessageSerializer;\n }\n\n private _getLogMethods(): ConsoleMethod[] {\n return this._config.logMethods ?? DEFAULT_LOG_METHODS;\n }\n\n private _patchConsoleMethod(\n method: ConsoleMethod,\n ): (original: Console[ConsoleMethod]) => Console[ConsoleMethod] {\n const instrumentation = this;\n\n return function patchConsoleMethod(original: Console[ConsoleMethod]) {\n return function (this: Console, ...args: unknown[]) {\n if (\n instrumentation._active &&\n instrumentation._getLogMethods().includes(method)\n ) {\n const logContext = context.active();\n const body = instrumentation._getMessageSerializer()(args);\n\n instrumentation.logger.emit({\n body,\n eventName: CONSOLE_LOG_EVENT_NAME,\n severityNumber: SEVERITY_MAP[method],\n severityText: method,\n context: logContext,\n attributes: {\n [ATTR_CONSOLE_METHOD]: method,\n },\n });\n }\n\n return original.apply(this, args);\n } as Console[ConsoleMethod];\n };\n }\n\n override enable(): void {\n this._active = true;\n if (this._isPatched) {\n return;\n }\n this._isPatched = true;\n for (const method of DEFAULT_LOG_METHODS) {\n if (typeof console[method] === 'function') {\n this._wrap(console, method, this._patchConsoleMethod(method));\n }\n }\n }\n\n override disable(): void {\n this._active = false;\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,sBAAuC;CAC3C;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,eAAsD;CAC1D,OAAO,eAAe;CACtB,KAAK,eAAe;CACpB,MAAM,eAAe;CACrB,MAAM,eAAe;CACrB,OAAO,eAAe;AACxB;;;;;AAMA,SAAS,yBAAyB,MAAyB;CACzD,OAAO,KACJ,KAAK,QAAQ;EACZ,IAAI,OAAO,QAAQ,YAAY,QAAQ,MACrC,IAAI;GACF,OAAO,KAAK,UAAU,GAAG;EAC3B,QAAQ;GAEN,OAAO,OAAO,GAAG;EACnB;EAEF,OAAO,OAAO,GAAG;CACnB,CAAC,CAAC,CACD,KAAK,GAAG;AACb;;;;AAKA,IAAa,yBAAb,cAA4C,oBAAkD;CAI5F,YAAY,SAAuC,CAAC,GAAG;EACrD,MAAM,kDAAkD,SAAS,MAAM;CACzE;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,wBAA6D;EAC3D,OAAO,KAAK,QAAQ,qBAAqB;CAC3C;CAEA,iBAA0C;EACxC,OAAO,KAAK,QAAQ,cAAc;CACpC;CAEA,oBACE,QAC8D;EAC9D,MAAM,kBAAkB;EAExB,OAAO,SAAS,mBAAmB,UAAkC;GACnE,OAAO,SAAyB,GAAG,MAAiB;IAClD,IACE,gBAAgB,WAChB,gBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,GAChD;KACA,MAAM,aAAa,QAAQ,OAAO;KAClC,MAAM,OAAO,gBAAgB,sBAAsB,CAAC,CAAC,IAAI;KAEzD,gBAAgB,OAAO,KAAK;MAC1B;MACA,WAAW;MACX,gBAAgB,aAAa;MAC7B,cAAc;MACd,SAAS;MACT,YAAY,GACT,sBAAsB,OACzB;KACF,CAAC;IACH;IAEA,OAAO,SAAS,MAAM,MAAM,IAAI;GAClC;EACF;CACF;CAEA,SAAwB;EACtB,KAAK,UAAU;EACf,IAAI,KAAK,YACP;EAEF,KAAK,aAAa;EAClB,KAAK,MAAM,UAAU,qBACnB,IAAI,OAAO,QAAQ,YAAY,YAC7B,KAAK,MAAM,SAAS,QAAQ,KAAK,oBAAoB,MAAM,CAAC;CAGlE;CAEA,UAAyB;EACvB,KAAK,UAAU;CACjB;AACF"}
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/console/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { context } from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { version } from '../../package.json' with { type: 'json' };\nimport { ATTR_CONSOLE_METHOD, CONSOLE_LOG_EVENT_NAME } from './semconv.ts';\nimport type { ConsoleInstrumentationConfig, ConsoleMethod } from './types.ts';\n\nconst DEFAULT_LOG_METHODS: ConsoleMethod[] = [\n 'log',\n 'warn',\n 'error',\n 'info',\n 'debug',\n];\n\nconst SEVERITY_MAP: Record<ConsoleMethod, SeverityNumber> = {\n debug: SeverityNumber.DEBUG,\n log: SeverityNumber.INFO,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n};\n\n/**\n * Default serializer for console arguments.\n * Joins arguments as strings.\n */\nfunction defaultMessageSerializer(args: unknown[]): string {\n return args\n .map((arg) => {\n if (typeof arg === 'object' && arg !== null) {\n try {\n return JSON.stringify(arg);\n } catch {\n // Circular reference or other error, fallback to String\n return String(arg);\n }\n }\n return String(arg);\n })\n .join(' ');\n}\n\n/**\n * OpenTelemetry instrumentation that captures console calls and emits them as OpenTelemetry logs.\n */\nexport class ConsoleInstrumentation extends InstrumentationBase<ConsoleInstrumentationConfig> {\n declare private _isPatched: boolean;\n declare private _active: boolean;\n\n constructor(config: ConsoleInstrumentationConfig = {}) {\n super('@opentelemetry/browser-instrumentation/console', version, config);\n }\n\n protected override init() {\n return [];\n }\n\n private _getMessageSerializer(): (args: unknown[]) => string {\n return this._config.messageSerializer ?? defaultMessageSerializer;\n }\n\n private _getLogMethods(): ConsoleMethod[] {\n return this._config.logMethods ?? DEFAULT_LOG_METHODS;\n }\n\n private _patchConsoleMethod(\n method: ConsoleMethod,\n ): (original: Console[ConsoleMethod]) => Console[ConsoleMethod] {\n const instrumentation = this;\n\n return function patchConsoleMethod(original: Console[ConsoleMethod]) {\n return function (this: Console, ...args: unknown[]) {\n if (\n instrumentation._active &&\n instrumentation._getLogMethods().includes(method)\n ) {\n const logContext = context.active();\n const body = instrumentation._getMessageSerializer()(args);\n\n instrumentation.logger.emit({\n body,\n eventName: CONSOLE_LOG_EVENT_NAME,\n severityNumber: SEVERITY_MAP[method],\n severityText: method,\n context: logContext,\n attributes: {\n [ATTR_CONSOLE_METHOD]: method,\n },\n });\n }\n\n return original.apply(this, args);\n } as Console[ConsoleMethod];\n };\n }\n\n override enable(): void {\n this._active = true;\n if (this._isPatched) {\n return;\n }\n this._isPatched = true;\n for (const method of DEFAULT_LOG_METHODS) {\n if (typeof console[method] === 'function') {\n this._wrap(console, method, this._patchConsoleMethod(method));\n }\n }\n }\n\n override disable(): void {\n this._active = false;\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,sBAAuC;CAC3C;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,eAAsD;CAC1D,OAAO,eAAe;CACtB,KAAK,eAAe;CACpB,MAAM,eAAe;CACrB,MAAM,eAAe;CACrB,OAAO,eAAe;AACxB;;;;;AAMA,SAAS,yBAAyB,MAAyB;CACzD,OAAO,KACJ,KAAK,QAAQ;EACZ,IAAI,OAAO,QAAQ,YAAY,QAAQ,MACrC,IAAI;GACF,OAAO,KAAK,UAAU,GAAG;EAC3B,QAAQ;GAEN,OAAO,OAAO,GAAG;EACnB;EAEF,OAAO,OAAO,GAAG;CACnB,CAAC,CAAC,CACD,KAAK,GAAG;AACb;;;;AAKA,IAAa,yBAAb,cAA4C,oBAAkD;CAI5F,YAAY,SAAuC,CAAC,GAAG;EACrD,MAAM,kDAAkD,SAAS,MAAM;CACzE;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,wBAA6D;EAC3D,OAAO,KAAK,QAAQ,qBAAqB;CAC3C;CAEA,iBAA0C;EACxC,OAAO,KAAK,QAAQ,cAAc;CACpC;CAEA,oBACE,QAC8D;EAC9D,MAAM,kBAAkB;EAExB,OAAO,SAAS,mBAAmB,UAAkC;GACnE,OAAO,SAAyB,GAAG,MAAiB;IAClD,IACE,gBAAgB,WAChB,gBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,GAChD;KACA,MAAM,aAAa,QAAQ,OAAO;KAClC,MAAM,OAAO,gBAAgB,sBAAsB,CAAC,CAAC,IAAI;KAEzD,gBAAgB,OAAO,KAAK;MAC1B;MACA,WAAW;MACX,gBAAgB,aAAa;MAC7B,cAAc;MACd,SAAS;MACT,YAAY,GACT,sBAAsB,OACzB;KACF,CAAC;IACH;IAEA,OAAO,SAAS,MAAM,MAAM,IAAI;GAClC;EACF;CACF;CAEA,SAAwB;EACtB,KAAK,UAAU;EACf,IAAI,KAAK,YACP;EAEF,KAAK,aAAa;EAClB,KAAK,MAAM,UAAU,qBACnB,IAAI,OAAO,QAAQ,YAAY,YAC7B,KAAK,MAAM,SAAS,QAAQ,KAAK,oBAAoB,MAAM,CAAC;CAGlE;CAEA,UAAyB;EACvB,KAAK,UAAU;CACjB;AACF"}
@@ -3,11 +3,11 @@ import { InstrumentationConfig } from "@opentelemetry/instrumentation";
3
3
  /**
4
4
  * Console methods that can be instrumented.
5
5
  */
6
- type ConsoleMethod = 'log' | 'warn' | 'error' | 'info' | 'debug';
6
+ export type ConsoleMethod = 'log' | 'warn' | 'error' | 'info' | 'debug';
7
7
  /**
8
8
  * ConsoleInstrumentation Configuration
9
9
  */
10
- interface ConsoleInstrumentationConfig extends InstrumentationConfig {
10
+ export interface ConsoleInstrumentationConfig extends InstrumentationConfig {
11
11
  /**
12
12
  * Console methods to instrument.
13
13
  * @default ['log', 'warn', 'error', 'info', 'debug']
@@ -20,5 +20,4 @@ interface ConsoleInstrumentationConfig extends InstrumentationConfig {
20
20
  messageSerializer?: (args: unknown[]) => string;
21
21
  }
22
22
  //#endregion
23
- export { ConsoleInstrumentationConfig, ConsoleMethod };
24
23
  //# sourceMappingURL=types.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { ErrorsInstrumentationConfig } from "./types.js";
2
2
  import { InstrumentationBase } from "@opentelemetry/instrumentation";
3
3
  //#region src/errors/instrumentation.d.ts
4
- declare class ErrorsInstrumentation extends InstrumentationBase<ErrorsInstrumentationConfig> {
4
+ export declare class ErrorsInstrumentation extends InstrumentationBase<ErrorsInstrumentationConfig> {
5
5
  private _isEnabled;
6
6
  private _onErrorHandler?;
7
7
  constructor(config?: ErrorsInstrumentationConfig);
@@ -12,5 +12,4 @@ declare class ErrorsInstrumentation extends InstrumentationBase<ErrorsInstrument
12
12
  private _applyCustomAttributes;
13
13
  }
14
14
  //#endregion
15
- export { ErrorsInstrumentation };
16
15
  //# sourceMappingURL=instrumentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/errors/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Attributes } from '@opentelemetry/api';\nimport type { AnyValueMap, LogRecord } from '@opentelemetry/api-logs';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport {\n InstrumentationBase,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport {\n ATTR_EXCEPTION_MESSAGE,\n ATTR_EXCEPTION_STACKTRACE,\n ATTR_EXCEPTION_TYPE,\n} from '@opentelemetry/semantic-conventions';\nimport { version } from '../../package.json' with { type: 'json' };\nimport type { ErrorsInstrumentationConfig } from './types.ts';\n\nconst EXCEPTION_EVENT_NAME = 'exception';\n\nexport class ErrorsInstrumentation extends InstrumentationBase<ErrorsInstrumentationConfig> {\n // Use `declare` to prevent JS class field initializers from running after\n // super(), which would reset values set by the enable() call that\n // InstrumentationBase makes during its constructor.\n private declare _isEnabled: boolean;\n private declare _onErrorHandler?: (\n event: ErrorEvent | PromiseRejectionEvent,\n ) => void;\n\n constructor(config: ErrorsInstrumentationConfig = {}) {\n super('@opentelemetry/browser-instrumentation/errors', version, config);\n }\n\n protected override init() {\n return [];\n }\n\n override enable(): void {\n if (this._isEnabled) {\n return;\n }\n this._isEnabled = true;\n\n this._onErrorHandler = (event) => this._onError(event);\n window.addEventListener('error', this._onErrorHandler);\n window.addEventListener('unhandledrejection', this._onErrorHandler);\n }\n\n override disable(): void {\n if (!this._isEnabled) {\n return;\n }\n this._isEnabled = false;\n\n if (this._onErrorHandler) {\n window.removeEventListener('error', this._onErrorHandler);\n window.removeEventListener('unhandledrejection', this._onErrorHandler);\n this._onErrorHandler = undefined;\n }\n }\n\n private _onError(event: ErrorEvent | PromiseRejectionEvent): void {\n const isRejection = 'reason' in event;\n let error: Error | string | null | undefined = isRejection\n ? event.reason\n : event.error;\n\n // Cross-origin scripts deliver an ErrorEvent with `event.error` null but\n // a populated `event.message` (the canonical \"Script error.\" sanitized\n // message). Treat that message as a string error so these events still\n // surface as an exception log instead of being silently dropped.\n // PromiseRejectionEvent has no analogous message field, so it stays a\n // no-op.\n if (\n error == null &&\n !isRejection &&\n typeof event.message === 'string' &&\n event.message.length > 0\n ) {\n error = event.message;\n }\n\n if (error == null) {\n // Emit a diag debug message so the drop is observable. Anything that\n // lands here had no usable signal (ErrorEvent with no error and no\n // message, or PromiseRejectionEvent rejected with null/undefined). Pass\n // the raw value too so the reader can tell null from undefined when\n // investigating a missing capture.\n this._diag.debug(\n isRejection\n ? 'ignored unhandledrejection event with no reason'\n : 'ignored error event with no error and no message',\n error,\n );\n return;\n }\n\n // Capture as a const so the type narrowing survives into the closure below.\n const capturedError = error;\n\n // This runs inside a global error listener, so an escaping throw would\n // re-trigger the listener and loop. Both attribute extraction (a rejection\n // can carry a value whose `stack` getter throws) and emit (a broken\n // LogRecordProcessor) can throw, so contain the whole path.\n safeExecuteInTheMiddle(\n () => {\n let errorAttributes: AnyValueMap;\n if (typeof capturedError === 'string') {\n errorAttributes = { [ATTR_EXCEPTION_MESSAGE]: capturedError };\n } else {\n errorAttributes = {\n [ATTR_EXCEPTION_TYPE]: capturedError.name,\n [ATTR_EXCEPTION_MESSAGE]: capturedError.message,\n [ATTR_EXCEPTION_STACKTRACE]: capturedError.stack,\n };\n }\n\n const customAttributes = this._applyCustomAttributes(capturedError);\n\n const logRecord: LogRecord = {\n eventName: EXCEPTION_EVENT_NAME,\n severityNumber: SeverityNumber.ERROR,\n attributes: { ...errorAttributes, ...customAttributes },\n };\n\n this.logger.emit(logRecord);\n },\n (err) => {\n if (err) {\n this._diag.error('failed to record exception', err);\n }\n },\n true,\n );\n }\n\n private _applyCustomAttributes(error: Error | string): Attributes {\n const hook = this.getConfig().applyCustomAttributes;\n if (!hook) {\n return {};\n }\n let result: Attributes = {};\n safeExecuteInTheMiddle(\n () => {\n result = hook(error);\n },\n (err) => {\n if (err) {\n this._diag.error('applyCustomAttributes hook failed', err);\n }\n },\n true,\n );\n return result;\n }\n}\n"],"mappings":";;;;;AAoBA,MAAM,uBAAuB;AAE7B,IAAa,wBAAb,cAA2C,oBAAiD;CAS1F,YAAY,SAAsC,CAAC,GAAG;EACpD,MAAM,iDAAiD,SAAS,MAAM;CACxE;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,SAAwB;EACtB,IAAI,KAAK,YACP;EAEF,KAAK,aAAa;EAElB,KAAK,mBAAmB,UAAU,KAAK,SAAS,KAAK;EACrD,OAAO,iBAAiB,SAAS,KAAK,eAAe;EACrD,OAAO,iBAAiB,sBAAsB,KAAK,eAAe;CACpE;CAEA,UAAyB;EACvB,IAAI,CAAC,KAAK,YACR;EAEF,KAAK,aAAa;EAElB,IAAI,KAAK,iBAAiB;GACxB,OAAO,oBAAoB,SAAS,KAAK,eAAe;GACxD,OAAO,oBAAoB,sBAAsB,KAAK,eAAe;GACrE,KAAK,kBAAkB,KAAA;EACzB;CACF;CAEA,SAAiB,OAAiD;EAChE,MAAM,cAAc,YAAY;EAChC,IAAI,QAA2C,cAC3C,MAAM,SACN,MAAM;EAQV,IACE,SAAS,QACT,CAAC,eACD,OAAO,MAAM,YAAY,YACzB,MAAM,QAAQ,SAAS,GAEvB,QAAQ,MAAM;EAGhB,IAAI,SAAS,MAAM;GAMjB,KAAK,MAAM,MACT,cACI,oDACA,oDACJ,KACF;GACA;EACF;EAGA,MAAM,gBAAgB;EAMtB,6BACQ;GACJ,IAAI;GACJ,IAAI,OAAO,kBAAkB,UAC3B,kBAAkB,GAAG,yBAAyB,cAAc;QAE5D,kBAAkB;KACf,sBAAsB,cAAc;KACpC,yBAAyB,cAAc;KACvC,4BAA4B,cAAc;GAC7C;GAGF,MAAM,mBAAmB,KAAK,uBAAuB,aAAa;GAElE,MAAM,YAAuB;IAC3B,WAAW;IACX,gBAAgB,eAAe;IAC/B,YAAY;KAAE,GAAG;KAAiB,GAAG;IAAiB;GACxD;GAEA,KAAK,OAAO,KAAK,SAAS;EAC5B,IACC,QAAQ;GACP,IAAI,KACF,KAAK,MAAM,MAAM,8BAA8B,GAAG;EAEtD,GACA,IACF;CACF;CAEA,uBAA+B,OAAmC;EAChE,MAAM,OAAO,KAAK,UAAU,CAAC,CAAC;EAC9B,IAAI,CAAC,MACH,OAAO,CAAC;EAEV,IAAI,SAAqB,CAAC;EAC1B,6BACQ;GACJ,SAAS,KAAK,KAAK;EACrB,IACC,QAAQ;GACP,IAAI,KACF,KAAK,MAAM,MAAM,qCAAqC,GAAG;EAE7D,GACA,IACF;EACA,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/errors/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Attributes } from '@opentelemetry/api';\nimport type { AnyValueMap, LogRecord } from '@opentelemetry/api-logs';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport {\n InstrumentationBase,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport {\n ATTR_EXCEPTION_MESSAGE,\n ATTR_EXCEPTION_STACKTRACE,\n ATTR_EXCEPTION_TYPE,\n} from '@opentelemetry/semantic-conventions';\nimport { version } from '../../package.json' with { type: 'json' };\nimport type { ErrorsInstrumentationConfig } from './types.ts';\n\nconst EXCEPTION_EVENT_NAME = 'exception';\n\nexport class ErrorsInstrumentation extends InstrumentationBase<ErrorsInstrumentationConfig> {\n // Use `declare` to prevent JS class field initializers from running after\n // super(), which would reset values set by the enable() call that\n // InstrumentationBase makes during its constructor.\n declare private _isEnabled: boolean;\n declare private _onErrorHandler?: (\n event: ErrorEvent | PromiseRejectionEvent,\n ) => void;\n\n constructor(config: ErrorsInstrumentationConfig = {}) {\n super('@opentelemetry/browser-instrumentation/errors', version, config);\n }\n\n protected override init() {\n return [];\n }\n\n override enable(): void {\n if (this._isEnabled) {\n return;\n }\n this._isEnabled = true;\n\n this._onErrorHandler = (event) => this._onError(event);\n window.addEventListener('error', this._onErrorHandler);\n window.addEventListener('unhandledrejection', this._onErrorHandler);\n }\n\n override disable(): void {\n if (!this._isEnabled) {\n return;\n }\n this._isEnabled = false;\n\n if (this._onErrorHandler) {\n window.removeEventListener('error', this._onErrorHandler);\n window.removeEventListener('unhandledrejection', this._onErrorHandler);\n this._onErrorHandler = undefined;\n }\n }\n\n private _onError(event: ErrorEvent | PromiseRejectionEvent): void {\n const isRejection = 'reason' in event;\n let error: Error | string | null | undefined = isRejection\n ? event.reason\n : event.error;\n\n // Cross-origin scripts deliver an ErrorEvent with `event.error` null but\n // a populated `event.message` (the canonical \"Script error.\" sanitized\n // message). Treat that message as a string error so these events still\n // surface as an exception log instead of being silently dropped.\n // PromiseRejectionEvent has no analogous message field, so it stays a\n // no-op.\n if (\n error == null &&\n !isRejection &&\n typeof event.message === 'string' &&\n event.message.length > 0\n ) {\n error = event.message;\n }\n\n if (error == null) {\n // Emit a diag debug message so the drop is observable. Anything that\n // lands here had no usable signal (ErrorEvent with no error and no\n // message, or PromiseRejectionEvent rejected with null/undefined). Pass\n // the raw value too so the reader can tell null from undefined when\n // investigating a missing capture.\n this._diag.debug(\n isRejection\n ? 'ignored unhandledrejection event with no reason'\n : 'ignored error event with no error and no message',\n error,\n );\n return;\n }\n\n // Capture as a const so the type narrowing survives into the closure below.\n const capturedError = error;\n\n // This runs inside a global error listener, so an escaping throw would\n // re-trigger the listener and loop. Both attribute extraction (a rejection\n // can carry a value whose `stack` getter throws) and emit (a broken\n // LogRecordProcessor) can throw, so contain the whole path.\n safeExecuteInTheMiddle(\n () => {\n let errorAttributes: AnyValueMap;\n if (typeof capturedError === 'string') {\n errorAttributes = { [ATTR_EXCEPTION_MESSAGE]: capturedError };\n } else {\n errorAttributes = {\n [ATTR_EXCEPTION_TYPE]: capturedError.name,\n [ATTR_EXCEPTION_MESSAGE]: capturedError.message,\n [ATTR_EXCEPTION_STACKTRACE]: capturedError.stack,\n };\n }\n\n const customAttributes = this._applyCustomAttributes(capturedError);\n\n const logRecord: LogRecord = {\n eventName: EXCEPTION_EVENT_NAME,\n severityNumber: SeverityNumber.ERROR,\n attributes: { ...errorAttributes, ...customAttributes },\n };\n\n this.logger.emit(logRecord);\n },\n (err) => {\n if (err) {\n this._diag.error('failed to record exception', err);\n }\n },\n true,\n );\n }\n\n private _applyCustomAttributes(error: Error | string): Attributes {\n const hook = this.getConfig().applyCustomAttributes;\n if (!hook) {\n return {};\n }\n let result: Attributes = {};\n safeExecuteInTheMiddle(\n () => {\n result = hook(error);\n },\n (err) => {\n if (err) {\n this._diag.error('applyCustomAttributes hook failed', err);\n }\n },\n true,\n );\n return result;\n }\n}\n"],"mappings":";;;;;AAoBA,MAAM,uBAAuB;AAE7B,IAAa,wBAAb,cAA2C,oBAAiD;CAS1F,YAAY,SAAsC,CAAC,GAAG;EACpD,MAAM,iDAAiD,SAAS,MAAM;CACxE;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,SAAwB;EACtB,IAAI,KAAK,YACP;EAEF,KAAK,aAAa;EAElB,KAAK,mBAAmB,UAAU,KAAK,SAAS,KAAK;EACrD,OAAO,iBAAiB,SAAS,KAAK,eAAe;EACrD,OAAO,iBAAiB,sBAAsB,KAAK,eAAe;CACpE;CAEA,UAAyB;EACvB,IAAI,CAAC,KAAK,YACR;EAEF,KAAK,aAAa;EAElB,IAAI,KAAK,iBAAiB;GACxB,OAAO,oBAAoB,SAAS,KAAK,eAAe;GACxD,OAAO,oBAAoB,sBAAsB,KAAK,eAAe;GACrE,KAAK,kBAAkB,KAAA;EACzB;CACF;CAEA,SAAiB,OAAiD;EAChE,MAAM,cAAc,YAAY;EAChC,IAAI,QAA2C,cAC3C,MAAM,SACN,MAAM;EAQV,IACE,SAAS,QACT,CAAC,eACD,OAAO,MAAM,YAAY,YACzB,MAAM,QAAQ,SAAS,GAEvB,QAAQ,MAAM;EAGhB,IAAI,SAAS,MAAM;GAMjB,KAAK,MAAM,MACT,cACI,oDACA,oDACJ,KACF;GACA;EACF;EAGA,MAAM,gBAAgB;EAMtB,6BACQ;GACJ,IAAI;GACJ,IAAI,OAAO,kBAAkB,UAC3B,kBAAkB,GAAG,yBAAyB,cAAc;QAE5D,kBAAkB;KACf,sBAAsB,cAAc;KACpC,yBAAyB,cAAc;KACvC,4BAA4B,cAAc;GAC7C;GAGF,MAAM,mBAAmB,KAAK,uBAAuB,aAAa;GAElE,MAAM,YAAuB;IAC3B,WAAW;IACX,gBAAgB,eAAe;IAC/B,YAAY;KAAE,GAAG;KAAiB,GAAG;IAAiB;GACxD;GAEA,KAAK,OAAO,KAAK,SAAS;EAC5B,IACC,QAAQ;GACP,IAAI,KACF,KAAK,MAAM,MAAM,8BAA8B,GAAG;EAEtD,GACA,IACF;CACF;CAEA,uBAA+B,OAAmC;EAChE,MAAM,OAAO,KAAK,UAAU,CAAC,CAAC;EAC9B,IAAI,CAAC,MACH,OAAO,CAAC;EAEV,IAAI,SAAqB,CAAC;EAC1B,6BACQ;GACJ,SAAS,KAAK,KAAK;EACrB,IACC,QAAQ;GACP,IAAI,KACF,KAAK,MAAM,MAAM,qCAAqC,GAAG;EAE7D,GACA,IACF;EACA,OAAO;CACT;AACF"}
@@ -1,11 +1,11 @@
1
1
  import { Attributes } from "@opentelemetry/api";
2
2
  import { InstrumentationConfig } from "@opentelemetry/instrumentation";
3
3
  //#region src/errors/types.d.ts
4
- type ApplyCustomAttributesFunction = (error: Error | string) => Attributes;
4
+ export type ApplyCustomAttributesFunction = (error: Error | string) => Attributes;
5
5
  /**
6
6
  * ErrorsInstrumentation Configuration
7
7
  */
8
- interface ErrorsInstrumentationConfig extends InstrumentationConfig {
8
+ export interface ErrorsInstrumentationConfig extends InstrumentationConfig {
9
9
  /**
10
10
  * Optional callback invoked for each captured error or unhandled rejection.
11
11
  * Returned attributes are merged onto the emitted log record (after the
@@ -14,5 +14,4 @@ interface ErrorsInstrumentationConfig extends InstrumentationConfig {
14
14
  applyCustomAttributes?: ApplyCustomAttributesFunction;
15
15
  }
16
16
  //#endregion
17
- export { ApplyCustomAttributesFunction, ErrorsInstrumentationConfig };
18
17
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,3 @@
1
+ import { FetchCustomAttributeFunction, FetchError, FetchInstrumentationConfig, FetchRequestHookFunction, FetchResponse } from "./types.js";
2
+ import { FetchInstrumentation } from "./instrumentation.js";
3
+ export { type FetchCustomAttributeFunction, type FetchError, FetchInstrumentation, type FetchInstrumentationConfig, type FetchRequestHookFunction, type FetchResponse };
@@ -0,0 +1,2 @@
1
+ import { FetchInstrumentation } from "./instrumentation.js";
2
+ export { FetchInstrumentation };
@@ -0,0 +1,35 @@
1
+ import { FetchInstrumentationConfig } from "./types.js";
2
+ import { InstrumentationBase } from "@opentelemetry/instrumentation";
3
+ //#region src/fetch/instrumentation.d.ts
4
+ export declare class FetchInstrumentation extends InstrumentationBase<FetchInstrumentationConfig> {
5
+ private _isEnabled;
6
+ private _isFetchPatched;
7
+ private _spanResources;
8
+ constructor(config?: FetchInstrumentationConfig);
9
+ protected init(): never[];
10
+ enable(): void;
11
+ disable(): void;
12
+ /**
13
+ * Patches the constructor of fetch
14
+ */
15
+ private _patchConstructor;
16
+ /**
17
+ * Creates a new span
18
+ */
19
+ private _createSpan;
20
+ /**
21
+ * Finish span, add attributes, etc.
22
+ */
23
+ private _endSpan;
24
+ private _applyAttributesAfterFetch;
25
+ /**
26
+ * Calls the request hook if defined
27
+ */
28
+ private _callRequestHook;
29
+ /**
30
+ * Add headers for the request and the given context if apply
31
+ */
32
+ private _addHeaders;
33
+ }
34
+ //#endregion
35
+ //# sourceMappingURL=instrumentation.d.ts.map
@@ -0,0 +1,223 @@
1
+ import { version } from "../package.js";
2
+ import { getNetworkContextRegistry } from "../utils/NetworkContextRegistry.js";
3
+ import { getFetchBodyLength, normalizeHttpRequestMethod } from "../utils/request.js";
4
+ import { matchesUrl, parseUrl, serverPortFromUrl } from "../utils/url.js";
5
+ import { ATTR_HTTP_REQUEST_BODY_SIZE } from "./semconv.js";
6
+ import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
7
+ import { InstrumentationBase, safeExecuteInTheMiddle } from "@opentelemetry/instrumentation";
8
+ import { ATTR_ERROR_TYPE, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_REQUEST_METHOD_ORIGINAL, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
9
+ //#region src/fetch/instrumentation.ts
10
+ const hasBrowserPerformanceAPI = typeof PerformanceObserver !== "undefined";
11
+ var FetchInstrumentation = class extends InstrumentationBase {
12
+ _spanResources = /* @__PURE__ */ new Map();
13
+ constructor(config = {}) {
14
+ super("@opentelemetry/browser-instrumentation/fetch", version, config);
15
+ }
16
+ init() {
17
+ return [];
18
+ }
19
+ enable() {
20
+ if (!hasBrowserPerformanceAPI) {
21
+ this._diag.warn("this instrumentation is intended for web usage only, it does not instrument server-side fetch()");
22
+ return;
23
+ }
24
+ if (this._isEnabled) return;
25
+ if (this._isFetchPatched) {
26
+ this._diag.debug("fetch constructor already patched");
27
+ this._isEnabled = true;
28
+ return;
29
+ }
30
+ try {
31
+ this._wrap(globalThis, "fetch", this._patchConstructor());
32
+ this._isFetchPatched = true;
33
+ this._isEnabled = true;
34
+ } catch (err) {
35
+ this._diag.warn("Failed to patch globalThis.fetch; instrumentation will not be enabled. Another script may have locked globalThis.fetch via Object.defineProperty.", err);
36
+ }
37
+ }
38
+ disable() {
39
+ if (!hasBrowserPerformanceAPI) return;
40
+ if (!this._isEnabled) return;
41
+ this._isEnabled = false;
42
+ }
43
+ /**
44
+ * Patches the constructor of fetch
45
+ */
46
+ _patchConstructor() {
47
+ return (original) => {
48
+ const instrumentation = this;
49
+ return function patchConstructor(...args) {
50
+ if (!instrumentation._isEnabled) return original.apply(this, args);
51
+ let trucatedSpan;
52
+ try {
53
+ const instrConfig = instrumentation.getConfig();
54
+ const url = parseUrl(args[0] instanceof Request ? args[0].url : String(args[0])).href;
55
+ if (matchesUrl(url, instrConfig.ignoreUrls)) return original.apply(this, args);
56
+ let options;
57
+ if (args[0] instanceof Request) options = args[1] != null ? new Request(args[0], args[1]) : args[0];
58
+ else options = args[1] || {};
59
+ const createdSpan = instrumentation._createSpan(url, options);
60
+ trucatedSpan = createdSpan;
61
+ if (instrConfig.measureRequestSize) getFetchBodyLength(...args).then((bodyLength) => {
62
+ if (typeof bodyLength === "number") createdSpan.setAttribute(ATTR_HTTP_REQUEST_BODY_SIZE, bodyLength);
63
+ }).catch((error) => {
64
+ instrumentation._diag.warn("getFetchBodyLength", error);
65
+ });
66
+ function endSpanOnError(span, error) {
67
+ instrumentation._applyAttributesAfterFetch(span, options, error);
68
+ instrumentation._endSpan(span, {
69
+ status: error.status,
70
+ error: error.name || error.message,
71
+ aborted: error.name === "AbortError"
72
+ });
73
+ }
74
+ function endSpanOnSuccess(span, response) {
75
+ instrumentation._applyAttributesAfterFetch(span, options, response);
76
+ instrumentation._endSpan(span, response);
77
+ }
78
+ function onSuccess(span, response) {
79
+ try {
80
+ const body = response.clone().body;
81
+ if (body) {
82
+ const reader = body.getReader();
83
+ const read = () => {
84
+ reader.read().then(({ done }) => {
85
+ try {
86
+ if (done) endSpanOnSuccess(span, response);
87
+ else read();
88
+ } catch (e) {
89
+ instrumentation._diag.error("Failed to finalize span after body read", e);
90
+ instrumentation._endSpan(span, { status: response.status });
91
+ }
92
+ }, (error) => {
93
+ try {
94
+ endSpanOnError(span, {
95
+ name: error?.name,
96
+ message: error?.message,
97
+ status: response.status
98
+ });
99
+ } catch (e) {
100
+ instrumentation._diag.error("Failed to end span after body read rejected", e);
101
+ instrumentation._endSpan(span, { status: response.status });
102
+ }
103
+ });
104
+ };
105
+ read();
106
+ } else endSpanOnSuccess(span, response);
107
+ } catch (error) {
108
+ instrumentation._diag.error("Failed to read fetch response body", error);
109
+ instrumentation._endSpan(span, { status: response.status });
110
+ }
111
+ return response;
112
+ }
113
+ function onError(span, error) {
114
+ try {
115
+ endSpanOnError(span, error);
116
+ } catch (e) {
117
+ instrumentation._diag.error("Failed to end span on fetch error", e);
118
+ instrumentation._endSpan(span, {});
119
+ }
120
+ throw error;
121
+ }
122
+ const fetchContext = trace.setSpan(context.active(), createdSpan);
123
+ return context.with(fetchContext, () => {
124
+ instrumentation._spanResources.set(createdSpan, {
125
+ name: url,
126
+ fetchStart: performance.now()
127
+ });
128
+ instrumentation._callRequestHook(createdSpan, options);
129
+ instrumentation._addHeaders(options, url, fetchContext);
130
+ return original.apply(globalThis, options instanceof Request ? [options] : [url, options]).then(onSuccess.bind(globalThis, createdSpan), onError.bind(globalThis, createdSpan));
131
+ });
132
+ } catch (e) {
133
+ instrumentation._diag.error("Failed to instrument fetch request", e);
134
+ }
135
+ if (trucatedSpan) instrumentation._spanResources.delete(trucatedSpan);
136
+ return original.apply(this, args);
137
+ };
138
+ };
139
+ }
140
+ /**
141
+ * Creates a new span
142
+ */
143
+ _createSpan(url, options = {}) {
144
+ const attributes = {};
145
+ const parsedUrl = parseUrl(url);
146
+ const origMethod = options.method;
147
+ const normMethod = normalizeHttpRequestMethod(options.method || "GET");
148
+ const serverPort = serverPortFromUrl(parsedUrl);
149
+ const name = normMethod;
150
+ const { sanitizeUrl } = this.getConfig();
151
+ attributes[ATTR_HTTP_REQUEST_METHOD] = normMethod;
152
+ attributes[ATTR_URL_FULL] = sanitizeUrl ? sanitizeUrl(url) : url;
153
+ attributes[ATTR_SERVER_ADDRESS] = parsedUrl.hostname;
154
+ if (serverPort) attributes[ATTR_SERVER_PORT] = serverPort;
155
+ if (origMethod && origMethod !== normMethod) attributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = origMethod;
156
+ return this.tracer.startSpan(name, {
157
+ kind: SpanKind.CLIENT,
158
+ attributes
159
+ });
160
+ }
161
+ /**
162
+ * Finish span, add attributes, etc.
163
+ */
164
+ _endSpan(span, response) {
165
+ if (response.status !== void 0) span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);
166
+ if (!response.aborted) {
167
+ if (response.status === void 0 || response.status >= 400 || response.error !== void 0) {
168
+ span.setStatus({ code: SpanStatusCode.ERROR });
169
+ const errorType = response.error ?? (response.status !== void 0 ? String(response.status) : void 0);
170
+ if (typeof errorType === "string") span.setAttribute(ATTR_ERROR_TYPE, errorType);
171
+ }
172
+ }
173
+ span.end();
174
+ const resource = this._spanResources.get(span);
175
+ if (resource) {
176
+ this._spanResources.delete(span);
177
+ getNetworkContextRegistry().register(span, {
178
+ key: resource.name,
179
+ startPerfNow: resource.fetchStart,
180
+ endPerfNow: performance.now()
181
+ });
182
+ }
183
+ }
184
+ _applyAttributesAfterFetch(span, request, result) {
185
+ const applyCustomAttributesOnSpan = this.getConfig().applyCustomAttributesOnSpan;
186
+ if (applyCustomAttributesOnSpan) safeExecuteInTheMiddle(() => applyCustomAttributesOnSpan(span, request, result), (error) => {
187
+ if (!error) return;
188
+ this._diag.error("applyCustomAttributesOnSpan", error);
189
+ }, true);
190
+ }
191
+ /**
192
+ * Calls the request hook if defined
193
+ */
194
+ _callRequestHook(span, request) {
195
+ const requestHook = this.getConfig().requestHook;
196
+ if (requestHook) safeExecuteInTheMiddle(() => requestHook(span, request), (error) => {
197
+ if (error) this._diag.error("requestHook", error);
198
+ }, true);
199
+ }
200
+ /**
201
+ * Add headers for the request and the given context if apply
202
+ */
203
+ _addHeaders(options, url, ctx) {
204
+ const urlsToPropagate = this.getConfig().propagateTraceHeaderCorsUrls;
205
+ const urlOrigin = parseUrl(url).origin;
206
+ if (location.origin === urlOrigin || matchesUrl(url, urlsToPropagate)) {
207
+ if (options instanceof Request) propagation.inject(ctx, options.headers, { set: (h, k, v) => h.set(k, typeof v === "string" ? v : String(v)) });
208
+ else {
209
+ const headers = new Headers(options.headers);
210
+ propagation.inject(ctx, headers, { set: (h, k, v) => h.set(k, typeof v === "string" ? v : String(v)) });
211
+ options.headers = headers;
212
+ }
213
+ } else {
214
+ const headers = {};
215
+ propagation.inject(ctx, headers);
216
+ if (Object.keys(headers).length > 0) this._diag.debug("headers inject skipped due to CORS policy");
217
+ }
218
+ }
219
+ };
220
+ //#endregion
221
+ export { FetchInstrumentation };
222
+
223
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/fetch/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { Attributes, Context, Span } from '@opentelemetry/api';\nimport {\n context,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace,\n} from '@opentelemetry/api';\nimport {\n InstrumentationBase,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport {\n ATTR_ERROR_TYPE,\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_REQUEST_METHOD_ORIGINAL,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_FULL,\n} from '@opentelemetry/semantic-conventions';\nimport { version } from '../../package.json' with { type: 'json' };\nimport { getNetworkContextRegistry } from '../utils/NetworkContextRegistry.ts';\nimport {\n getFetchBodyLength,\n normalizeHttpRequestMethod,\n} from '../utils/request.ts';\nimport { matchesUrl, parseUrl, serverPortFromUrl } from '../utils/url.ts';\nimport { ATTR_HTTP_REQUEST_BODY_SIZE } from './semconv.ts';\nimport type {\n FetchError,\n FetchInstrumentationConfig,\n FetchResponse,\n} from './types.ts';\n\nconst hasBrowserPerformanceAPI = typeof PerformanceObserver !== 'undefined';\n\nexport class FetchInstrumentation extends InstrumentationBase<FetchInstrumentationConfig> {\n // Note: Intentionally *not* using `_enabled` as the field name to avoid\n // any possible confusion with the `_enabled` field used on the *Node.js*\n // InstrumentationBase class.\n // Also not initializing the fields to `false` because the base class\n // constructor already call `enable` modifying their values and it will\n // set the instrumentations in a base state (enabled, patched but with flags set to false)\n declare private _isEnabled: boolean;\n declare private _isFetchPatched: boolean;\n\n // To keep track of the resources\n private _spanResources: Map<Span, PerformanceResourceTiming> = new Map();\n\n constructor(config: FetchInstrumentationConfig = {}) {\n super('@opentelemetry/browser-instrumentation/fetch', version, config);\n }\n\n protected override init() {\n return [];\n }\n\n override enable(): void {\n if (!hasBrowserPerformanceAPI) {\n this._diag.warn(\n 'this instrumentation is intended for web usage only, it does not instrument server-side fetch()',\n );\n return;\n }\n\n if (this._isEnabled) {\n return;\n }\n\n if (this._isFetchPatched) {\n this._diag.debug('fetch constructor already patched');\n this._isEnabled = true;\n return;\n }\n\n try {\n // `_wrap` throws if a third-party script has locked globalThis.fetch via\n // Object.defineProperty(window, 'fetch', { writable: false, ... }).\n this._wrap(globalThis, 'fetch', this._patchConstructor());\n this._isFetchPatched = true;\n this._isEnabled = true;\n } catch (err) {\n this._diag.warn(\n 'Failed to patch globalThis.fetch; instrumentation will not be enabled. ' +\n 'Another script may have locked globalThis.fetch via Object.defineProperty.',\n err,\n );\n }\n }\n\n override disable(): void {\n if (!hasBrowserPerformanceAPI) {\n return;\n }\n\n if (!this._isEnabled) {\n return;\n }\n this._isEnabled = false;\n }\n\n /**\n * Patches the constructor of fetch\n */\n private _patchConstructor(): (original: typeof fetch) => typeof fetch {\n return (original) => {\n const instrumentation = this;\n\n return function patchConstructor(\n this: typeof globalThis,\n ...args: Parameters<typeof fetch>\n ): Promise<Response> {\n if (!instrumentation._isEnabled) {\n return original.apply(this, args);\n }\n\n // Reference to the Span in case there is an exception after creating it (wil not end)\n // It will be used to keep the Map only with the Spans that will be ended\n let trucatedSpan: Span | undefined;\n try {\n const instrConfig = instrumentation.getConfig();\n const url = parseUrl(\n args[0] instanceof Request ? args[0].url : String(args[0]),\n ).href;\n\n const shouldIgnoreUrl = matchesUrl(url, instrConfig.ignoreUrls);\n if (shouldIgnoreUrl) {\n return original.apply(this, args);\n }\n\n // Per the Fetch spec, when fetch() is called with a Request object\n // and a separate init object, the init properties override the\n // Request's properties. Merge them into a new Request so that\n // downstream consumers (hooks, header injection, the actual fetch\n // call) see the correct final values.\n // See: https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#parameters\n let options: Request | RequestInit;\n if (args[0] instanceof Request) {\n options = args[1] != null ? new Request(args[0], args[1]) : args[0];\n } else {\n options = args[1] || {};\n }\n\n const createdSpan = instrumentation._createSpan(url, options);\n trucatedSpan = createdSpan;\n if (instrConfig.measureRequestSize) {\n getFetchBodyLength(...args)\n .then((bodyLength) => {\n if (typeof bodyLength === 'number') {\n createdSpan.setAttribute(\n ATTR_HTTP_REQUEST_BODY_SIZE,\n bodyLength,\n );\n }\n })\n .catch((error) => {\n instrumentation._diag.warn('getFetchBodyLength', error);\n });\n }\n\n function endSpanOnError(span: Span, error: FetchError) {\n instrumentation._applyAttributesAfterFetch(span, options, error);\n instrumentation._endSpan(span, {\n status: error.status,\n error: error.name || error.message,\n aborted: error.name === 'AbortError',\n });\n }\n\n function endSpanOnSuccess(span: Span, response: Response) {\n instrumentation._applyAttributesAfterFetch(span, options, response);\n instrumentation._endSpan(span, response);\n }\n\n function onSuccess(span: Span, response: Response): Response {\n try {\n // Clone the response and eagerly consume the clone to detect\n // when the body transfer completes. The original response is\n // returned to the caller untouched so that it passes internal\n // brand-checks required by APIs such as\n // WebAssembly.compileStreaming.\n // It consumes the entire body even if the user cancels reading it\n // from the original response. But it does work with `AbortController.abort()`\n // because it aborts the underlying fetch cancelling the original and clone streams\n // ref: https://github.com/open-telemetry/opentelemetry-js/pull/6521\n const resClone = response.clone();\n const body = resClone.body;\n if (body) {\n const reader = body.getReader();\n\n const read = (): void => {\n reader.read().then(\n ({ done }) => {\n try {\n if (done) {\n endSpanOnSuccess(span, response);\n } else {\n read();\n }\n } catch (e) {\n instrumentation._diag.error(\n 'Failed to finalize span after body read',\n e,\n );\n instrumentation._endSpan(span, {\n status: response.status,\n });\n }\n },\n (error) => {\n try {\n endSpanOnError(span, {\n name: error?.name,\n message: error?.message,\n status: response.status,\n });\n } catch (e) {\n instrumentation._diag.error(\n 'Failed to end span after body read rejected',\n e,\n );\n instrumentation._endSpan(span, {\n status: response.status,\n });\n }\n },\n );\n };\n read();\n } else {\n // some older browsers don't have .body implemented\n endSpanOnSuccess(span, response);\n }\n } catch (error) {\n instrumentation._diag.error(\n 'Failed to read fetch response body',\n error,\n );\n instrumentation._endSpan(span, { status: response.status });\n }\n return response;\n }\n\n function onError(span: Span, error: FetchError): never {\n try {\n endSpanOnError(span, error);\n } catch (e: unknown) {\n // endSpanOnError failed — fall back to ending the span directly.\n // No `status`: no response was ever received for this request.\n instrumentation._diag.error(\n 'Failed to end span on fetch error',\n e,\n );\n instrumentation._endSpan(span, {});\n }\n throw error;\n }\n\n const fetchContext = trace.setSpan(context.active(), createdSpan);\n return context.with(fetchContext, () => {\n instrumentation._spanResources.set(createdSpan, {\n name: url,\n fetchStart: performance.now(),\n } as PerformanceResourceTiming);\n\n // Call request hook before injection so hooks cannot tamper with propagation headers.\n // Also, this means the hook will see `options.headers` in the same type as passed in,\n // rather than as a `Headers` instance set by `_addHeaders()`.\n instrumentation._callRequestHook(createdSpan, options);\n instrumentation._addHeaders(options, url, fetchContext);\n\n return original\n .apply(\n globalThis,\n options instanceof Request ? [options] : [url, options],\n )\n .then(\n onSuccess.bind(globalThis, createdSpan),\n onError.bind(globalThis, createdSpan),\n );\n });\n } catch (e: unknown) {\n instrumentation._diag.error('Failed to instrument fetch request', e);\n }\n // Remove the span if it was created and stored before the exception\n if (trucatedSpan) {\n instrumentation._spanResources.delete(trucatedSpan);\n }\n return original.apply(this, args);\n };\n };\n }\n\n /**\n * Creates a new span\n */\n private _createSpan(\n url: string,\n options: Partial<Request | RequestInit> = {},\n ): Span {\n const attributes = {} as Attributes;\n const parsedUrl = parseUrl(url);\n const origMethod = options.method;\n const normMethod = normalizeHttpRequestMethod(options.method || 'GET');\n const serverPort = serverPortFromUrl(parsedUrl);\n const name = normMethod;\n const { sanitizeUrl } = this.getConfig();\n\n attributes[ATTR_HTTP_REQUEST_METHOD] = normMethod;\n attributes[ATTR_URL_FULL] = sanitizeUrl ? sanitizeUrl(url) : url;\n attributes[ATTR_SERVER_ADDRESS] = parsedUrl.hostname;\n if (serverPort) {\n attributes[ATTR_SERVER_PORT] = serverPort;\n }\n if (origMethod && origMethod !== normMethod) {\n attributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = origMethod;\n }\n\n return this.tracer.startSpan(name, {\n kind: SpanKind.CLIENT,\n attributes,\n });\n }\n\n /**\n * Finish span, add attributes, etc.\n */\n private _endSpan(span: Span, response: FetchResponse) {\n if (response.status !== undefined) {\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, response.status);\n }\n\n // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#status\n // 1xx/2xx/3xx MUST be left unset UNLESS there was another error (e.g. a\n // network error receiving the response body)\n if (!response.aborted) {\n const isErrorStatus =\n response.status === undefined ||\n response.status >= 400 ||\n response.error !== undefined;\n if (isErrorStatus) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n const errorType =\n response.error ??\n (response.status !== undefined ? String(response.status) : undefined);\n if (typeof errorType === 'string') {\n span.setAttribute(ATTR_ERROR_TYPE, errorType);\n }\n }\n }\n\n span.end();\n\n // Register the resource context for other instrumentations\n const resource = this._spanResources.get(span);\n if (resource) {\n this._spanResources.delete(span);\n getNetworkContextRegistry().register(span, {\n key: resource.name,\n startPerfNow: resource.fetchStart,\n endPerfNow: performance.now(),\n });\n }\n }\n\n private _applyAttributesAfterFetch(\n span: Span,\n request: Request | RequestInit,\n result: Response | FetchError,\n ) {\n const applyCustomAttributesOnSpan =\n this.getConfig().applyCustomAttributesOnSpan;\n if (applyCustomAttributesOnSpan) {\n safeExecuteInTheMiddle(\n () => applyCustomAttributesOnSpan(span, request, result),\n (error) => {\n if (!error) {\n return;\n }\n this._diag.error('applyCustomAttributesOnSpan', error);\n },\n true,\n );\n }\n }\n\n /**\n * Calls the request hook if defined\n */\n private _callRequestHook(span: Span, request: Request | RequestInit) {\n const requestHook = this.getConfig().requestHook;\n\n if (requestHook) {\n safeExecuteInTheMiddle(\n () => requestHook(span, request),\n (error) => {\n if (error) {\n this._diag.error('requestHook', error);\n }\n },\n true,\n );\n }\n }\n\n /**\n * Add headers for the request and the given context if apply\n */\n private _addHeaders(\n options: Request | RequestInit,\n url: string,\n ctx: Context,\n ): void {\n // Propagate only if in request goes to same origin or is in the allow list\n const urlsToPropagate = this.getConfig().propagateTraceHeaderCorsUrls;\n const urlOrigin = parseUrl(url).origin;\n const sameOrigin = location.origin === urlOrigin;\n const shouldPropagate = sameOrigin || matchesUrl(url, urlsToPropagate);\n\n if (shouldPropagate) {\n if (options instanceof Request) {\n // This mutates `Request.headers` in-place, because it is read-only\n // (per https://developer.mozilla.org/en-US/docs/Web/API/Request/headers),\n // so we cannot (easily) replace it.\n propagation.inject(ctx, options.headers, {\n set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),\n });\n } else {\n // Otherwise, create a new Headers to avoid mutating the caller's\n // possibly re-used headers.\n const headers = new Headers(options.headers);\n propagation.inject(ctx, headers, {\n set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)),\n });\n options.headers = headers;\n }\n } else {\n const headers: Partial<Record<string, unknown>> = {};\n propagation.inject(ctx, headers);\n if (Object.keys(headers).length > 0) {\n this._diag.debug('headers inject skipped due to CORS policy');\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region src/fetch/semconv.ts
2
+ /**
3
+ * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size.
4
+ *
5
+ * @example 3495
6
+ *
7
+ * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
8
+ */
9
+ const ATTR_HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
10
+ //#endregion
11
+ export { ATTR_HTTP_REQUEST_BODY_SIZE };
12
+
13
+ //# sourceMappingURL=semconv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semconv.js","names":[],"sources":["../../src/fetch/semconv.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * This file contains a copy of unstable semantic convention definitions\n * used by this package.\n * @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv\n */\n\n/**\n * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size.\n *\n * @example 3495\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_HTTP_REQUEST_BODY_SIZE = 'http.request.body.size' as const;\n"],"mappings":";;;;;;;;AAkBA,MAAa,8BAA8B"}
@@ -0,0 +1,51 @@
1
+ import { Span } from "@opentelemetry/api";
2
+ import { InstrumentationConfig } from "@opentelemetry/instrumentation";
3
+ //#region src/fetch/types.d.ts
4
+ /**
5
+ * Interface used to provide information to finish span on fetch response.
6
+ * `status` is omitted when no HTTP response was ever received (e.g. a
7
+ * network error), per
8
+ * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#status
9
+ */
10
+ export interface FetchResponse {
11
+ status?: number;
12
+ /**
13
+ * Error identifier used as `error.type` (e.g. the exception's `name`).
14
+ * Not the HTTP status reason phrase. Also forces the span into an error
15
+ * state even if `status` is a non-error code.
16
+ */
17
+ error?: string;
18
+ /** Whether the request was intentionally cancelled by the caller (e.g. via AbortController). */
19
+ aborted?: boolean;
20
+ }
21
+ /**
22
+ * Interface used to provide information to finish span on fetch error
23
+ */
24
+ export interface FetchError {
25
+ status?: number;
26
+ message: string;
27
+ /** The error's exception type (e.g. `TypeError`, `AbortError`), if available. */
28
+ name?: string;
29
+ }
30
+ export type FetchCustomAttributeFunction = (span: Span, request: Request | RequestInit, result: Response | FetchError) => void;
31
+ export type FetchRequestHookFunction = (span: Span, request: Request | RequestInit) => void;
32
+ export interface FetchInstrumentationConfig extends InstrumentationConfig {
33
+ /** urls which should include trace headers when origin doesn't match */
34
+ propagateTraceHeaderCorsUrls?: Array<string | RegExp>;
35
+ /**
36
+ * URLs that partially match any regex in ignoreUrls will not be traced.
37
+ * In addition, URLs that are _exact matches_ of strings in ignoreUrls will
38
+ * also not be traced.
39
+ */
40
+ ignoreUrls?: Array<string | RegExp>;
41
+ /** Function for adding custom attributes on the span */
42
+ applyCustomAttributesOnSpan?: FetchCustomAttributeFunction;
43
+ /** Function for adding custom attributes or headers before the request is handled */
44
+ requestHook?: FetchRequestHookFunction;
45
+ /** Measure outgoing request size */
46
+ measureRequestSize?: boolean;
47
+ /** Custom function to sanitize URLs before adding to log records. */
48
+ sanitizeUrl?: (url: string) => string;
49
+ }
50
+ //#endregion
51
+ //# sourceMappingURL=types.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { NavigationInstrumentationConfig } from "./types.js";
2
2
  import { InstrumentationBase } from "@opentelemetry/instrumentation";
3
3
  //#region src/navigation/instrumentation.d.ts
4
- declare class NavigationInstrumentation extends InstrumentationBase<NavigationInstrumentationConfig> {
4
+ export declare class NavigationInstrumentation extends InstrumentationBase<NavigationInstrumentationConfig> {
5
5
  private _isEnabled;
6
6
  private _isHistoryPatched;
7
7
  private _hasProcessedInitialLoad;
@@ -24,5 +24,4 @@ declare class NavigationInstrumentation extends InstrumentationBase<NavigationIn
24
24
  private _mapChangeStateToType;
25
25
  }
26
26
  //#endregion
27
- export { NavigationInstrumentation };
28
27
  //# sourceMappingURL=instrumentation.d.ts.map