@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.
- package/README.md +93 -0
- package/dist/console/instrumentation.d.ts +1 -2
- package/dist/console/instrumentation.js.map +1 -1
- package/dist/console/types.d.ts +2 -3
- package/dist/errors/instrumentation.d.ts +1 -2
- package/dist/errors/instrumentation.js.map +1 -1
- package/dist/errors/types.d.ts +2 -3
- package/dist/fetch/index.d.ts +3 -0
- package/dist/fetch/index.js +2 -0
- package/dist/fetch/instrumentation.d.ts +35 -0
- package/dist/fetch/instrumentation.js +223 -0
- package/dist/fetch/instrumentation.js.map +1 -0
- package/dist/fetch/semconv.js +13 -0
- package/dist/fetch/semconv.js.map +1 -0
- package/dist/fetch/types.d.ts +51 -0
- package/dist/navigation/instrumentation.d.ts +1 -2
- package/dist/navigation/instrumentation.js.map +1 -1
- package/dist/navigation/types.d.ts +4 -5
- package/dist/navigation-timing/instrumentation.d.ts +1 -2
- package/dist/navigation-timing/instrumentation.js.map +1 -1
- package/dist/navigation-timing/types.d.ts +1 -2
- package/dist/package.js +1 -1
- package/dist/resource-timing/instrumentation.d.ts +1 -2
- package/dist/resource-timing/instrumentation.js +12 -11
- package/dist/resource-timing/instrumentation.js.map +1 -1
- package/dist/resource-timing/types.d.ts +1 -2
- package/dist/user-action/instrumentation.d.ts +1 -2
- package/dist/user-action/instrumentation.js.map +1 -1
- package/dist/user-action/types.d.ts +3 -4
- package/dist/utils/ContextRegistry.js +77 -0
- package/dist/utils/ContextRegistry.js.map +1 -0
- package/dist/utils/NetworkContextRegistry.js +23 -0
- package/dist/utils/NetworkContextRegistry.js.map +1 -0
- package/dist/utils/getElementCSSSelector.js.map +1 -1
- package/dist/utils/request.js +131 -0
- package/dist/utils/request.js.map +1 -0
- package/dist/utils/url.js +36 -0
- package/dist/utils/url.js.map +1 -0
- package/dist/web-vitals/instrumentation.d.ts +1 -2
- package/dist/web-vitals/instrumentation.js.map +1 -1
- package/dist/web-vitals/types.d.ts +1 -2
- package/dist/xhr/index.d.ts +3 -0
- package/dist/xhr/index.js +2 -0
- package/dist/xhr/instrumentation.d.ts +38 -0
- package/dist/xhr/instrumentation.js +187 -0
- package/dist/xhr/instrumentation.js.map +1 -0
- package/dist/xhr/semconv.js +13 -0
- package/dist/xhr/semconv.js.map +1 -0
- package/dist/xhr/types.d.ts +22 -0
- 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
|
|
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"}
|
package/dist/console/types.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/errors/types.d.ts
CHANGED
|
@@ -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,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
|