@logbrew/sdk 0.1.38 → 0.1.39
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/automatic-delivery.cjs +271 -0
- package/batch-sender.cjs +164 -0
- package/bounded-number.cjs +34 -0
- package/browser-breadcrumbs.cjs +346 -136
- package/capture-support.cjs +100 -0
- package/client-options.cjs +292 -0
- package/client-report.cjs +232 -0
- package/client.cjs +447 -0
- package/console-arguments.cjs +151 -0
- package/console-capture.cjs +194 -0
- package/core.cjs +30 -2810
- package/delivery-health.cjs +133 -0
- package/error-causes.cjs +130 -0
- package/error-details.cjs +124 -0
- package/error-issue.cjs +187 -0
- package/event-clone.cjs +55 -0
- package/event-metadata.cjs +40 -0
- package/event-pipeline.cjs +325 -0
- package/event-queue.cjs +306 -0
- package/event-store.cjs +237 -0
- package/event-validation.cjs +159 -0
- package/examples/agent-timeline.cjs +57 -46
- package/examples/agent-timeline.mjs +41 -35
- package/examples/index.mjs +122 -94
- package/examples/readme-example.cjs +36 -27
- package/examples/readme-example.mjs +21 -17
- package/examples/real-user-smoke.cjs +41 -32
- package/examples/real-user-smoke.mjs +26 -22
- package/exception-chain.cjs +205 -0
- package/index.d.ts +144 -1076
- package/index.js +1 -1
- package/issue-exception.cjs +13 -8
- package/metric-validation.cjs +95 -0
- package/network-milestone.cjs +155 -0
- package/null-value.cjs +4 -0
- package/opentelemetry-bridge.cjs +330 -0
- package/opentelemetry-context.cjs +245 -0
- package/opentelemetry-exceptions.cjs +69 -0
- package/opentelemetry-keys.cjs +183 -0
- package/opentelemetry-span.cjs +286 -0
- package/opentelemetry-summary.cjs +213 -0
- package/opentelemetry-time.cjs +131 -0
- package/opentelemetry.cjs +99 -1068
- package/package.json +55 -1
- package/pino-destination.cjs +108 -0
- package/pino-record.cjs +237 -0
- package/product-action.cjs +163 -0
- package/provided-breadcrumbs.cjs +117 -0
- package/random-hex.cjs +29 -0
- package/react-native.js +1 -1
- package/recording-transport.cjs +68 -0
- package/release-artifacts-build.cjs +263 -84
- package/release-artifacts-common.js +347 -66
- package/release-artifacts-debug-id.js +178 -0
- package/release-artifacts-inspect.js +213 -0
- package/release-artifacts-issue-event.js +99 -0
- package/release-artifacts-manifest-artifact.js +107 -0
- package/release-artifacts-manifest.js +77 -0
- package/release-artifacts-mappings.js +265 -0
- package/release-artifacts-prepare.js +297 -0
- package/release-artifacts-source-context.js +210 -0
- package/release-artifacts-symbolication.js +272 -421
- package/release-artifacts-upload-files.js +122 -0
- package/release-artifacts-upload-request.js +246 -0
- package/release-artifacts-upload.js +155 -300
- package/release-artifacts-urls.js +49 -0
- package/release-artifacts.js +189 -662
- package/retry-delay.cjs +32 -0
- package/span-validation.cjs +191 -0
- package/stored-events.cjs +243 -0
- package/types/client.d.ts +67 -0
- package/types/delivery.d.ts +125 -0
- package/types/issues.d.ts +249 -0
- package/types/logging.d.ts +175 -0
- package/types/opentelemetry.d.ts +205 -0
- package/types/product.d.ts +128 -0
- package/types/recording-transport.d.ts +15 -0
- package/types/sdk-error.d.ts +9 -0
- package/types/telemetry.d.ts +171 -0
- package/types/trace-context.d.ts +110 -0
- package/types/transport-error.d.ts +10 -0
- package/utf8.cjs +37 -0
- package/validation.cjs +49 -1
- package/vite-release-artifacts-config.cjs +120 -0
- package/vite-release-artifacts.cjs +199 -99
- package/vite-release-artifacts.d.cts +65 -40
- package/vite-release-artifacts.d.ts +65 -40
- package/vite-release-artifacts.js +2 -2
- package/winston-info.cjs +205 -0
- package/winston.cjs +189 -289
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
const { NULL_VALUE } = require("./null-value.cjs");
|
|
2
|
+
const { SdkError } = require("./sdk-error.cjs");
|
|
3
|
+
const {
|
|
4
|
+
ZERO_SPAN_ID,
|
|
5
|
+
isObjectValue,
|
|
6
|
+
optionalProperty,
|
|
7
|
+
requireSpanId,
|
|
8
|
+
requireTraceId
|
|
9
|
+
} = require("./validation.cjs");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @import { LogCorrelationTraceContext } from "./index.cjs"
|
|
13
|
+
* @typedef {{ traceId: string, spanId: string, sampled: boolean }} SpanContextIds
|
|
14
|
+
* @typedef {Readonly<{ spanId?: unknown, spanIdFactory?: unknown }>} ChildSpanOptions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const SPAN_ID_BYTES = 8;
|
|
18
|
+
const MAX_SPAN_ID_ATTEMPTS = 3;
|
|
19
|
+
const FIRST_ATTEMPT = 0;
|
|
20
|
+
const ATTEMPT_STEP = 1;
|
|
21
|
+
const BYTE_VALUES = 256;
|
|
22
|
+
const HEX_RADIX = 16;
|
|
23
|
+
const HEX_BYTE_WIDTH = 2;
|
|
24
|
+
const SAMPLED_BIT_BASE = 2;
|
|
25
|
+
const SAMPLED_BIT = 1;
|
|
26
|
+
const OPENTELEMETRY_API_PACKAGE = "@opentelemetry/api";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {unknown} traceId
|
|
30
|
+
* @returns {string | undefined}
|
|
31
|
+
*/
|
|
32
|
+
function normalizeTraceId(traceId) {
|
|
33
|
+
try {
|
|
34
|
+
return requireTraceId(traceId).toLowerCase();
|
|
35
|
+
} catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {unknown} spanId
|
|
42
|
+
* @returns {string | undefined}
|
|
43
|
+
*/
|
|
44
|
+
function normalizeSpanId(spanId) {
|
|
45
|
+
try {
|
|
46
|
+
return requireSpanId("trace spanId", spanId).toLowerCase();
|
|
47
|
+
} catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {unknown} traceFlags
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
56
|
+
function openTelemetryTraceFlagsSampled(traceFlags) {
|
|
57
|
+
const sampled = optionalProperty(traceFlags, "sampled");
|
|
58
|
+
if (typeof sampled === "boolean") {
|
|
59
|
+
return sampled;
|
|
60
|
+
}
|
|
61
|
+
if (typeof traceFlags === "number" && Number.isFinite(traceFlags)) {
|
|
62
|
+
return Math.abs(Math.trunc(traceFlags) % SAMPLED_BIT_BASE) === SAMPLED_BIT;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {unknown} spanContext
|
|
69
|
+
* @returns {SpanContextIds | undefined}
|
|
70
|
+
*/
|
|
71
|
+
function normalizeOpenTelemetrySpanContextIds(spanContext) {
|
|
72
|
+
if (!isObjectValue(spanContext) || spanContext["isValid"] === false) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const traceId = normalizeTraceId(spanContext["traceId"]);
|
|
76
|
+
const spanId = normalizeSpanId(spanContext["spanId"]);
|
|
77
|
+
if (traceId === undefined || spanId === undefined) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
traceId,
|
|
82
|
+
|
|
83
|
+
spanId,
|
|
84
|
+
|
|
85
|
+
sampled: openTelemetryTraceFlagsSampled(spanContext["traceFlags"])
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {number} byteLength
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
function randomHex(byteLength) {
|
|
94
|
+
const bytes = new Uint8Array(byteLength);
|
|
95
|
+
const crypto = optionalProperty(globalThis, "crypto");
|
|
96
|
+
const getRandomValues = optionalProperty(crypto, "getRandomValues");
|
|
97
|
+
if (typeof getRandomValues === "function") {
|
|
98
|
+
Reflect.apply(getRandomValues, crypto, [bytes]);
|
|
99
|
+
} else {
|
|
100
|
+
for (const index of bytes.keys()) {
|
|
101
|
+
bytes[index] = Math.floor(Math.random() * BYTE_VALUES);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return Array.from(bytes, (byte) => byte.toString(HEX_RADIX).padStart(HEX_BYTE_WIDTH, "0")).join("");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @returns {string}
|
|
109
|
+
*/
|
|
110
|
+
function defaultSpanIdFactory() {
|
|
111
|
+
for (let attempt = FIRST_ATTEMPT; attempt < MAX_SPAN_ID_ATTEMPTS; attempt += ATTEMPT_STEP) {
|
|
112
|
+
const spanId = randomHex(SPAN_ID_BYTES);
|
|
113
|
+
if (spanId !== ZERO_SPAN_ID) {
|
|
114
|
+
return spanId;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw new SdkError("configuration_error", "spanIdFactory must return a non-zero 16-character hex span id");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {ChildSpanOptions} [options]
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
function resolveLogBrewChildSpanId(options = {}) {
|
|
125
|
+
const explicitSpanId = options.spanId;
|
|
126
|
+
if (explicitSpanId !== undefined) {
|
|
127
|
+
return requireSpanId("spanId", explicitSpanId).toLowerCase();
|
|
128
|
+
}
|
|
129
|
+
const { spanIdFactory } = options;
|
|
130
|
+
/** @type {unknown} */
|
|
131
|
+
const spanId = typeof spanIdFactory === "function"
|
|
132
|
+
? Reflect.apply(spanIdFactory, undefined, [])
|
|
133
|
+
: defaultSpanIdFactory();
|
|
134
|
+
return requireSpanId("spanId", spanId).toLowerCase();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {unknown} method
|
|
139
|
+
* @param {unknown} thisArg
|
|
140
|
+
* @returns {{ value: unknown } | undefined}
|
|
141
|
+
*/
|
|
142
|
+
function callWithoutArguments(method, thisArg) {
|
|
143
|
+
if (typeof method !== "function") {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return { value: Reflect.apply(method, thisArg, []) };
|
|
148
|
+
} catch {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @param {unknown} spanContext
|
|
155
|
+
* @param {ChildSpanOptions} [options]
|
|
156
|
+
* @returns {LogCorrelationTraceContext | null}
|
|
157
|
+
*/
|
|
158
|
+
function logbrewTraceContextFromOpenTelemetrySpanContext(spanContext, options = {}) {
|
|
159
|
+
const context = normalizeOpenTelemetrySpanContextIds(spanContext);
|
|
160
|
+
if (context === undefined) {
|
|
161
|
+
return NULL_VALUE;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
traceId: context.traceId,
|
|
165
|
+
|
|
166
|
+
spanId: resolveLogBrewChildSpanId(options),
|
|
167
|
+
|
|
168
|
+
parentSpanId: context.spanId,
|
|
169
|
+
sampled: context.sampled
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {unknown} span
|
|
175
|
+
* @param {ChildSpanOptions} [options]
|
|
176
|
+
* @returns {LogCorrelationTraceContext | null}
|
|
177
|
+
*/
|
|
178
|
+
function logbrewTraceContextFromOpenTelemetrySpan(span, options = {}) {
|
|
179
|
+
if (!isObjectValue(span)) {
|
|
180
|
+
return NULL_VALUE;
|
|
181
|
+
}
|
|
182
|
+
const getSpanContext = typeof span["spanContext"] === "function"
|
|
183
|
+
? span["spanContext"]
|
|
184
|
+
: span["getSpanContext"];
|
|
185
|
+
const spanContext = callWithoutArguments(getSpanContext, span);
|
|
186
|
+
if (spanContext === undefined) {
|
|
187
|
+
return NULL_VALUE;
|
|
188
|
+
}
|
|
189
|
+
return logbrewTraceContextFromOpenTelemetrySpanContext(spanContext.value, options);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @returns {((id: string) => unknown) | undefined}
|
|
194
|
+
*/
|
|
195
|
+
function optionalRequire() {
|
|
196
|
+
/** @type {unknown} */
|
|
197
|
+
const moduleRequire = typeof module === "undefined" ? undefined : Reflect.get(module, "require");
|
|
198
|
+
if (typeof moduleRequire === "function") {
|
|
199
|
+
return (id) => {
|
|
200
|
+
/** @type {unknown} */
|
|
201
|
+
const required = Reflect.apply(moduleRequire, module, [id]);
|
|
202
|
+
return required;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return typeof require === "function" ? require : undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @returns {unknown}
|
|
210
|
+
*/
|
|
211
|
+
function optionalOpenTelemetryApi() {
|
|
212
|
+
const requireOptional = optionalRequire();
|
|
213
|
+
if (requireOptional === undefined) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
return requireOptional(OPENTELEMETRY_API_PACKAGE);
|
|
218
|
+
} catch {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @param {ChildSpanOptions} [options]
|
|
225
|
+
* @returns {LogCorrelationTraceContext | null}
|
|
226
|
+
*/
|
|
227
|
+
function logbrewTraceContextFromCurrentOpenTelemetrySpan(options = {}) {
|
|
228
|
+
const openTelemetryApi = optionalProperty(options, "openTelemetryApi") ?? optionalOpenTelemetryApi();
|
|
229
|
+
const trace = optionalProperty(openTelemetryApi, "trace");
|
|
230
|
+
const getActiveSpan = optionalProperty(trace, "getActiveSpan");
|
|
231
|
+
const activeSpan = callWithoutArguments(getActiveSpan, trace);
|
|
232
|
+
if (activeSpan === undefined) {
|
|
233
|
+
return NULL_VALUE;
|
|
234
|
+
}
|
|
235
|
+
return logbrewTraceContextFromOpenTelemetrySpan(activeSpan.value, options);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
defaultSpanIdFactory,
|
|
240
|
+
logbrewTraceContextFromCurrentOpenTelemetrySpan,
|
|
241
|
+
logbrewTraceContextFromOpenTelemetrySpan,
|
|
242
|
+
logbrewTraceContextFromOpenTelemetrySpanContext,
|
|
243
|
+
normalizeOpenTelemetrySpanContextIds,
|
|
244
|
+
normalizeSpanId
|
|
245
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { isList, isRecord, optionalProperty } = require("./validation.cjs");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import { Metadata } from "./index.cjs"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const MAX_EXCEPTION_TYPES = 8;
|
|
8
|
+
const NO_EXCEPTIONS = 0;
|
|
9
|
+
const EXCEPTION_TYPE_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$:.]{0,127}$/u;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {unknown} value
|
|
13
|
+
* @returns {string | undefined}
|
|
14
|
+
*/
|
|
15
|
+
function safeOpenTelemetryExceptionType(value) {
|
|
16
|
+
return typeof value === "string" && EXCEPTION_TYPE_PATTERN.test(value) ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class OpenTelemetryExceptionSummary {
|
|
20
|
+
count = NO_EXCEPTIONS;
|
|
21
|
+
escapedCount = NO_EXCEPTIONS;
|
|
22
|
+
/** @type {string[]} */
|
|
23
|
+
types = [];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {Readonly<Record<string, unknown>>} event
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
add(event) {
|
|
30
|
+
this.count += 1;
|
|
31
|
+
if (optionalProperty(event["attributes"], "exception.escaped") === true) {
|
|
32
|
+
this.escapedCount += 1;
|
|
33
|
+
}
|
|
34
|
+
const type = safeOpenTelemetryExceptionType(optionalProperty(event["attributes"], "exception.type"));
|
|
35
|
+
if (type !== undefined && !this.types.includes(type) && this.types.length < MAX_EXCEPTION_TYPES) {
|
|
36
|
+
this.types.push(type);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @returns {Metadata}
|
|
42
|
+
*/
|
|
43
|
+
metadata() {
|
|
44
|
+
return {
|
|
45
|
+
"otel.exception_event_count": this.count,
|
|
46
|
+
...(this.escapedCount > NO_EXCEPTIONS ? { "otel.exception_escaped_count": this.escapedCount } : {}),
|
|
47
|
+
...(this.types.length > NO_EXCEPTIONS ? { "otel.exception_types": this.types.join(",") } : {})
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {unknown} events
|
|
54
|
+
* @returns {OpenTelemetryExceptionSummary | undefined}
|
|
55
|
+
*/
|
|
56
|
+
function openTelemetryExceptionEventSummary(events) {
|
|
57
|
+
if (!isList(events) || events.length === NO_EXCEPTIONS) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const summary = new OpenTelemetryExceptionSummary();
|
|
61
|
+
for (const event of events) {
|
|
62
|
+
if (isRecord(event) && event["name"] === "exception") {
|
|
63
|
+
summary.add(event);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return summary.count > NO_EXCEPTIONS ? summary : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { openTelemetryExceptionEventSummary, safeOpenTelemetryExceptionType };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const { compactMetadata, isMetadataValue } = require("./log-context.cjs");
|
|
2
|
+
const { SdkError } = require("./sdk-error.cjs");
|
|
3
|
+
const { isList, isRecord, requireNonEmpty } = require("./validation.cjs");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @import { Metadata } from "./index.cjs"
|
|
7
|
+
* @typedef {Readonly<ReadonlySet<string>>} KeySet
|
|
8
|
+
* @typedef {Readonly<{
|
|
9
|
+
* attributeKeys?: unknown,
|
|
10
|
+
* captureUnsampled?: unknown,
|
|
11
|
+
* eventAttributeKeys?: unknown,
|
|
12
|
+
* includeSpanEvents?: unknown,
|
|
13
|
+
* includeSpanLinks?: unknown,
|
|
14
|
+
* linkAttributeKeys?: unknown,
|
|
15
|
+
* metadata?: unknown,
|
|
16
|
+
* resourceAttributeKeys?: unknown
|
|
17
|
+
* }>} ReadableSpanOptions
|
|
18
|
+
* @typedef {Readonly<{
|
|
19
|
+
* attributeKeys: KeySet,
|
|
20
|
+
* captureUnsampled: boolean,
|
|
21
|
+
* eventAttributeKeys: KeySet,
|
|
22
|
+
* includeSpanEvents: boolean,
|
|
23
|
+
* includeSpanLinks: boolean,
|
|
24
|
+
* linkAttributeKeys: KeySet,
|
|
25
|
+
* metadata: Readonly<Metadata>,
|
|
26
|
+
* resourceAttributeKeys: KeySet
|
|
27
|
+
* }>} ResolvedSpanOptions
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const SPAN_ATTRIBUTE_KEYS = [
|
|
31
|
+
"db.operation.name",
|
|
32
|
+
"db.system",
|
|
33
|
+
"faas.trigger",
|
|
34
|
+
"graphql.operation.name",
|
|
35
|
+
"graphql.operation.type",
|
|
36
|
+
"http.method",
|
|
37
|
+
"http.request.method",
|
|
38
|
+
"http.response.status_code",
|
|
39
|
+
"http.route",
|
|
40
|
+
"http.status_code",
|
|
41
|
+
"messaging.operation.name",
|
|
42
|
+
"messaging.system",
|
|
43
|
+
"rpc.method",
|
|
44
|
+
"rpc.service",
|
|
45
|
+
"rpc.system"
|
|
46
|
+
];
|
|
47
|
+
const RESOURCE_ATTRIBUTE_KEYS = [
|
|
48
|
+
"deployment.environment",
|
|
49
|
+
"deployment.environment.name",
|
|
50
|
+
"service.name",
|
|
51
|
+
"service.version",
|
|
52
|
+
"telemetry.sdk.language",
|
|
53
|
+
"telemetry.sdk.name",
|
|
54
|
+
"telemetry.sdk.version"
|
|
55
|
+
];
|
|
56
|
+
/** @type {KeySet} */
|
|
57
|
+
const DEFAULT_SPAN_ATTRIBUTE_KEYS = new Set(SPAN_ATTRIBUTE_KEYS);
|
|
58
|
+
/** @type {KeySet} */
|
|
59
|
+
const DEFAULT_RESOURCE_ATTRIBUTE_KEYS = new Set(RESOURCE_ATTRIBUTE_KEYS);
|
|
60
|
+
/** @type {KeySet} */
|
|
61
|
+
const DEFAULT_EVENT_ATTRIBUTE_KEYS = new Set(["exception.escaped", "exception.type"]);
|
|
62
|
+
/** @type {KeySet} */
|
|
63
|
+
const DEFAULT_LINK_ATTRIBUTE_KEYS = new Set();
|
|
64
|
+
/** @type {KeySet} */
|
|
65
|
+
const TRACE_SUMMARY_METADATA_KEYS = new Set([...RESOURCE_ATTRIBUTE_KEYS, ...SPAN_ATTRIBUTE_KEYS]);
|
|
66
|
+
/** @type {KeySet} */
|
|
67
|
+
const SENSITIVE_OTEL_ATTRIBUTE_KEYS = new Set([
|
|
68
|
+
"code.stacktrace",
|
|
69
|
+
"db.statement",
|
|
70
|
+
"exception.message",
|
|
71
|
+
"exception.stacktrace",
|
|
72
|
+
"http.request.body",
|
|
73
|
+
"http.response.body",
|
|
74
|
+
"http.url",
|
|
75
|
+
"url.full"
|
|
76
|
+
]);
|
|
77
|
+
const SENSITIVE_OTEL_ATTRIBUTE_PREFIXES = ["http.request.header.", "http.response.header."];
|
|
78
|
+
const SENSITIVE_OTEL_ATTRIBUTE_PATTERN = /(?:^|[._-])(?:authorization|body|cookie|credential|fragment|header|headers|payload|password|passwd|private[-_]?key|query|secret|stack|stacktrace|token)(?:[._-]|$)/iu;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} key
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
function isSensitiveOpenTelemetryAttributeKey(key) {
|
|
85
|
+
if (SENSITIVE_OTEL_ATTRIBUTE_KEYS.has(key)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (SENSITIVE_OTEL_ATTRIBUTE_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return SENSITIVE_OTEL_ATTRIBUTE_PATTERN.test(key);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} label
|
|
96
|
+
* @param {unknown} extraKey
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
function allowedExtraKey(label, extraKey) {
|
|
100
|
+
const key = requireNonEmpty(label, extraKey);
|
|
101
|
+
if (isSensitiveOpenTelemetryAttributeKey(key)) {
|
|
102
|
+
throw new SdkError("validation_error", `${label} cannot include sensitive key: ${key}`);
|
|
103
|
+
}
|
|
104
|
+
return key;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {KeySet} defaultKeys
|
|
109
|
+
* @param {unknown} extraKeys
|
|
110
|
+
* @param {string} label
|
|
111
|
+
* @returns {KeySet}
|
|
112
|
+
*/
|
|
113
|
+
function openTelemetryAttributeKeySet(defaultKeys, extraKeys, label) {
|
|
114
|
+
const allowedKeys = new Set(defaultKeys);
|
|
115
|
+
if (extraKeys === undefined) {
|
|
116
|
+
return allowedKeys;
|
|
117
|
+
}
|
|
118
|
+
if (!isList(extraKeys)) {
|
|
119
|
+
throw new SdkError("validation_error", `${label} must be an array`);
|
|
120
|
+
}
|
|
121
|
+
for (const extraKey of extraKeys) {
|
|
122
|
+
allowedKeys.add(allowedExtraKey(label, extraKey));
|
|
123
|
+
}
|
|
124
|
+
return allowedKeys;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {ReadableSpanOptions} [options]
|
|
129
|
+
* @returns {ResolvedSpanOptions}
|
|
130
|
+
*/
|
|
131
|
+
function resolveOpenTelemetryReadableSpanOptions(options = {}) {
|
|
132
|
+
return {
|
|
133
|
+
attributeKeys: openTelemetryAttributeKeySet(
|
|
134
|
+
DEFAULT_SPAN_ATTRIBUTE_KEYS,
|
|
135
|
+
options.attributeKeys,
|
|
136
|
+
"OpenTelemetry attributeKeys"
|
|
137
|
+
),
|
|
138
|
+
captureUnsampled: options.captureUnsampled === true,
|
|
139
|
+
eventAttributeKeys: openTelemetryAttributeKeySet(
|
|
140
|
+
DEFAULT_EVENT_ATTRIBUTE_KEYS,
|
|
141
|
+
options.eventAttributeKeys,
|
|
142
|
+
"OpenTelemetry eventAttributeKeys"
|
|
143
|
+
),
|
|
144
|
+
includeSpanEvents: options.includeSpanEvents !== false,
|
|
145
|
+
includeSpanLinks: options.includeSpanLinks !== false,
|
|
146
|
+
linkAttributeKeys: openTelemetryAttributeKeySet(
|
|
147
|
+
DEFAULT_LINK_ATTRIBUTE_KEYS,
|
|
148
|
+
options.linkAttributeKeys,
|
|
149
|
+
"OpenTelemetry linkAttributeKeys"
|
|
150
|
+
),
|
|
151
|
+
metadata: compactMetadata(options.metadata),
|
|
152
|
+
resourceAttributeKeys: openTelemetryAttributeKeySet(
|
|
153
|
+
DEFAULT_RESOURCE_ATTRIBUTE_KEYS,
|
|
154
|
+
options.resourceAttributeKeys,
|
|
155
|
+
"OpenTelemetry resourceAttributeKeys"
|
|
156
|
+
)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {unknown} attributes
|
|
162
|
+
* @param {KeySet} allowedKeys
|
|
163
|
+
* @returns {Metadata}
|
|
164
|
+
*/
|
|
165
|
+
function openTelemetrySelectedMetadata(attributes, allowedKeys) {
|
|
166
|
+
/** @type {Metadata} */
|
|
167
|
+
const metadata = {};
|
|
168
|
+
if (!isRecord(attributes)) {
|
|
169
|
+
return metadata;
|
|
170
|
+
}
|
|
171
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
172
|
+
if (allowedKeys.has(key) && !isSensitiveOpenTelemetryAttributeKey(key) && isMetadataValue(value)) {
|
|
173
|
+
metadata[key] = value;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return metadata;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
TRACE_SUMMARY_METADATA_KEYS,
|
|
181
|
+
openTelemetrySelectedMetadata,
|
|
182
|
+
resolveOpenTelemetryReadableSpanOptions
|
|
183
|
+
};
|