@telemetry-dev/sdk 0.1.1 → 0.1.3
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 +11 -0
- package/dist/index.d.mts +13 -3
- package/dist/index.mjs +45 -19
- package/package.json +3 -3
- package/src/client.ts +10 -1
- package/src/config.ts +26 -1
- package/src/index.ts +7 -1
- package/src/logs.ts +22 -2
- package/src/span.ts +23 -4
package/README.md
CHANGED
|
@@ -60,6 +60,17 @@ Other `init()` options: `enabled` (kill switch), `registerGlobal`, `exportMode:
|
|
|
60
60
|
`exportTimeoutMillis` 30000), `spanFilter`, `resourceAttributes`, `logLevel` (SDK diagnostics,
|
|
61
61
|
default `"warn"`), `fetch`, `waitUntil`, `onError`.
|
|
62
62
|
|
|
63
|
+
Root spans remain independent by default. `init({ sessionMode: "process" })` groups otherwise
|
|
64
|
+
uncorrelated spans and logs using a fresh opaque UUID for each enabled SDK initialization.
|
|
65
|
+
Explicit and propagated session IDs take precedence, and valid parent trace context is preserved.
|
|
66
|
+
The generated session ends when the client is shut down or replaced; a later `init()` generates a
|
|
67
|
+
new ID. It is not persisted across processes.
|
|
68
|
+
|
|
69
|
+
`sampler` accepts an OpenTelemetry `Sampler` and overrides `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
|
|
70
|
+
The default is parent-based always-on. Session roots keep the configured root policy.
|
|
71
|
+
Nested spans and explicitly parented spans keep the real parent's policy.
|
|
72
|
+
The SDK installs the [session sampling wrapper](../otel/README.md#correlation-attributes) on its provider. This includes the global provider that Eve uses.
|
|
73
|
+
|
|
63
74
|
## API
|
|
64
75
|
|
|
65
76
|
- `init(options?)` — create the client (isolated OTel TracerProvider by default).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AttributeValue, Context, Link, Span, SpanContext, SpanKind } from "@opentelemetry/api";
|
|
2
2
|
import { LogRecordExporter } from "@opentelemetry/sdk-logs";
|
|
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, activeContext, propagateAttributes, withContext } from "@telemetry-dev/otel";
|
|
3
|
+
import { ReadableSpan, Sampler, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
4
|
+
import { BatchOptions, BatchOptions as BatchOptions$1, ExportMode, ExportMode as ExportMode$1, LogLevel, PropagatedAttributes, SCOPE_NAME, SdkLogLevel, SdkLogLevel as SdkLogLevel$1, SessionRootOf, TelemetrySpanProcessor, TelemetrySpanProcessorOptions, activeContext, propagateAttributes, withContext } from "@telemetry-dev/otel";
|
|
5
5
|
import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
|
|
6
6
|
|
|
7
7
|
//#region src/config.d.ts
|
|
@@ -20,10 +20,14 @@ interface TelemetryOptions {
|
|
|
20
20
|
serviceName?: string;
|
|
21
21
|
/** Kill switch: `false` makes the SDK a complete no-op (tests). Default true. */
|
|
22
22
|
enabled?: boolean;
|
|
23
|
+
/** Group otherwise uncorrelated spans and logs for this SDK initialization. Default "explicit". */
|
|
24
|
+
sessionMode?: "explicit" | "process";
|
|
23
25
|
/** Register this SDK's TracerProvider/context manager/propagator globally. Default false (isolated). */
|
|
24
26
|
registerGlobal?: boolean;
|
|
25
27
|
/** "batched" (default) buffers spans; "immediate" exports per span (serverless). */
|
|
26
28
|
exportMode?: ExportMode$1;
|
|
29
|
+
/** OTel sampling policy. Defaults to OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG. */
|
|
30
|
+
sampler?: Sampler;
|
|
27
31
|
/** Capture span inputs (args, prompts) by default. Default true. */
|
|
28
32
|
captureInput?: boolean;
|
|
29
33
|
/** Capture span outputs (results, completions) by default. Default true. */
|
|
@@ -35,6 +39,12 @@ interface TelemetryOptions {
|
|
|
35
39
|
batch?: BatchOptions$1;
|
|
36
40
|
/** Export filter. Default when registerGlobal: only spans from this SDK's instrumentation scope. */
|
|
37
41
|
spanFilter?: (span: ReadableSpan) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* With registerGlobal: spans from other tracers that this returns a session id for are
|
|
44
|
+
* parented under the session trace even when a parent span is active. Use it for frameworks
|
|
45
|
+
* that start each turn inside their own engine span.
|
|
46
|
+
*/
|
|
47
|
+
sessionRootOf?: SessionRootOf;
|
|
38
48
|
/** Extra resource attributes merged onto service.name / deployment.environment.name. */
|
|
39
49
|
resourceAttributes?: Record<string, AttributeValue>;
|
|
40
50
|
/** SDK self-diagnostics console level. Default "warn"; "silent" disables. */
|
|
@@ -195,4 +205,4 @@ interface ObserveOptions extends StartSpanOptions {
|
|
|
195
205
|
*/
|
|
196
206
|
declare function observe<TThis, TArgs extends unknown[], TReturn>(fn: (this: TThis, ...args: TArgs) => TReturn, options?: ObserveOptions): (...args: TArgs) => TReturn;
|
|
197
207
|
//#endregion
|
|
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 };
|
|
208
|
+
export { type AgentFields, type BatchOptions, type ClientOverrides, type ExportMode, type GenerationFields, type LogLevel, type LogOptions, type MaskFn, type ObserveOptions, type ParentRef, type PropagatedAttributes, SCOPE_NAME, type SdkLogLevel, type SpanFields, type SpanHandle, type SpanType, type StartSpanOptions, type TelemetryClient, type TelemetryOptions, TelemetrySpanProcessor, type TelemetrySpanProcessorOptions, type TokenUsage, type ToolFields, activeContext, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
package/dist/index.mjs
CHANGED
|
@@ -3,10 +3,11 @@ import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator, p
|
|
|
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, 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";
|
|
6
|
+
import { AlsContextManager, BATCHED_METRIC_INTERVAL_MS, DEFAULT_BASE_URL, DEFAULT_BATCH, DORMANT_INTERVAL_MS, PROPAGATED_KEY, SCOPE_NAME, SCOPE_NAME as SCOPE_NAME$1, SCOPE_VERSION, StampingSpanProcessor, TelemetrySpanProcessor, activeContext, activeContext as activeContext$1, als, createLogExporter, createMetricExporter, createMetricsPipeline, createTraceExporter, diag, jsonAttr, omitUndefined, otlpHeaders, propagateAttributes, propagatedFromContext, reportError, resolveEnv, sessionIdOf, sessionRootTracerProvider, sessionSampler, setLogLevel, withContext, withContext as withContext$1, withSessionParent } from "@telemetry-dev/otel";
|
|
7
7
|
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
8
8
|
function resolveConfig(options = {}) {
|
|
9
9
|
const env = resolveEnv();
|
|
10
|
+
if (options.sessionMode !== void 0 && options.sessionMode !== "explicit" && options.sessionMode !== "process") throw new TypeError(`Invalid sessionMode: ${String(options.sessionMode)}`);
|
|
10
11
|
const baseUrl = (options.baseUrl ?? env.TELEMETRY_DEV_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
11
12
|
return {
|
|
12
13
|
apiKey: options.apiKey ?? env.TELEMETRY_DEV_API_KEY,
|
|
@@ -14,8 +15,10 @@ function resolveConfig(options = {}) {
|
|
|
14
15
|
environment: options.environment ?? env.TELEMETRY_DEV_ENVIRONMENT ?? "production",
|
|
15
16
|
serviceName: options.serviceName ?? env.OTEL_SERVICE_NAME ?? "unknown_service",
|
|
16
17
|
enabled: options.enabled ?? true,
|
|
18
|
+
sessionMode: options.sessionMode ?? "explicit",
|
|
17
19
|
registerGlobal: options.registerGlobal ?? false,
|
|
18
20
|
exportMode: options.exportMode ?? "batched",
|
|
21
|
+
sampler: options.sampler,
|
|
19
22
|
captureInput: options.captureInput ?? true,
|
|
20
23
|
captureOutput: options.captureOutput ?? true,
|
|
21
24
|
mask: options.mask,
|
|
@@ -25,6 +28,7 @@ function resolveConfig(options = {}) {
|
|
|
25
28
|
...options.batch
|
|
26
29
|
},
|
|
27
30
|
spanFilter: options.spanFilter,
|
|
31
|
+
sessionRootOf: options.sessionRootOf,
|
|
28
32
|
resourceAttributes: options.resourceAttributes,
|
|
29
33
|
logLevel: options.logLevel ?? "warn",
|
|
30
34
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
@@ -71,6 +75,7 @@ function initInner(options, overrides) {
|
|
|
71
75
|
activeClient = NOOP_CLIENT;
|
|
72
76
|
return NOOP_CLIENT;
|
|
73
77
|
}
|
|
78
|
+
if (config.sessionMode === "process") config.processSessionId = globalThis.crypto.randomUUID();
|
|
74
79
|
const resource = resourceFromAttributes({
|
|
75
80
|
"service.name": config.serviceName,
|
|
76
81
|
"deployment.environment.name": config.environment,
|
|
@@ -94,25 +99,27 @@ function initInner(options, overrides) {
|
|
|
94
99
|
exporter: metricExporter,
|
|
95
100
|
exportIntervalMillis: config.exportMode === "batched" ? BATCHED_METRIC_INTERVAL_MS : DORMANT_INTERVAL_MS
|
|
96
101
|
}) : void 0;
|
|
97
|
-
const spanFilter = config.spanFilter ?? (config.registerGlobal ? (span) => span.instrumentationScope.name === SCOPE_NAME : void 0);
|
|
102
|
+
const spanFilter = config.spanFilter ?? (config.registerGlobal ? (span) => span.instrumentationScope.name === SCOPE_NAME$1 : void 0);
|
|
103
|
+
const processor = new StampingSpanProcessor({
|
|
104
|
+
exporter: spanExporter,
|
|
105
|
+
exportMode: config.exportMode,
|
|
106
|
+
batch: config.batch,
|
|
107
|
+
spanFilter,
|
|
108
|
+
recordMetrics: metrics ? (span) => metrics.record(span) : void 0,
|
|
109
|
+
onError: config.onError
|
|
110
|
+
});
|
|
98
111
|
const provider = new BasicTracerProvider({
|
|
99
112
|
resource,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
exportMode: config.exportMode,
|
|
103
|
-
batch: config.batch,
|
|
104
|
-
spanFilter,
|
|
105
|
-
recordMetrics: metrics ? (span) => metrics.record(span) : void 0,
|
|
106
|
-
onError: config.onError
|
|
107
|
-
})],
|
|
113
|
+
sampler: sessionSampler(config.sampler),
|
|
114
|
+
spanProcessors: [processor],
|
|
108
115
|
spanLimits: { attributeValueLengthLimit: config.maxAttributeLength }
|
|
109
116
|
});
|
|
110
|
-
const tracer = provider.getTracer(SCOPE_NAME, SCOPE_VERSION);
|
|
117
|
+
const tracer = provider.getTracer(SCOPE_NAME$1, SCOPE_VERSION);
|
|
111
118
|
let registeredTrace = false;
|
|
112
119
|
let registeredContext = false;
|
|
113
120
|
let registeredPropagation = false;
|
|
114
121
|
if (config.registerGlobal) {
|
|
115
|
-
registeredTrace = trace.setGlobalTracerProvider(provider);
|
|
122
|
+
registeredTrace = trace.setGlobalTracerProvider(config.sessionRootOf ? sessionRootTracerProvider(provider, config.apiKey, config.sessionRootOf, config.onError) : provider);
|
|
116
123
|
if (als) registeredContext = context.setGlobalContextManager(new AlsContextManager(als));
|
|
117
124
|
registeredPropagation = propagation.setGlobalPropagator(new W3CTraceContextPropagator());
|
|
118
125
|
if (!registeredTrace) diag.warn("a global TracerProvider is already registered; attach the TelemetrySpanProcessor from '@telemetry-dev/otel' to your own provider instead");
|
|
@@ -132,7 +139,7 @@ function initInner(options, overrides) {
|
|
|
132
139
|
processors: [logProcessor]
|
|
133
140
|
});
|
|
134
141
|
logs = {
|
|
135
|
-
logger: loggerProvider.getLogger(SCOPE_NAME, SCOPE_VERSION),
|
|
142
|
+
logger: loggerProvider.getLogger(SCOPE_NAME$1, SCOPE_VERSION),
|
|
136
143
|
forceFlush: () => loggerProvider.forceFlush(),
|
|
137
144
|
shutdown: () => loggerProvider.shutdown()
|
|
138
145
|
};
|
|
@@ -233,7 +240,14 @@ function log(message, options) {
|
|
|
233
240
|
onError: cfg.onError
|
|
234
241
|
};
|
|
235
242
|
const severity = SEVERITIES[options?.level ?? "info"];
|
|
236
|
-
const
|
|
243
|
+
const active = activeContext$1();
|
|
244
|
+
const explicitSession = options?.attributes?.["gen_ai.conversation.id"];
|
|
245
|
+
const propagatedSession = propagatedFromContext(active)?.["gen_ai.conversation.id"];
|
|
246
|
+
const sessionId = explicitSession !== void 0 ? void 0 : typeof propagatedSession === "string" ? propagatedSession : cfg.processSessionId;
|
|
247
|
+
const ctx = sessionId === void 0 ? active : active.setValue(PROPAGATED_KEY, {
|
|
248
|
+
...propagatedFromContext(active),
|
|
249
|
+
"gen_ai.conversation.id": sessionId
|
|
250
|
+
});
|
|
237
251
|
const attributes = {};
|
|
238
252
|
for (const [key, value] of Object.entries(options?.attributes ?? {})) {
|
|
239
253
|
if (value === void 0) continue;
|
|
@@ -420,15 +434,27 @@ function metaFor(core, options) {
|
|
|
420
434
|
}
|
|
421
435
|
function createSpanHandle(core, name, options = {}) {
|
|
422
436
|
const meta = metaFor(core, options);
|
|
423
|
-
const
|
|
437
|
+
const resolved = resolveParent(options.parent);
|
|
438
|
+
const sessionId = sessionIdOf(resolved, options.attributes) ?? core.config.processSessionId;
|
|
439
|
+
const parentCtx = withSessionParent(sessionId === void 0 ? resolved : resolved.setValue(PROPAGATED_KEY, {
|
|
440
|
+
...propagatedFromContext(resolved),
|
|
441
|
+
"gen_ai.conversation.id": sessionId
|
|
442
|
+
}), sessionId, core.config.apiKey);
|
|
443
|
+
const attrs = fieldsToAttributes(options, meta.type, meta.capture);
|
|
424
444
|
const span = core.tracer.startSpan(name, {
|
|
425
445
|
kind: options.kind ?? SpanKind.INTERNAL,
|
|
426
446
|
startTime: options.startTime,
|
|
427
447
|
links: options.links,
|
|
428
|
-
attributes: {
|
|
448
|
+
attributes: {
|
|
449
|
+
"gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type],
|
|
450
|
+
...propagatedFromContext(parentCtx),
|
|
451
|
+
...attrs
|
|
452
|
+
}
|
|
429
453
|
}, parentCtx);
|
|
430
454
|
SPAN_META.set(span, meta);
|
|
431
|
-
|
|
455
|
+
if (Object.keys(attrs).length > 0) span.setAttributes(attrs);
|
|
456
|
+
if (options.name) span.updateName(options.name);
|
|
457
|
+
if (options.error !== void 0) applyError(span, options.error);
|
|
432
458
|
const spanContext = span.spanContext();
|
|
433
459
|
const flags = (spanContext.traceFlags & 255).toString(16).padStart(2, "0");
|
|
434
460
|
const handle = {
|
|
@@ -491,7 +517,7 @@ function startActiveSpan(name, optionsOrFn, maybeFn) {
|
|
|
491
517
|
const isFn = typeof optionsOrFn === "function";
|
|
492
518
|
const fn = isFn ? optionsOrFn : maybeFn;
|
|
493
519
|
const handle = startSpan(name, isFn ? void 0 : optionsOrFn);
|
|
494
|
-
if (
|
|
520
|
+
if (handle === NOOP_SPAN_HANDLE) return fn(handle);
|
|
495
521
|
return runWithHandle(handle, fn);
|
|
496
522
|
}
|
|
497
523
|
/** Update the innermost active span; no-op when none is recording. */
|
|
@@ -573,4 +599,4 @@ function observe(fn, options) {
|
|
|
573
599
|
return wrapped;
|
|
574
600
|
}
|
|
575
601
|
//#endregion
|
|
576
|
-
export { activeContext, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
|
602
|
+
export { SCOPE_NAME, TelemetrySpanProcessor, 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.3",
|
|
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",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/telemetry-dev/
|
|
19
|
+
"url": "git+https://github.com/telemetry-dev/sdks.git",
|
|
20
20
|
"directory": "packages/sdk"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
@@ -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.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@opentelemetry/api": "^1.9.1",
|
package/src/client.ts
CHANGED
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
reportError,
|
|
30
30
|
SCOPE_NAME,
|
|
31
31
|
SCOPE_VERSION,
|
|
32
|
+
sessionRootTracerProvider,
|
|
33
|
+
sessionSampler,
|
|
32
34
|
setLogLevel,
|
|
33
35
|
StampingSpanProcessor,
|
|
34
36
|
type Transport,
|
|
@@ -110,6 +112,8 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
110
112
|
return NOOP_CLIENT;
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
if (config.sessionMode === "process") config.processSessionId = globalThis.crypto.randomUUID();
|
|
116
|
+
|
|
113
117
|
const resource = resourceFromAttributes({
|
|
114
118
|
"service.name": config.serviceName,
|
|
115
119
|
"deployment.environment.name": config.environment,
|
|
@@ -153,6 +157,7 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
153
157
|
|
|
154
158
|
const provider = new BasicTracerProvider({
|
|
155
159
|
resource,
|
|
160
|
+
sampler: sessionSampler(config.sampler),
|
|
156
161
|
spanProcessors: [processor],
|
|
157
162
|
spanLimits: { attributeValueLengthLimit: config.maxAttributeLength },
|
|
158
163
|
});
|
|
@@ -162,7 +167,11 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
|
|
|
162
167
|
let registeredContext = false;
|
|
163
168
|
let registeredPropagation = false;
|
|
164
169
|
if (config.registerGlobal) {
|
|
165
|
-
registeredTrace = trace.setGlobalTracerProvider(
|
|
170
|
+
registeredTrace = trace.setGlobalTracerProvider(
|
|
171
|
+
config.sessionRootOf
|
|
172
|
+
? sessionRootTracerProvider(provider, config.apiKey, config.sessionRootOf, config.onError)
|
|
173
|
+
: provider,
|
|
174
|
+
);
|
|
166
175
|
if (als) {
|
|
167
176
|
registeredContext = apiContext.setGlobalContextManager(new AlsContextManager(als));
|
|
168
177
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AttributeValue } from "@opentelemetry/api";
|
|
2
|
-
import type { ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
import type { ReadableSpan, Sampler } from "@opentelemetry/sdk-trace-base";
|
|
3
3
|
import {
|
|
4
4
|
type BatchOptions,
|
|
5
5
|
DEFAULT_BASE_URL,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type ExportMode,
|
|
8
8
|
resolveEnv,
|
|
9
9
|
type SdkLogLevel,
|
|
10
|
+
type SessionRootOf,
|
|
10
11
|
} from "@telemetry-dev/otel";
|
|
11
12
|
|
|
12
13
|
export type { BatchOptions, ExportMode, LogLevel, SdkLogLevel } from "@telemetry-dev/otel";
|
|
@@ -25,10 +26,14 @@ export interface TelemetryOptions {
|
|
|
25
26
|
serviceName?: string;
|
|
26
27
|
/** Kill switch: `false` makes the SDK a complete no-op (tests). Default true. */
|
|
27
28
|
enabled?: boolean;
|
|
29
|
+
/** Group otherwise uncorrelated spans and logs for this SDK initialization. Default "explicit". */
|
|
30
|
+
sessionMode?: "explicit" | "process";
|
|
28
31
|
/** Register this SDK's TracerProvider/context manager/propagator globally. Default false (isolated). */
|
|
29
32
|
registerGlobal?: boolean;
|
|
30
33
|
/** "batched" (default) buffers spans; "immediate" exports per span (serverless). */
|
|
31
34
|
exportMode?: ExportMode;
|
|
35
|
+
/** OTel sampling policy. Defaults to OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG. */
|
|
36
|
+
sampler?: Sampler;
|
|
32
37
|
/** Capture span inputs (args, prompts) by default. Default true. */
|
|
33
38
|
captureInput?: boolean;
|
|
34
39
|
/** Capture span outputs (results, completions) by default. Default true. */
|
|
@@ -40,6 +45,12 @@ export interface TelemetryOptions {
|
|
|
40
45
|
batch?: BatchOptions;
|
|
41
46
|
/** Export filter. Default when registerGlobal: only spans from this SDK's instrumentation scope. */
|
|
42
47
|
spanFilter?: (span: ReadableSpan) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* With registerGlobal: spans from other tracers that this returns a session id for are
|
|
50
|
+
* parented under the session trace even when a parent span is active. Use it for frameworks
|
|
51
|
+
* that start each turn inside their own engine span.
|
|
52
|
+
*/
|
|
53
|
+
sessionRootOf?: SessionRootOf;
|
|
43
54
|
/** Extra resource attributes merged onto service.name / deployment.environment.name. */
|
|
44
55
|
resourceAttributes?: Record<string, AttributeValue>;
|
|
45
56
|
/** SDK self-diagnostics console level. Default "warn"; "silent" disables. */
|
|
@@ -60,14 +71,18 @@ export interface ResolvedConfig {
|
|
|
60
71
|
environment: string;
|
|
61
72
|
serviceName: string;
|
|
62
73
|
enabled: boolean;
|
|
74
|
+
sessionMode: "explicit" | "process";
|
|
75
|
+
processSessionId?: string;
|
|
63
76
|
registerGlobal: boolean;
|
|
64
77
|
exportMode: ExportMode;
|
|
78
|
+
sampler?: Sampler;
|
|
65
79
|
captureInput: boolean;
|
|
66
80
|
captureOutput: boolean;
|
|
67
81
|
mask?: MaskFn;
|
|
68
82
|
maxAttributeLength: number;
|
|
69
83
|
batch: Required<BatchOptions>;
|
|
70
84
|
spanFilter?: (span: ReadableSpan) => boolean;
|
|
85
|
+
sessionRootOf?: SessionRootOf;
|
|
71
86
|
resourceAttributes?: Record<string, AttributeValue>;
|
|
72
87
|
logLevel: SdkLogLevel;
|
|
73
88
|
fetchImpl: typeof fetch;
|
|
@@ -80,6 +95,13 @@ export const DEFAULT_MAX_ATTRIBUTE_LENGTH = 65536;
|
|
|
80
95
|
|
|
81
96
|
export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
|
|
82
97
|
const env = resolveEnv();
|
|
98
|
+
if (
|
|
99
|
+
options.sessionMode !== undefined &&
|
|
100
|
+
options.sessionMode !== "explicit" &&
|
|
101
|
+
options.sessionMode !== "process"
|
|
102
|
+
) {
|
|
103
|
+
throw new TypeError(`Invalid sessionMode: ${String(options.sessionMode)}`);
|
|
104
|
+
}
|
|
83
105
|
const baseUrl = (options.baseUrl ?? env.TELEMETRY_DEV_BASE_URL ?? DEFAULT_BASE_URL).replace(
|
|
84
106
|
/\/+$/,
|
|
85
107
|
"",
|
|
@@ -90,14 +112,17 @@ export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
|
|
|
90
112
|
environment: options.environment ?? env.TELEMETRY_DEV_ENVIRONMENT ?? "production",
|
|
91
113
|
serviceName: options.serviceName ?? env.OTEL_SERVICE_NAME ?? "unknown_service",
|
|
92
114
|
enabled: options.enabled ?? true,
|
|
115
|
+
sessionMode: options.sessionMode ?? "explicit",
|
|
93
116
|
registerGlobal: options.registerGlobal ?? false,
|
|
94
117
|
exportMode: options.exportMode ?? "batched",
|
|
118
|
+
sampler: options.sampler,
|
|
95
119
|
captureInput: options.captureInput ?? true,
|
|
96
120
|
captureOutput: options.captureOutput ?? true,
|
|
97
121
|
mask: options.mask,
|
|
98
122
|
maxAttributeLength: options.maxAttributeLength ?? DEFAULT_MAX_ATTRIBUTE_LENGTH,
|
|
99
123
|
batch: { ...DEFAULT_BATCH, ...options.batch },
|
|
100
124
|
spanFilter: options.spanFilter,
|
|
125
|
+
sessionRootOf: options.sessionRootOf,
|
|
101
126
|
resourceAttributes: options.resourceAttributes,
|
|
102
127
|
logLevel: options.logLevel ?? "warn",
|
|
103
128
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,13 @@ export type {
|
|
|
15
15
|
SdkLogLevel,
|
|
16
16
|
TelemetryOptions,
|
|
17
17
|
} from "./config.ts";
|
|
18
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
propagateAttributes,
|
|
20
|
+
type PropagatedAttributes,
|
|
21
|
+
SCOPE_NAME,
|
|
22
|
+
TelemetrySpanProcessor,
|
|
23
|
+
type TelemetrySpanProcessorOptions,
|
|
24
|
+
} from "@telemetry-dev/otel";
|
|
19
25
|
export { log, type LogOptions } from "./logs.ts";
|
|
20
26
|
export { observe, type ObserveOptions } from "./observe.ts";
|
|
21
27
|
export {
|
package/src/logs.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { AttributeValue } from "@opentelemetry/api";
|
|
2
2
|
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
activeContext,
|
|
6
|
+
PROPAGATED_KEY,
|
|
7
|
+
propagatedFromContext,
|
|
8
|
+
reportError,
|
|
9
|
+
} from "@telemetry-dev/otel";
|
|
5
10
|
|
|
6
11
|
import { prepareCaptureValue, truncate } from "./capture.ts";
|
|
7
12
|
import { currentClient } from "./client.ts";
|
|
@@ -40,7 +45,22 @@ export function log(message: string, options?: LogOptions): void {
|
|
|
40
45
|
onError: cfg.onError,
|
|
41
46
|
};
|
|
42
47
|
const severity = SEVERITIES[options?.level ?? "info"];
|
|
43
|
-
const
|
|
48
|
+
const active = activeContext();
|
|
49
|
+
const explicitSession = options?.attributes?.["gen_ai.conversation.id"];
|
|
50
|
+
const propagatedSession = propagatedFromContext(active)?.["gen_ai.conversation.id"];
|
|
51
|
+
const sessionId =
|
|
52
|
+
explicitSession !== undefined
|
|
53
|
+
? undefined
|
|
54
|
+
: typeof propagatedSession === "string"
|
|
55
|
+
? propagatedSession
|
|
56
|
+
: cfg.processSessionId;
|
|
57
|
+
const ctx =
|
|
58
|
+
sessionId === undefined
|
|
59
|
+
? active
|
|
60
|
+
: active.setValue(PROPAGATED_KEY, {
|
|
61
|
+
...propagatedFromContext(active),
|
|
62
|
+
"gen_ai.conversation.id": sessionId,
|
|
63
|
+
});
|
|
44
64
|
|
|
45
65
|
const attributes: Record<string, AttributeValue> = {};
|
|
46
66
|
for (const [key, value] of Object.entries(options?.attributes ?? {})) {
|
package/src/span.ts
CHANGED
|
@@ -23,7 +23,9 @@ import {
|
|
|
23
23
|
PROPAGATED_KEY,
|
|
24
24
|
propagatedFromContext,
|
|
25
25
|
reportError,
|
|
26
|
+
sessionIdOf,
|
|
26
27
|
withContext,
|
|
28
|
+
withSessionParent,
|
|
27
29
|
} from "@telemetry-dev/otel";
|
|
28
30
|
|
|
29
31
|
import {
|
|
@@ -171,19 +173,36 @@ export function createSpanHandle(
|
|
|
171
173
|
options: StartSpanOptions = {},
|
|
172
174
|
): SpanHandle {
|
|
173
175
|
const meta = metaFor(core, options);
|
|
174
|
-
const
|
|
176
|
+
const resolved = resolveParent(options.parent);
|
|
177
|
+
const sessionId = sessionIdOf(resolved, options.attributes) ?? core.config.processSessionId;
|
|
178
|
+
const sessionContext =
|
|
179
|
+
sessionId === undefined
|
|
180
|
+
? resolved
|
|
181
|
+
: resolved.setValue(PROPAGATED_KEY, {
|
|
182
|
+
...propagatedFromContext(resolved),
|
|
183
|
+
"gen_ai.conversation.id": sessionId,
|
|
184
|
+
});
|
|
185
|
+
const parentCtx = withSessionParent(sessionContext, sessionId, core.config.apiKey);
|
|
186
|
+
const attrs = fieldsToAttributes(options, meta.type, meta.capture);
|
|
175
187
|
const span = core.tracer.startSpan(
|
|
176
188
|
name,
|
|
177
189
|
{
|
|
178
190
|
kind: options.kind ?? SpanKind.INTERNAL,
|
|
179
191
|
startTime: options.startTime,
|
|
180
192
|
links: options.links,
|
|
181
|
-
attributes: {
|
|
193
|
+
attributes: {
|
|
194
|
+
"gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type],
|
|
195
|
+
...propagatedFromContext(parentCtx),
|
|
196
|
+
...attrs,
|
|
197
|
+
},
|
|
182
198
|
},
|
|
183
199
|
parentCtx,
|
|
184
200
|
);
|
|
185
201
|
SPAN_META.set(span, meta);
|
|
186
|
-
|
|
202
|
+
// The processor stamps propagation on start; explicit fields still win.
|
|
203
|
+
if (Object.keys(attrs).length > 0) span.setAttributes(attrs);
|
|
204
|
+
if (options.name) span.updateName(options.name);
|
|
205
|
+
if (options.error !== undefined) applyError(span, options.error);
|
|
187
206
|
|
|
188
207
|
const spanContext = span.spanContext();
|
|
189
208
|
const flags = (spanContext.traceFlags & 0xff).toString(16).padStart(2, "0");
|
|
@@ -266,7 +285,7 @@ export function startActiveSpan<T>(
|
|
|
266
285
|
const fn = isFn ? optionsOrFn : maybeFn!;
|
|
267
286
|
const options = isFn ? undefined : optionsOrFn;
|
|
268
287
|
const handle = startSpan(name, options);
|
|
269
|
-
if (
|
|
288
|
+
if (handle === NOOP_SPAN_HANDLE) return fn(handle);
|
|
270
289
|
return runWithHandle(handle, fn);
|
|
271
290
|
}
|
|
272
291
|
|