@telemetry-dev/sdk 0.1.2 → 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 CHANGED
@@ -60,6 +60,12 @@ 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
+
63
69
  `sampler` accepts an OpenTelemetry `Sampler` and overrides `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
64
70
  The default is parent-based always-on. Session roots keep the configured root policy.
65
71
  Nested spans and explicitly parented spans keep the real parent's policy.
package/dist/index.d.mts CHANGED
@@ -20,6 +20,8 @@ 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). */
package/dist/index.mjs CHANGED
@@ -7,6 +7,7 @@ import { AlsContextManager, BATCHED_METRIC_INTERVAL_MS, DEFAULT_BASE_URL, DEFAUL
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,6 +15,7 @@ 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",
19
21
  sampler: options.sampler,
@@ -73,6 +75,7 @@ function initInner(options, overrides) {
73
75
  activeClient = NOOP_CLIENT;
74
76
  return NOOP_CLIENT;
75
77
  }
78
+ if (config.sessionMode === "process") config.processSessionId = globalThis.crypto.randomUUID();
76
79
  const resource = resourceFromAttributes({
77
80
  "service.name": config.serviceName,
78
81
  "deployment.environment.name": config.environment,
@@ -237,7 +240,14 @@ function log(message, options) {
237
240
  onError: cfg.onError
238
241
  };
239
242
  const severity = SEVERITIES[options?.level ?? "info"];
240
- const ctx = activeContext$1();
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
+ });
241
251
  const attributes = {};
242
252
  for (const [key, value] of Object.entries(options?.attributes ?? {})) {
243
253
  if (value === void 0) continue;
@@ -425,7 +435,11 @@ function metaFor(core, options) {
425
435
  function createSpanHandle(core, name, options = {}) {
426
436
  const meta = metaFor(core, options);
427
437
  const resolved = resolveParent(options.parent);
428
- const parentCtx = withSessionParent(resolved, sessionIdOf(resolved, options.attributes), core.config.apiKey);
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);
429
443
  const attrs = fieldsToAttributes(options, meta.type, meta.capture);
430
444
  const span = core.tracer.startSpan(name, {
431
445
  kind: options.kind ?? SpanKind.INTERNAL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telemetry-dev/sdk",
3
- "version": "0.1.2",
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/telemetry.dev.git",
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.2"
45
+ "@telemetry-dev/otel": "^0.1.3"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@opentelemetry/api": "^1.9.1",
package/src/client.ts CHANGED
@@ -112,6 +112,8 @@ function initInner(options?: TelemetryOptions, overrides?: ClientOverrides): Tel
112
112
  return NOOP_CLIENT;
113
113
  }
114
114
 
115
+ if (config.sessionMode === "process") config.processSessionId = globalThis.crypto.randomUUID();
116
+
115
117
  const resource = resourceFromAttributes({
116
118
  "service.name": config.serviceName,
117
119
  "deployment.environment.name": config.environment,
package/src/config.ts CHANGED
@@ -26,6 +26,8 @@ export interface TelemetryOptions {
26
26
  serviceName?: string;
27
27
  /** Kill switch: `false` makes the SDK a complete no-op (tests). Default true. */
28
28
  enabled?: boolean;
29
+ /** Group otherwise uncorrelated spans and logs for this SDK initialization. Default "explicit". */
30
+ sessionMode?: "explicit" | "process";
29
31
  /** Register this SDK's TracerProvider/context manager/propagator globally. Default false (isolated). */
30
32
  registerGlobal?: boolean;
31
33
  /** "batched" (default) buffers spans; "immediate" exports per span (serverless). */
@@ -69,6 +71,8 @@ export interface ResolvedConfig {
69
71
  environment: string;
70
72
  serviceName: string;
71
73
  enabled: boolean;
74
+ sessionMode: "explicit" | "process";
75
+ processSessionId?: string;
72
76
  registerGlobal: boolean;
73
77
  exportMode: ExportMode;
74
78
  sampler?: Sampler;
@@ -91,6 +95,13 @@ export const DEFAULT_MAX_ATTRIBUTE_LENGTH = 65536;
91
95
 
92
96
  export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
93
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
+ }
94
105
  const baseUrl = (options.baseUrl ?? env.TELEMETRY_DEV_BASE_URL ?? DEFAULT_BASE_URL).replace(
95
106
  /\/+$/,
96
107
  "",
@@ -101,6 +112,7 @@ export function resolveConfig(options: TelemetryOptions = {}): ResolvedConfig {
101
112
  environment: options.environment ?? env.TELEMETRY_DEV_ENVIRONMENT ?? "production",
102
113
  serviceName: options.serviceName ?? env.OTEL_SERVICE_NAME ?? "unknown_service",
103
114
  enabled: options.enabled ?? true,
115
+ sessionMode: options.sessionMode ?? "explicit",
104
116
  registerGlobal: options.registerGlobal ?? false,
105
117
  exportMode: options.exportMode ?? "batched",
106
118
  sampler: options.sampler,
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 { activeContext, propagatedFromContext, reportError } from "@telemetry-dev/otel";
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 ctx = activeContext();
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
@@ -174,11 +174,15 @@ export function createSpanHandle(
174
174
  ): SpanHandle {
175
175
  const meta = metaFor(core, options);
176
176
  const resolved = resolveParent(options.parent);
177
- const parentCtx = withSessionParent(
178
- resolved,
179
- sessionIdOf(resolved, options.attributes),
180
- core.config.apiKey,
181
- );
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);
182
186
  const attrs = fieldsToAttributes(options, meta.type, meta.capture);
183
187
  const span = core.tracer.startSpan(
184
188
  name,