@telemetry-dev/sdk 0.1.7 → 0.1.9
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 +51 -12
- package/dist/index.d.mts +17 -2
- package/dist/index.mjs +10 -2
- package/package.json +2 -2
- package/src/capture.ts +1 -1
- package/src/client.ts +5 -0
- package/src/config.ts +5 -1
- package/src/index.ts +1 -0
- package/src/noop.ts +1 -0
- package/src/span.ts +13 -0
package/README.md
CHANGED
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
Pure TypeScript SDK for [telemetry.dev](https://telemetry.dev): add LLM traces, logs, and GenAI
|
|
4
4
|
metrics to any app. Built on OpenTelemetry — spans use the `gen_ai.*` semantic conventions and
|
|
5
5
|
ship as OTLP protobuf to `https://ingest.telemetry.dev`. Works on Node >= 20.19 and edge runtimes
|
|
6
|
-
(Vercel Edge, Cloudflare Workers with
|
|
7
|
-
|
|
6
|
+
(Vercel Edge, and Cloudflare Workers with Node.js compatibility, which is on by default for
|
|
7
|
+
compatibility dates of 2026-08-04 or later; earlier dates need the `nodejs_compat` flag). ESM-only;
|
|
8
|
+
CJS consumers can `require()` it on Node >= 20.19.
|
|
9
|
+
|
|
10
|
+
Docs: https://telemetry.dev/docs/sdk/typescript
|
|
8
11
|
|
|
9
12
|
```sh
|
|
10
13
|
npm install @telemetry-dev/sdk
|
|
@@ -12,10 +15,20 @@ npm install @telemetry-dev/sdk
|
|
|
12
15
|
|
|
13
16
|
## Quickstart
|
|
14
17
|
|
|
18
|
+
Set `TELEMETRY_DEV_API_KEY` to your project API key. `init()` reads it from the environment.
|
|
19
|
+
|
|
15
20
|
```ts
|
|
16
21
|
import { init, observe, startSpan, propagateAttributes, log, flush } from "@telemetry-dev/sdk";
|
|
17
22
|
|
|
18
|
-
init(
|
|
23
|
+
init();
|
|
24
|
+
|
|
25
|
+
// Stand-in for your model client, so the example runs on its own. Replace it with a real call.
|
|
26
|
+
async function callModel(question: string) {
|
|
27
|
+
return {
|
|
28
|
+
message: { role: "assistant", content: `You asked: ${question}` },
|
|
29
|
+
usage: { input: 12, output: 9 },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
19
32
|
|
|
20
33
|
const answer = observe(
|
|
21
34
|
async function answer(question: string) {
|
|
@@ -25,13 +38,13 @@ const answer = observe(
|
|
|
25
38
|
provider: "openai",
|
|
26
39
|
input: [{ role: "user", content: question }],
|
|
27
40
|
});
|
|
28
|
-
const res = await
|
|
41
|
+
const res = await callModel(question);
|
|
29
42
|
generation.end({
|
|
30
|
-
output: res.message,
|
|
43
|
+
output: [res.message],
|
|
31
44
|
usage: { inputTokens: res.usage.input, outputTokens: res.usage.output },
|
|
32
45
|
});
|
|
33
46
|
log("generation finished", { attributes: { cached: false } });
|
|
34
|
-
return res.message;
|
|
47
|
+
return res.message.content;
|
|
35
48
|
},
|
|
36
49
|
{ type: "agent" },
|
|
37
50
|
);
|
|
@@ -70,6 +83,12 @@ still record non-content metadata such as response IDs and models, finish reason
|
|
|
70
83
|
and errors. Capture flags do not gate stop sequences, tool descriptions and definitions (`gen_ai.tool.description`, `gen_ai.tool.definitions`), caller-supplied metadata or raw attributes,
|
|
71
84
|
or exception messages and stack traces; redact those separately when needed.
|
|
72
85
|
|
|
86
|
+
`mask` receives input and output content, system instructions, log messages, log attributes
|
|
87
|
+
other than numbers and booleans, and content that integrations pass to
|
|
88
|
+
`handle.serializeContent`. `ctx.key` names the attribute being written. It does not receive
|
|
89
|
+
span or propagated `metadata`, raw `attributes`, or exception messages and stack traces; redact
|
|
90
|
+
those separately.
|
|
91
|
+
|
|
73
92
|
Root spans remain independent by default. `init({ sessionMode: "process" })` groups otherwise
|
|
74
93
|
uncorrelated spans and logs using a fresh opaque UUID for each enabled SDK initialization.
|
|
75
94
|
Explicit and propagated session IDs take precedence, and valid parent trace context is preserved.
|
|
@@ -79,7 +98,7 @@ new ID. It is not persisted across processes.
|
|
|
79
98
|
`sampler` accepts an OpenTelemetry `Sampler` and overrides `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
|
|
80
99
|
The default is parent-based always-on. Session roots keep the configured root policy.
|
|
81
100
|
Nested spans and explicitly parented spans keep the real parent's policy.
|
|
82
|
-
The SDK installs the [session sampling wrapper](
|
|
101
|
+
The SDK installs the [session sampling wrapper](https://github.com/telemetry-dev/telemetry/blob/main/packages/otel/README.md#correlation-attributes) on its provider. This includes the global provider that Eve uses.
|
|
83
102
|
|
|
84
103
|
## API
|
|
85
104
|
|
|
@@ -89,6 +108,13 @@ The SDK installs the [session sampling wrapper](../otel/README.md#correlation-at
|
|
|
89
108
|
- `startSpan(name, options?)` — manual handle; does **not** activate context. `handle.update()`,
|
|
90
109
|
`handle.end()`, `handle.recordOutputChunk(timestampMs?)`, `handle.traceparent`, `handle.span`
|
|
91
110
|
(raw OTel span).
|
|
111
|
+
- `handle.serializeContent(value, key)` — prepare content that an integration writes outside
|
|
112
|
+
`input` and `output`, such as an error message or a raw attribute, the way `input` and `output`
|
|
113
|
+
are prepared: the configured `mask` with `key`, JSON stringify (strings pass through), then
|
|
114
|
+
truncation to `maxAttributeLength`. Returns `undefined` when the mask throws (reported through
|
|
115
|
+
`onError`) or drops the value, or the value cannot be serialized; write nothing then. It does not
|
|
116
|
+
check `captureInput` / `captureOutput`. Integrations that support older cores should treat a
|
|
117
|
+
missing method as a configured mask.
|
|
92
118
|
- `startActiveSpan(name, options?, fn)` — callback-scoped; activates context, auto-ends.
|
|
93
119
|
- `updateActiveSpan(fields)` — update the innermost active span.
|
|
94
120
|
- `propagateAttributes({ userId, sessionId, metadata }, fn)` — stamps `user.id`,
|
|
@@ -102,6 +128,8 @@ The SDK installs the [session sampling wrapper](../otel/README.md#correlation-at
|
|
|
102
128
|
or inject W3C trace context. Baggage is omitted unless `includeBaggage` is true.
|
|
103
129
|
- `captureEnabled("input" | "output")` — report whether the active initialized client permits that
|
|
104
130
|
capture direction. Returns `false` when no client is initialized.
|
|
131
|
+
- `captureMasked()` — report whether the active initialized client has a `mask` configured.
|
|
132
|
+
Returns `false` when no client is initialized.
|
|
105
133
|
- `boundedCapture(value, options?)` — copy a JSON-compatible value within bounded work and retained
|
|
106
134
|
size. Defaults to 48 KiB, depth 32, and 1,000 traversal items. Returns `{ value, truncated }`;
|
|
107
135
|
`value` can be partial or `undefined` when a limit, cycle, getter, or skip rule prevents capture.
|
|
@@ -126,9 +154,9 @@ histogram state and does not retain individual intervals. Pull-based timing incl
|
|
|
126
154
|
between reads. An explicit timestamp uses the same monotonic millisecond clock for every chunk.
|
|
127
155
|
|
|
128
156
|
Provider integrations feature-detect stream APIs. With an older core SDK that lacks the stream
|
|
129
|
-
capture policy, streams are still traced with usage, model, finish reason, and timing
|
|
130
|
-
|
|
131
|
-
withheld. Instrumentation failures are not reported to `onError` with such a core. If only
|
|
157
|
+
capture policy, streams are still traced with usage, model, finish reason, and timing. Output is
|
|
158
|
+
retained within the same fixed bounds as with a current core, but a mask is assumed to be
|
|
159
|
+
configured, so incomplete output is withheld. Instrumentation failures are not reported to `onError` with such a core. If only
|
|
132
160
|
`recordOutputChunk` is unavailable, tracing and reconstruction continue but chunk-interval metrics
|
|
133
161
|
are unavailable. Upgrade the core SDK with provider integrations to enable the full stream
|
|
134
162
|
telemetry.
|
|
@@ -163,10 +191,21 @@ that mode only this SDK's spans are exported by default.
|
|
|
163
191
|
Any vanilla OTel app can also skip the SDK entirely: point a stock OTLP/HTTP protobuf exporter at
|
|
164
192
|
`https://ingest.telemetry.dev/v1/traces` with an `Authorization: Bearer td_live_...` header.
|
|
165
193
|
|
|
194
|
+
### Native Claude SDK spans
|
|
195
|
+
|
|
196
|
+
`@anthropic-ai/sdk` 0.134.0 and later emits OpenTelemetry spans natively through the global tracer provider.
|
|
197
|
+
To send these spans to telemetry.dev, refer to [Claude SDK](https://telemetry.dev/docs/integrations/claude-sdk).
|
|
198
|
+
If you use `init({ registerGlobal: true })`, also set `spanFilter: () => true`. The default filter of that mode exports only the spans of this SDK.
|
|
199
|
+
Do not use the native spans and `@telemetry-dev/anthropic` together. If you use the two, each request has two spans.
|
|
200
|
+
|
|
166
201
|
## Notes
|
|
167
202
|
|
|
168
|
-
- AsyncLocalStorage powers context propagation. On runtimes without it (Workers without
|
|
169
|
-
|
|
203
|
+
- AsyncLocalStorage powers context propagation. On runtimes without it (Cloudflare Workers without
|
|
204
|
+
Node.js compatibility) automatic nesting is unavailable entirely unless the host app registered its
|
|
170
205
|
own global OTel context manager — pass `parent` explicitly there.
|
|
171
206
|
- `@opentelemetry/api` is a peer dependency (installed automatically by npm/pnpm) so the SDK
|
|
172
207
|
shares the host app's OTel API singleton.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT. See [LICENSE](https://github.com/telemetry-dev/telemetry/blob/main/packages/sdk/LICENSE).
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,11 @@ import { BatchOptions, BatchOptions as BatchOptions$1, ExportMode, ExportMode as
|
|
|
5
5
|
import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
|
|
6
6
|
|
|
7
7
|
//#region src/config.d.ts
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Redaction hook applied to captured content (input/output, log messages and attributes, and
|
|
10
|
+
* content passed to `SpanHandle.serializeContent`) before JSON stringification. Not applied to
|
|
11
|
+
* metadata or raw attributes.
|
|
12
|
+
*/
|
|
9
13
|
type MaskFn<T = unknown> = (value: T, ctx: {
|
|
10
14
|
key: string;
|
|
11
15
|
}) => T;
|
|
@@ -154,6 +158,8 @@ interface TelemetryClient {
|
|
|
154
158
|
shutdown(): Promise<void>;
|
|
155
159
|
}
|
|
156
160
|
declare function captureEnabled(kind: "input" | "output"): boolean;
|
|
161
|
+
/** Whether the active client runs captured values through a mask; false before `init`. */
|
|
162
|
+
declare function captureMasked(): boolean;
|
|
157
163
|
declare function flush(): Promise<void>;
|
|
158
164
|
declare function shutdown(): Promise<void>;
|
|
159
165
|
declare function init(options?: TelemetryOptions, overrides?: ClientOverrides): TelemetryClient;
|
|
@@ -209,6 +215,15 @@ interface SpanHandle {
|
|
|
209
215
|
/** Records an output chunk arrival. Pull-based streams include consumer delay between pulls. */
|
|
210
216
|
recordOutputChunk(timestampMs?: number): void;
|
|
211
217
|
reportError?(cause: unknown): void;
|
|
218
|
+
/**
|
|
219
|
+
* Prepares content that the span writes outside `input` and `output` the way those are
|
|
220
|
+
* prepared: the configured mask with `key` (the attribute the result is written to), JSON
|
|
221
|
+
* stringify (strings pass through), then truncation to `maxAttributeLength`. Returns undefined
|
|
222
|
+
* when the mask throws (reported to `onError`) or drops the value, or the value cannot be
|
|
223
|
+
* serialized. Capture flags are not checked. Absent on cores that predate it; treat a missing
|
|
224
|
+
* method as masked.
|
|
225
|
+
*/
|
|
226
|
+
serializeContent?(value: unknown, key: string): string | undefined;
|
|
212
227
|
end(fields?: SpanFields & {
|
|
213
228
|
endTime?: Date | number;
|
|
214
229
|
}): void;
|
|
@@ -242,4 +257,4 @@ interface ObserveOptions extends StartSpanOptions {
|
|
|
242
257
|
*/
|
|
243
258
|
declare function observe<TThis, TArgs extends unknown[], TReturn>(fn: (this: TThis, ...args: TArgs) => TReturn, options?: ObserveOptions): (...args: TArgs) => TReturn;
|
|
244
259
|
//#endregion
|
|
245
|
-
export { type AgentFields, type BatchOptions, type BoundedCaptureDetails, type BoundedCaptureOptions, type BoundedCaptureResult, type ClientOverrides, type ExportMode, type GenerationFields, type LogLevel, type LogOptions, type MaskFn, type ObserveOptions, type ParentRef, type PropagatedAttributes, SCOPE_NAME, type SdkLogLevel, type SpanCapturePolicy, type SpanFields, type SpanHandle, type SpanType, type StartSpanOptions, type TelemetryClient, type TelemetryOptions, TelemetrySpanProcessor, type TelemetrySpanProcessorOptions, type TokenUsage, type ToolFields, activeContext, boundedCapture, boundedCaptureDetails, captureEnabled, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
|
260
|
+
export { type AgentFields, type BatchOptions, type BoundedCaptureDetails, type BoundedCaptureOptions, type BoundedCaptureResult, type ClientOverrides, type ExportMode, type GenerationFields, type LogLevel, type LogOptions, type MaskFn, type ObserveOptions, type ParentRef, type PropagatedAttributes, SCOPE_NAME, type SdkLogLevel, type SpanCapturePolicy, type SpanFields, type SpanHandle, type SpanType, type StartSpanOptions, type TelemetryClient, type TelemetryOptions, TelemetrySpanProcessor, type TelemetrySpanProcessorOptions, type TokenUsage, type ToolFields, activeContext, boundedCapture, boundedCaptureDetails, captureEnabled, captureMasked, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
package/dist/index.mjs
CHANGED
|
@@ -63,6 +63,10 @@ function captureEnabled(kind) {
|
|
|
63
63
|
const config = currentClient().core?.config;
|
|
64
64
|
return kind === "input" ? config?.captureInput ?? false : config?.captureOutput ?? false;
|
|
65
65
|
}
|
|
66
|
+
/** Whether the active client runs captured values through a mask; false before `init`. */
|
|
67
|
+
function captureMasked() {
|
|
68
|
+
return currentClient().core?.config.mask !== void 0;
|
|
69
|
+
}
|
|
66
70
|
function flush() {
|
|
67
71
|
return currentClient().flush();
|
|
68
72
|
}
|
|
@@ -353,7 +357,7 @@ function prepareCaptureValue(key, value, cfg) {
|
|
|
353
357
|
if (cfg.mask) try {
|
|
354
358
|
masked = cfg.mask(value, { key });
|
|
355
359
|
} catch (error) {
|
|
356
|
-
reportError(cfg.onError, error
|
|
360
|
+
reportError(cfg.onError, error);
|
|
357
361
|
return;
|
|
358
362
|
}
|
|
359
363
|
const serialized = jsonAttr(masked);
|
|
@@ -536,6 +540,7 @@ const NOOP_SPAN_HANDLE = {
|
|
|
536
540
|
update: () => NOOP_SPAN_HANDLE,
|
|
537
541
|
recordOutputChunk: () => {},
|
|
538
542
|
reportError: () => {},
|
|
543
|
+
serializeContent: () => void 0,
|
|
539
544
|
end: () => {}
|
|
540
545
|
};
|
|
541
546
|
//#endregion
|
|
@@ -678,6 +683,9 @@ function createSpanHandle(core, name, options = {}) {
|
|
|
678
683
|
reportError(cause) {
|
|
679
684
|
reportError(meta.onError, cause instanceof Error ? cause : new Error(String(cause)));
|
|
680
685
|
},
|
|
686
|
+
serializeContent(value, key) {
|
|
687
|
+
return prepareCaptureValue(key, value, meta.capture);
|
|
688
|
+
},
|
|
681
689
|
end(fields) {
|
|
682
690
|
try {
|
|
683
691
|
if (fields) applyFields(span, meta, fields);
|
|
@@ -805,4 +813,4 @@ function observe(fn, options) {
|
|
|
805
813
|
return wrapped;
|
|
806
814
|
}
|
|
807
815
|
//#endregion
|
|
808
|
-
export { SCOPE_NAME, TelemetrySpanProcessor, activeContext, boundedCapture, boundedCaptureDetails, captureEnabled, extractW3cContext, flush, getTraceparent, init, injectW3cContext, log, observe, propagateAttributes, shutdown, startActiveSpan, startSpan, updateActiveSpan, withContext };
|
|
816
|
+
export { SCOPE_NAME, TelemetrySpanProcessor, activeContext, boundedCapture, boundedCaptureDetails, captureEnabled, captureMasked, 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.9",
|
|
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.6"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@opentelemetry/api": "^1.9.1",
|
package/src/capture.ts
CHANGED
|
@@ -271,7 +271,7 @@ export function prepareCaptureValue(
|
|
|
271
271
|
try {
|
|
272
272
|
masked = cfg.mask(value, { key });
|
|
273
273
|
} catch (error) {
|
|
274
|
-
reportError(cfg.onError, error
|
|
274
|
+
reportError(cfg.onError, error);
|
|
275
275
|
|
|
276
276
|
return undefined;
|
|
277
277
|
}
|
package/src/client.ts
CHANGED
|
@@ -94,6 +94,11 @@ export function captureEnabled(kind: "input" | "output"): boolean {
|
|
|
94
94
|
return kind === "input" ? (config?.captureInput ?? false) : (config?.captureOutput ?? false);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/** Whether the active client runs captured values through a mask; false before `init`. */
|
|
98
|
+
export function captureMasked(): boolean {
|
|
99
|
+
return currentClient().core?.config.mask !== undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
export function flush(): Promise<void> {
|
|
98
103
|
return currentClient().flush();
|
|
99
104
|
}
|
package/src/config.ts
CHANGED
|
@@ -12,7 +12,11 @@ import {
|
|
|
12
12
|
|
|
13
13
|
export type { BatchOptions, ExportMode, LogLevel, SdkLogLevel } from "@telemetry-dev/otel";
|
|
14
14
|
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Redaction hook applied to captured content (input/output, log messages and attributes, and
|
|
17
|
+
* content passed to `SpanHandle.serializeContent`) before JSON stringification. Not applied to
|
|
18
|
+
* metadata or raw attributes.
|
|
19
|
+
*/
|
|
16
20
|
export type MaskFn<T = unknown> = (value: T, ctx: { key: string }) => T;
|
|
17
21
|
|
|
18
22
|
export interface TelemetryOptions {
|
package/src/index.ts
CHANGED
package/src/noop.ts
CHANGED
package/src/span.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
type SpanFields,
|
|
39
39
|
type SpanType,
|
|
40
40
|
} from "./attrs.ts";
|
|
41
|
+
import { prepareCaptureValue } from "./capture.ts";
|
|
41
42
|
import { type ClientCore, currentClient } from "./client.ts";
|
|
42
43
|
import { NOOP_SPAN_HANDLE } from "./noop.ts";
|
|
43
44
|
|
|
@@ -80,6 +81,15 @@ export interface SpanHandle {
|
|
|
80
81
|
/** Records an output chunk arrival. Pull-based streams include consumer delay between pulls. */
|
|
81
82
|
recordOutputChunk(timestampMs?: number): void;
|
|
82
83
|
reportError?(cause: unknown): void;
|
|
84
|
+
/**
|
|
85
|
+
* Prepares content that the span writes outside `input` and `output` the way those are
|
|
86
|
+
* prepared: the configured mask with `key` (the attribute the result is written to), JSON
|
|
87
|
+
* stringify (strings pass through), then truncation to `maxAttributeLength`. Returns undefined
|
|
88
|
+
* when the mask throws (reported to `onError`) or drops the value, or the value cannot be
|
|
89
|
+
* serialized. Capture flags are not checked. Absent on cores that predate it; treat a missing
|
|
90
|
+
* method as masked.
|
|
91
|
+
*/
|
|
92
|
+
serializeContent?(value: unknown, key: string): string | undefined;
|
|
83
93
|
end(fields?: SpanFields & { endTime?: Date | number }): void;
|
|
84
94
|
}
|
|
85
95
|
|
|
@@ -303,6 +313,9 @@ export function createSpanHandle(
|
|
|
303
313
|
reportError(cause) {
|
|
304
314
|
reportError(meta.onError, cause instanceof Error ? cause : new Error(String(cause)));
|
|
305
315
|
},
|
|
316
|
+
serializeContent(value, key) {
|
|
317
|
+
return prepareCaptureValue(key, value, meta.capture);
|
|
318
|
+
},
|
|
306
319
|
end(fields) {
|
|
307
320
|
try {
|
|
308
321
|
if (fields) applyFields(span, meta, fields);
|