@telemetry-dev/sdk 0.1.8 → 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 +46 -9
- package/dist/index.d.mts +14 -1
- package/dist/index.mjs +5 -1
- package/package.json +2 -2
- package/src/capture.ts +1 -1
- package/src/config.ts +5 -1
- 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`,
|
|
@@ -165,10 +191,21 @@ that mode only this SDK's spans are exported by default.
|
|
|
165
191
|
Any vanilla OTel app can also skip the SDK entirely: point a stock OTLP/HTTP protobuf exporter at
|
|
166
192
|
`https://ingest.telemetry.dev/v1/traces` with an `Authorization: Bearer td_live_...` header.
|
|
167
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
|
+
|
|
168
201
|
## Notes
|
|
169
202
|
|
|
170
|
-
- AsyncLocalStorage powers context propagation. On runtimes without it (Workers without
|
|
171
|
-
|
|
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
|
|
172
205
|
own global OTel context manager — pass `parent` explicitly there.
|
|
173
206
|
- `@opentelemetry/api` is a peer dependency (installed automatically by npm/pnpm) so the SDK
|
|
174
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;
|
|
@@ -211,6 +215,15 @@ interface SpanHandle {
|
|
|
211
215
|
/** Records an output chunk arrival. Pull-based streams include consumer delay between pulls. */
|
|
212
216
|
recordOutputChunk(timestampMs?: number): void;
|
|
213
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;
|
|
214
227
|
end(fields?: SpanFields & {
|
|
215
228
|
endTime?: Date | number;
|
|
216
229
|
}): void;
|
package/dist/index.mjs
CHANGED
|
@@ -357,7 +357,7 @@ function prepareCaptureValue(key, value, cfg) {
|
|
|
357
357
|
if (cfg.mask) try {
|
|
358
358
|
masked = cfg.mask(value, { key });
|
|
359
359
|
} catch (error) {
|
|
360
|
-
reportError(cfg.onError, error
|
|
360
|
+
reportError(cfg.onError, error);
|
|
361
361
|
return;
|
|
362
362
|
}
|
|
363
363
|
const serialized = jsonAttr(masked);
|
|
@@ -540,6 +540,7 @@ const NOOP_SPAN_HANDLE = {
|
|
|
540
540
|
update: () => NOOP_SPAN_HANDLE,
|
|
541
541
|
recordOutputChunk: () => {},
|
|
542
542
|
reportError: () => {},
|
|
543
|
+
serializeContent: () => void 0,
|
|
543
544
|
end: () => {}
|
|
544
545
|
};
|
|
545
546
|
//#endregion
|
|
@@ -682,6 +683,9 @@ function createSpanHandle(core, name, options = {}) {
|
|
|
682
683
|
reportError(cause) {
|
|
683
684
|
reportError(meta.onError, cause instanceof Error ? cause : new Error(String(cause)));
|
|
684
685
|
},
|
|
686
|
+
serializeContent(value, key) {
|
|
687
|
+
return prepareCaptureValue(key, value, meta.capture);
|
|
688
|
+
},
|
|
685
689
|
end(fields) {
|
|
686
690
|
try {
|
|
687
691
|
if (fields) applyFields(span, meta, fields);
|
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/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/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);
|