@opentelemetry/browser-instrumentation 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -194,10 +194,24 @@ By default the instrumentation captures `click` events. You can configure which
194
194
 
195
195
  ```typescript
196
196
  new UserActionInstrumentation({
197
- autoCapturedActions: [], // default is ['click']
197
+ // Array of actions to automatically capture. Default: ['click'].
198
+ autoCapturedActions: [],
199
+
200
+ // Mutate the log record before it is emitted (e.g. attach custom attributes).
201
+ applyCustomLogRecordData: (logRecord) => {
202
+ logRecord.attributes = {
203
+ ...logRecord.attributes,
204
+ 'app.user.role': 'admin',
205
+ };
206
+ },
198
207
  });
199
208
  ```
200
209
 
210
+ | Option | Type | Default | Description |
211
+ |--------|------|---------|-------------|
212
+ | `autoCapturedActions` | `AutoCapturedUserAction[]` | `['click']` | Array of actions to automatically capture. |
213
+ | `applyCustomLogRecordData` | `(logRecord: LogRecord) => void` | — | Hook to modify log records before they are emitted. Errors thrown from this hook are caught and logged via the instrumentation diag logger. |
214
+
201
215
  #### Additional Attributes
202
216
 
203
217
  Data attributes with the prefix `data-otel-` on the target element will be added as additional attributes to the generated log record. For example:
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.6.0";
2
+ var version = "0.7.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -10,6 +10,7 @@ declare class UserActionInstrumentation extends InstrumentationBase<UserActionIn
10
10
  protected init(): never[];
11
11
  private _getMouseButtonFromMouseEvent;
12
12
  private onClick;
13
+ private _applyCustomLogRecordData;
13
14
  enable(): void;
14
15
  disable(): void;
15
16
  }
@@ -2,7 +2,7 @@ import { version } from "../package.js";
2
2
  import { getElementCSSSelector } from "../utils/getElementCSSSelector.js";
3
3
  import { ATTR_CSS_SELECTOR, ATTR_MOUSE_EVENT_BUTTON, ATTR_PAGE_X, ATTR_PAGE_Y, ATTR_TAGS, ATTR_TAG_NAME, CLICK_EVENT_NAME } from "./semconv.js";
4
4
  import { SeverityNumber } from "@opentelemetry/api-logs";
5
- import { InstrumentationBase } from "@opentelemetry/instrumentation";
5
+ import { InstrumentationBase, safeExecuteInTheMiddle } from "@opentelemetry/instrumentation";
6
6
  //#region src/user-action/instrumentation.ts
7
7
  const DEFAULT_AUTO_CAPTURED_ACTIONS = ["click"];
8
8
  const OTEL_ELEMENT_ATTRIBUTE_PREFIX = "data-otel-";
@@ -34,7 +34,7 @@ var UserActionInstrumentation = class extends InstrumentationBase {
34
34
  });
35
35
  const otelPrefixedAttributes = {};
36
36
  for (const attr of element.attributes) if (attr.name.startsWith(OTEL_ELEMENT_ATTRIBUTE_PREFIX)) otelPrefixedAttributes[attr.name.slice(10)] = attr.value;
37
- this.logger.emit({
37
+ const logRecord = {
38
38
  severityNumber: SeverityNumber.INFO,
39
39
  eventName: CLICK_EVENT_NAME,
40
40
  attributes: {
@@ -45,7 +45,15 @@ var UserActionInstrumentation = class extends InstrumentationBase {
45
45
  [ATTR_MOUSE_EVENT_BUTTON]: this._getMouseButtonFromMouseEvent(event),
46
46
  [ATTR_CSS_SELECTOR]: cssSelector
47
47
  }
48
- });
48
+ };
49
+ this._applyCustomLogRecordData(logRecord);
50
+ this.logger.emit(logRecord);
51
+ }
52
+ _applyCustomLogRecordData(logRecord) {
53
+ const applyCustomLogRecordData = this.getConfig().applyCustomLogRecordData;
54
+ if (applyCustomLogRecordData) safeExecuteInTheMiddle(() => applyCustomLogRecordData(logRecord), (error) => {
55
+ if (error) this._diag.error("applyCustomLogRecordData hook failed", error);
56
+ }, true);
49
57
  }
50
58
  enable() {
51
59
  const autoCapturedActions = this._config.autoCapturedActions ?? DEFAULT_AUTO_CAPTURED_ACTIONS;
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/user-action/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { getElementCSSSelector } from '#utils';\nimport { version } from '../../package.json' with { type: 'json' };\nimport {\n ATTR_CSS_SELECTOR,\n ATTR_MOUSE_EVENT_BUTTON,\n ATTR_PAGE_X,\n ATTR_PAGE_Y,\n ATTR_TAG_NAME,\n ATTR_TAGS,\n CLICK_EVENT_NAME,\n} from './semconv.ts';\nimport type {\n AutoCapturedUserAction,\n MouseButton,\n UserActionInstrumentationConfig,\n} from './types.ts';\n\nconst DEFAULT_AUTO_CAPTURED_ACTIONS: AutoCapturedUserAction[] = ['click'];\nconst OTEL_ELEMENT_ATTRIBUTE_PREFIX = 'data-otel-';\n\n/**\n * This class automatically instruments different User Actions within the browser.\n */\nexport class UserActionInstrumentation extends InstrumentationBase<UserActionInstrumentationConfig> {\n private declare _onClickHandler?: (event: MouseEvent) => void;\n\n constructor(config: UserActionInstrumentationConfig = {}) {\n super(\n '@opentelemetry/browser-instrumentation/user-action',\n version,\n config,\n );\n }\n\n protected override init() {\n return [];\n }\n\n private _getMouseButtonFromMouseEvent(event: MouseEvent): MouseButton {\n switch (event.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n default:\n return 'left';\n }\n }\n\n private onClick(event: MouseEvent) {\n const element = event.target;\n\n if (!(element instanceof HTMLElement)) {\n return;\n }\n\n if (element.hasAttribute('disabled')) {\n return;\n }\n\n const cssSelector = getElementCSSSelector(element, {\n useIdForTargetElement: true,\n useIdForAncestors: true,\n });\n const otelPrefixedAttributes: Record<string, string> = {};\n\n // Grab all the attributes in the element that start with data-otel-*\n for (const attr of element.attributes) {\n if (attr.name.startsWith(OTEL_ELEMENT_ATTRIBUTE_PREFIX)) {\n otelPrefixedAttributes[\n attr.name.slice(OTEL_ELEMENT_ATTRIBUTE_PREFIX.length)\n ] = attr.value;\n }\n }\n\n this.logger.emit({\n severityNumber: SeverityNumber.INFO,\n eventName: CLICK_EVENT_NAME,\n attributes: {\n [ATTR_PAGE_X]: event.pageX,\n [ATTR_PAGE_Y]: event.pageY,\n [ATTR_TAG_NAME]: element.tagName,\n [ATTR_TAGS]: otelPrefixedAttributes,\n [ATTR_MOUSE_EVENT_BUTTON]: this._getMouseButtonFromMouseEvent(event),\n [ATTR_CSS_SELECTOR]: cssSelector,\n },\n });\n }\n\n override enable(): void {\n const autoCapturedActions =\n this._config.autoCapturedActions ?? DEFAULT_AUTO_CAPTURED_ACTIONS;\n if (!this._onClickHandler) {\n this._onClickHandler = this.onClick.bind(this);\n }\n\n if (autoCapturedActions.includes('click')) {\n document.addEventListener('click', this._onClickHandler, true);\n }\n }\n\n override disable(): void {\n if (this._onClickHandler) {\n document.removeEventListener('click', this._onClickHandler, true);\n }\n }\n}\n"],"mappings":";;;;;;AAwBA,MAAM,gCAA0D,CAAC,OAAO;AACxE,MAAM,gCAAgC;;;;AAKtC,IAAa,4BAAb,cAA+C,oBAAqD;CAGlG,YAAY,SAA0C,CAAC,GAAG;EACxD,MACE,sDACA,SACA,MACF;CACF;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,8BAAsC,OAAgC;EACpE,QAAQ,MAAM,QAAd;GACE,KAAK,GACH,OAAO;GACT,KAAK,GACH,OAAO;GACT,KAAK,GACH,OAAO;GACT,SACE,OAAO;EACX;CACF;CAEA,QAAgB,OAAmB;EACjC,MAAM,UAAU,MAAM;EAEtB,IAAI,EAAE,mBAAmB,cACvB;EAGF,IAAI,QAAQ,aAAa,UAAU,GACjC;EAGF,MAAM,cAAc,sBAAsB,SAAS;GACjD,uBAAuB;GACvB,mBAAmB;EACrB,CAAC;EACD,MAAM,yBAAiD,CAAC;EAGxD,KAAK,MAAM,QAAQ,QAAQ,YACzB,IAAI,KAAK,KAAK,WAAW,6BAA6B,GACpD,uBACE,KAAK,KAAK,MAAM,EAAoC,KAClD,KAAK;EAIb,KAAK,OAAO,KAAK;GACf,gBAAgB,eAAe;GAC/B,WAAW;GACX,YAAY;KACT,cAAc,MAAM;KACpB,cAAc,MAAM;KACpB,gBAAgB,QAAQ;KACxB,YAAY;KACZ,0BAA0B,KAAK,8BAA8B,KAAK;KAClE,oBAAoB;GACvB;EACF,CAAC;CACH;CAEA,SAAwB;EACtB,MAAM,sBACJ,KAAK,QAAQ,uBAAuB;EACtC,IAAI,CAAC,KAAK,iBACR,KAAK,kBAAkB,KAAK,QAAQ,KAAK,IAAI;EAG/C,IAAI,oBAAoB,SAAS,OAAO,GACtC,SAAS,iBAAiB,SAAS,KAAK,iBAAiB,IAAI;CAEjE;CAEA,UAAyB;EACvB,IAAI,KAAK,iBACP,SAAS,oBAAoB,SAAS,KAAK,iBAAiB,IAAI;CAEpE;AACF"}
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/user-action/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { LogRecord } from '@opentelemetry/api-logs';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport {\n InstrumentationBase,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport { getElementCSSSelector } from '#utils';\nimport { version } from '../../package.json' with { type: 'json' };\nimport {\n ATTR_CSS_SELECTOR,\n ATTR_MOUSE_EVENT_BUTTON,\n ATTR_PAGE_X,\n ATTR_PAGE_Y,\n ATTR_TAG_NAME,\n ATTR_TAGS,\n CLICK_EVENT_NAME,\n} from './semconv.ts';\nimport type {\n AutoCapturedUserAction,\n MouseButton,\n UserActionInstrumentationConfig,\n} from './types.ts';\n\nconst DEFAULT_AUTO_CAPTURED_ACTIONS: AutoCapturedUserAction[] = ['click'];\nconst OTEL_ELEMENT_ATTRIBUTE_PREFIX = 'data-otel-';\n\n/**\n * This class automatically instruments different User Actions within the browser.\n */\nexport class UserActionInstrumentation extends InstrumentationBase<UserActionInstrumentationConfig> {\n private declare _onClickHandler?: (event: MouseEvent) => void;\n\n constructor(config: UserActionInstrumentationConfig = {}) {\n super(\n '@opentelemetry/browser-instrumentation/user-action',\n version,\n config,\n );\n }\n\n protected override init() {\n return [];\n }\n\n private _getMouseButtonFromMouseEvent(event: MouseEvent): MouseButton {\n switch (event.button) {\n case 0:\n return 'left';\n case 1:\n return 'middle';\n case 2:\n return 'right';\n default:\n return 'left';\n }\n }\n\n private onClick(event: MouseEvent) {\n const element = event.target;\n\n if (!(element instanceof HTMLElement)) {\n return;\n }\n\n if (element.hasAttribute('disabled')) {\n return;\n }\n\n const cssSelector = getElementCSSSelector(element, {\n useIdForTargetElement: true,\n useIdForAncestors: true,\n });\n const otelPrefixedAttributes: Record<string, string> = {};\n\n // Grab all the attributes in the element that start with data-otel-*\n for (const attr of element.attributes) {\n if (attr.name.startsWith(OTEL_ELEMENT_ATTRIBUTE_PREFIX)) {\n otelPrefixedAttributes[\n attr.name.slice(OTEL_ELEMENT_ATTRIBUTE_PREFIX.length)\n ] = attr.value;\n }\n }\n\n const logRecord: LogRecord = {\n severityNumber: SeverityNumber.INFO,\n eventName: CLICK_EVENT_NAME,\n attributes: {\n [ATTR_PAGE_X]: event.pageX,\n [ATTR_PAGE_Y]: event.pageY,\n [ATTR_TAG_NAME]: element.tagName,\n [ATTR_TAGS]: otelPrefixedAttributes,\n [ATTR_MOUSE_EVENT_BUTTON]: this._getMouseButtonFromMouseEvent(event),\n [ATTR_CSS_SELECTOR]: cssSelector,\n },\n };\n\n this._applyCustomLogRecordData(logRecord);\n\n this.logger.emit(logRecord);\n }\n\n private _applyCustomLogRecordData(logRecord: LogRecord) {\n const applyCustomLogRecordData = this.getConfig().applyCustomLogRecordData;\n if (applyCustomLogRecordData) {\n safeExecuteInTheMiddle(\n () => applyCustomLogRecordData(logRecord),\n (error) => {\n if (error) {\n this._diag.error('applyCustomLogRecordData hook failed', error);\n }\n },\n true,\n );\n }\n }\n\n override enable(): void {\n const autoCapturedActions =\n this._config.autoCapturedActions ?? DEFAULT_AUTO_CAPTURED_ACTIONS;\n if (!this._onClickHandler) {\n this._onClickHandler = this.onClick.bind(this);\n }\n\n if (autoCapturedActions.includes('click')) {\n document.addEventListener('click', this._onClickHandler, true);\n }\n }\n\n override disable(): void {\n if (this._onClickHandler) {\n document.removeEventListener('click', this._onClickHandler, true);\n }\n }\n}\n"],"mappings":";;;;;;AA2BA,MAAM,gCAA0D,CAAC,OAAO;AACxE,MAAM,gCAAgC;;;;AAKtC,IAAa,4BAAb,cAA+C,oBAAqD;CAGlG,YAAY,SAA0C,CAAC,GAAG;EACxD,MACE,sDACA,SACA,MACF;CACF;CAEA,OAA0B;EACxB,OAAO,CAAC;CACV;CAEA,8BAAsC,OAAgC;EACpE,QAAQ,MAAM,QAAd;GACE,KAAK,GACH,OAAO;GACT,KAAK,GACH,OAAO;GACT,KAAK,GACH,OAAO;GACT,SACE,OAAO;EACX;CACF;CAEA,QAAgB,OAAmB;EACjC,MAAM,UAAU,MAAM;EAEtB,IAAI,EAAE,mBAAmB,cACvB;EAGF,IAAI,QAAQ,aAAa,UAAU,GACjC;EAGF,MAAM,cAAc,sBAAsB,SAAS;GACjD,uBAAuB;GACvB,mBAAmB;EACrB,CAAC;EACD,MAAM,yBAAiD,CAAC;EAGxD,KAAK,MAAM,QAAQ,QAAQ,YACzB,IAAI,KAAK,KAAK,WAAW,6BAA6B,GACpD,uBACE,KAAK,KAAK,MAAM,EAAoC,KAClD,KAAK;EAIb,MAAM,YAAuB;GAC3B,gBAAgB,eAAe;GAC/B,WAAW;GACX,YAAY;KACT,cAAc,MAAM;KACpB,cAAc,MAAM;KACpB,gBAAgB,QAAQ;KACxB,YAAY;KACZ,0BAA0B,KAAK,8BAA8B,KAAK;KAClE,oBAAoB;GACvB;EACF;EAEA,KAAK,0BAA0B,SAAS;EAExC,KAAK,OAAO,KAAK,SAAS;CAC5B;CAEA,0BAAkC,WAAsB;EACtD,MAAM,2BAA2B,KAAK,UAAU,CAAC,CAAC;EAClD,IAAI,0BACF,6BACQ,yBAAyB,SAAS,IACvC,UAAU;GACT,IAAI,OACF,KAAK,MAAM,MAAM,wCAAwC,KAAK;EAElE,GACA,IACF;CAEJ;CAEA,SAAwB;EACtB,MAAM,sBACJ,KAAK,QAAQ,uBAAuB;EACtC,IAAI,CAAC,KAAK,iBACR,KAAK,kBAAkB,KAAK,QAAQ,KAAK,IAAI;EAG/C,IAAI,oBAAoB,SAAS,OAAO,GACtC,SAAS,iBAAiB,SAAS,KAAK,iBAAiB,IAAI;CAEjE;CAEA,UAAyB;EACvB,IAAI,KAAK,iBACP,SAAS,oBAAoB,SAAS,KAAK,iBAAiB,IAAI;CAEpE;AACF"}
@@ -1,12 +1,16 @@
1
+ import { LogRecord } from "@opentelemetry/api-logs";
1
2
  import { InstrumentationConfig } from "@opentelemetry/instrumentation";
2
3
  //#region src/user-action/types.d.ts
3
4
  type AutoCapturedUserAction = 'click';
5
+ type ApplyCustomLogRecordDataFunction = (logRecord: LogRecord) => void;
4
6
  /**
5
7
  * UserActionInstrumentation Configuration
6
8
  */
7
9
  interface UserActionInstrumentationConfig extends InstrumentationConfig {
8
10
  autoCapturedActions?: AutoCapturedUserAction[];
11
+ /** Hook to modify log records before they are emitted. */
12
+ applyCustomLogRecordData?: ApplyCustomLogRecordDataFunction;
9
13
  }
10
14
  //#endregion
11
- export { AutoCapturedUserAction, UserActionInstrumentationConfig };
15
+ export { ApplyCustomLogRecordDataFunction, AutoCapturedUserAction, UserActionInstrumentationConfig };
12
16
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentelemetry/browser-instrumentation",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "OpenTelemetry browser instrumentations.",
5
5
  "keywords": [
6
6
  "opentelemetry",
@@ -51,9 +51,9 @@
51
51
  "test:coverage": "vitest --coverage"
52
52
  },
53
53
  "dependencies": {
54
- "@opentelemetry/api-logs": "^0.220.0",
55
- "@opentelemetry/core": "^2.9.0",
56
- "@opentelemetry/instrumentation": "^0.220.0",
54
+ "@opentelemetry/api-logs": "^0.221.0",
55
+ "@opentelemetry/core": "^2.10.0",
56
+ "@opentelemetry/instrumentation": "^0.221.0",
57
57
  "@opentelemetry/semantic-conventions": "^1.43.0",
58
58
  "web-vitals": "^5.3.0"
59
59
  },
@@ -61,7 +61,7 @@
61
61
  "@opentelemetry/api": "^1.9.1"
62
62
  },
63
63
  "devDependencies": {
64
- "@opentelemetry/sdk-logs": "0.220.0"
64
+ "@opentelemetry/sdk-logs": "0.221.0"
65
65
  },
66
66
  "publishConfig": {
67
67
  "access": "public"