@dash0/sdk-web 0.13.4 → 0.14.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 +21 -0
- package/dist/dash0.iife.js +1 -1
- package/dist/dash0.iife.js.map +1 -1
- package/dist/dash0.js +1 -1
- package/dist/dash0.js.map +1 -1
- package/dist/dash0.umd.cjs +1 -1
- package/dist/dash0.umd.cjs.map +1 -1
- package/dist/modules/api/init.js +19 -4
- package/dist/modules/api/init_test.js +86 -0
- package/dist/modules/attributes/url.js +22 -12
- package/dist/modules/attributes/url_test.js +326 -0
- package/dist/modules/types/errors.js +1 -0
- package/dist/modules/types/globals.js +1 -0
- package/dist/modules/types/options.js +1 -0
- package/dist/modules/types/otlp.js +1 -0
- package/dist/modules/utils/fn.js +3 -0
- package/dist/modules/vars.js +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/api/attributes.d.ts +1 -1
- package/dist/types/api/events.d.ts +1 -1
- package/dist/types/api/init.d.ts +1 -30
- package/dist/types/api/init_test.d.ts +1 -0
- package/dist/types/api/report-error.d.ts +1 -1
- package/dist/types/attributes/common.d.ts +1 -1
- package/dist/types/attributes/url.d.ts +11 -1
- package/dist/types/attributes/url_test.d.ts +1 -0
- package/dist/types/entrypoint/npm-package.d.ts +1 -1
- package/dist/types/instrumentations/errors/unhandled-error.d.ts +1 -1
- package/dist/types/transport/index.d.ts +1 -1
- package/dist/types/types/errors.d.ts +8 -0
- package/dist/types/types/globals.d.ts +12 -0
- package/dist/types/types/options.d.ts +35 -0
- package/dist/types/types/otlp.d.ts +104 -0
- package/dist/types/utils/fn.d.ts +1 -0
- package/dist/types/utils/otel/attributes.d.ts +1 -1
- package/dist/types/utils/otel/error.d.ts +1 -1
- package/dist/types/utils/otel/span.d.ts +1 -1
- package/dist/types/vars.d.ts +11 -1
- package/package.json +1 -1
- package/src/api/attributes.ts +1 -1
- package/src/api/events.ts +1 -1
- package/src/api/init.ts +25 -52
- package/src/api/init_test.ts +108 -0
- package/src/api/report-error.ts +1 -1
- package/src/attributes/common.ts +1 -1
- package/src/attributes/common_test.ts +1 -1
- package/src/attributes/url.ts +35 -13
- package/src/attributes/url_test.ts +417 -0
- package/src/entrypoint/npm-package.ts +2 -1
- package/src/instrumentations/errors/unhandled-error.ts +2 -2
- package/src/instrumentations/navigation/event.ts +1 -1
- package/src/instrumentations/navigation/page-load.ts +1 -1
- package/src/instrumentations/web-vitals.ts +1 -1
- package/src/transport/index.ts +1 -1
- package/src/types/errors.ts +9 -0
- package/src/types/globals.ts +19 -0
- package/src/types/options.ts +56 -0
- package/src/types/otlp.ts +121 -0
- package/src/utils/fn.ts +4 -0
- package/src/utils/otel/attributes.ts +1 -1
- package/src/utils/otel/attributes_test.ts +1 -1
- package/src/utils/otel/error.ts +1 -1
- package/src/utils/otel/span.ts +1 -1
- package/src/vars.ts +14 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AttributeValueType } from "../utils/otel";
|
|
2
|
+
import { AnyValue } from "./otlp";
|
|
3
|
+
import { Endpoint, Vars } from "../vars";
|
|
4
|
+
export type InstrumentationName = "@dash0/navigation" | "@dash0/web-vitals" | "@dash0/error" | "@dash0/fetch";
|
|
5
|
+
export type InitOptions = {
|
|
6
|
+
serviceName: string;
|
|
7
|
+
serviceVersion?: string;
|
|
8
|
+
environment?: string;
|
|
9
|
+
deploymentName?: string;
|
|
10
|
+
deploymentId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Additional attributes to include with transmitted signals
|
|
13
|
+
*/
|
|
14
|
+
additionalSignalAttributes?: Record<string, AttributeValueType | AnyValue>;
|
|
15
|
+
/**
|
|
16
|
+
* OTLP endpoints to which the generated telemetry should be sent to.
|
|
17
|
+
*/
|
|
18
|
+
endpoint: Endpoint | Endpoint[];
|
|
19
|
+
/**
|
|
20
|
+
* Which instrumentations to enable. Defaults to undefined, which means all instrumentations.
|
|
21
|
+
*/
|
|
22
|
+
enabledInstrumentations?: InstrumentationName[];
|
|
23
|
+
/**
|
|
24
|
+
* The session inactivity timeout. Session inactivity is the maximum
|
|
25
|
+
* allowed time to pass between two page loads before the session is considered
|
|
26
|
+
* to be expired. Also think of cache time-to-idle configuration options.
|
|
27
|
+
*/
|
|
28
|
+
sessionInactivityTimeoutMillis?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The default session termination timeout. Session termination is the maximum
|
|
31
|
+
* allowed time to pass since session start before the session is considered
|
|
32
|
+
* to be expired. Also think of cache time-to-live configuration options.
|
|
33
|
+
*/
|
|
34
|
+
sessionTerminationTimeoutMillis?: number;
|
|
35
|
+
} & Partial<Pick<Vars, "ignoreUrls" | "ignoreErrorMessages" | "wrapEventHandlers" | "wrapTimers" | "propagateTraceHeadersCorsURLs" | "maxWaitForResourceTimingsMillis" | "maxToleranceForResourceTimingsMillis" | "headersToCapture" | "urlAttributeScrubber" | "pageViewInstrumentation">>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { LOG_SEVERITY_NUMBER, LOG_SEVERITY_TEXT } from "../semantic-conventions";
|
|
2
|
+
export type AnyValue = {
|
|
3
|
+
["stringValue"]?: string;
|
|
4
|
+
["boolValue"]?: boolean;
|
|
5
|
+
/** Format: int64 */
|
|
6
|
+
["intValue"]?: string;
|
|
7
|
+
/** Format: double */
|
|
8
|
+
["doubleValue"]?: number;
|
|
9
|
+
["arrayValue"]?: ArrayValue;
|
|
10
|
+
["kvlistValue"]?: KeyValueList;
|
|
11
|
+
/** Format: byte */
|
|
12
|
+
["bytesValue"]?: string;
|
|
13
|
+
};
|
|
14
|
+
export type ArrayValue = {
|
|
15
|
+
["values"]: AnyValue[];
|
|
16
|
+
};
|
|
17
|
+
export type KeyValue = {
|
|
18
|
+
["key"]: string;
|
|
19
|
+
["value"]: AnyValue;
|
|
20
|
+
};
|
|
21
|
+
export type KeyValueList = {
|
|
22
|
+
["values"]: KeyValue[];
|
|
23
|
+
};
|
|
24
|
+
export type Resource = {
|
|
25
|
+
["attributes"]: KeyValue[];
|
|
26
|
+
};
|
|
27
|
+
export type InstrumentationScope = {
|
|
28
|
+
["name"]?: string;
|
|
29
|
+
["version"]?: string;
|
|
30
|
+
["attributes"]: KeyValue[];
|
|
31
|
+
};
|
|
32
|
+
export type ExportLogsServiceRequest = {
|
|
33
|
+
resourceLogs: ResourceLogs[];
|
|
34
|
+
};
|
|
35
|
+
export type ResourceLogs = {
|
|
36
|
+
resource: Resource;
|
|
37
|
+
scopeLogs: ScopeLogs[];
|
|
38
|
+
};
|
|
39
|
+
export type ScopeLogs = {
|
|
40
|
+
scope?: InstrumentationScope;
|
|
41
|
+
logRecords: LogRecord[];
|
|
42
|
+
schemaUrl?: string;
|
|
43
|
+
};
|
|
44
|
+
export type LogRecord = {
|
|
45
|
+
/** Format: uint64 */
|
|
46
|
+
timeUnixNano: string;
|
|
47
|
+
severityNumber?: LOG_SEVERITY_NUMBER;
|
|
48
|
+
severityText?: LOG_SEVERITY_TEXT;
|
|
49
|
+
body?: AnyValue;
|
|
50
|
+
attributes: KeyValue[];
|
|
51
|
+
/** Format: byte */
|
|
52
|
+
traceId?: string;
|
|
53
|
+
/** Format: byte */
|
|
54
|
+
spanId?: string;
|
|
55
|
+
};
|
|
56
|
+
export type ExportTraceServiceRequest = {
|
|
57
|
+
resourceSpans: ResourceSpans[];
|
|
58
|
+
};
|
|
59
|
+
export type ResourceSpans = {
|
|
60
|
+
resource: Resource;
|
|
61
|
+
scopeSpans: ScopeSpans[];
|
|
62
|
+
};
|
|
63
|
+
export type ScopeSpans = {
|
|
64
|
+
scope?: InstrumentationScope;
|
|
65
|
+
spans: Span[];
|
|
66
|
+
};
|
|
67
|
+
export type SpanStatus = {
|
|
68
|
+
message?: string;
|
|
69
|
+
code: 0 | 1 | 2;
|
|
70
|
+
};
|
|
71
|
+
export type SpanEvent = {
|
|
72
|
+
timeUnixNano: string;
|
|
73
|
+
name: string;
|
|
74
|
+
attributes: KeyValue[];
|
|
75
|
+
};
|
|
76
|
+
export type SpanLink = {
|
|
77
|
+
/** Format: byte */
|
|
78
|
+
traceId: string;
|
|
79
|
+
/** Format: byte */
|
|
80
|
+
spanId: string;
|
|
81
|
+
traceState?: string;
|
|
82
|
+
attributes: KeyValue[];
|
|
83
|
+
droppedAttributesCount: number;
|
|
84
|
+
flags?: number;
|
|
85
|
+
};
|
|
86
|
+
export type Span = {
|
|
87
|
+
/** Format: byte */
|
|
88
|
+
traceId: string;
|
|
89
|
+
/** Format: byte */
|
|
90
|
+
spanId: string;
|
|
91
|
+
traceState?: string;
|
|
92
|
+
/** Format: byte */
|
|
93
|
+
parentSpanId?: string;
|
|
94
|
+
name: string;
|
|
95
|
+
kind: number;
|
|
96
|
+
startTimeUnixNano: string;
|
|
97
|
+
endTimeUnixNano: string;
|
|
98
|
+
attributes: KeyValue[];
|
|
99
|
+
events: SpanEvent[];
|
|
100
|
+
/** Format: int64 */
|
|
101
|
+
droppedEventsCount?: number;
|
|
102
|
+
links: SpanLink[];
|
|
103
|
+
status: SpanStatus;
|
|
104
|
+
};
|
package/dist/types/utils/fn.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyValue, KeyValue } from "
|
|
1
|
+
import { AnyValue, KeyValue } from "../../types/otlp";
|
|
2
2
|
type PrimitiveAttributeValue = string | number | boolean;
|
|
3
3
|
export type AttributeValueType = PrimitiveAttributeValue | Array<PrimitiveAttributeValue> | Record<string, PrimitiveAttributeValue>;
|
|
4
4
|
export declare function toAnyValue(value: AttributeValueType | AnyValue): AnyValue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyValue, Span, SpanStatus } from "
|
|
1
|
+
import { KeyValue, Span, SpanStatus } from "../../types/otlp";
|
|
2
2
|
export type InProgressSpan = Omit<Span, "endTimeUnixNano">;
|
|
3
3
|
export declare function startSpan(name: string): InProgressSpan;
|
|
4
4
|
export declare function endSpan(span: InProgressSpan, status: SpanStatus | undefined, durationNano: number | undefined): Span;
|
package/dist/types/vars.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AnyValue, InstrumentationScope, KeyValue, Resource } from "../types/otlp";
|
|
2
1
|
import { AttributeValueType } from "./utils/otel";
|
|
2
|
+
import { AnyValue, InstrumentationScope, KeyValue, Resource } from "./types/otlp";
|
|
3
|
+
import { UrlAttributeScrubber } from "./attributes";
|
|
3
4
|
export type Endpoint = {
|
|
4
5
|
/**
|
|
5
6
|
* OTLP HTTP URL excluding the /v1/* prefix
|
|
@@ -118,6 +119,15 @@ export type Vars = {
|
|
|
118
119
|
* These headers will be transferred as span attributes
|
|
119
120
|
*/
|
|
120
121
|
headersToCapture: RegExp[];
|
|
122
|
+
/**
|
|
123
|
+
* Allows the application of a custom scrubbing function to url attributes before they are applied to signals.
|
|
124
|
+
* This is invoked for each url processed for inclusion in signal attributes. For example this applies both to `page.url.*`
|
|
125
|
+
* and `url.*` attribute namespaces.
|
|
126
|
+
* Sensitive parts of the url attributes should be replaced with `REDACTED`,
|
|
127
|
+
* avoid partially or fully dropping attributes to preserve telemetry quality.
|
|
128
|
+
* Note: basic auth credentials in urls are automatically redacted before this is invoked.
|
|
129
|
+
*/
|
|
130
|
+
urlAttributeScrubber: UrlAttributeScrubber;
|
|
121
131
|
pageViewInstrumentation: PageViewInstrumentationSettings;
|
|
122
132
|
};
|
|
123
133
|
export declare const vars: Vars;
|
package/package.json
CHANGED
package/src/api/attributes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AnyValue } from "../../types/otlp";
|
|
2
1
|
import { addAttribute, AttributeValueType, removeAttribute } from "../utils/otel";
|
|
3
2
|
import { vars } from "../vars";
|
|
3
|
+
import { AnyValue } from "../types/otlp";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Adds a signal attribute to be transmitted with every signal.
|
package/src/api/events.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { AnyValue, KeyValue } from "../../types/otlp";
|
|
2
1
|
import { EVENT_NAME, EVENT_NAMES, WEB_EVENT_TITLE, LOG_SEVERITIES, LOG_SEVERITY_TEXT } from "../semantic-conventions";
|
|
3
2
|
import { sendLog } from "../transport";
|
|
4
3
|
import { addCommonAttributes } from "../attributes";
|
|
5
4
|
import { addAttribute, AttributeValueType, toAnyValue } from "../utils/otel";
|
|
6
5
|
import { nowNanos, TimeInput, toNanosTs, warn } from "../utils";
|
|
6
|
+
import { AnyValue, KeyValue } from "../types/otlp";
|
|
7
7
|
|
|
8
8
|
type EventOptions = {
|
|
9
9
|
/**
|
package/src/api/init.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { vars } from "../vars";
|
|
2
2
|
import {
|
|
3
3
|
DEPLOYMENT_ENVIRONMENT_NAME,
|
|
4
4
|
DEPLOYMENT_ID,
|
|
@@ -23,57 +23,12 @@ import {
|
|
|
23
23
|
import { trackSessions } from "./session";
|
|
24
24
|
import { startWebVitalsInstrumentation } from "../instrumentations/web-vitals";
|
|
25
25
|
import { startErrorInstrumentation } from "../instrumentations/errors";
|
|
26
|
-
import { addAttribute
|
|
26
|
+
import { addAttribute } from "../utils/otel";
|
|
27
27
|
import { instrumentFetch } from "../instrumentations/http/fetch";
|
|
28
|
-
import { AnyValue } from "../../types/otlp";
|
|
29
28
|
import { startNavigationInstrumentation } from "../instrumentations/navigation";
|
|
30
29
|
import { merge } from "ts-deepmerge";
|
|
31
30
|
import { initializeTabId } from "../utils/tab-id";
|
|
32
|
-
|
|
33
|
-
export type InitOptions = {
|
|
34
|
-
serviceName: string;
|
|
35
|
-
serviceVersion?: string;
|
|
36
|
-
environment?: string;
|
|
37
|
-
deploymentName?: string;
|
|
38
|
-
deploymentId?: string;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Additional attributes to include with transmitted signals
|
|
42
|
-
*/
|
|
43
|
-
additionalSignalAttributes?: Record<string, AttributeValueType | AnyValue>;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* OTLP endpoints to which the generated telemetry should be sent to.
|
|
47
|
-
*/
|
|
48
|
-
endpoint: Endpoint | Endpoint[];
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The session inactivity timeout. Session inactivity is the maximum
|
|
52
|
-
* allowed time to pass between two page loads before the session is considered
|
|
53
|
-
* to be expired. Also think of cache time-to-idle configuration options.
|
|
54
|
-
*/
|
|
55
|
-
sessionInactivityTimeoutMillis?: number;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* The default session termination timeout. Session termination is the maximum
|
|
59
|
-
* allowed time to pass since session start before the session is considered
|
|
60
|
-
* to be expired. Also think of cache time-to-live configuration options.
|
|
61
|
-
*/
|
|
62
|
-
sessionTerminationTimeoutMillis?: number;
|
|
63
|
-
} & Partial<
|
|
64
|
-
Pick<
|
|
65
|
-
Vars,
|
|
66
|
-
| "ignoreUrls"
|
|
67
|
-
| "ignoreErrorMessages"
|
|
68
|
-
| "wrapEventHandlers"
|
|
69
|
-
| "wrapTimers"
|
|
70
|
-
| "propagateTraceHeadersCorsURLs"
|
|
71
|
-
| "maxWaitForResourceTimingsMillis"
|
|
72
|
-
| "maxToleranceForResourceTimingsMillis"
|
|
73
|
-
| "headersToCapture"
|
|
74
|
-
| "pageViewInstrumentation"
|
|
75
|
-
>
|
|
76
|
-
>;
|
|
31
|
+
import { InitOptions, InstrumentationName } from "../types/options";
|
|
77
32
|
|
|
78
33
|
let hasBeenInitialised: boolean = false;
|
|
79
34
|
|
|
@@ -112,6 +67,7 @@ export function init(opts: InitOptions) {
|
|
|
112
67
|
"maxWaitForResourceTimingsMillis",
|
|
113
68
|
"maxToleranceForResourceTimingsMillis",
|
|
114
69
|
"headersToCapture",
|
|
70
|
+
"urlAttributeScrubber",
|
|
115
71
|
"pageViewInstrumentation",
|
|
116
72
|
])
|
|
117
73
|
)
|
|
@@ -121,10 +77,19 @@ export function init(opts: InitOptions) {
|
|
|
121
77
|
initializeSignalAttributes(opts);
|
|
122
78
|
initializeTabId();
|
|
123
79
|
trackSessions(opts.sessionInactivityTimeoutMillis, opts.sessionTerminationTimeoutMillis);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
80
|
+
|
|
81
|
+
if (isInstrumentationEnabled("@dash0/navigation", opts)) {
|
|
82
|
+
startNavigationInstrumentation();
|
|
83
|
+
}
|
|
84
|
+
if (isInstrumentationEnabled("@dash0/web-vitals", opts)) {
|
|
85
|
+
startWebVitalsInstrumentation();
|
|
86
|
+
}
|
|
87
|
+
if (isInstrumentationEnabled("@dash0/error", opts)) {
|
|
88
|
+
startErrorInstrumentation();
|
|
89
|
+
}
|
|
90
|
+
if (isInstrumentationEnabled("@dash0/fetch", opts)) {
|
|
91
|
+
instrumentFetch();
|
|
92
|
+
}
|
|
128
93
|
|
|
129
94
|
hasBeenInitialised = true;
|
|
130
95
|
}
|
|
@@ -219,3 +184,11 @@ function detectDeploymentId(opts: InitOptions): string | undefined {
|
|
|
219
184
|
return undefined;
|
|
220
185
|
}
|
|
221
186
|
}
|
|
187
|
+
|
|
188
|
+
function isInstrumentationEnabled(name: InstrumentationName, opts: InitOptions): boolean {
|
|
189
|
+
const instrumentations = opts.enabledInstrumentations;
|
|
190
|
+
|
|
191
|
+
if (!instrumentations) return true;
|
|
192
|
+
|
|
193
|
+
return instrumentations.includes(name);
|
|
194
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { InitOptions, InstrumentationName } from "../types/options";
|
|
3
|
+
|
|
4
|
+
// Mock all the instrumentation modules
|
|
5
|
+
vi.mock("../instrumentations/web-vitals", () => ({
|
|
6
|
+
startWebVitalsInstrumentation: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("../instrumentations/errors", () => ({
|
|
10
|
+
startErrorInstrumentation: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock("../instrumentations/http/fetch", () => ({
|
|
14
|
+
instrumentFetch: vi.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("../instrumentations/navigation", () => ({
|
|
18
|
+
startNavigationInstrumentation: vi.fn(),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
import { startWebVitalsInstrumentation } from "../instrumentations/web-vitals";
|
|
22
|
+
import { startErrorInstrumentation } from "../instrumentations/errors";
|
|
23
|
+
import { instrumentFetch } from "../instrumentations/http/fetch";
|
|
24
|
+
import { startNavigationInstrumentation } from "../instrumentations/navigation";
|
|
25
|
+
|
|
26
|
+
describe("init", () => {
|
|
27
|
+
const baseOptions: InitOptions = {
|
|
28
|
+
serviceName: "test-service",
|
|
29
|
+
endpoint: { url: "https://test-endpoint.com", authToken: "invalid" },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.clearAllMocks();
|
|
34
|
+
// Reset the hasBeenInitialised flag by re-importing the module
|
|
35
|
+
vi.resetModules();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
vi.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("instrumentation enablement", () => {
|
|
43
|
+
it("should enable all instrumentations when enabledInstrumentations is undefined", async () => {
|
|
44
|
+
const { init } = await import("./init");
|
|
45
|
+
|
|
46
|
+
init({
|
|
47
|
+
...baseOptions,
|
|
48
|
+
enabledInstrumentations: undefined,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(startNavigationInstrumentation).toHaveBeenCalled();
|
|
52
|
+
expect(startWebVitalsInstrumentation).toHaveBeenCalled();
|
|
53
|
+
expect(startErrorInstrumentation).toHaveBeenCalled();
|
|
54
|
+
expect(instrumentFetch).toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const instrumentations: InstrumentationName[] = [
|
|
58
|
+
"@dash0/navigation",
|
|
59
|
+
"@dash0/web-vitals",
|
|
60
|
+
"@dash0/error",
|
|
61
|
+
"@dash0/fetch",
|
|
62
|
+
];
|
|
63
|
+
const instrumentationMocks = {
|
|
64
|
+
"@dash0/navigation": startNavigationInstrumentation,
|
|
65
|
+
"@dash0/web-vitals": startWebVitalsInstrumentation,
|
|
66
|
+
"@dash0/error": startErrorInstrumentation,
|
|
67
|
+
"@dash0/fetch": instrumentFetch,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
instrumentations.forEach((instrumentation) => {
|
|
71
|
+
it(`should enable ${instrumentation} instrumentation when present in enabledInstrumentations array`, async () => {
|
|
72
|
+
const { init } = await import("./init");
|
|
73
|
+
|
|
74
|
+
init({
|
|
75
|
+
...baseOptions,
|
|
76
|
+
enabledInstrumentations: [instrumentation],
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(instrumentationMocks[instrumentation]).toHaveBeenCalled();
|
|
80
|
+
|
|
81
|
+
// Check that other instrumentations are not called
|
|
82
|
+
Object.entries(instrumentationMocks).forEach(([name, mock]) => {
|
|
83
|
+
if (name !== instrumentation) {
|
|
84
|
+
expect(mock).not.toHaveBeenCalled();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it(`should not enable ${instrumentation} instrumentation when not present in enabledInstrumentations array`, async () => {
|
|
90
|
+
const { init } = await import("./init");
|
|
91
|
+
|
|
92
|
+
const otherInstrumentations = instrumentations.filter((i) => i !== instrumentation);
|
|
93
|
+
|
|
94
|
+
init({
|
|
95
|
+
...baseOptions,
|
|
96
|
+
enabledInstrumentations: otherInstrumentations,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(instrumentationMocks[instrumentation]).not.toHaveBeenCalled();
|
|
100
|
+
|
|
101
|
+
// Check that other instrumentations are called
|
|
102
|
+
otherInstrumentations.forEach((name) => {
|
|
103
|
+
expect(instrumentationMocks[name]).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
package/src/api/report-error.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ErrorLike, ReportErrorOpts } from "../../types/errors";
|
|
2
1
|
import { reportError as reportErrorInternal } from "../instrumentations/errors/unhandled-error";
|
|
2
|
+
import { ReportErrorOpts, ErrorLike } from "../types/errors";
|
|
3
3
|
|
|
4
4
|
export function reportError(error: string | ErrorLike, opts?: ReportErrorOpts) {
|
|
5
5
|
reportErrorInternal(error, opts);
|
package/src/attributes/common.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { KeyValue } from "../../types/otlp";
|
|
2
1
|
import { generateUniqueId, nav, NO_VALUE_FALLBACK, WEB_EVENT_ID_BYTES, win } from "../utils";
|
|
3
2
|
import {
|
|
4
3
|
BROWSER_TAB_ID,
|
|
@@ -14,6 +13,7 @@ import { vars } from "../vars";
|
|
|
14
13
|
import { addAttribute } from "../utils/otel";
|
|
15
14
|
import { addUrlAttributes } from "./url";
|
|
16
15
|
import { tabId } from "../utils/tab-id";
|
|
16
|
+
import { KeyValue } from "../types/otlp";
|
|
17
17
|
|
|
18
18
|
type Options = {
|
|
19
19
|
/**
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { addCommonAttributes } from "./common";
|
|
3
|
-
import { KeyValue } from "../../types/otlp";
|
|
4
3
|
import * as utils from "../utils";
|
|
5
4
|
import * as varsModule from "../vars";
|
|
6
5
|
import * as urlModule from "./url";
|
|
7
6
|
import * as sessionApi from "../api/session";
|
|
8
7
|
import * as tabIdModule from "../utils/tab-id";
|
|
8
|
+
import { KeyValue } from "../types/otlp";
|
|
9
9
|
|
|
10
10
|
describe("addCommonAttributes", () => {
|
|
11
11
|
beforeEach(() => {
|
package/src/attributes/url.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import { KeyValue } from "../../types/otlp";
|
|
2
1
|
import { addAttribute, AttrPrefix, withPrefix } from "../utils/otel";
|
|
3
2
|
import { URL_DOMAIN, URL_FRAGMENT, URL_FULL, URL_PATH, URL_QUERY, URL_SCHEME } from "../semantic-conventions";
|
|
4
|
-
import { parseUrl } from "../utils";
|
|
3
|
+
import { identity, parseUrl } from "../utils";
|
|
4
|
+
import { KeyValue } from "../types/otlp";
|
|
5
|
+
import { vars } from "../vars";
|
|
6
|
+
|
|
7
|
+
export type UrlAttributeRecord = {
|
|
8
|
+
[URL_FULL]: string;
|
|
9
|
+
[URL_PATH]?: string | undefined;
|
|
10
|
+
[URL_DOMAIN]?: string | undefined;
|
|
11
|
+
[URL_SCHEME]?: string | undefined;
|
|
12
|
+
[URL_FRAGMENT]?: string | undefined;
|
|
13
|
+
[URL_QUERY]?: string | undefined;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type UrlAttributeScrubber = (attr: UrlAttributeRecord) => UrlAttributeRecord;
|
|
5
17
|
|
|
6
18
|
export function addUrlAttributes(attributes: KeyValue[], url: string | URL, prefix?: AttrPrefix) {
|
|
7
19
|
const applyPrefix = withPrefix(prefix);
|
|
@@ -11,17 +23,27 @@ export function addUrlAttributes(attributes: KeyValue[], url: string | URL, pref
|
|
|
11
23
|
if (parsed.username) parsed.username = "REDACTED";
|
|
12
24
|
if (parsed.password) parsed.password = "REDACTED";
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
const attrs = vars.urlAttributeScrubber({
|
|
27
|
+
[URL_FULL]: parsed.href,
|
|
28
|
+
[URL_PATH]: parsed.pathname,
|
|
29
|
+
[URL_DOMAIN]: parsed.hostname,
|
|
30
|
+
[URL_SCHEME]: parsed.protocol.replace(":", ""),
|
|
31
|
+
[URL_FRAGMENT]: parsed.hash ? parsed.hash.replace("#", "") : undefined,
|
|
32
|
+
[URL_QUERY]: parsed.search ? parsed.search.replace("?", "") : undefined,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
Object.entries(attrs).forEach(([key, value]) => {
|
|
36
|
+
if (value !== undefined) {
|
|
37
|
+
addAttribute(attributes, applyPrefix(key), value);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
24
40
|
} catch (_e) {
|
|
25
|
-
|
|
41
|
+
// This is fallback handling in case the url failed to parse or the user defined attribute scrubber behaved unexpectedly
|
|
42
|
+
// If the user did not attempt scrubbing we'll supply the full url for debugging purposes, otherwise we drop all url
|
|
43
|
+
// attributes
|
|
44
|
+
if (vars.urlAttributeScrubber === identity) {
|
|
45
|
+
// `identity` is the default assignment for this option
|
|
46
|
+
addAttribute(attributes, applyPrefix(URL_FULL), String(url));
|
|
47
|
+
}
|
|
26
48
|
}
|
|
27
49
|
}
|