@telemetry-dev/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.d.mts +26 -12
- package/dist/index.mjs +47 -29
- package/package.json +2 -2
- package/src/attrs.ts +6 -6
- package/src/capture.ts +3 -3
- package/src/client.ts +5 -5
- package/src/config.ts +7 -3
- package/src/index.ts +4 -0
- package/src/logs.ts +5 -5
- package/src/observe.ts +8 -8
- package/src/span.ts +72 -20
package/README.md
CHANGED
|
@@ -74,6 +74,10 @@ default `"warn"`), `fetch`, `waitUntil`, `onError`.
|
|
|
74
74
|
- `log(message, { level, eventName, attributes }?)` — log record correlated to the active trace.
|
|
75
75
|
- `getTraceparent()` — W3C traceparent of the active span (pass to other services; accept it via
|
|
76
76
|
`startSpan(name, { parent })`).
|
|
77
|
+
- `activeContext()` / `withContext(context, fn)` — read or temporarily activate an OpenTelemetry
|
|
78
|
+
context.
|
|
79
|
+
- `extractW3cContext(carrier, options?)` / `injectW3cContext(context, carrier, options?)` — extract
|
|
80
|
+
or inject W3C trace context. Baggage is omitted unless `includeBaggage` is true.
|
|
77
81
|
- `flush()` / `shutdown()` — export pending data; required before serverless freeze/exit.
|
|
78
82
|
|
|
79
83
|
Span types: `span` (default) | `generation` | `tool` | `agent` | `embedding` — mapped to
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { AttributeValue, Context, Span, SpanContext } from "@opentelemetry/api";
|
|
1
|
+
import { AttributeValue, Context, Link, Span, SpanContext, SpanKind } from "@opentelemetry/api";
|
|
2
2
|
import { LogRecordExporter } from "@opentelemetry/sdk-logs";
|
|
3
3
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
4
|
-
import { BatchOptions, BatchOptions as BatchOptions$1, ExportMode, ExportMode as ExportMode$1, LogLevel, PropagatedAttributes, SdkLogLevel, SdkLogLevel as SdkLogLevel$1, propagateAttributes } from "@telemetry-dev/otel";
|
|
4
|
+
import { BatchOptions, BatchOptions as BatchOptions$1, ExportMode, ExportMode as ExportMode$1, LogLevel, PropagatedAttributes, SdkLogLevel, SdkLogLevel as SdkLogLevel$1, activeContext, propagateAttributes, withContext } from "@telemetry-dev/otel";
|
|
5
5
|
import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
|
|
6
6
|
|
|
7
7
|
//#region src/config.d.ts
|
|
8
8
|
/** Redaction hook applied to captured content (input/output/log message) before JSON stringification. */
|
|
9
|
-
type MaskFn = (value:
|
|
9
|
+
type MaskFn<T = unknown> = (value: T, ctx: {
|
|
10
10
|
key: string;
|
|
11
|
-
}) =>
|
|
11
|
+
}) => T;
|
|
12
12
|
interface TelemetryOptions {
|
|
13
13
|
/** telemetry.dev ingest key (`td_live_…`). Falls back to `TELEMETRY_DEV_API_KEY`. When absent the SDK is a no-op. */
|
|
14
14
|
apiKey?: string;
|
|
@@ -44,7 +44,9 @@ interface TelemetryOptions {
|
|
|
44
44
|
/** Serverless extender (e.g. Cloudflare `ctx.waitUntil`). When provided, `flush()` does not await exports. */
|
|
45
45
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
46
46
|
/** Receives any error raised while emitting telemetry; the SDK never throws into user code. */
|
|
47
|
-
onError?: (
|
|
47
|
+
onError?: (cause: Error) => void;
|
|
48
|
+
/** Header value for a custom telemetry.dev integration. */
|
|
49
|
+
sdkName?: string;
|
|
48
50
|
}
|
|
49
51
|
//#endregion
|
|
50
52
|
//#region src/attrs.d.ts
|
|
@@ -60,7 +62,7 @@ interface TokenUsage {
|
|
|
60
62
|
interface GenerationFields {
|
|
61
63
|
model?: string;
|
|
62
64
|
provider?: string;
|
|
63
|
-
systemInstructions?:
|
|
65
|
+
systemInstructions?: Parameters<MaskFn>[0];
|
|
64
66
|
responseModel?: string;
|
|
65
67
|
responseId?: string;
|
|
66
68
|
usage?: TokenUsage;
|
|
@@ -90,12 +92,12 @@ interface AgentFields {
|
|
|
90
92
|
interface SpanFields extends GenerationFields, ToolFields, AgentFields {
|
|
91
93
|
/** Rename the span (update only). */
|
|
92
94
|
name?: string;
|
|
93
|
-
input?:
|
|
94
|
-
output?:
|
|
95
|
+
input?: Parameters<MaskFn>[0];
|
|
96
|
+
output?: Parameters<MaskFn>[0];
|
|
95
97
|
/** Marks the span failed: status ERROR + error.type + an "exception" event. */
|
|
96
98
|
error?: unknown;
|
|
97
99
|
/** td.metadata.<key> on this span only; use propagateAttributes for scope-wide metadata. */
|
|
98
|
-
metadata?: Record<string,
|
|
100
|
+
metadata?: Record<string, Parameters<MaskFn>[0]>;
|
|
99
101
|
/** Raw attribute escape hatch, merged last. */
|
|
100
102
|
attributes?: Record<string, AttributeValue>;
|
|
101
103
|
}
|
|
@@ -122,7 +124,7 @@ declare function init(options?: TelemetryOptions, overrides?: ClientOverrides):
|
|
|
122
124
|
interface LogOptions {
|
|
123
125
|
/** Default "info". */
|
|
124
126
|
level?: LogLevel;
|
|
125
|
-
attributes?: Record<string,
|
|
127
|
+
attributes?: Record<string, Parameters<MaskFn>[0]>;
|
|
126
128
|
eventName?: string;
|
|
127
129
|
timestamp?: Date | number;
|
|
128
130
|
}
|
|
@@ -137,8 +139,12 @@ type ParentRef = string | SpanHandle | SpanContext | Context;
|
|
|
137
139
|
interface StartSpanOptions extends SpanFields {
|
|
138
140
|
/** Default "span" → operation "function". */
|
|
139
141
|
type?: SpanType;
|
|
142
|
+
/** OpenTelemetry span kind; defaults to INTERNAL. */
|
|
143
|
+
kind?: SpanKind;
|
|
140
144
|
/** Parent override; defaults to the active context. */
|
|
141
145
|
parent?: ParentRef;
|
|
146
|
+
/** Links to associate with the span. */
|
|
147
|
+
links?: Link[];
|
|
142
148
|
startTime?: Date | number;
|
|
143
149
|
/** Per-call override of the global captureInput. */
|
|
144
150
|
captureInput?: boolean;
|
|
@@ -166,6 +172,14 @@ declare function startActiveSpan<T>(name: string, fn: (span: SpanHandle) => T):
|
|
|
166
172
|
declare function startActiveSpan<T>(name: string, options: StartSpanOptions, fn: (span: SpanHandle) => T): T;
|
|
167
173
|
/** Update the innermost active span; no-op when none is recording. */
|
|
168
174
|
declare function updateActiveSpan(fields: SpanFields): void;
|
|
175
|
+
/** Extract W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
176
|
+
declare function extractW3cContext(carrier: Record<string, unknown>, options?: {
|
|
177
|
+
includeBaggage?: boolean;
|
|
178
|
+
}): Context;
|
|
179
|
+
/** Inject W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
180
|
+
declare function injectW3cContext(context: Context, carrier: Record<string, unknown>, options?: {
|
|
181
|
+
includeBaggage?: boolean;
|
|
182
|
+
}): void;
|
|
169
183
|
/** Serialize the active span context as a W3C traceparent; null when there is none. */
|
|
170
184
|
declare function getTraceparent(): string | null;
|
|
171
185
|
//#endregion
|
|
@@ -179,6 +193,6 @@ interface ObserveOptions extends StartSpanOptions {
|
|
|
179
193
|
* output, thrown/rejected errors captured and rethrown. Activates context so nested SDK calls
|
|
180
194
|
* parent to it. Resolves the client at call time, so wrapping at module load before init() works.
|
|
181
195
|
*/
|
|
182
|
-
declare function observe<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn, options?: ObserveOptions): (...args: TArgs) => TReturn;
|
|
196
|
+
declare function observe<TThis, TArgs extends unknown[], TReturn>(fn: (this: TThis, ...args: TArgs) => TReturn, options?: ObserveOptions): (...args: TArgs) => TReturn;
|
|
183
197
|
//#endregion
|
|
184
|
-
export { type AgentFields, type BatchOptions, type ClientOverrides, type ExportMode, type GenerationFields, type LogLevel, type LogOptions, type MaskFn, type ObserveOptions, type ParentRef, type PropagatedAttributes, type SdkLogLevel, type SpanFields, type SpanHandle, type SpanType, type StartSpanOptions, type TelemetryClient, type TelemetryOptions, type TokenUsage, type ToolFields, flush, getTraceparent, init, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan };
|
|
198
|
+
export { type AgentFields, type BatchOptions, type ClientOverrides, type ExportMode, type GenerationFields, type LogLevel, type LogOptions, type MaskFn, type ObserveOptions, type ParentRef, type PropagatedAttributes, type SdkLogLevel, type SpanFields, type SpanHandle, type SpanType, type StartSpanOptions, type TelemetryClient, type TelemetryOptions, type TokenUsage, type ToolFields, activeContext, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { INVALID_SPAN_CONTEXT, ROOT_CONTEXT, SpanKind, SpanStatusCode, context, defaultTextMapSetter, propagation, trace } from "@opentelemetry/api";
|
|
2
|
-
import { W3CTraceContextPropagator, parseTraceParent } from "@opentelemetry/core";
|
|
1
|
+
import { INVALID_SPAN_CONTEXT, ROOT_CONTEXT, SpanKind, SpanStatusCode, context, defaultTextMapGetter, defaultTextMapSetter, propagation, trace } from "@opentelemetry/api";
|
|
2
|
+
import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator, parseTraceParent } from "@opentelemetry/core";
|
|
3
3
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
4
4
|
import { BatchLogRecordProcessor, LoggerProvider, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
5
5
|
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
6
|
-
import { AlsContextManager, BATCHED_METRIC_INTERVAL_MS, DEFAULT_BASE_URL, DEFAULT_BATCH, DORMANT_INTERVAL_MS, PROPAGATED_KEY, SCOPE_NAME, SCOPE_VERSION, StampingSpanProcessor, activeContext, als, createLogExporter, createMetricExporter, createMetricsPipeline, createTraceExporter, diag, jsonAttr, omitUndefined, otlpHeaders, propagateAttributes, propagatedFromContext, reportError, resolveEnv, setLogLevel, withContext } from "@telemetry-dev/otel";
|
|
6
|
+
import { AlsContextManager, BATCHED_METRIC_INTERVAL_MS, DEFAULT_BASE_URL, DEFAULT_BATCH, DORMANT_INTERVAL_MS, PROPAGATED_KEY, SCOPE_NAME, SCOPE_VERSION, StampingSpanProcessor, activeContext, activeContext as activeContext$1, als, createLogExporter, createMetricExporter, createMetricsPipeline, createTraceExporter, diag, jsonAttr, omitUndefined, otlpHeaders, propagateAttributes, propagatedFromContext, reportError, resolveEnv, setLogLevel, withContext, withContext as withContext$1 } from "@telemetry-dev/otel";
|
|
7
7
|
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
8
8
|
function resolveConfig(options = {}) {
|
|
9
9
|
const env = resolveEnv();
|
|
@@ -29,7 +29,8 @@ function resolveConfig(options = {}) {
|
|
|
29
29
|
logLevel: options.logLevel ?? "warn",
|
|
30
30
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
31
31
|
waitUntil: options.waitUntil,
|
|
32
|
-
onError: options.onError
|
|
32
|
+
onError: options.onError,
|
|
33
|
+
sdkName: options.sdkName ?? "@telemetry-dev/sdk"
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
//#endregion
|
|
@@ -53,7 +54,7 @@ function init(options, overrides) {
|
|
|
53
54
|
try {
|
|
54
55
|
return initInner(options, overrides);
|
|
55
56
|
} catch (error) {
|
|
56
|
-
reportError(options?.onError, error);
|
|
57
|
+
reportError(options?.onError, error instanceof Error ? error : new Error(String(error)));
|
|
57
58
|
activeClient = NOOP_CLIENT;
|
|
58
59
|
return NOOP_CLIENT;
|
|
59
60
|
}
|
|
@@ -79,7 +80,7 @@ function initInner(options, overrides) {
|
|
|
79
80
|
fetchImpl: config.fetchImpl,
|
|
80
81
|
onError: config.onError
|
|
81
82
|
};
|
|
82
|
-
const headers = config.apiKey ? otlpHeaders(config.apiKey) : void 0;
|
|
83
|
+
const headers = config.apiKey ? otlpHeaders(config.apiKey, config.sdkName) : void 0;
|
|
83
84
|
const spanExporter = overrides?.spanExporter ?? createTraceExporter({
|
|
84
85
|
url: `${config.baseUrl}/v1/traces`,
|
|
85
86
|
headers
|
|
@@ -188,7 +189,7 @@ function prepareCaptureValue(key, value, cfg) {
|
|
|
188
189
|
if (cfg.mask) try {
|
|
189
190
|
masked = cfg.mask(value, { key });
|
|
190
191
|
} catch (error) {
|
|
191
|
-
reportError(cfg.onError, error);
|
|
192
|
+
reportError(cfg.onError, error instanceof Error ? error : new Error(String(error)));
|
|
192
193
|
return;
|
|
193
194
|
}
|
|
194
195
|
const serialized = jsonAttr(masked);
|
|
@@ -232,7 +233,7 @@ function log(message, options) {
|
|
|
232
233
|
onError: cfg.onError
|
|
233
234
|
};
|
|
234
235
|
const severity = SEVERITIES[options?.level ?? "info"];
|
|
235
|
-
const ctx = activeContext();
|
|
236
|
+
const ctx = activeContext$1();
|
|
236
237
|
const attributes = {};
|
|
237
238
|
for (const [key, value] of Object.entries(options?.attributes ?? {})) {
|
|
238
239
|
if (value === void 0) continue;
|
|
@@ -245,7 +246,7 @@ function log(message, options) {
|
|
|
245
246
|
}
|
|
246
247
|
const propagated = propagatedFromContext(ctx);
|
|
247
248
|
if (propagated) {
|
|
248
|
-
for (const [key, value] of Object.entries(propagated)) if (value !== void 0) attributes[key] =
|
|
249
|
+
for (const [key, value] of Object.entries(propagated)) if (value !== void 0) attributes[key] = value.constructor === String ? truncate(String(value), cfg.maxAttributeLength) : value;
|
|
249
250
|
}
|
|
250
251
|
logger.emit({
|
|
251
252
|
body: prepareCaptureValue("log.message", message, captureCfg) ?? "",
|
|
@@ -257,7 +258,7 @@ function log(message, options) {
|
|
|
257
258
|
attributes
|
|
258
259
|
});
|
|
259
260
|
} catch (error) {
|
|
260
|
-
reportError(core.config.onError, error);
|
|
261
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
264
|
//#endregion
|
|
@@ -379,13 +380,14 @@ function resolveParent(parent) {
|
|
|
379
380
|
isRemote: true
|
|
380
381
|
});
|
|
381
382
|
}
|
|
382
|
-
|
|
383
|
-
if (
|
|
384
|
-
return
|
|
383
|
+
const ref = parent;
|
|
384
|
+
if (isSpanHandle(ref)) return withAmbientPropagated(ref.context);
|
|
385
|
+
if (isContext(ref)) return withAmbientPropagated(ref);
|
|
386
|
+
return trace.setSpanContext(base, ref);
|
|
385
387
|
}
|
|
386
388
|
function applyError(span, error) {
|
|
387
389
|
const err = error instanceof Error ? error : void 0;
|
|
388
|
-
const errorType = err?.name ?? "Error";
|
|
390
|
+
const errorType = err === void 0 ? "Error" : err.name !== "Error" && err.name.length > 0 ? err.name : (err.constructor?.name ?? "Error") || "Error";
|
|
389
391
|
const message = err?.message ?? String(error);
|
|
390
392
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
391
393
|
span.setAttribute("error.type", errorType);
|
|
@@ -420,8 +422,9 @@ function createSpanHandle(core, name, options = {}) {
|
|
|
420
422
|
const meta = metaFor(core, options);
|
|
421
423
|
const parentCtx = resolveParent(options.parent);
|
|
422
424
|
const span = core.tracer.startSpan(name, {
|
|
423
|
-
kind: SpanKind.INTERNAL,
|
|
425
|
+
kind: options.kind ?? SpanKind.INTERNAL,
|
|
424
426
|
startTime: options.startTime,
|
|
427
|
+
links: options.links,
|
|
425
428
|
attributes: { "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type] }
|
|
426
429
|
}, parentCtx);
|
|
427
430
|
SPAN_META.set(span, meta);
|
|
@@ -441,7 +444,7 @@ function createSpanHandle(core, name, options = {}) {
|
|
|
441
444
|
try {
|
|
442
445
|
applyFields(span, meta, fields);
|
|
443
446
|
} catch (error) {
|
|
444
|
-
reportError(meta.onError, error);
|
|
447
|
+
reportError(meta.onError, error instanceof Error ? error : new Error(String(error)));
|
|
445
448
|
}
|
|
446
449
|
return handle;
|
|
447
450
|
},
|
|
@@ -449,7 +452,7 @@ function createSpanHandle(core, name, options = {}) {
|
|
|
449
452
|
try {
|
|
450
453
|
if (fields) applyFields(span, meta, fields);
|
|
451
454
|
} catch (error) {
|
|
452
|
-
reportError(meta.onError, error);
|
|
455
|
+
reportError(meta.onError, error instanceof Error ? error : new Error(String(error)));
|
|
453
456
|
}
|
|
454
457
|
span.end(fields?.endTime);
|
|
455
458
|
}
|
|
@@ -463,7 +466,7 @@ function startSpan(name, options) {
|
|
|
463
466
|
try {
|
|
464
467
|
return createSpanHandle(core, name, options);
|
|
465
468
|
} catch (error) {
|
|
466
|
-
reportError(core.config.onError, error);
|
|
469
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
467
470
|
return NOOP_SPAN_HANDLE;
|
|
468
471
|
}
|
|
469
472
|
}
|
|
@@ -485,8 +488,9 @@ function runWithHandle(handle, fn) {
|
|
|
485
488
|
}
|
|
486
489
|
}
|
|
487
490
|
function startActiveSpan(name, optionsOrFn, maybeFn) {
|
|
488
|
-
const
|
|
489
|
-
const
|
|
491
|
+
const isFn = typeof optionsOrFn === "function";
|
|
492
|
+
const fn = isFn ? optionsOrFn : maybeFn;
|
|
493
|
+
const handle = startSpan(name, isFn ? void 0 : optionsOrFn);
|
|
490
494
|
if (!handle.isRecording) return fn(handle);
|
|
491
495
|
return runWithHandle(handle, fn);
|
|
492
496
|
}
|
|
@@ -499,14 +503,28 @@ function updateActiveSpan(fields) {
|
|
|
499
503
|
if (!span?.isRecording()) return;
|
|
500
504
|
applyFields(span, SPAN_META.get(span) ?? metaFor(core), fields);
|
|
501
505
|
} catch (error) {
|
|
502
|
-
reportError(core.config.onError, error);
|
|
506
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
503
507
|
}
|
|
504
508
|
}
|
|
505
|
-
const
|
|
509
|
+
const TRACE_CONTEXT_PROPAGATOR = new W3CTraceContextPropagator();
|
|
510
|
+
const W3C_PROPAGATOR = new CompositePropagator({ propagators: [TRACE_CONTEXT_PROPAGATOR, new W3CBaggagePropagator()] });
|
|
511
|
+
/** Extract W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
512
|
+
function extractW3cContext(carrier, options = {}) {
|
|
513
|
+
return (options.includeBaggage === true ? W3C_PROPAGATOR : TRACE_CONTEXT_PROPAGATOR).extract(ROOT_CONTEXT, carrier, defaultTextMapGetter);
|
|
514
|
+
}
|
|
515
|
+
/** Inject W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
516
|
+
function injectW3cContext(context, carrier, options = {}) {
|
|
517
|
+
const propagator = options.includeBaggage === true ? W3C_PROPAGATOR : TRACE_CONTEXT_PROPAGATOR;
|
|
518
|
+
for (const key of Object.keys(carrier)) {
|
|
519
|
+
const normalized = key.toLowerCase();
|
|
520
|
+
if (normalized === "traceparent" || normalized === "tracestate" || normalized === "baggage") delete carrier[key];
|
|
521
|
+
}
|
|
522
|
+
propagator.inject(context, carrier, defaultTextMapSetter);
|
|
523
|
+
}
|
|
506
524
|
/** Serialize the active span context as a W3C traceparent; null when there is none. */
|
|
507
525
|
function getTraceparent() {
|
|
508
|
-
const carrier = {};
|
|
509
|
-
|
|
526
|
+
const carrier = { traceparent: void 0 };
|
|
527
|
+
TRACE_CONTEXT_PROPAGATOR.inject(activeContext(), carrier, defaultTextMapSetter);
|
|
510
528
|
return carrier.traceparent ?? null;
|
|
511
529
|
}
|
|
512
530
|
//#endregion
|
|
@@ -529,22 +547,22 @@ function observe(fn, options) {
|
|
|
529
547
|
input
|
|
530
548
|
});
|
|
531
549
|
} catch (error) {
|
|
532
|
-
reportError(core.config.onError, error);
|
|
550
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
533
551
|
return fn.apply(this, args);
|
|
534
552
|
}
|
|
535
553
|
try {
|
|
536
|
-
const result = withContext(handle.context, () => fn.apply(this, args));
|
|
554
|
+
const result = withContext$1(handle.context, () => fn.apply(this, args));
|
|
537
555
|
if (isThenable(result)) return result.then((value) => {
|
|
538
556
|
handle.end({ output: value });
|
|
539
557
|
return value;
|
|
540
558
|
}, (error) => {
|
|
541
|
-
handle.end({ error });
|
|
559
|
+
handle.end({ error: error instanceof Error ? error : new Error(String(error)) });
|
|
542
560
|
throw error;
|
|
543
561
|
});
|
|
544
562
|
handle.end({ output: result });
|
|
545
563
|
return result;
|
|
546
564
|
} catch (error) {
|
|
547
|
-
handle.end({ error });
|
|
565
|
+
handle.end({ error: error instanceof Error ? error : new Error(String(error)) });
|
|
548
566
|
throw error;
|
|
549
567
|
}
|
|
550
568
|
};
|
|
@@ -555,4 +573,4 @@ function observe(fn, options) {
|
|
|
555
573
|
return wrapped;
|
|
556
574
|
}
|
|
557
575
|
//#endregion
|
|
558
|
-
export { flush, getTraceparent, init, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan };
|
|
576
|
+
export { activeContext, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telemetry-dev/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Pure TypeScript SDK for telemetry.dev: LLM traces, logs, and GenAI metrics on OpenTelemetry, for Node and edge runtimes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"genai",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@opentelemetry/sdk-logs": "^0.218.0",
|
|
43
43
|
"@opentelemetry/sdk-metrics": "^2.7.1",
|
|
44
44
|
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
45
|
-
"@telemetry-dev/otel": "^0.1.
|
|
45
|
+
"@telemetry-dev/otel": "^0.1.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@opentelemetry/api": "^1.9.1",
|
package/src/attrs.ts
CHANGED
|
@@ -9,13 +9,13 @@ export type SpanType = "span" | "generation" | "tool" | "agent" | "embedding";
|
|
|
9
9
|
// Operation names are taken verbatim by the ingest's deriveOperation. Plain spans always get an
|
|
10
10
|
// explicit "function": without one, the propagated gen_ai.conversation.id would trip the ingest's
|
|
11
11
|
// hasGenAi heuristic and misclassify them as "llm".
|
|
12
|
-
export const SPAN_TYPE_OPERATIONS
|
|
12
|
+
export const SPAN_TYPE_OPERATIONS = {
|
|
13
13
|
span: "function",
|
|
14
14
|
generation: "chat",
|
|
15
15
|
tool: "execute_tool",
|
|
16
16
|
agent: "invoke_agent",
|
|
17
17
|
embedding: "embeddings",
|
|
18
|
-
}
|
|
18
|
+
} satisfies Record<SpanType, string>;
|
|
19
19
|
|
|
20
20
|
export function inputKeyFor(type: SpanType): string {
|
|
21
21
|
return type === "tool" ? "gen_ai.tool.call.arguments" : "gen_ai.input.messages";
|
|
@@ -37,7 +37,7 @@ export interface TokenUsage {
|
|
|
37
37
|
export interface GenerationFields {
|
|
38
38
|
model?: string;
|
|
39
39
|
provider?: string;
|
|
40
|
-
systemInstructions?:
|
|
40
|
+
systemInstructions?: Parameters<import("./config.ts").MaskFn>[0];
|
|
41
41
|
responseModel?: string;
|
|
42
42
|
responseId?: string;
|
|
43
43
|
usage?: TokenUsage;
|
|
@@ -70,12 +70,12 @@ export interface AgentFields {
|
|
|
70
70
|
export interface SpanFields extends GenerationFields, ToolFields, AgentFields {
|
|
71
71
|
/** Rename the span (update only). */
|
|
72
72
|
name?: string;
|
|
73
|
-
input?:
|
|
74
|
-
output?:
|
|
73
|
+
input?: Parameters<import("./config.ts").MaskFn>[0];
|
|
74
|
+
output?: Parameters<import("./config.ts").MaskFn>[0];
|
|
75
75
|
/** Marks the span failed: status ERROR + error.type + an "exception" event. */
|
|
76
76
|
error?: unknown;
|
|
77
77
|
/** td.metadata.<key> on this span only; use propagateAttributes for scope-wide metadata. */
|
|
78
|
-
metadata?: Record<string,
|
|
78
|
+
metadata?: Record<string, Parameters<import("./config.ts").MaskFn>[0]>;
|
|
79
79
|
/** Raw attribute escape hatch, merged last. */
|
|
80
80
|
attributes?: Record<string, AttributeValue>;
|
|
81
81
|
}
|
package/src/capture.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const TRUNCATION_MARKER = "...[truncated]";
|
|
|
8
8
|
export interface CaptureConfig {
|
|
9
9
|
mask?: MaskFn;
|
|
10
10
|
maxAttributeLength: number;
|
|
11
|
-
onError?: (
|
|
11
|
+
onError?: (cause: Error) => void;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function truncate(value: string, maxLength: number): string {
|
|
@@ -21,7 +21,7 @@ export function truncate(value: string, maxLength: number): string {
|
|
|
21
21
|
/** The single content funnel: mask → JSON stringify → truncate. */
|
|
22
22
|
export function prepareCaptureValue(
|
|
23
23
|
key: string,
|
|
24
|
-
value:
|
|
24
|
+
value: Parameters<MaskFn>[0],
|
|
25
25
|
cfg: CaptureConfig,
|
|
26
26
|
): string | undefined {
|
|
27
27
|
let masked = value;
|
|
@@ -29,7 +29,7 @@ export function prepareCaptureValue(
|
|
|
29
29
|
try {
|
|
30
30
|
masked = cfg.mask(value, { key });
|
|
31
31
|
} catch (error) {
|
|
32
|
-
reportError(cfg.onError, error);
|
|
32
|
+
reportError(cfg.onError, error instanceof Error ? error : new Error(String(error)));
|
|
33
33
|
return undefined;
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/client.ts
CHANGED
|
@@ -85,7 +85,7 @@ export function init(options?: TelemetryOptions, overrides?: ClientOverrides): T
|
|
|
85
85
|
try {
|
|
86
86
|
return initInner(options, overrides);
|
|
87
87
|
} catch (error) {
|
|
88
|
-
reportError(options?.onError, error);
|
|
88
|
+
reportError(options?.onError, error instanceof Error ? error : new Error(String(error)));
|
|
89
89
|
activeClient = NOOP_CLIENT;
|
|
90
90
|
return NOOP_CLIENT;
|
|
91
91
|
}
|
|
@@ -98,7 +98,7 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
98
98
|
if (activeClient && activeClient !== NOOP_CLIENT) {
|
|
99
99
|
diag.warn("init() called again; replacing the previous client");
|
|
100
100
|
// shutdown() synchronously releases global registrations before this init claims them.
|
|
101
|
-
void activeClient.shutdown().catch((error:
|
|
101
|
+
void activeClient.shutdown().catch((error: Error) => reportError(config.onError, error));
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const enabled = config.enabled && Boolean(config.apiKey ?? overrides?.spanExporter);
|
|
@@ -116,7 +116,7 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
116
116
|
...config.resourceAttributes,
|
|
117
117
|
});
|
|
118
118
|
const transport: Transport = { fetchImpl: config.fetchImpl, onError: config.onError };
|
|
119
|
-
const headers = config.apiKey ? otlpHeaders(config.apiKey) : undefined;
|
|
119
|
+
const headers = config.apiKey ? otlpHeaders(config.apiKey, config.sdkName) : undefined;
|
|
120
120
|
|
|
121
121
|
const spanExporter =
|
|
122
122
|
overrides?.spanExporter ??
|
|
@@ -204,12 +204,12 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
204
204
|
|
|
205
205
|
let torn = false;
|
|
206
206
|
const collect = (op: "forceFlush" | "shutdown"): Promise<void> => {
|
|
207
|
-
const parts: Promise<
|
|
207
|
+
const parts: Promise<void>[] = [provider[op]()];
|
|
208
208
|
if (logs) parts.push(logs[op]());
|
|
209
209
|
if (metrics) parts.push(metrics[op]());
|
|
210
210
|
return Promise.all(parts)
|
|
211
211
|
.then(() => undefined)
|
|
212
|
-
.catch((error:
|
|
212
|
+
.catch((error: Error) => reportError(config.onError, error));
|
|
213
213
|
};
|
|
214
214
|
const handoff = (p: Promise<void>): Promise<void> => {
|
|
215
215
|
if (config.waitUntil) {
|
package/src/config.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
export type { BatchOptions, ExportMode, LogLevel, SdkLogLevel } from "@telemetry-dev/otel";
|
|
13
13
|
|
|
14
14
|
/** Redaction hook applied to captured content (input/output/log message) before JSON stringification. */
|
|
15
|
-
export type MaskFn = (value:
|
|
15
|
+
export type MaskFn<T = unknown> = (value: T, ctx: { key: string }) => T;
|
|
16
16
|
|
|
17
17
|
export interface TelemetryOptions {
|
|
18
18
|
/** telemetry.dev ingest key (`td_live_…`). Falls back to `TELEMETRY_DEV_API_KEY`. When absent the SDK is a no-op. */
|
|
@@ -49,7 +49,9 @@ export interface TelemetryOptions {
|
|
|
49
49
|
/** Serverless extender (e.g. Cloudflare `ctx.waitUntil`). When provided, `flush()` does not await exports. */
|
|
50
50
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
51
51
|
/** Receives any error raised while emitting telemetry; the SDK never throws into user code. */
|
|
52
|
-
onError?: (
|
|
52
|
+
onError?: (cause: Error) => void;
|
|
53
|
+
/** Header value for a custom telemetry.dev integration. */
|
|
54
|
+
sdkName?: string;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
export interface ResolvedConfig {
|
|
@@ -70,7 +72,8 @@ export interface ResolvedConfig {
|
|
|
70
72
|
logLevel: SdkLogLevel;
|
|
71
73
|
fetchImpl: typeof fetch;
|
|
72
74
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
73
|
-
onError?: (
|
|
75
|
+
onError?: (cause: Error) => void;
|
|
76
|
+
sdkName: string;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
export const DEFAULT_MAX_ATTRIBUTE_LENGTH = 65536;
|
|
@@ -100,5 +103,6 @@ export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
|
|
|
100
103
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
101
104
|
waitUntil: options.waitUntil,
|
|
102
105
|
onError: options.onError,
|
|
106
|
+
sdkName: options.sdkName ?? "@telemetry-dev/sdk",
|
|
103
107
|
};
|
|
104
108
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,11 +19,15 @@ export { propagateAttributes, type PropagatedAttributes } from "@telemetry-dev/o
|
|
|
19
19
|
export { log, type LogOptions } from "./logs.ts";
|
|
20
20
|
export { observe, type ObserveOptions } from "./observe.ts";
|
|
21
21
|
export {
|
|
22
|
+
activeContext,
|
|
23
|
+
extractW3cContext,
|
|
22
24
|
getTraceparent,
|
|
25
|
+
injectW3cContext,
|
|
23
26
|
type ParentRef,
|
|
24
27
|
type SpanHandle,
|
|
25
28
|
startActiveSpan,
|
|
26
29
|
startSpan,
|
|
27
30
|
type StartSpanOptions,
|
|
28
31
|
updateActiveSpan,
|
|
32
|
+
withContext,
|
|
29
33
|
} from "./span.ts";
|
package/src/logs.ts
CHANGED
|
@@ -8,17 +8,17 @@ import { currentClient } from "./client.ts";
|
|
|
8
8
|
import type { LogLevel } from "./config.ts";
|
|
9
9
|
|
|
10
10
|
// Severity numbers match the ingest's severityFromNumber buckets exactly.
|
|
11
|
-
const SEVERITIES
|
|
11
|
+
const SEVERITIES = {
|
|
12
12
|
debug: { number: SeverityNumber.DEBUG, text: "DEBUG" },
|
|
13
13
|
info: { number: SeverityNumber.INFO, text: "INFO" },
|
|
14
14
|
warn: { number: SeverityNumber.WARN, text: "WARN" },
|
|
15
15
|
error: { number: SeverityNumber.ERROR, text: "ERROR" },
|
|
16
|
-
};
|
|
16
|
+
} satisfies Record<LogLevel, { number: SeverityNumber; text: string }>;
|
|
17
17
|
|
|
18
18
|
export interface LogOptions {
|
|
19
19
|
/** Default "info". */
|
|
20
20
|
level?: LogLevel;
|
|
21
|
-
attributes?: Record<string,
|
|
21
|
+
attributes?: Record<string, Parameters<import("./config.ts").MaskFn>[0]>;
|
|
22
22
|
eventName?: string;
|
|
23
23
|
timestamp?: Date | number;
|
|
24
24
|
}
|
|
@@ -57,7 +57,7 @@ export function log(message: string, options?: LogOptions): void {
|
|
|
57
57
|
for (const [key, value] of Object.entries(propagated)) {
|
|
58
58
|
if (value !== undefined) {
|
|
59
59
|
attributes[key] =
|
|
60
|
-
|
|
60
|
+
value.constructor === String ? truncate(String(value), cfg.maxAttributeLength) : value;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -72,6 +72,6 @@ export function log(message: string, options?: LogOptions): void {
|
|
|
72
72
|
attributes,
|
|
73
73
|
});
|
|
74
74
|
} catch (error) {
|
|
75
|
-
reportError(core.config.onError, error);
|
|
75
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/observe.ts
CHANGED
|
@@ -13,11 +13,11 @@ export interface ObserveOptions extends StartSpanOptions {
|
|
|
13
13
|
* output, thrown/rejected errors captured and rethrown. Activates context so nested SDK calls
|
|
14
14
|
* parent to it. Resolves the client at call time, so wrapping at module load before init() works.
|
|
15
15
|
*/
|
|
16
|
-
export function observe<TArgs extends unknown[], TReturn>(
|
|
17
|
-
fn: (...args: TArgs) => TReturn,
|
|
16
|
+
export function observe<TThis, TArgs extends unknown[], TReturn>(
|
|
17
|
+
fn: (this: TThis, ...args: TArgs) => TReturn,
|
|
18
18
|
options?: ObserveOptions,
|
|
19
19
|
): (...args: TArgs) => TReturn {
|
|
20
|
-
const wrapped = function (this:
|
|
20
|
+
const wrapped = function (this: TThis, ...args: TArgs): TReturn {
|
|
21
21
|
const core = currentClient().core;
|
|
22
22
|
if (!core) return fn.apply(this, args);
|
|
23
23
|
|
|
@@ -34,20 +34,20 @@ export function observe<TArgs extends unknown[], TReturn>(
|
|
|
34
34
|
: args;
|
|
35
35
|
handle = createSpanHandle(core, name, { ...options, input });
|
|
36
36
|
} catch (error) {
|
|
37
|
-
reportError(core.config.onError, error);
|
|
37
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
38
38
|
return fn.apply(this, args);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
const result = withContext(handle.context, () => fn.apply(this, args));
|
|
43
43
|
if (isThenable(result)) {
|
|
44
|
-
return
|
|
44
|
+
return result.then(
|
|
45
45
|
(value) => {
|
|
46
46
|
handle.end({ output: value });
|
|
47
47
|
return value;
|
|
48
48
|
},
|
|
49
|
-
(error:
|
|
50
|
-
handle.end({ error });
|
|
49
|
+
(error: Error) => {
|
|
50
|
+
handle.end({ error: error instanceof Error ? error : new Error(String(error)) });
|
|
51
51
|
throw error;
|
|
52
52
|
},
|
|
53
53
|
) as TReturn;
|
|
@@ -55,7 +55,7 @@ export function observe<TArgs extends unknown[], TReturn>(
|
|
|
55
55
|
handle.end({ output: result });
|
|
56
56
|
return result;
|
|
57
57
|
} catch (error) {
|
|
58
|
-
handle.end({ error });
|
|
58
|
+
handle.end({ error: error instanceof Error ? error : new Error(String(error)) });
|
|
59
59
|
throw error;
|
|
60
60
|
}
|
|
61
61
|
};
|
package/src/span.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Context,
|
|
3
|
+
defaultTextMapGetter,
|
|
3
4
|
defaultTextMapSetter,
|
|
5
|
+
type Link,
|
|
6
|
+
ROOT_CONTEXT,
|
|
4
7
|
type Span,
|
|
5
8
|
type SpanContext,
|
|
6
9
|
SpanKind,
|
|
7
10
|
SpanStatusCode,
|
|
8
11
|
trace,
|
|
9
12
|
} from "@opentelemetry/api";
|
|
10
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
CompositePropagator,
|
|
15
|
+
parseTraceParent,
|
|
16
|
+
W3CBaggagePropagator,
|
|
17
|
+
W3CTraceContextPropagator,
|
|
18
|
+
} from "@opentelemetry/core";
|
|
11
19
|
|
|
12
20
|
import {
|
|
13
21
|
activeContext,
|
|
@@ -33,8 +41,12 @@ export type ParentRef = string /* W3C traceparent */ | SpanHandle | SpanContext
|
|
|
33
41
|
export interface StartSpanOptions extends SpanFields {
|
|
34
42
|
/** Default "span" → operation "function". */
|
|
35
43
|
type?: SpanType;
|
|
44
|
+
/** OpenTelemetry span kind; defaults to INTERNAL. */
|
|
45
|
+
kind?: SpanKind;
|
|
36
46
|
/** Parent override; defaults to the active context. */
|
|
37
47
|
parent?: ParentRef;
|
|
48
|
+
/** Links to associate with the span. */
|
|
49
|
+
links?: Link[];
|
|
38
50
|
startTime?: Date | number;
|
|
39
51
|
/** Per-call override of the global captureInput. */
|
|
40
52
|
captureInput?: boolean;
|
|
@@ -59,7 +71,7 @@ export interface SpanHandle {
|
|
|
59
71
|
interface SpanMeta {
|
|
60
72
|
type: SpanType;
|
|
61
73
|
capture: FieldCaptureConfig;
|
|
62
|
-
onError?: (
|
|
74
|
+
onError?: (cause: Error) => void;
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
// Remembers each span's type + capture flags so update()/end()/updateActiveSpan map
|
|
@@ -68,7 +80,7 @@ const SPAN_META = new WeakMap<Span, SpanMeta>();
|
|
|
68
80
|
|
|
69
81
|
const SEVERITY_ERROR = 17;
|
|
70
82
|
|
|
71
|
-
export function isThenable(value:
|
|
83
|
+
export function isThenable<T>(value: T): value is T & PromiseLike<Awaited<T>> {
|
|
72
84
|
return (
|
|
73
85
|
value != null &&
|
|
74
86
|
(typeof value === "object" || typeof value === "function") &&
|
|
@@ -77,11 +89,11 @@ export function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
|
77
89
|
);
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
function isContext(value:
|
|
92
|
+
function isContext(value: Exclude<ParentRef, string>): value is Context {
|
|
81
93
|
return "getValue" in value && typeof value.getValue === "function";
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
function isSpanHandle(value:
|
|
96
|
+
function isSpanHandle(value: Exclude<ParentRef, string>): value is SpanHandle {
|
|
85
97
|
return "span" in value && "context" in value && "update" in value;
|
|
86
98
|
}
|
|
87
99
|
|
|
@@ -101,14 +113,22 @@ function resolveParent(parent: ParentRef | undefined): Context {
|
|
|
101
113
|
if (!parsed) return base;
|
|
102
114
|
return trace.setSpanContext(base, { ...parsed, isRemote: true });
|
|
103
115
|
}
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
116
|
+
const ref = parent as Exclude<ParentRef, string>;
|
|
117
|
+
if (isSpanHandle(ref)) return withAmbientPropagated(ref.context);
|
|
118
|
+
if (isContext(ref)) return withAmbientPropagated(ref);
|
|
119
|
+
return trace.setSpanContext(base, ref);
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
function applyError(span: Span, error: unknown): void {
|
|
110
123
|
const err = error instanceof Error ? error : undefined;
|
|
111
|
-
|
|
124
|
+
// Prefer the specific subclass name (Python parity: type(error).__name__) — many SDK
|
|
125
|
+
// error classes (e.g. openai's BadRequestError) never override the inherited "Error" name.
|
|
126
|
+
const errorType =
|
|
127
|
+
err === undefined
|
|
128
|
+
? "Error"
|
|
129
|
+
: err.name !== "Error" && err.name.length > 0
|
|
130
|
+
? err.name
|
|
131
|
+
: (err.constructor?.name ?? "Error") || "Error";
|
|
112
132
|
const message = err?.message ?? String(error);
|
|
113
133
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
114
134
|
span.setAttribute("error.type", errorType);
|
|
@@ -155,8 +175,9 @@ export function createSpanHandle(
|
|
|
155
175
|
const span = core.tracer.startSpan(
|
|
156
176
|
name,
|
|
157
177
|
{
|
|
158
|
-
kind: SpanKind.INTERNAL,
|
|
178
|
+
kind: options.kind ?? SpanKind.INTERNAL,
|
|
159
179
|
startTime: options.startTime,
|
|
180
|
+
links: options.links,
|
|
160
181
|
attributes: { "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type] },
|
|
161
182
|
},
|
|
162
183
|
parentCtx,
|
|
@@ -179,7 +200,7 @@ export function createSpanHandle(
|
|
|
179
200
|
try {
|
|
180
201
|
applyFields(span, meta, fields);
|
|
181
202
|
} catch (error) {
|
|
182
|
-
reportError(meta.onError, error);
|
|
203
|
+
reportError(meta.onError, error instanceof Error ? error : new Error(String(error)));
|
|
183
204
|
}
|
|
184
205
|
return handle;
|
|
185
206
|
},
|
|
@@ -187,7 +208,7 @@ export function createSpanHandle(
|
|
|
187
208
|
try {
|
|
188
209
|
if (fields) applyFields(span, meta, fields);
|
|
189
210
|
} catch (error) {
|
|
190
|
-
reportError(meta.onError, error);
|
|
211
|
+
reportError(meta.onError, error instanceof Error ? error : new Error(String(error)));
|
|
191
212
|
}
|
|
192
213
|
span.end(fields?.endTime);
|
|
193
214
|
},
|
|
@@ -202,7 +223,7 @@ export function startSpan(name: string, options?: StartSpanOptions): SpanHandle
|
|
|
202
223
|
try {
|
|
203
224
|
return createSpanHandle(core, name, options);
|
|
204
225
|
} catch (error) {
|
|
205
|
-
reportError(core.config.onError, error);
|
|
226
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
206
227
|
return NOOP_SPAN_HANDLE;
|
|
207
228
|
}
|
|
208
229
|
}
|
|
@@ -211,7 +232,7 @@ export function runWithHandle<T>(handle: SpanHandle, fn: (span: SpanHandle) => T
|
|
|
211
232
|
try {
|
|
212
233
|
const result = withContext(handle.context, () => fn(handle));
|
|
213
234
|
if (isThenable(result)) {
|
|
214
|
-
return
|
|
235
|
+
return result.then(
|
|
215
236
|
(value) => {
|
|
216
237
|
handle.end();
|
|
217
238
|
return value;
|
|
@@ -241,8 +262,9 @@ export function startActiveSpan<T>(
|
|
|
241
262
|
optionsOrFn: StartSpanOptions | ((span: SpanHandle) => T),
|
|
242
263
|
maybeFn?: (span: SpanHandle) => T,
|
|
243
264
|
): T {
|
|
244
|
-
const
|
|
245
|
-
const
|
|
265
|
+
const isFn = typeof optionsOrFn === "function";
|
|
266
|
+
const fn = isFn ? optionsOrFn : maybeFn!;
|
|
267
|
+
const options = isFn ? undefined : optionsOrFn;
|
|
246
268
|
const handle = startSpan(name, options);
|
|
247
269
|
if (!handle.isRecording) return fn(handle);
|
|
248
270
|
return runWithHandle(handle, fn);
|
|
@@ -258,15 +280,45 @@ export function updateActiveSpan(fields: SpanFields): void {
|
|
|
258
280
|
const meta = SPAN_META.get(span) ?? metaFor(core);
|
|
259
281
|
applyFields(span, meta, fields);
|
|
260
282
|
} catch (error) {
|
|
261
|
-
reportError(core.config.onError, error);
|
|
283
|
+
reportError(core.config.onError, error instanceof Error ? error : new Error(String(error)));
|
|
262
284
|
}
|
|
263
285
|
}
|
|
264
286
|
|
|
265
|
-
const
|
|
287
|
+
const TRACE_CONTEXT_PROPAGATOR = new W3CTraceContextPropagator();
|
|
288
|
+
const W3C_PROPAGATOR = new CompositePropagator({
|
|
289
|
+
propagators: [TRACE_CONTEXT_PROPAGATOR, new W3CBaggagePropagator()],
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
export { activeContext, withContext };
|
|
293
|
+
|
|
294
|
+
/** Extract W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
295
|
+
export function extractW3cContext(
|
|
296
|
+
carrier: Record<string, unknown>,
|
|
297
|
+
options: { includeBaggage?: boolean } = {},
|
|
298
|
+
): Context {
|
|
299
|
+
const propagator = options.includeBaggage === true ? W3C_PROPAGATOR : TRACE_CONTEXT_PROPAGATOR;
|
|
300
|
+
return propagator.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Inject W3C trace context; baggage is omitted unless includeBaggage is true. */
|
|
304
|
+
export function injectW3cContext(
|
|
305
|
+
context: Context,
|
|
306
|
+
carrier: Record<string, unknown>,
|
|
307
|
+
options: { includeBaggage?: boolean } = {},
|
|
308
|
+
): void {
|
|
309
|
+
const propagator = options.includeBaggage === true ? W3C_PROPAGATOR : TRACE_CONTEXT_PROPAGATOR;
|
|
310
|
+
for (const key of Object.keys(carrier)) {
|
|
311
|
+
const normalized = key.toLowerCase();
|
|
312
|
+
if (normalized === "traceparent" || normalized === "tracestate" || normalized === "baggage") {
|
|
313
|
+
delete carrier[key];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
propagator.inject(context, carrier, defaultTextMapSetter);
|
|
317
|
+
}
|
|
266
318
|
|
|
267
319
|
/** Serialize the active span context as a W3C traceparent; null when there is none. */
|
|
268
320
|
export function getTraceparent(): string | null {
|
|
269
|
-
const carrier
|
|
270
|
-
|
|
321
|
+
const carrier = { traceparent: undefined };
|
|
322
|
+
TRACE_CONTEXT_PROPAGATOR.inject(activeContext(), carrier, defaultTextMapSetter);
|
|
271
323
|
return carrier.traceparent ?? null;
|
|
272
324
|
}
|