@opentelemetry/browser-instrumentation 0.1.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 +123 -0
- package/dist/navigation-timing/index.d.ts +2 -0
- package/dist/navigation-timing/index.js +2 -0
- package/dist/navigation-timing/instrumentation.d.ts +41 -0
- package/dist/navigation-timing/instrumentation.js +144 -0
- package/dist/navigation-timing/instrumentation.js.map +1 -0
- package/dist/navigation-timing/semconv.js +102 -0
- package/dist/navigation-timing/semconv.js.map +1 -0
- package/dist/navigation-timing/types.d.ts +10 -0
- package/dist/user-action/index.d.ts +2 -0
- package/dist/user-action/index.js +2 -0
- package/dist/user-action/instrumentation.d.ts +19 -0
- package/dist/user-action/instrumentation.js +61 -0
- package/dist/user-action/instrumentation.js.map +1 -0
- package/dist/user-action/semconv.js +42 -0
- package/dist/user-action/semconv.js.map +1 -0
- package/dist/user-action/types.d.ts +13 -0
- package/dist/web-utils/dist/getElementCSSSelector.js +43 -0
- package/dist/web-utils/dist/getElementCSSSelector.js.map +1 -0
- package/dist/web-vitals/index.d.ts +3 -0
- package/dist/web-vitals/index.js +2 -0
- package/dist/web-vitals/instrumentation.d.ts +40 -0
- package/dist/web-vitals/instrumentation.js +99 -0
- package/dist/web-vitals/instrumentation.js.map +1 -0
- package/dist/web-vitals/semconv.js +30 -0
- package/dist/web-vitals/semconv.js.map +1 -0
- package/dist/web-vitals/types.d.ts +26 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @opentelemetry/browser-instrumentation
|
|
2
|
+
|
|
3
|
+
[![NPM Published Version][npm-img]][npm-url]
|
|
4
|
+
[![Apache License][license-image]][license-image]
|
|
5
|
+
|
|
6
|
+
OpenTelemetry browser instrumentations, available as subpath exports under `./experimental/*`.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @opentelemetry/browser-instrumentation
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Instrumentations
|
|
15
|
+
|
|
16
|
+
- [Navigation Timing](#navigation-timing) — automatic instrumentation for navigation timing
|
|
17
|
+
- [User Action](#user-action) — automatic instrumentation for user actions (clicks)
|
|
18
|
+
- [Web Vitals](#web-vitals) — automatic instrumentation for Core Web Vitals
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { logs } from '@opentelemetry/api-logs';
|
|
24
|
+
import {
|
|
25
|
+
ConsoleLogRecordExporter,
|
|
26
|
+
LoggerProvider,
|
|
27
|
+
SimpleLogRecordProcessor,
|
|
28
|
+
} from '@opentelemetry/sdk-logs';
|
|
29
|
+
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
30
|
+
import { NavigationTimingInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/navigation-timing';
|
|
31
|
+
import { UserActionInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/user-action';
|
|
32
|
+
import { WebVitalsInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/web-vitals';
|
|
33
|
+
|
|
34
|
+
const logProvider = new LoggerProvider({
|
|
35
|
+
processors: [
|
|
36
|
+
new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()),
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
logs.setGlobalLoggerProvider(logProvider);
|
|
40
|
+
|
|
41
|
+
registerInstrumentations({
|
|
42
|
+
instrumentations: [
|
|
43
|
+
new NavigationTimingInstrumentation(),
|
|
44
|
+
new UserActionInstrumentation(),
|
|
45
|
+
new WebVitalsInstrumentation(),
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Navigation Timing
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { NavigationTimingInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/navigation-timing';
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Provides automatic instrumentation for [Navigation Timing](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming) in web applications.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### User Action
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { UserActionInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/user-action';
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Provides automatic instrumentation for user actions in web applications.
|
|
69
|
+
|
|
70
|
+
Compatible with OpenTelemetry JS API and SDK `1.0+`.
|
|
71
|
+
|
|
72
|
+
#### Configuration
|
|
73
|
+
|
|
74
|
+
By default the instrumentation captures `click` events. You can configure which events to capture by passing an options object:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
new UserActionInstrumentation({
|
|
78
|
+
autoCapturedActions: [], // default is ['click']
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Additional Attributes
|
|
83
|
+
|
|
84
|
+
Data attributes with the prefix `data-otel-` on the target element will be added as additional attributes to the generated log record. For example:
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<button id="btn1" data-otel-user-id="12345" data-otel-feature="signup">
|
|
88
|
+
Sign Up
|
|
89
|
+
</button>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### Web Vitals
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { WebVitalsInstrumentation } from '@opentelemetry/browser-instrumentation/experimental/web-vitals';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Provides automatic instrumentation for [Core Web Vitals](https://web.dev/vitals/) using the [`web-vitals`](https://github.com/GoogleChrome/web-vitals) library.
|
|
101
|
+
|
|
102
|
+
#### Configuration
|
|
103
|
+
|
|
104
|
+
| Option | Type | Default | Description |
|
|
105
|
+
|--------|------|---------|-------------|
|
|
106
|
+
| `includeRawAttribution` | `boolean` | `false` | When true, sets the log record body to the JSON-stringified `web-vitals` attribution object. |
|
|
107
|
+
| `applyCustomLogRecordData` | `(logRecord: LogRecord) => void` | — | Hook to modify log records before they are emitted. |
|
|
108
|
+
|
|
109
|
+
## Useful links
|
|
110
|
+
|
|
111
|
+
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
|
|
112
|
+
- For more about OpenTelemetry Browser: <https://github.com/open-telemetry/opentelemetry-browser>
|
|
113
|
+
- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
Apache 2.0 - See [LICENSE][license-url] for more information.
|
|
118
|
+
|
|
119
|
+
[discussions-url]: https://github.com/open-telemetry/opentelemetry-browser/discussions/landing
|
|
120
|
+
[license-url]: https://github.com/open-telemetry/opentelemetry-browser/blob/main/LICENSE
|
|
121
|
+
[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat
|
|
122
|
+
[npm-url]: https://www.npmjs.com/package/@opentelemetry/browser-instrumentation
|
|
123
|
+
[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fbrowser-instrumentation.svg
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NavigationTimingInstrumentationConfig } from "./types.js";
|
|
2
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
3
|
+
|
|
4
|
+
//#region src/navigation-timing/instrumentation.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* This class automatically instruments navigation timing within the browser.
|
|
7
|
+
*/
|
|
8
|
+
declare class NavigationTimingInstrumentation extends InstrumentationBase<NavigationTimingInstrumentationConfig> {
|
|
9
|
+
private _lastEntry?;
|
|
10
|
+
private _didEmit;
|
|
11
|
+
private _completeDelayTimeoutId?;
|
|
12
|
+
private _retryCount;
|
|
13
|
+
private _isEnabled;
|
|
14
|
+
private _onLoad;
|
|
15
|
+
private _onPageHide;
|
|
16
|
+
constructor(config?: NavigationTimingInstrumentationConfig);
|
|
17
|
+
protected init(): never[];
|
|
18
|
+
enable(): void;
|
|
19
|
+
disable(): void;
|
|
20
|
+
private _getLatestNavigationEntry;
|
|
21
|
+
private _calculateBackoffDelay;
|
|
22
|
+
/**
|
|
23
|
+
* Attempts to emit the navigation timing event.
|
|
24
|
+
*
|
|
25
|
+
* - Emits immediately if a complete `PerformanceNavigationTiming` entry is available.
|
|
26
|
+
* - If the page is still loading, waits for `window.load` and retries.
|
|
27
|
+
* - If the page is already loaded but the entry is not finalized yet, schedules one
|
|
28
|
+
* deferred re-check (to allow the browser to populate the timing fields).
|
|
29
|
+
*
|
|
30
|
+
* This method can be called multiple times (from `enable()`, the load handler, or the
|
|
31
|
+
* deferred timeout), so it must be safe to re-enter.
|
|
32
|
+
*/
|
|
33
|
+
private _tryEmitOrSchedule;
|
|
34
|
+
private _handleUnload;
|
|
35
|
+
private _emitAndCleanup;
|
|
36
|
+
private _emitNavigationTiming;
|
|
37
|
+
private _unsubscribeAll;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { NavigationTimingInstrumentation };
|
|
41
|
+
//# sourceMappingURL=instrumentation.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ATTR_NAVIGATION_CONNECT_END, ATTR_NAVIGATION_CONNECT_START, ATTR_NAVIGATION_DECODED_BODY_SIZE, ATTR_NAVIGATION_DOMAIN_LOOKUP_END, ATTR_NAVIGATION_DOMAIN_LOOKUP_START, ATTR_NAVIGATION_DOM_COMPLETE, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START, ATTR_NAVIGATION_DOM_INTERACTIVE, ATTR_NAVIGATION_DURATION, ATTR_NAVIGATION_ENCODED_BODY_SIZE, ATTR_NAVIGATION_FETCH_START, ATTR_NAVIGATION_LOAD_EVENT_END, ATTR_NAVIGATION_LOAD_EVENT_START, ATTR_NAVIGATION_REDIRECT_COUNT, ATTR_NAVIGATION_REQUEST_START, ATTR_NAVIGATION_RESPONSE_END, ATTR_NAVIGATION_RESPONSE_START, ATTR_NAVIGATION_SECURE_CONNECTION_START, ATTR_NAVIGATION_TRANSFER_SIZE, ATTR_NAVIGATION_TYPE, ATTR_NAVIGATION_UNLOAD_EVENT_END, ATTR_NAVIGATION_UNLOAD_EVENT_START, ATTR_NAVIGATION_URL, NAVIGATION_TIMING_EVENT_NAME } from "./semconv.js";
|
|
2
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
3
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
4
|
+
//#region src/navigation-timing/instrumentation.ts
|
|
5
|
+
const BASE_DELAY_MS = 50;
|
|
6
|
+
const MAX_RETRIES = 5;
|
|
7
|
+
/**
|
|
8
|
+
* This class automatically instruments navigation timing within the browser.
|
|
9
|
+
*/
|
|
10
|
+
var NavigationTimingInstrumentation = class extends InstrumentationBase {
|
|
11
|
+
_lastEntry;
|
|
12
|
+
_didEmit = false;
|
|
13
|
+
_completeDelayTimeoutId;
|
|
14
|
+
_retryCount = 0;
|
|
15
|
+
_isEnabled = false;
|
|
16
|
+
_onLoad = () => {
|
|
17
|
+
this._tryEmitOrSchedule();
|
|
18
|
+
};
|
|
19
|
+
_onPageHide = () => {
|
|
20
|
+
this._handleUnload();
|
|
21
|
+
};
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
super("@opentelemetry/instrumentation-navigation-timing", "0.1.0", config);
|
|
24
|
+
}
|
|
25
|
+
init() {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
enable() {
|
|
29
|
+
if (this._isEnabled) return;
|
|
30
|
+
this._isEnabled = true;
|
|
31
|
+
this._tryEmitOrSchedule();
|
|
32
|
+
if (this._didEmit) return;
|
|
33
|
+
window.addEventListener("pagehide", this._onPageHide);
|
|
34
|
+
}
|
|
35
|
+
disable() {
|
|
36
|
+
this._isEnabled = false;
|
|
37
|
+
this._unsubscribeAll();
|
|
38
|
+
this._lastEntry = void 0;
|
|
39
|
+
this._didEmit = false;
|
|
40
|
+
this._retryCount = 0;
|
|
41
|
+
}
|
|
42
|
+
_getLatestNavigationEntry() {
|
|
43
|
+
const entries = performance?.getEntriesByType?.("navigation");
|
|
44
|
+
if (!entries || entries.length === 0) return;
|
|
45
|
+
return entries[entries.length - 1];
|
|
46
|
+
}
|
|
47
|
+
_calculateBackoffDelay() {
|
|
48
|
+
return this._retryCount * BASE_DELAY_MS;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Attempts to emit the navigation timing event.
|
|
52
|
+
*
|
|
53
|
+
* - Emits immediately if a complete `PerformanceNavigationTiming` entry is available.
|
|
54
|
+
* - If the page is still loading, waits for `window.load` and retries.
|
|
55
|
+
* - If the page is already loaded but the entry is not finalized yet, schedules one
|
|
56
|
+
* deferred re-check (to allow the browser to populate the timing fields).
|
|
57
|
+
*
|
|
58
|
+
* This method can be called multiple times (from `enable()`, the load handler, or the
|
|
59
|
+
* deferred timeout), so it must be safe to re-enter.
|
|
60
|
+
*/
|
|
61
|
+
_tryEmitOrSchedule() {
|
|
62
|
+
if (this._didEmit) return;
|
|
63
|
+
const entry = this._getLatestNavigationEntry();
|
|
64
|
+
if (entry) this._lastEntry = entry;
|
|
65
|
+
if (entry && entry.loadEventEnd > 0) {
|
|
66
|
+
this._emitAndCleanup(entry);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (document.readyState !== "complete") {
|
|
70
|
+
window.addEventListener("load", this._onLoad, { once: true });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (this._completeDelayTimeoutId !== void 0 || this._retryCount > MAX_RETRIES) return;
|
|
74
|
+
else {
|
|
75
|
+
const delay = this._calculateBackoffDelay();
|
|
76
|
+
this._retryCount++;
|
|
77
|
+
this._completeDelayTimeoutId = window.setTimeout(() => {
|
|
78
|
+
this._completeDelayTimeoutId = void 0;
|
|
79
|
+
this._tryEmitOrSchedule();
|
|
80
|
+
}, delay);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
_handleUnload() {
|
|
84
|
+
if (this._didEmit) return;
|
|
85
|
+
const entry = this._getLatestNavigationEntry() ?? this._lastEntry;
|
|
86
|
+
if (!entry) {
|
|
87
|
+
this._unsubscribeAll();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this._emitAndCleanup(entry);
|
|
91
|
+
}
|
|
92
|
+
_emitAndCleanup(entry) {
|
|
93
|
+
if (this._didEmit) return;
|
|
94
|
+
this._didEmit = true;
|
|
95
|
+
this._emitNavigationTiming(entry);
|
|
96
|
+
this._lastEntry = void 0;
|
|
97
|
+
this._unsubscribeAll();
|
|
98
|
+
}
|
|
99
|
+
_emitNavigationTiming(entry) {
|
|
100
|
+
if (!entry) return;
|
|
101
|
+
this.logger.emit({
|
|
102
|
+
eventName: NAVIGATION_TIMING_EVENT_NAME,
|
|
103
|
+
severityNumber: SeverityNumber.INFO,
|
|
104
|
+
attributes: {
|
|
105
|
+
[ATTR_NAVIGATION_TYPE]: entry.type,
|
|
106
|
+
[ATTR_NAVIGATION_URL]: entry.name,
|
|
107
|
+
[ATTR_NAVIGATION_DURATION]: entry.duration,
|
|
108
|
+
[ATTR_NAVIGATION_DOM_COMPLETE]: entry.domComplete,
|
|
109
|
+
[ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END]: entry.domContentLoadedEventEnd,
|
|
110
|
+
[ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START]: entry.domContentLoadedEventStart,
|
|
111
|
+
[ATTR_NAVIGATION_DOM_INTERACTIVE]: entry.domInteractive,
|
|
112
|
+
[ATTR_NAVIGATION_LOAD_EVENT_END]: entry.loadEventEnd,
|
|
113
|
+
[ATTR_NAVIGATION_LOAD_EVENT_START]: entry.loadEventStart,
|
|
114
|
+
[ATTR_NAVIGATION_REDIRECT_COUNT]: entry.redirectCount,
|
|
115
|
+
[ATTR_NAVIGATION_UNLOAD_EVENT_END]: entry.unloadEventEnd,
|
|
116
|
+
[ATTR_NAVIGATION_UNLOAD_EVENT_START]: entry.unloadEventStart,
|
|
117
|
+
[ATTR_NAVIGATION_FETCH_START]: entry.fetchStart,
|
|
118
|
+
[ATTR_NAVIGATION_DOMAIN_LOOKUP_START]: entry.domainLookupStart,
|
|
119
|
+
[ATTR_NAVIGATION_DOMAIN_LOOKUP_END]: entry.domainLookupEnd,
|
|
120
|
+
[ATTR_NAVIGATION_CONNECT_START]: entry.connectStart,
|
|
121
|
+
[ATTR_NAVIGATION_CONNECT_END]: entry.connectEnd,
|
|
122
|
+
[ATTR_NAVIGATION_SECURE_CONNECTION_START]: entry.secureConnectionStart,
|
|
123
|
+
[ATTR_NAVIGATION_REQUEST_START]: entry.requestStart,
|
|
124
|
+
[ATTR_NAVIGATION_RESPONSE_START]: entry.responseStart,
|
|
125
|
+
[ATTR_NAVIGATION_RESPONSE_END]: entry.responseEnd,
|
|
126
|
+
[ATTR_NAVIGATION_TRANSFER_SIZE]: entry.transferSize,
|
|
127
|
+
[ATTR_NAVIGATION_ENCODED_BODY_SIZE]: entry.encodedBodySize,
|
|
128
|
+
[ATTR_NAVIGATION_DECODED_BODY_SIZE]: entry.decodedBodySize
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
_unsubscribeAll() {
|
|
133
|
+
if (this._completeDelayTimeoutId) {
|
|
134
|
+
clearTimeout(this._completeDelayTimeoutId);
|
|
135
|
+
this._completeDelayTimeoutId = void 0;
|
|
136
|
+
}
|
|
137
|
+
window.removeEventListener("load", this._onLoad);
|
|
138
|
+
window.removeEventListener("pagehide", this._onPageHide);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
//#endregion
|
|
142
|
+
export { NavigationTimingInstrumentation };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/navigation-timing/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 {\n ATTR_NAVIGATION_CONNECT_END,\n ATTR_NAVIGATION_CONNECT_START,\n ATTR_NAVIGATION_DECODED_BODY_SIZE,\n ATTR_NAVIGATION_DOM_COMPLETE,\n ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END,\n ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START,\n ATTR_NAVIGATION_DOM_INTERACTIVE,\n ATTR_NAVIGATION_DOMAIN_LOOKUP_END,\n ATTR_NAVIGATION_DOMAIN_LOOKUP_START,\n ATTR_NAVIGATION_DURATION,\n ATTR_NAVIGATION_ENCODED_BODY_SIZE,\n ATTR_NAVIGATION_FETCH_START,\n ATTR_NAVIGATION_LOAD_EVENT_END,\n ATTR_NAVIGATION_LOAD_EVENT_START,\n ATTR_NAVIGATION_REDIRECT_COUNT,\n ATTR_NAVIGATION_REQUEST_START,\n ATTR_NAVIGATION_RESPONSE_END,\n ATTR_NAVIGATION_RESPONSE_START,\n ATTR_NAVIGATION_SECURE_CONNECTION_START,\n ATTR_NAVIGATION_TRANSFER_SIZE,\n ATTR_NAVIGATION_TYPE,\n ATTR_NAVIGATION_UNLOAD_EVENT_END,\n ATTR_NAVIGATION_UNLOAD_EVENT_START,\n ATTR_NAVIGATION_URL,\n NAVIGATION_TIMING_EVENT_NAME,\n} from './semconv.ts';\nimport type { NavigationTimingInstrumentationConfig } from './types.ts';\n\nconst BASE_DELAY_MS = 50;\nconst MAX_RETRIES = 5;\n\n/**\n * This class automatically instruments navigation timing within the browser.\n */\nexport class NavigationTimingInstrumentation extends InstrumentationBase<NavigationTimingInstrumentationConfig> {\n private _lastEntry?: PerformanceNavigationTiming;\n private _didEmit = false;\n private _completeDelayTimeoutId?: number;\n private _retryCount = 0;\n private _isEnabled = false;\n\n private _onLoad = () => {\n this._tryEmitOrSchedule();\n };\n\n private _onPageHide = () => {\n this._handleUnload();\n };\n\n constructor(config: NavigationTimingInstrumentationConfig = {}) {\n super('@opentelemetry/instrumentation-navigation-timing', '0.1.0', 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 // Try emitting immediately (e.g. when enabled after load),\n // otherwise schedule for `load` or fall back to unload.\n this._tryEmitOrSchedule();\n if (this._didEmit) {\n return;\n }\n\n window.addEventListener('pagehide', this._onPageHide);\n }\n\n override disable(): void {\n this._isEnabled = false;\n this._unsubscribeAll();\n this._lastEntry = undefined;\n this._didEmit = false;\n this._retryCount = 0;\n }\n\n private _getLatestNavigationEntry(): PerformanceNavigationTiming | undefined {\n const entries = performance?.getEntriesByType?.('navigation') as\n | PerformanceNavigationTiming[]\n | undefined;\n if (!entries || entries.length === 0) {\n return;\n }\n\n return entries[entries.length - 1];\n }\n\n private _calculateBackoffDelay(): number {\n return this._retryCount * BASE_DELAY_MS;\n }\n\n /**\n * Attempts to emit the navigation timing event.\n *\n * - Emits immediately if a complete `PerformanceNavigationTiming` entry is available.\n * - If the page is still loading, waits for `window.load` and retries.\n * - If the page is already loaded but the entry is not finalized yet, schedules one\n * deferred re-check (to allow the browser to populate the timing fields).\n *\n * This method can be called multiple times (from `enable()`, the load handler, or the\n * deferred timeout), so it must be safe to re-enter.\n */\n private _tryEmitOrSchedule(): void {\n if (this._didEmit) {\n return;\n }\n\n const entry = this._getLatestNavigationEntry();\n if (entry) {\n this._lastEntry = entry;\n }\n\n // Prefer emitting a \"complete\" navigation entry.\n if (entry && entry.loadEventEnd > 0) {\n this._emitAndCleanup(entry);\n return;\n }\n\n // If the document is still loading, wait for `load` and try again.\n if (document.readyState !== 'complete') {\n window.addEventListener('load', this._onLoad, { once: true });\n return;\n }\n\n // If the document is already complete but navigation timings are not finalized yet,\n // schedule a deferred re-check with linear backoff to allow the browser to finish\n // populating the entry.\n if (\n this._completeDelayTimeoutId !== undefined ||\n this._retryCount > MAX_RETRIES\n ) {\n return;\n } else {\n const delay = this._calculateBackoffDelay();\n this._retryCount++;\n\n this._completeDelayTimeoutId = window.setTimeout(() => {\n this._completeDelayTimeoutId = undefined;\n this._tryEmitOrSchedule();\n }, delay);\n }\n }\n\n private _handleUnload(): void {\n if (this._didEmit) {\n return;\n }\n\n const entry = this._getLatestNavigationEntry() ?? this._lastEntry;\n if (!entry) {\n this._unsubscribeAll();\n return;\n }\n\n // Emit even if partial (e.g. loadEventEnd === 0).\n this._emitAndCleanup(entry);\n }\n\n private _emitAndCleanup(entry: PerformanceNavigationTiming): void {\n if (this._didEmit) {\n return;\n }\n\n this._didEmit = true;\n\n this._emitNavigationTiming(entry);\n this._lastEntry = undefined;\n this._unsubscribeAll();\n }\n\n private _emitNavigationTiming(entry: PerformanceNavigationTiming) {\n if (!entry) {\n return;\n }\n\n this.logger.emit({\n eventName: NAVIGATION_TIMING_EVENT_NAME,\n severityNumber: SeverityNumber.INFO,\n attributes: {\n [ATTR_NAVIGATION_TYPE]: entry.type,\n [ATTR_NAVIGATION_URL]: entry.name,\n [ATTR_NAVIGATION_DURATION]: entry.duration,\n [ATTR_NAVIGATION_DOM_COMPLETE]: entry.domComplete,\n [ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END]:\n entry.domContentLoadedEventEnd,\n [ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START]:\n entry.domContentLoadedEventStart,\n [ATTR_NAVIGATION_DOM_INTERACTIVE]: entry.domInteractive,\n [ATTR_NAVIGATION_LOAD_EVENT_END]: entry.loadEventEnd,\n [ATTR_NAVIGATION_LOAD_EVENT_START]: entry.loadEventStart,\n [ATTR_NAVIGATION_REDIRECT_COUNT]: entry.redirectCount,\n [ATTR_NAVIGATION_UNLOAD_EVENT_END]: entry.unloadEventEnd,\n [ATTR_NAVIGATION_UNLOAD_EVENT_START]: entry.unloadEventStart,\n [ATTR_NAVIGATION_FETCH_START]: entry.fetchStart,\n [ATTR_NAVIGATION_DOMAIN_LOOKUP_START]: entry.domainLookupStart,\n [ATTR_NAVIGATION_DOMAIN_LOOKUP_END]: entry.domainLookupEnd,\n [ATTR_NAVIGATION_CONNECT_START]: entry.connectStart,\n [ATTR_NAVIGATION_CONNECT_END]: entry.connectEnd,\n [ATTR_NAVIGATION_SECURE_CONNECTION_START]: entry.secureConnectionStart,\n [ATTR_NAVIGATION_REQUEST_START]: entry.requestStart,\n [ATTR_NAVIGATION_RESPONSE_START]: entry.responseStart,\n [ATTR_NAVIGATION_RESPONSE_END]: entry.responseEnd,\n [ATTR_NAVIGATION_TRANSFER_SIZE]: entry.transferSize,\n [ATTR_NAVIGATION_ENCODED_BODY_SIZE]: entry.encodedBodySize,\n [ATTR_NAVIGATION_DECODED_BODY_SIZE]: entry.decodedBodySize,\n },\n });\n }\n\n private _unsubscribeAll(): void {\n if (this._completeDelayTimeoutId) {\n clearTimeout(this._completeDelayTimeoutId);\n this._completeDelayTimeoutId = undefined;\n }\n\n window.removeEventListener('load', this._onLoad);\n window.removeEventListener('pagehide', this._onPageHide);\n }\n}\n"],"mappings":";;;;AAoCA,MAAM,gBAAgB;AACtB,MAAM,cAAc;;;;AAKpB,IAAa,kCAAb,cAAqD,oBAA2D;CAC9G;CACA,WAAmB;CACnB;CACA,cAAsB;CACtB,aAAqB;CAErB,gBAAwB;AACtB,OAAK,oBAAoB;;CAG3B,oBAA4B;AAC1B,OAAK,eAAe;;CAGtB,YAAY,SAAgD,EAAE,EAAE;AAC9D,QAAM,oDAAoD,SAAS,OAAO;;CAG5E,OAA0B;AACxB,SAAO,EAAE;;CAGX,SAAwB;AACtB,MAAI,KAAK,WACP;AAEF,OAAK,aAAa;AAIlB,OAAK,oBAAoB;AACzB,MAAI,KAAK,SACP;AAGF,SAAO,iBAAiB,YAAY,KAAK,YAAY;;CAGvD,UAAyB;AACvB,OAAK,aAAa;AAClB,OAAK,iBAAiB;AACtB,OAAK,aAAa,KAAA;AAClB,OAAK,WAAW;AAChB,OAAK,cAAc;;CAGrB,4BAA6E;EAC3E,MAAM,UAAU,aAAa,mBAAmB,aAAa;AAG7D,MAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;AAGF,SAAO,QAAQ,QAAQ,SAAS;;CAGlC,yBAAyC;AACvC,SAAO,KAAK,cAAc;;;;;;;;;;;;;CAc5B,qBAAmC;AACjC,MAAI,KAAK,SACP;EAGF,MAAM,QAAQ,KAAK,2BAA2B;AAC9C,MAAI,MACF,MAAK,aAAa;AAIpB,MAAI,SAAS,MAAM,eAAe,GAAG;AACnC,QAAK,gBAAgB,MAAM;AAC3B;;AAIF,MAAI,SAAS,eAAe,YAAY;AACtC,UAAO,iBAAiB,QAAQ,KAAK,SAAS,EAAE,MAAM,MAAM,CAAC;AAC7D;;AAMF,MACE,KAAK,4BAA4B,KAAA,KACjC,KAAK,cAAc,YAEnB;OACK;GACL,MAAM,QAAQ,KAAK,wBAAwB;AAC3C,QAAK;AAEL,QAAK,0BAA0B,OAAO,iBAAiB;AACrD,SAAK,0BAA0B,KAAA;AAC/B,SAAK,oBAAoB;MACxB,MAAM;;;CAIb,gBAA8B;AAC5B,MAAI,KAAK,SACP;EAGF,MAAM,QAAQ,KAAK,2BAA2B,IAAI,KAAK;AACvD,MAAI,CAAC,OAAO;AACV,QAAK,iBAAiB;AACtB;;AAIF,OAAK,gBAAgB,MAAM;;CAG7B,gBAAwB,OAA0C;AAChE,MAAI,KAAK,SACP;AAGF,OAAK,WAAW;AAEhB,OAAK,sBAAsB,MAAM;AACjC,OAAK,aAAa,KAAA;AAClB,OAAK,iBAAiB;;CAGxB,sBAA8B,OAAoC;AAChE,MAAI,CAAC,MACH;AAGF,OAAK,OAAO,KAAK;GACf,WAAW;GACX,gBAAgB,eAAe;GAC/B,YAAY;KACT,uBAAuB,MAAM;KAC7B,sBAAsB,MAAM;KAC5B,2BAA2B,MAAM;KACjC,+BAA+B,MAAM;KACrC,+CACC,MAAM;KACP,iDACC,MAAM;KACP,kCAAkC,MAAM;KACxC,iCAAiC,MAAM;KACvC,mCAAmC,MAAM;KACzC,iCAAiC,MAAM;KACvC,mCAAmC,MAAM;KACzC,qCAAqC,MAAM;KAC3C,8BAA8B,MAAM;KACpC,sCAAsC,MAAM;KAC5C,oCAAoC,MAAM;KAC1C,gCAAgC,MAAM;KACtC,8BAA8B,MAAM;KACpC,0CAA0C,MAAM;KAChD,gCAAgC,MAAM;KACtC,iCAAiC,MAAM;KACvC,+BAA+B,MAAM;KACrC,gCAAgC,MAAM;KACtC,oCAAoC,MAAM;KAC1C,oCAAoC,MAAM;IAC5C;GACF,CAAC;;CAGJ,kBAAgC;AAC9B,MAAI,KAAK,yBAAyB;AAChC,gBAAa,KAAK,wBAAwB;AAC1C,QAAK,0BAA0B,KAAA;;AAGjC,SAAO,oBAAoB,QAAQ,KAAK,QAAQ;AAChD,SAAO,oBAAoB,YAAY,KAAK,YAAY"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/navigation-timing/semconv.ts
|
|
2
|
+
const NAVIGATION_TIMING_EVENT_NAME = "browser.navigation_timing";
|
|
3
|
+
/**
|
|
4
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
5
|
+
*/
|
|
6
|
+
const ATTR_NAVIGATION_TYPE = "navigation.type";
|
|
7
|
+
/**
|
|
8
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
9
|
+
*/
|
|
10
|
+
const ATTR_NAVIGATION_URL = "navigation.url";
|
|
11
|
+
/**
|
|
12
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
13
|
+
*/
|
|
14
|
+
const ATTR_NAVIGATION_DURATION = "navigation.duration";
|
|
15
|
+
/**
|
|
16
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
17
|
+
*/
|
|
18
|
+
const ATTR_NAVIGATION_DOM_COMPLETE = "navigation.dom_complete";
|
|
19
|
+
/**
|
|
20
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
21
|
+
*/
|
|
22
|
+
const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END = "navigation.dom_content_loaded_event_end";
|
|
23
|
+
/**
|
|
24
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
25
|
+
*/
|
|
26
|
+
const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START = "navigation.dom_content_loaded_event_start";
|
|
27
|
+
/**
|
|
28
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
29
|
+
*/
|
|
30
|
+
const ATTR_NAVIGATION_DOM_INTERACTIVE = "navigation.dom_interactive";
|
|
31
|
+
/**
|
|
32
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
33
|
+
*/
|
|
34
|
+
const ATTR_NAVIGATION_LOAD_EVENT_END = "navigation.load_event_end";
|
|
35
|
+
/**
|
|
36
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
37
|
+
*/
|
|
38
|
+
const ATTR_NAVIGATION_LOAD_EVENT_START = "navigation.load_event_start";
|
|
39
|
+
/**
|
|
40
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
41
|
+
*/
|
|
42
|
+
const ATTR_NAVIGATION_REDIRECT_COUNT = "navigation.redirect_count";
|
|
43
|
+
/**
|
|
44
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
45
|
+
*/
|
|
46
|
+
const ATTR_NAVIGATION_UNLOAD_EVENT_END = "navigation.unload_event_end";
|
|
47
|
+
/**
|
|
48
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
49
|
+
*/
|
|
50
|
+
const ATTR_NAVIGATION_UNLOAD_EVENT_START = "navigation.unload_event_start";
|
|
51
|
+
/**
|
|
52
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
53
|
+
*/
|
|
54
|
+
const ATTR_NAVIGATION_FETCH_START = "navigation.fetch_start";
|
|
55
|
+
/**
|
|
56
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
57
|
+
*/
|
|
58
|
+
const ATTR_NAVIGATION_DOMAIN_LOOKUP_START = "navigation.domain_lookup_start";
|
|
59
|
+
/**
|
|
60
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
61
|
+
*/
|
|
62
|
+
const ATTR_NAVIGATION_DOMAIN_LOOKUP_END = "navigation.domain_lookup_end";
|
|
63
|
+
/**
|
|
64
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
65
|
+
*/
|
|
66
|
+
const ATTR_NAVIGATION_CONNECT_START = "navigation.connect_start";
|
|
67
|
+
/**
|
|
68
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
69
|
+
*/
|
|
70
|
+
const ATTR_NAVIGATION_CONNECT_END = "navigation.connect_end";
|
|
71
|
+
/**
|
|
72
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
73
|
+
*/
|
|
74
|
+
const ATTR_NAVIGATION_SECURE_CONNECTION_START = "navigation.secure_connection_start";
|
|
75
|
+
/**
|
|
76
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
77
|
+
*/
|
|
78
|
+
const ATTR_NAVIGATION_REQUEST_START = "navigation.request_start";
|
|
79
|
+
/**
|
|
80
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
81
|
+
*/
|
|
82
|
+
const ATTR_NAVIGATION_RESPONSE_START = "navigation.response_start";
|
|
83
|
+
/**
|
|
84
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
85
|
+
*/
|
|
86
|
+
const ATTR_NAVIGATION_RESPONSE_END = "navigation.response_end";
|
|
87
|
+
/**
|
|
88
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
89
|
+
*/
|
|
90
|
+
const ATTR_NAVIGATION_TRANSFER_SIZE = "navigation.transfer_size";
|
|
91
|
+
/**
|
|
92
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
93
|
+
*/
|
|
94
|
+
const ATTR_NAVIGATION_ENCODED_BODY_SIZE = "navigation.encoded_body_size";
|
|
95
|
+
/**
|
|
96
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
97
|
+
*/
|
|
98
|
+
const ATTR_NAVIGATION_DECODED_BODY_SIZE = "navigation.decoded_body_size";
|
|
99
|
+
//#endregion
|
|
100
|
+
export { ATTR_NAVIGATION_CONNECT_END, ATTR_NAVIGATION_CONNECT_START, ATTR_NAVIGATION_DECODED_BODY_SIZE, ATTR_NAVIGATION_DOMAIN_LOOKUP_END, ATTR_NAVIGATION_DOMAIN_LOOKUP_START, ATTR_NAVIGATION_DOM_COMPLETE, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START, ATTR_NAVIGATION_DOM_INTERACTIVE, ATTR_NAVIGATION_DURATION, ATTR_NAVIGATION_ENCODED_BODY_SIZE, ATTR_NAVIGATION_FETCH_START, ATTR_NAVIGATION_LOAD_EVENT_END, ATTR_NAVIGATION_LOAD_EVENT_START, ATTR_NAVIGATION_REDIRECT_COUNT, ATTR_NAVIGATION_REQUEST_START, ATTR_NAVIGATION_RESPONSE_END, ATTR_NAVIGATION_RESPONSE_START, ATTR_NAVIGATION_SECURE_CONNECTION_START, ATTR_NAVIGATION_TRANSFER_SIZE, ATTR_NAVIGATION_TYPE, ATTR_NAVIGATION_UNLOAD_EVENT_END, ATTR_NAVIGATION_UNLOAD_EVENT_START, ATTR_NAVIGATION_URL, NAVIGATION_TIMING_EVENT_NAME };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=semconv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semconv.js","names":[],"sources":["../../src/navigation-timing/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\nexport const NAVIGATION_TIMING_EVENT_NAME = 'browser.navigation_timing';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TYPE = 'navigation.type';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_URL = 'navigation.url';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DURATION = 'navigation.duration';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_COMPLETE = 'navigation.dom_complete';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END =\n 'navigation.dom_content_loaded_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START =\n 'navigation.dom_content_loaded_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_INTERACTIVE = 'navigation.dom_interactive';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_END = 'navigation.load_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_START = 'navigation.load_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REDIRECT_COUNT = 'navigation.redirect_count';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_END = 'navigation.unload_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_START =\n 'navigation.unload_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_FETCH_START = 'navigation.fetch_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_START =\n 'navigation.domain_lookup_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_END = 'navigation.domain_lookup_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_START = 'navigation.connect_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_END = 'navigation.connect_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_SECURE_CONNECTION_START =\n 'navigation.secure_connection_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REQUEST_START = 'navigation.request_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_START = 'navigation.response_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_END = 'navigation.response_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TRANSFER_SIZE = 'navigation.transfer_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_ENCODED_BODY_SIZE = 'navigation.encoded_body_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DECODED_BODY_SIZE = 'navigation.decoded_body_size';\n"],"mappings":";AAWA,MAAa,+BAA+B;;;;AAK5C,MAAa,uBAAuB;;;;AAKpC,MAAa,sBAAsB;;;;AAKnC,MAAa,2BAA2B;;;;AAKxC,MAAa,+BAA+B;;;;AAK5C,MAAa,+CACX;;;;AAKF,MAAa,iDACX;;;;AAKF,MAAa,kCAAkC;;;;AAK/C,MAAa,iCAAiC;;;;AAK9C,MAAa,mCAAmC;;;;AAKhD,MAAa,iCAAiC;;;;AAK9C,MAAa,mCAAmC;;;;AAKhD,MAAa,qCACX;;;;AAKF,MAAa,8BAA8B;;;;AAK3C,MAAa,sCACX;;;;AAKF,MAAa,oCAAoC;;;;AAKjD,MAAa,gCAAgC;;;;AAK7C,MAAa,8BAA8B;;;;AAK3C,MAAa,0CACX;;;;AAKF,MAAa,gCAAgC;;;;AAK7C,MAAa,iCAAiC;;;;AAK9C,MAAa,+BAA+B;;;;AAK5C,MAAa,gCAAgC;;;;AAK7C,MAAa,oCAAoC;;;;AAKjD,MAAa,oCAAoC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InstrumentationConfig } from "@opentelemetry/instrumentation";
|
|
2
|
+
|
|
3
|
+
//#region src/navigation-timing/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* NavigationTimingInstrumentation Configuration
|
|
6
|
+
*/
|
|
7
|
+
interface NavigationTimingInstrumentationConfig extends InstrumentationConfig {}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { NavigationTimingInstrumentationConfig };
|
|
10
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { UserActionInstrumentationConfig } from "./types.js";
|
|
2
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
3
|
+
|
|
4
|
+
//#region src/user-action/instrumentation.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* This class automatically instruments different User Actions within the browser.
|
|
7
|
+
*/
|
|
8
|
+
declare class UserActionInstrumentation extends InstrumentationBase<UserActionInstrumentationConfig> {
|
|
9
|
+
private _onClickHandler?;
|
|
10
|
+
constructor(config?: UserActionInstrumentationConfig);
|
|
11
|
+
protected init(): never[];
|
|
12
|
+
private _getMouseButtonFromMouseEvent;
|
|
13
|
+
private onClick;
|
|
14
|
+
enable(): void;
|
|
15
|
+
disable(): void;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { UserActionInstrumentation };
|
|
19
|
+
//# sourceMappingURL=instrumentation.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getElementCSSSelector } from "../web-utils/dist/getElementCSSSelector.js";
|
|
2
|
+
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";
|
|
3
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
4
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
5
|
+
//#region src/user-action/instrumentation.ts
|
|
6
|
+
const DEFAULT_AUTO_CAPTURED_ACTIONS = ["click"];
|
|
7
|
+
const OTEL_ELEMENT_ATTRIBUTE_PREFIX = "data-otel-";
|
|
8
|
+
/**
|
|
9
|
+
* This class automatically instruments different User Actions within the browser.
|
|
10
|
+
*/
|
|
11
|
+
var UserActionInstrumentation = class extends InstrumentationBase {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
super("@opentelemetry/instrumentation-user-action", "0.1.0", config);
|
|
14
|
+
}
|
|
15
|
+
init() {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
_getMouseButtonFromMouseEvent(event) {
|
|
19
|
+
switch (event.button) {
|
|
20
|
+
case 0: return "left";
|
|
21
|
+
case 1: return "middle";
|
|
22
|
+
case 2: return "right";
|
|
23
|
+
default: return "left";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
onClick(event) {
|
|
27
|
+
const element = event.target;
|
|
28
|
+
if (!(element instanceof HTMLElement)) return;
|
|
29
|
+
if (element.hasAttribute("disabled")) return;
|
|
30
|
+
const cssSelector = getElementCSSSelector(element, {
|
|
31
|
+
useIdForTargetElement: true,
|
|
32
|
+
useIdForAncestors: true
|
|
33
|
+
});
|
|
34
|
+
const otelPrefixedAttributes = {};
|
|
35
|
+
for (const attr of element.attributes) if (attr.name.startsWith(OTEL_ELEMENT_ATTRIBUTE_PREFIX)) otelPrefixedAttributes[attr.name.slice(10)] = attr.value;
|
|
36
|
+
this.logger.emit({
|
|
37
|
+
severityNumber: SeverityNumber.INFO,
|
|
38
|
+
eventName: CLICK_EVENT_NAME,
|
|
39
|
+
attributes: {
|
|
40
|
+
[ATTR_PAGE_X]: event.pageX,
|
|
41
|
+
[ATTR_PAGE_Y]: event.pageY,
|
|
42
|
+
[ATTR_TAG_NAME]: element.tagName,
|
|
43
|
+
[ATTR_TAGS]: otelPrefixedAttributes,
|
|
44
|
+
[ATTR_MOUSE_EVENT_BUTTON]: this._getMouseButtonFromMouseEvent(event),
|
|
45
|
+
[ATTR_CSS_SELECTOR]: cssSelector
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
enable() {
|
|
50
|
+
const autoCapturedActions = this._config.autoCapturedActions ?? DEFAULT_AUTO_CAPTURED_ACTIONS;
|
|
51
|
+
if (!this._onClickHandler) this._onClickHandler = this.onClick.bind(this);
|
|
52
|
+
if (autoCapturedActions.includes("click")) document.addEventListener("click", this._onClickHandler, true);
|
|
53
|
+
}
|
|
54
|
+
disable() {
|
|
55
|
+
if (this._onClickHandler) document.removeEventListener("click", this._onClickHandler, true);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { UserActionInstrumentation };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +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 '@opentelemetry/web-utils';\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('@opentelemetry/instrumentation-user-action', '0.1.0', config);\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":";;;;;AAuBA,MAAM,gCAA0D,CAAC,QAAQ;AACzE,MAAM,gCAAgC;;;;AAKtC,IAAa,4BAAb,cAA+C,oBAAqD;CAGlG,YAAY,SAA0C,EAAE,EAAE;AACxD,QAAM,8CAA8C,SAAS,OAAO;;CAGtE,OAA0B;AACxB,SAAO,EAAE;;CAGX,8BAAsC,OAAgC;AACpE,UAAQ,MAAM,QAAd;GACE,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,KAAK,EACH,QAAO;GACT,QACE,QAAO;;;CAIb,QAAgB,OAAmB;EACjC,MAAM,UAAU,MAAM;AAEtB,MAAI,EAAE,mBAAmB,aACvB;AAGF,MAAI,QAAQ,aAAa,WAAW,CAClC;EAGF,MAAM,cAAc,sBAAsB,SAAS;GACjD,uBAAuB;GACvB,mBAAmB;GACpB,CAAC;EACF,MAAM,yBAAiD,EAAE;AAGzD,OAAK,MAAM,QAAQ,QAAQ,WACzB,KAAI,KAAK,KAAK,WAAW,8BAA8B,CACrD,wBACE,KAAK,KAAK,MAAM,GAAqC,IACnD,KAAK;AAIb,OAAK,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,MAAM;KACnE,oBAAoB;IACtB;GACF,CAAC;;CAGJ,SAAwB;EACtB,MAAM,sBACJ,KAAK,QAAQ,uBAAuB;AACtC,MAAI,CAAC,KAAK,gBACR,MAAK,kBAAkB,KAAK,QAAQ,KAAK,KAAK;AAGhD,MAAI,oBAAoB,SAAS,QAAQ,CACvC,UAAS,iBAAiB,SAAS,KAAK,iBAAiB,KAAK;;CAIlE,UAAyB;AACvB,MAAI,KAAK,gBACP,UAAS,oBAAoB,SAAS,KAAK,iBAAiB,KAAK"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/user-action/semconv.ts
|
|
2
|
+
const CLICK_EVENT_NAME = "browser.user_action.click";
|
|
3
|
+
/**
|
|
4
|
+
* @example left
|
|
5
|
+
*
|
|
6
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
7
|
+
*/
|
|
8
|
+
const ATTR_MOUSE_EVENT_BUTTON = "browser.mouse_event.button";
|
|
9
|
+
/**
|
|
10
|
+
* @example 10
|
|
11
|
+
*
|
|
12
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
13
|
+
*/
|
|
14
|
+
const ATTR_PAGE_X = "browser.page.x";
|
|
15
|
+
/**
|
|
16
|
+
* @example 10
|
|
17
|
+
*
|
|
18
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
19
|
+
*/
|
|
20
|
+
const ATTR_PAGE_Y = "browser.page.y";
|
|
21
|
+
/**
|
|
22
|
+
* @example "BUTTON"
|
|
23
|
+
*
|
|
24
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
25
|
+
*/
|
|
26
|
+
const ATTR_TAG_NAME = "browser.tag_name";
|
|
27
|
+
/**
|
|
28
|
+
* @example {"id": "123", "name": "Name"}
|
|
29
|
+
*
|
|
30
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
31
|
+
*/
|
|
32
|
+
const ATTR_TAGS = "browser.element.attributes";
|
|
33
|
+
/**
|
|
34
|
+
* @example "#main > div:nth-child(2) > button.submit"
|
|
35
|
+
*
|
|
36
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
37
|
+
*/
|
|
38
|
+
const ATTR_CSS_SELECTOR = "browser.css_selector";
|
|
39
|
+
//#endregion
|
|
40
|
+
export { ATTR_CSS_SELECTOR, ATTR_MOUSE_EVENT_BUTTON, ATTR_PAGE_X, ATTR_PAGE_Y, ATTR_TAGS, ATTR_TAG_NAME, CLICK_EVENT_NAME };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=semconv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semconv.js","names":[],"sources":["../../src/user-action/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\nexport const CLICK_EVENT_NAME = 'browser.user_action.click';\n\n/**\n * @example left\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_MOUSE_EVENT_BUTTON = 'browser.mouse_event.button';\n\n/**\n * @example 10\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_PAGE_X = 'browser.page.x';\n\n/**\n * @example 10\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_PAGE_Y = 'browser.page.y';\n\n/**\n * @example \"BUTTON\"\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_TAG_NAME = 'browser.tag_name';\n\n/**\n * @example {\"id\": \"123\", \"name\": \"Name\"}\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_TAGS = 'browser.element.attributes';\n\n/**\n * @example \"#main > div:nth-child(2) > button.submit\"\n *\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_CSS_SELECTOR = 'browser.css_selector';\n"],"mappings":";AAWA,MAAa,mBAAmB;;;;;;AAOhC,MAAa,0BAA0B;;;;;;AAOvC,MAAa,cAAc;;;;;;AAO3B,MAAa,cAAc;;;;;;AAO3B,MAAa,gBAAgB;;;;;;AAO7B,MAAa,YAAY;;;;;;AAOzB,MAAa,oBAAoB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InstrumentationConfig } from "@opentelemetry/instrumentation";
|
|
2
|
+
|
|
3
|
+
//#region src/user-action/types.d.ts
|
|
4
|
+
type AutoCapturedUserAction = 'click';
|
|
5
|
+
/**
|
|
6
|
+
* UserActionInstrumentation Configuration
|
|
7
|
+
*/
|
|
8
|
+
interface UserActionInstrumentationConfig extends InstrumentationConfig {
|
|
9
|
+
autoCapturedActions?: AutoCapturedUserAction[];
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { UserActionInstrumentationConfig };
|
|
13
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region ../web-utils/dist/getElementCSSSelector.js
|
|
2
|
+
/**
|
|
3
|
+
* Generates the CSS selector of a given element in the DOM tree.
|
|
4
|
+
*
|
|
5
|
+
* @example #main > div:nth-child(2) > button.submit
|
|
6
|
+
* @example #unique-id
|
|
7
|
+
*/
|
|
8
|
+
const getElementCSSSelector = (element, { useIdForTargetElement = false, useIdForAncestors = false } = {}) => {
|
|
9
|
+
if (element.nodeType === Node.DOCUMENT_NODE) return "";
|
|
10
|
+
const htmlElement = element;
|
|
11
|
+
const nodeValue = getNodeSelector(htmlElement, useIdForTargetElement || useIdForAncestors);
|
|
12
|
+
if (nodeValue.startsWith("#")) return nodeValue;
|
|
13
|
+
const parent = htmlElement.parentElement;
|
|
14
|
+
const parentSelector = parent ? getElementCSSSelector(parent, {
|
|
15
|
+
useIdForAncestors,
|
|
16
|
+
useIdForTargetElement: false
|
|
17
|
+
}) : "";
|
|
18
|
+
return parentSelector ? `${parentSelector} > ${nodeValue}` : nodeValue;
|
|
19
|
+
};
|
|
20
|
+
const getNodeSelector = (element, useElementId = false) => {
|
|
21
|
+
if (element.nodeType !== Node.ELEMENT_NODE) return "";
|
|
22
|
+
const htmlElement = element;
|
|
23
|
+
const id = htmlElement.getAttribute("id");
|
|
24
|
+
if (useElementId && id) {
|
|
25
|
+
if (htmlElement.ownerDocument.querySelectorAll(`#${CSS.escape(id)}`).length === 1) return `#${CSS.escape(id)}`;
|
|
26
|
+
}
|
|
27
|
+
let selector = getFullClassSelector(htmlElement);
|
|
28
|
+
const index = getNthChild(htmlElement);
|
|
29
|
+
if (index > 0) selector += `:nth-child(${index})`;
|
|
30
|
+
return selector;
|
|
31
|
+
};
|
|
32
|
+
const getNthChild = (element) => {
|
|
33
|
+
if (!element.parentElement) return 0;
|
|
34
|
+
const selector = getFullClassSelector(element);
|
|
35
|
+
const siblings = Array.from(element.parentElement.children).filter((sibling) => getFullClassSelector(sibling) === selector);
|
|
36
|
+
if (siblings.length > 1) return siblings.indexOf(element) + 1;
|
|
37
|
+
return 0;
|
|
38
|
+
};
|
|
39
|
+
const getFullClassSelector = (element) => element.localName + (element.classList.length > 0 ? Array.from(element.classList).map((cls) => `.${CSS.escape(cls)}`).join("") : "");
|
|
40
|
+
//#endregion
|
|
41
|
+
export { getElementCSSSelector };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=getElementCSSSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getElementCSSSelector.js","names":[],"sources":["../../../../web-utils/dist/getElementCSSSelector.js"],"sourcesContent":["//#region src/getElementCSSSelector.ts\n/**\n* Generates the CSS selector of a given element in the DOM tree.\n*\n* @example #main > div:nth-child(2) > button.submit\n* @example #unique-id\n*/\nconst getElementCSSSelector = (element, { useIdForTargetElement = false, useIdForAncestors = false } = {}) => {\n\tif (element.nodeType === Node.DOCUMENT_NODE) return \"\";\n\tconst htmlElement = element;\n\tconst nodeValue = getNodeSelector(htmlElement, useIdForTargetElement || useIdForAncestors);\n\tif (nodeValue.startsWith(\"#\")) return nodeValue;\n\tconst parent = htmlElement.parentElement;\n\tconst parentSelector = parent ? getElementCSSSelector(parent, {\n\t\tuseIdForAncestors,\n\t\tuseIdForTargetElement: false\n\t}) : \"\";\n\treturn parentSelector ? `${parentSelector} > ${nodeValue}` : nodeValue;\n};\nconst getNodeSelector = (element, useElementId = false) => {\n\tif (element.nodeType !== Node.ELEMENT_NODE) return \"\";\n\tconst htmlElement = element;\n\tconst id = htmlElement.getAttribute(\"id\");\n\tif (useElementId && id) {\n\t\tif (htmlElement.ownerDocument.querySelectorAll(`#${CSS.escape(id)}`).length === 1) return `#${CSS.escape(id)}`;\n\t}\n\tlet selector = getFullClassSelector(htmlElement);\n\tconst index = getNthChild(htmlElement);\n\tif (index > 0) selector += `:nth-child(${index})`;\n\treturn selector;\n};\nconst getNthChild = (element) => {\n\tif (!element.parentElement) return 0;\n\tconst selector = getFullClassSelector(element);\n\tconst siblings = Array.from(element.parentElement.children).filter((sibling) => getFullClassSelector(sibling) === selector);\n\tif (siblings.length > 1) return siblings.indexOf(element) + 1;\n\treturn 0;\n};\nconst getFullClassSelector = (element) => element.localName + (element.classList.length > 0 ? Array.from(element.classList).map((cls) => `.${CSS.escape(cls)}`).join(\"\") : \"\");\n//#endregion\nexport { getElementCSSSelector };\n\n//# sourceMappingURL=getElementCSSSelector.js.map"],"mappings":";;;;;;;AAOA,MAAM,yBAAyB,SAAS,EAAE,wBAAwB,OAAO,oBAAoB,UAAU,EAAE,KAAK;AAC7G,KAAI,QAAQ,aAAa,KAAK,cAAe,QAAO;CACpD,MAAM,cAAc;CACpB,MAAM,YAAY,gBAAgB,aAAa,yBAAyB,kBAAkB;AAC1F,KAAI,UAAU,WAAW,IAAI,CAAE,QAAO;CACtC,MAAM,SAAS,YAAY;CAC3B,MAAM,iBAAiB,SAAS,sBAAsB,QAAQ;EAC7D;EACA,uBAAuB;EACvB,CAAC,GAAG;AACL,QAAO,iBAAiB,GAAG,eAAe,KAAK,cAAc;;AAE9D,MAAM,mBAAmB,SAAS,eAAe,UAAU;AAC1D,KAAI,QAAQ,aAAa,KAAK,aAAc,QAAO;CACnD,MAAM,cAAc;CACpB,MAAM,KAAK,YAAY,aAAa,KAAK;AACzC,KAAI,gBAAgB;MACf,YAAY,cAAc,iBAAiB,IAAI,IAAI,OAAO,GAAG,GAAG,CAAC,WAAW,EAAG,QAAO,IAAI,IAAI,OAAO,GAAG;;CAE7G,IAAI,WAAW,qBAAqB,YAAY;CAChD,MAAM,QAAQ,YAAY,YAAY;AACtC,KAAI,QAAQ,EAAG,aAAY,cAAc,MAAM;AAC/C,QAAO;;AAER,MAAM,eAAe,YAAY;AAChC,KAAI,CAAC,QAAQ,cAAe,QAAO;CACnC,MAAM,WAAW,qBAAqB,QAAQ;CAC9C,MAAM,WAAW,MAAM,KAAK,QAAQ,cAAc,SAAS,CAAC,QAAQ,YAAY,qBAAqB,QAAQ,KAAK,SAAS;AAC3H,KAAI,SAAS,SAAS,EAAG,QAAO,SAAS,QAAQ,QAAQ,GAAG;AAC5D,QAAO;;AAER,MAAM,wBAAwB,YAAY,QAAQ,aAAa,QAAQ,UAAU,SAAS,IAAI,MAAM,KAAK,QAAQ,UAAU,CAAC,KAAK,QAAQ,IAAI,IAAI,OAAO,IAAI,GAAG,CAAC,KAAK,GAAG,GAAG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { WebVitalsInstrumentationConfig } from "./types.js";
|
|
2
|
+
import { InstrumentationBase } from "@opentelemetry/instrumentation";
|
|
3
|
+
|
|
4
|
+
//#region src/web-vitals/instrumentation.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Instrumentation for Core Web Vitals using the `web-vitals` library.
|
|
7
|
+
* https://github.com/GoogleChrome/web-vitals
|
|
8
|
+
*
|
|
9
|
+
* Note: The `web-vitals` library does not support removing listeners once
|
|
10
|
+
* registered. Calling `disable()` will stop emitting logs, but the underlying
|
|
11
|
+
* listeners remain active. Calling `enable()` again will resume emission.
|
|
12
|
+
*/
|
|
13
|
+
declare class WebVitalsInstrumentation extends InstrumentationBase<WebVitalsInstrumentationConfig> {
|
|
14
|
+
private _isEnabled;
|
|
15
|
+
private _listenersRegistered;
|
|
16
|
+
private _applyCustomLogRecordData?;
|
|
17
|
+
private _includeRawAttribution;
|
|
18
|
+
constructor(config?: WebVitalsInstrumentationConfig);
|
|
19
|
+
protected init(): never[];
|
|
20
|
+
/**
|
|
21
|
+
* Enables the instrumentation and registers web-vitals listeners.
|
|
22
|
+
* Listeners are registered only once. If disabled, subsequent calls resume emission.
|
|
23
|
+
*/
|
|
24
|
+
enable(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Disables the instrumentation, pausing log emission.
|
|
27
|
+
* Listeners remain active due to web-vitals library limitations.
|
|
28
|
+
*/
|
|
29
|
+
disable(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Gets the timestamp for a metric based on attribution timing.
|
|
32
|
+
* Returns undefined to let OTel use the current time for metrics without
|
|
33
|
+
* specific timing information.
|
|
34
|
+
*/
|
|
35
|
+
private _getTimestampForMetric;
|
|
36
|
+
private _emitWebVital;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { WebVitalsInstrumentation };
|
|
40
|
+
//# sourceMappingURL=instrumentation.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ATTR_WEB_VITAL_DELTA, ATTR_WEB_VITAL_ID, ATTR_WEB_VITAL_NAME, ATTR_WEB_VITAL_NAVIGATION_TYPE, ATTR_WEB_VITAL_RATING, ATTR_WEB_VITAL_VALUE, WEB_VITAL_EVENT_NAME } from "./semconv.js";
|
|
2
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
3
|
+
import { InstrumentationBase, safeExecuteInTheMiddle } from "@opentelemetry/instrumentation";
|
|
4
|
+
import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals/attribution";
|
|
5
|
+
//#region src/web-vitals/instrumentation.ts
|
|
6
|
+
/**
|
|
7
|
+
* Instrumentation for Core Web Vitals using the `web-vitals` library.
|
|
8
|
+
* https://github.com/GoogleChrome/web-vitals
|
|
9
|
+
*
|
|
10
|
+
* Note: The `web-vitals` library does not support removing listeners once
|
|
11
|
+
* registered. Calling `disable()` will stop emitting logs, but the underlying
|
|
12
|
+
* listeners remain active. Calling `enable()` again will resume emission.
|
|
13
|
+
*/
|
|
14
|
+
var WebVitalsInstrumentation = class extends InstrumentationBase {
|
|
15
|
+
_applyCustomLogRecordData;
|
|
16
|
+
_includeRawAttribution;
|
|
17
|
+
constructor(config = {}) {
|
|
18
|
+
super("@opentelemetry/instrumentation-web-vitals", "0.1.0", config);
|
|
19
|
+
this._applyCustomLogRecordData = config.applyCustomLogRecordData;
|
|
20
|
+
this._includeRawAttribution = config.includeRawAttribution ?? false;
|
|
21
|
+
}
|
|
22
|
+
init() {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Enables the instrumentation and registers web-vitals listeners.
|
|
27
|
+
* Listeners are registered only once. If disabled, subsequent calls resume emission.
|
|
28
|
+
*/
|
|
29
|
+
enable() {
|
|
30
|
+
if (typeof PerformanceObserver === "undefined") {
|
|
31
|
+
this._diag.debug("PerformanceObserver not supported, web vitals will not be collected");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this._isEnabled = true;
|
|
35
|
+
if (this._listenersRegistered) {
|
|
36
|
+
this._diag.debug("Listeners already registered, resuming emission");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this._listenersRegistered = true;
|
|
40
|
+
this._diag.debug(`Registering listeners`);
|
|
41
|
+
onCLS((metric) => this._emitWebVital(metric));
|
|
42
|
+
onINP((metric) => this._emitWebVital(metric));
|
|
43
|
+
onLCP((metric) => this._emitWebVital(metric));
|
|
44
|
+
onFCP((metric) => this._emitWebVital(metric));
|
|
45
|
+
onTTFB((metric) => this._emitWebVital(metric));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Disables the instrumentation, pausing log emission.
|
|
49
|
+
* Listeners remain active due to web-vitals library limitations.
|
|
50
|
+
*/
|
|
51
|
+
disable() {
|
|
52
|
+
this._isEnabled = false;
|
|
53
|
+
this._diag.debug("Instrumentation disabled, pausing emission");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Gets the timestamp for a metric based on attribution timing.
|
|
57
|
+
* Returns undefined to let OTel use the current time for metrics without
|
|
58
|
+
* specific timing information.
|
|
59
|
+
*/
|
|
60
|
+
_getTimestampForMetric(metric) {
|
|
61
|
+
if (metric.name === "CLS") {
|
|
62
|
+
const { attribution } = metric;
|
|
63
|
+
if (attribution.largestShiftTime !== void 0) return attribution.largestShiftTime;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (metric.name === "INP") {
|
|
67
|
+
const { attribution } = metric;
|
|
68
|
+
return attribution.interactionTime;
|
|
69
|
+
}
|
|
70
|
+
return metric.value;
|
|
71
|
+
}
|
|
72
|
+
_emitWebVital(metric) {
|
|
73
|
+
if (!this._isEnabled) return;
|
|
74
|
+
const attributes = {
|
|
75
|
+
[ATTR_WEB_VITAL_NAME]: metric.name.toLowerCase(),
|
|
76
|
+
[ATTR_WEB_VITAL_VALUE]: metric.value,
|
|
77
|
+
[ATTR_WEB_VITAL_DELTA]: metric.delta,
|
|
78
|
+
[ATTR_WEB_VITAL_RATING]: metric.rating,
|
|
79
|
+
[ATTR_WEB_VITAL_ID]: metric.id,
|
|
80
|
+
[ATTR_WEB_VITAL_NAVIGATION_TYPE]: metric.navigationType
|
|
81
|
+
};
|
|
82
|
+
const timestamp = this._getTimestampForMetric(metric);
|
|
83
|
+
const logRecord = {
|
|
84
|
+
eventName: WEB_VITAL_EVENT_NAME,
|
|
85
|
+
severityNumber: SeverityNumber.INFO,
|
|
86
|
+
attributes,
|
|
87
|
+
...this._includeRawAttribution ? { body: JSON.stringify(metric.attribution) } : {},
|
|
88
|
+
...timestamp !== void 0 ? { timestamp } : {}
|
|
89
|
+
};
|
|
90
|
+
if (this._applyCustomLogRecordData) safeExecuteInTheMiddle(() => this._applyCustomLogRecordData?.(logRecord), (error) => {
|
|
91
|
+
if (error) this._diag.error("applyCustomLogRecordData hook failed", error);
|
|
92
|
+
}, true);
|
|
93
|
+
this.logger.emit(logRecord);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { WebVitalsInstrumentation };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/web-vitals/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 { LogRecord } from '@opentelemetry/api-logs';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport {\n InstrumentationBase,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport type {\n CLSMetricWithAttribution,\n INPMetricWithAttribution,\n MetricWithAttribution,\n} from 'web-vitals/attribution';\nimport { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals/attribution';\nimport {\n ATTR_WEB_VITAL_DELTA,\n ATTR_WEB_VITAL_ID,\n ATTR_WEB_VITAL_NAME,\n ATTR_WEB_VITAL_NAVIGATION_TYPE,\n ATTR_WEB_VITAL_RATING,\n ATTR_WEB_VITAL_VALUE,\n WEB_VITAL_EVENT_NAME,\n} from './semconv.ts';\nimport type { WebVitalsInstrumentationConfig } from './types.ts';\n\n/**\n * Instrumentation for Core Web Vitals using the `web-vitals` library.\n * https://github.com/GoogleChrome/web-vitals\n *\n * Note: The `web-vitals` library does not support removing listeners once\n * registered. Calling `disable()` will stop emitting logs, but the underlying\n * listeners remain active. Calling `enable()` again will resume emission.\n */\nexport class WebVitalsInstrumentation extends InstrumentationBase<WebVitalsInstrumentationConfig> {\n // Using `declare` is required here: InstrumentationBase calls enable() during\n // construction, and standard field initialization would reset this flag after\n // super() returns, breaking the duplicate-registration guard.\n private declare _isEnabled: boolean;\n private declare _listenersRegistered: boolean;\n private _applyCustomLogRecordData?: (logRecord: LogRecord) => void;\n private _includeRawAttribution: boolean;\n\n constructor(config: WebVitalsInstrumentationConfig = {}) {\n super('@opentelemetry/instrumentation-web-vitals', '0.1.0', config);\n this._applyCustomLogRecordData = config.applyCustomLogRecordData;\n this._includeRawAttribution = config.includeRawAttribution ?? false;\n }\n\n protected override init() {\n return [];\n }\n\n /**\n * Enables the instrumentation and registers web-vitals listeners.\n * Listeners are registered only once. If disabled, subsequent calls resume emission.\n */\n override enable(): void {\n if (typeof PerformanceObserver === 'undefined') {\n this._diag.debug(\n 'PerformanceObserver not supported, web vitals will not be collected',\n );\n return;\n }\n\n this._isEnabled = true;\n\n if (this._listenersRegistered) {\n this._diag.debug('Listeners already registered, resuming emission');\n return;\n }\n\n this._listenersRegistered = true;\n this._diag.debug(`Registering listeners`);\n // CLS is only supported in Chromium. See:\n // https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#browser-support\n onCLS((metric) => this._emitWebVital(metric));\n onINP((metric) => this._emitWebVital(metric));\n onLCP((metric) => this._emitWebVital(metric));\n onFCP((metric) => this._emitWebVital(metric));\n onTTFB((metric) => this._emitWebVital(metric));\n }\n\n /**\n * Disables the instrumentation, pausing log emission.\n * Listeners remain active due to web-vitals library limitations.\n */\n override disable(): void {\n this._isEnabled = false;\n this._diag.debug('Instrumentation disabled, pausing emission');\n }\n\n /**\n * Gets the timestamp for a metric based on attribution timing.\n * Returns undefined to let OTel use the current time for metrics without\n * specific timing information.\n */\n private _getTimestampForMetric(\n metric: MetricWithAttribution,\n ): number | undefined {\n if (metric.name === 'CLS') {\n const { attribution } = metric as CLSMetricWithAttribution;\n if (attribution.largestShiftTime !== undefined) {\n return attribution.largestShiftTime;\n }\n return undefined;\n }\n if (metric.name === 'INP') {\n const { attribution } = metric as INPMetricWithAttribution;\n return attribution.interactionTime;\n }\n // FCP, LCP, TTFB: metric.value is already DOMHighResTimeStamp of the event\n return metric.value;\n }\n\n private _emitWebVital(metric: MetricWithAttribution): void {\n if (!this._isEnabled) {\n return;\n }\n const attributes: Attributes = {\n [ATTR_WEB_VITAL_NAME]: metric.name.toLowerCase(),\n [ATTR_WEB_VITAL_VALUE]: metric.value,\n // `delta` equals `value` on the first emission; subsequent emissions report only the change\n [ATTR_WEB_VITAL_DELTA]: metric.delta,\n [ATTR_WEB_VITAL_RATING]: metric.rating,\n [ATTR_WEB_VITAL_ID]: metric.id,\n [ATTR_WEB_VITAL_NAVIGATION_TYPE]: metric.navigationType,\n };\n\n const timestamp = this._getTimestampForMetric(metric);\n\n const logRecord: LogRecord = {\n eventName: WEB_VITAL_EVENT_NAME,\n severityNumber: SeverityNumber.INFO,\n attributes,\n ...(this._includeRawAttribution\n ? { body: JSON.stringify(metric.attribution) }\n : {}),\n ...(timestamp !== undefined ? { timestamp } : {}),\n };\n\n if (this._applyCustomLogRecordData) {\n safeExecuteInTheMiddle(\n () => this._applyCustomLogRecordData?.(logRecord),\n (error) => {\n if (error) {\n this._diag.error('applyCustomLogRecordData hook failed', error);\n }\n },\n true,\n );\n }\n\n this.logger.emit(logRecord);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAqCA,IAAa,2BAAb,cAA8C,oBAAoD;CAMhG;CACA;CAEA,YAAY,SAAyC,EAAE,EAAE;AACvD,QAAM,6CAA6C,SAAS,OAAO;AACnE,OAAK,4BAA4B,OAAO;AACxC,OAAK,yBAAyB,OAAO,yBAAyB;;CAGhE,OAA0B;AACxB,SAAO,EAAE;;;;;;CAOX,SAAwB;AACtB,MAAI,OAAO,wBAAwB,aAAa;AAC9C,QAAK,MAAM,MACT,sEACD;AACD;;AAGF,OAAK,aAAa;AAElB,MAAI,KAAK,sBAAsB;AAC7B,QAAK,MAAM,MAAM,kDAAkD;AACnE;;AAGF,OAAK,uBAAuB;AAC5B,OAAK,MAAM,MAAM,wBAAwB;AAGzC,SAAO,WAAW,KAAK,cAAc,OAAO,CAAC;AAC7C,SAAO,WAAW,KAAK,cAAc,OAAO,CAAC;AAC7C,SAAO,WAAW,KAAK,cAAc,OAAO,CAAC;AAC7C,SAAO,WAAW,KAAK,cAAc,OAAO,CAAC;AAC7C,UAAQ,WAAW,KAAK,cAAc,OAAO,CAAC;;;;;;CAOhD,UAAyB;AACvB,OAAK,aAAa;AAClB,OAAK,MAAM,MAAM,6CAA6C;;;;;;;CAQhE,uBACE,QACoB;AACpB,MAAI,OAAO,SAAS,OAAO;GACzB,MAAM,EAAE,gBAAgB;AACxB,OAAI,YAAY,qBAAqB,KAAA,EACnC,QAAO,YAAY;AAErB;;AAEF,MAAI,OAAO,SAAS,OAAO;GACzB,MAAM,EAAE,gBAAgB;AACxB,UAAO,YAAY;;AAGrB,SAAO,OAAO;;CAGhB,cAAsB,QAAqC;AACzD,MAAI,CAAC,KAAK,WACR;EAEF,MAAM,aAAyB;IAC5B,sBAAsB,OAAO,KAAK,aAAa;IAC/C,uBAAuB,OAAO;IAE9B,uBAAuB,OAAO;IAC9B,wBAAwB,OAAO;IAC/B,oBAAoB,OAAO;IAC3B,iCAAiC,OAAO;GAC1C;EAED,MAAM,YAAY,KAAK,uBAAuB,OAAO;EAErD,MAAM,YAAuB;GAC3B,WAAW;GACX,gBAAgB,eAAe;GAC/B;GACA,GAAI,KAAK,yBACL,EAAE,MAAM,KAAK,UAAU,OAAO,YAAY,EAAE,GAC5C,EAAE;GACN,GAAI,cAAc,KAAA,IAAY,EAAE,WAAW,GAAG,EAAE;GACjD;AAED,MAAI,KAAK,0BACP,8BACQ,KAAK,4BAA4B,UAAU,GAChD,UAAU;AACT,OAAI,MACF,MAAK,MAAM,MAAM,wCAAwC,MAAM;KAGnE,KACD;AAGH,OAAK,OAAO,KAAK,UAAU"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/web-vitals/semconv.ts
|
|
2
|
+
const WEB_VITAL_EVENT_NAME = "browser.web_vital";
|
|
3
|
+
/**
|
|
4
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
5
|
+
*/
|
|
6
|
+
const ATTR_WEB_VITAL_NAME = "browser.web_vital.name";
|
|
7
|
+
/**
|
|
8
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
9
|
+
*/
|
|
10
|
+
const ATTR_WEB_VITAL_VALUE = "browser.web_vital.value";
|
|
11
|
+
/**
|
|
12
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
13
|
+
*/
|
|
14
|
+
const ATTR_WEB_VITAL_RATING = "browser.web_vital.rating";
|
|
15
|
+
/**
|
|
16
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
17
|
+
*/
|
|
18
|
+
const ATTR_WEB_VITAL_DELTA = "browser.web_vital.delta";
|
|
19
|
+
/**
|
|
20
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
21
|
+
*/
|
|
22
|
+
const ATTR_WEB_VITAL_ID = "browser.web_vital.id";
|
|
23
|
+
/**
|
|
24
|
+
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
|
|
25
|
+
*/
|
|
26
|
+
const ATTR_WEB_VITAL_NAVIGATION_TYPE = "browser.web_vital.navigation_type";
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ATTR_WEB_VITAL_DELTA, ATTR_WEB_VITAL_ID, ATTR_WEB_VITAL_NAME, ATTR_WEB_VITAL_NAVIGATION_TYPE, ATTR_WEB_VITAL_RATING, ATTR_WEB_VITAL_VALUE, WEB_VITAL_EVENT_NAME };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=semconv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semconv.js","names":[],"sources":["../../src/web-vitals/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\nexport const WEB_VITAL_EVENT_NAME = 'browser.web_vital';\n\n// Core metric attributes\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_NAME = 'browser.web_vital.name';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_VALUE = 'browser.web_vital.value';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_RATING = 'browser.web_vital.rating';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_DELTA = 'browser.web_vital.delta';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_ID = 'browser.web_vital.id';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_WEB_VITAL_NAVIGATION_TYPE =\n 'browser.web_vital.navigation_type';\n"],"mappings":";AAWA,MAAa,uBAAuB;;;;AAOpC,MAAa,sBAAsB;;;;AAKnC,MAAa,uBAAuB;;;;AAKpC,MAAa,wBAAwB;;;;AAKrC,MAAa,uBAAuB;;;;AAKpC,MAAa,oBAAoB;;;;AAKjC,MAAa,iCACX"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LogRecord } from "@opentelemetry/api-logs";
|
|
2
|
+
import { InstrumentationConfig } from "@opentelemetry/instrumentation";
|
|
3
|
+
|
|
4
|
+
//#region src/web-vitals/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* WebVitalsInstrumentation Configuration
|
|
7
|
+
*/
|
|
8
|
+
interface WebVitalsInstrumentationConfig extends InstrumentationConfig {
|
|
9
|
+
/**
|
|
10
|
+
* @experimental
|
|
11
|
+
* When true, sets the log record body to the JSON-stringified
|
|
12
|
+
* `web-vitals` attribution object for the metric.
|
|
13
|
+
*
|
|
14
|
+
* Note: `applyCustomLogRecordData` runs after the body is set.
|
|
15
|
+
* If the hook assigns a new `body`, it will overwrite the attribution data.
|
|
16
|
+
*/
|
|
17
|
+
includeRawAttribution?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Hook to modify log records before they are emitted.
|
|
20
|
+
* Use this to add custom attributes or modify the log record.
|
|
21
|
+
*/
|
|
22
|
+
applyCustomLogRecordData?: (logRecord: LogRecord) => void;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { WebVitalsInstrumentationConfig };
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opentelemetry/browser-instrumentation",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenTelemetry browser instrumentations.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"opentelemetry",
|
|
7
|
+
"browser",
|
|
8
|
+
"web",
|
|
9
|
+
"instrumentation",
|
|
10
|
+
"navigation-timing",
|
|
11
|
+
"user-action",
|
|
12
|
+
"web-vitals"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/open-telemetry/opentelemetry-browser",
|
|
15
|
+
"bugs": "https://github.com/open-telemetry/opentelemetry-browser/issues",
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"author": "OpenTelemetry Authors",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/open-telemetry/opentelemetry-browser.git",
|
|
21
|
+
"directory": "packages/instrumentation"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"exports": {
|
|
25
|
+
"./experimental/navigation-timing": "./dist/navigation-timing/index.js",
|
|
26
|
+
"./experimental/user-action": "./dist/user-action/index.js",
|
|
27
|
+
"./experimental/web-vitals": "./dist/web-vitals/index.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsdown",
|
|
34
|
+
"watch": "tsdown --watch --no-clean",
|
|
35
|
+
"check-types": "tsc",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"test:coverage": "vitest --coverage"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@opentelemetry/api-logs": "^0.213.0",
|
|
42
|
+
"@opentelemetry/instrumentation": "^0.213.0",
|
|
43
|
+
"web-vitals": "^5.1.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@opentelemetry/api": "^1.9.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@opentelemetry/test-utils": "*",
|
|
50
|
+
"@opentelemetry/web-utils": "*"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|