@telemetry-dev/sdk 0.1.1 → 0.1.2

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 CHANGED
@@ -60,6 +60,11 @@ 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
+ `sampler` accepts an OpenTelemetry `Sampler` and overrides `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
64
+ The default is parent-based always-on. Session roots keep the configured root policy.
65
+ Nested spans and explicitly parented spans keep the real parent's policy.
66
+ The SDK installs the [session sampling wrapper](../otel/README.md#correlation-attributes) on its provider. This includes the global provider that Eve uses.
67
+
63
68
  ## API
64
69
 
65
70
  - `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
@@ -24,6 +24,8 @@ interface TelemetryOptions {
24
24
  registerGlobal?: boolean;
25
25
  /** "batched" (default) buffers spans; "immediate" exports per span (serverless). */
26
26
  exportMode?: ExportMode$1;
27
+ /** OTel sampling policy. Defaults to OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG. */
28
+ sampler?: Sampler;
27
29
  /** Capture span inputs (args, prompts) by default. Default true. */
28
30
  captureInput?: boolean;
29
31
  /** Capture span outputs (results, completions) by default. Default true. */
@@ -35,6 +37,12 @@ interface TelemetryOptions {
35
37
  batch?: BatchOptions$1;
36
38
  /** Export filter. Default when registerGlobal: only spans from this SDK's instrumentation scope. */
37
39
  spanFilter?: (span: ReadableSpan) => boolean;
40
+ /**
41
+ * With registerGlobal: spans from other tracers that this returns a session id for are
42
+ * parented under the session trace even when a parent span is active. Use it for frameworks
43
+ * that start each turn inside their own engine span.
44
+ */
45
+ sessionRootOf?: SessionRootOf;
38
46
  /** Extra resource attributes merged onto service.name / deployment.environment.name. */
39
47
  resourceAttributes?: Record<string, AttributeValue>;
40
48
  /** SDK self-diagnostics console level. Default "warn"; "silent" disables. */
@@ -195,4 +203,4 @@ interface ObserveOptions extends StartSpanOptions {
195
203
  */
196
204
  declare function observe<TThis, TArgs extends unknown[], TReturn>(fn: (this: TThis, ...args: TArgs) => TReturn, options?: ObserveOptions): (...args: TArgs) => TReturn;
197
205
  //#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 };
206
+ 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,7 +3,7 @@ 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();
@@ -16,6 +16,7 @@ function resolveConfig(options = {}) {
16
16
  enabled: options.enabled ?? true,
17
17
  registerGlobal: options.registerGlobal ?? false,
18
18
  exportMode: options.exportMode ?? "batched",
19
+ sampler: options.sampler,
19
20
  captureInput: options.captureInput ?? true,
20
21
  captureOutput: options.captureOutput ?? true,
21
22
  mask: options.mask,
@@ -25,6 +26,7 @@ function resolveConfig(options = {}) {
25
26
  ...options.batch
26
27
  },
27
28
  spanFilter: options.spanFilter,
29
+ sessionRootOf: options.sessionRootOf,
28
30
  resourceAttributes: options.resourceAttributes,
29
31
  logLevel: options.logLevel ?? "warn",
30
32
  fetchImpl: options.fetch ?? globalThis.fetch,
@@ -94,25 +96,27 @@ function initInner(options, overrides) {
94
96
  exporter: metricExporter,
95
97
  exportIntervalMillis: config.exportMode === "batched" ? BATCHED_METRIC_INTERVAL_MS : DORMANT_INTERVAL_MS
96
98
  }) : void 0;
97
- const spanFilter = config.spanFilter ?? (config.registerGlobal ? (span) => span.instrumentationScope.name === SCOPE_NAME : void 0);
99
+ const spanFilter = config.spanFilter ?? (config.registerGlobal ? (span) => span.instrumentationScope.name === SCOPE_NAME$1 : void 0);
100
+ const processor = new StampingSpanProcessor({
101
+ exporter: spanExporter,
102
+ exportMode: config.exportMode,
103
+ batch: config.batch,
104
+ spanFilter,
105
+ recordMetrics: metrics ? (span) => metrics.record(span) : void 0,
106
+ onError: config.onError
107
+ });
98
108
  const provider = new BasicTracerProvider({
99
109
  resource,
100
- spanProcessors: [new StampingSpanProcessor({
101
- exporter: spanExporter,
102
- exportMode: config.exportMode,
103
- batch: config.batch,
104
- spanFilter,
105
- recordMetrics: metrics ? (span) => metrics.record(span) : void 0,
106
- onError: config.onError
107
- })],
110
+ sampler: sessionSampler(config.sampler),
111
+ spanProcessors: [processor],
108
112
  spanLimits: { attributeValueLengthLimit: config.maxAttributeLength }
109
113
  });
110
- const tracer = provider.getTracer(SCOPE_NAME, SCOPE_VERSION);
114
+ const tracer = provider.getTracer(SCOPE_NAME$1, SCOPE_VERSION);
111
115
  let registeredTrace = false;
112
116
  let registeredContext = false;
113
117
  let registeredPropagation = false;
114
118
  if (config.registerGlobal) {
115
- registeredTrace = trace.setGlobalTracerProvider(provider);
119
+ registeredTrace = trace.setGlobalTracerProvider(config.sessionRootOf ? sessionRootTracerProvider(provider, config.apiKey, config.sessionRootOf, config.onError) : provider);
116
120
  if (als) registeredContext = context.setGlobalContextManager(new AlsContextManager(als));
117
121
  registeredPropagation = propagation.setGlobalPropagator(new W3CTraceContextPropagator());
118
122
  if (!registeredTrace) diag.warn("a global TracerProvider is already registered; attach the TelemetrySpanProcessor from '@telemetry-dev/otel' to your own provider instead");
@@ -132,7 +136,7 @@ function initInner(options, overrides) {
132
136
  processors: [logProcessor]
133
137
  });
134
138
  logs = {
135
- logger: loggerProvider.getLogger(SCOPE_NAME, SCOPE_VERSION),
139
+ logger: loggerProvider.getLogger(SCOPE_NAME$1, SCOPE_VERSION),
136
140
  forceFlush: () => loggerProvider.forceFlush(),
137
141
  shutdown: () => loggerProvider.shutdown()
138
142
  };
@@ -420,15 +424,23 @@ function metaFor(core, options) {
420
424
  }
421
425
  function createSpanHandle(core, name, options = {}) {
422
426
  const meta = metaFor(core, options);
423
- const parentCtx = resolveParent(options.parent);
427
+ const resolved = resolveParent(options.parent);
428
+ const parentCtx = withSessionParent(resolved, sessionIdOf(resolved, options.attributes), core.config.apiKey);
429
+ const attrs = fieldsToAttributes(options, meta.type, meta.capture);
424
430
  const span = core.tracer.startSpan(name, {
425
431
  kind: options.kind ?? SpanKind.INTERNAL,
426
432
  startTime: options.startTime,
427
433
  links: options.links,
428
- attributes: { "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type] }
434
+ attributes: {
435
+ "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type],
436
+ ...propagatedFromContext(parentCtx),
437
+ ...attrs
438
+ }
429
439
  }, parentCtx);
430
440
  SPAN_META.set(span, meta);
431
- applyFields(span, meta, options);
441
+ if (Object.keys(attrs).length > 0) span.setAttributes(attrs);
442
+ if (options.name) span.updateName(options.name);
443
+ if (options.error !== void 0) applyError(span, options.error);
432
444
  const spanContext = span.spanContext();
433
445
  const flags = (spanContext.traceFlags & 255).toString(16).padStart(2, "0");
434
446
  const handle = {
@@ -491,7 +503,7 @@ function startActiveSpan(name, optionsOrFn, maybeFn) {
491
503
  const isFn = typeof optionsOrFn === "function";
492
504
  const fn = isFn ? optionsOrFn : maybeFn;
493
505
  const handle = startSpan(name, isFn ? void 0 : optionsOrFn);
494
- if (!handle.isRecording) return fn(handle);
506
+ if (handle === NOOP_SPAN_HANDLE) return fn(handle);
495
507
  return runWithHandle(handle, fn);
496
508
  }
497
509
  /** Update the innermost active span; no-op when none is recording. */
@@ -573,4 +585,4 @@ function observe(fn, options) {
573
585
  return wrapped;
574
586
  }
575
587
  //#endregion
576
- export { activeContext, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
588
+ 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.1",
3
+ "version": "0.1.2",
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.1"
45
+ "@telemetry-dev/otel": "^0.1.2"
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,
@@ -153,6 +155,7 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
153
155
 
154
156
  const provider = new BasicTracerProvider({
155
157
  resource,
158
+ sampler: sessionSampler(config.sampler),
156
159
  spanProcessors: [processor],
157
160
  spanLimits: { attributeValueLengthLimit: config.maxAttributeLength },
158
161
  });
@@ -162,7 +165,11 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
162
165
  let registeredContext = false;
163
166
  let registeredPropagation = false;
164
167
  if (config.registerGlobal) {
165
- registeredTrace = trace.setGlobalTracerProvider(provider);
168
+ registeredTrace = trace.setGlobalTracerProvider(
169
+ config.sessionRootOf
170
+ ? sessionRootTracerProvider(provider, config.apiKey, config.sessionRootOf, config.onError)
171
+ : provider,
172
+ );
166
173
  if (als) {
167
174
  registeredContext = apiContext.setGlobalContextManager(new AlsContextManager(als));
168
175
  }
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";
@@ -29,6 +30,8 @@ export interface TelemetryOptions {
29
30
  registerGlobal?: boolean;
30
31
  /** "batched" (default) buffers spans; "immediate" exports per span (serverless). */
31
32
  exportMode?: ExportMode;
33
+ /** OTel sampling policy. Defaults to OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG. */
34
+ sampler?: Sampler;
32
35
  /** Capture span inputs (args, prompts) by default. Default true. */
33
36
  captureInput?: boolean;
34
37
  /** Capture span outputs (results, completions) by default. Default true. */
@@ -40,6 +43,12 @@ export interface TelemetryOptions {
40
43
  batch?: BatchOptions;
41
44
  /** Export filter. Default when registerGlobal: only spans from this SDK's instrumentation scope. */
42
45
  spanFilter?: (span: ReadableSpan) => boolean;
46
+ /**
47
+ * With registerGlobal: spans from other tracers that this returns a session id for are
48
+ * parented under the session trace even when a parent span is active. Use it for frameworks
49
+ * that start each turn inside their own engine span.
50
+ */
51
+ sessionRootOf?: SessionRootOf;
43
52
  /** Extra resource attributes merged onto service.name / deployment.environment.name. */
44
53
  resourceAttributes?: Record<string, AttributeValue>;
45
54
  /** SDK self-diagnostics console level. Default "warn"; "silent" disables. */
@@ -62,12 +71,14 @@ export interface ResolvedConfig {
62
71
  enabled: boolean;
63
72
  registerGlobal: boolean;
64
73
  exportMode: ExportMode;
74
+ sampler?: Sampler;
65
75
  captureInput: boolean;
66
76
  captureOutput: boolean;
67
77
  mask?: MaskFn;
68
78
  maxAttributeLength: number;
69
79
  batch: Required<BatchOptions>;
70
80
  spanFilter?: (span: ReadableSpan) => boolean;
81
+ sessionRootOf?: SessionRootOf;
71
82
  resourceAttributes?: Record<string, AttributeValue>;
72
83
  logLevel: SdkLogLevel;
73
84
  fetchImpl: typeof fetch;
@@ -92,12 +103,14 @@ export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
92
103
  enabled: options.enabled ?? true,
93
104
  registerGlobal: options.registerGlobal ?? false,
94
105
  exportMode: options.exportMode ?? "batched",
106
+ sampler: options.sampler,
95
107
  captureInput: options.captureInput ?? true,
96
108
  captureOutput: options.captureOutput ?? true,
97
109
  mask: options.mask,
98
110
  maxAttributeLength: options.maxAttributeLength ?? DEFAULT_MAX_ATTRIBUTE_LENGTH,
99
111
  batch: { ...DEFAULT_BATCH, ...options.batch },
100
112
  spanFilter: options.spanFilter,
113
+ sessionRootOf: options.sessionRootOf,
101
114
  resourceAttributes: options.resourceAttributes,
102
115
  logLevel: options.logLevel ?? "warn",
103
116
  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 { propagateAttributes, type PropagatedAttributes } from "@telemetry-dev/otel";
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/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,32 @@ export function createSpanHandle(
171
173
  options: StartSpanOptions = {},
172
174
  ): SpanHandle {
173
175
  const meta = metaFor(core, options);
174
- const parentCtx = resolveParent(options.parent);
176
+ const resolved = resolveParent(options.parent);
177
+ const parentCtx = withSessionParent(
178
+ resolved,
179
+ sessionIdOf(resolved, options.attributes),
180
+ core.config.apiKey,
181
+ );
182
+ const attrs = fieldsToAttributes(options, meta.type, meta.capture);
175
183
  const span = core.tracer.startSpan(
176
184
  name,
177
185
  {
178
186
  kind: options.kind ?? SpanKind.INTERNAL,
179
187
  startTime: options.startTime,
180
188
  links: options.links,
181
- attributes: { "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type] },
189
+ attributes: {
190
+ "gen_ai.operation.name": SPAN_TYPE_OPERATIONS[meta.type],
191
+ ...propagatedFromContext(parentCtx),
192
+ ...attrs,
193
+ },
182
194
  },
183
195
  parentCtx,
184
196
  );
185
197
  SPAN_META.set(span, meta);
186
- applyFields(span, meta, options);
198
+ // The processor stamps propagation on start; explicit fields still win.
199
+ if (Object.keys(attrs).length > 0) span.setAttributes(attrs);
200
+ if (options.name) span.updateName(options.name);
201
+ if (options.error !== undefined) applyError(span, options.error);
187
202
 
188
203
  const spanContext = span.spanContext();
189
204
  const flags = (spanContext.traceFlags & 0xff).toString(16).padStart(2, "0");
@@ -266,7 +281,7 @@ export function startActiveSpan<T>(
266
281
  const fn = isFn ? optionsOrFn : maybeFn!;
267
282
  const options = isFn ? undefined : optionsOrFn;
268
283
  const handle = startSpan(name, options);
269
- if (!handle.isRecording) return fn(handle);
284
+ if (handle === NOOP_SPAN_HANDLE) return fn(handle);
270
285
  return runWithHandle(handle, fn);
271
286
  }
272
287