@telemetry-dev/otel 0.1.0
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/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/index.d.mts +195 -0
- package/dist/index.mjs +553 -0
- package/package.json +65 -0
- package/src/attrs.ts +30 -0
- package/src/config.ts +29 -0
- package/src/context.ts +139 -0
- package/src/debug.ts +32 -0
- package/src/index.ts +46 -0
- package/src/metrics.ts +91 -0
- package/src/otel.ts +166 -0
- package/src/processor.ts +72 -0
- package/src/transport.ts +244 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 telemetry.dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @telemetry-dev/otel
|
|
2
|
+
|
|
3
|
+
Bring-your-own OpenTelemetry base layer for [telemetry.dev](https://telemetry.dev). It provides the span processor and OTLP/protobuf trace exporter used by `@telemetry-dev/sdk`, without requiring the SDK runtime.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm i @telemetry-dev/otel
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`@opentelemetry/api` is a peer dependency and is installed automatically by current npm and pnpm versions.
|
|
10
|
+
|
|
11
|
+
## NodeSDK
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
15
|
+
import { TelemetrySpanProcessor } from "@telemetry-dev/otel";
|
|
16
|
+
|
|
17
|
+
const sdk = new NodeSDK({
|
|
18
|
+
spanProcessors: [new TelemetrySpanProcessor()],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
sdk.start();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Vercel / Next.js `registerOTel`
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { registerOTel } from "@vercel/otel";
|
|
28
|
+
import { TelemetrySpanProcessor } from "@telemetry-dev/otel";
|
|
29
|
+
|
|
30
|
+
export function register() {
|
|
31
|
+
registerOTel({
|
|
32
|
+
serviceName: "my-app",
|
|
33
|
+
spanProcessors: [new TelemetrySpanProcessor()],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## BasicTracerProvider
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { trace } from "@opentelemetry/api";
|
|
42
|
+
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
43
|
+
import { TelemetrySpanProcessor } from "@telemetry-dev/otel";
|
|
44
|
+
|
|
45
|
+
const provider = new BasicTracerProvider({
|
|
46
|
+
spanProcessors: [new TelemetrySpanProcessor()],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
trace.setGlobalTracerProvider(provider);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Options
|
|
53
|
+
|
|
54
|
+
| Option | Fallback | Default / behavior |
|
|
55
|
+
| -------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| `apiKey` | `TELEMETRY_DEV_API_KEY` | When absent and no `spanExporter` override is provided, the processor is a no-op. |
|
|
57
|
+
| `baseUrl` | `TELEMETRY_DEV_BASE_URL`, then `https://ingest.telemetry.dev` | Trailing slashes are removed before building OTLP URLs. |
|
|
58
|
+
| `exportMode` | — | `"batched"`; use `"immediate"` for serverless paths that flush explicitly. |
|
|
59
|
+
| `batch` | — | Merged over `{ maxExportBatchSize: 64, scheduledDelayMillis: 1000, maxQueueSize: 2048, exportTimeoutMillis: 30000 }`. |
|
|
60
|
+
| `spanFilter` | — | Exports every span by default because attaching the processor is an explicit BYO choice. |
|
|
61
|
+
| `metrics` | — | `true`; records GenAI duration/token histograms when an API key is present. |
|
|
62
|
+
| `serviceName` | `OTEL_SERVICE_NAME` | `unknown_service`; used on the metrics resource. |
|
|
63
|
+
| `environment` | `TELEMETRY_DEV_ENVIRONMENT` | `production`; used as `deployment.environment.name` on the metrics resource. |
|
|
64
|
+
| `fetch` | — | `globalThis.fetch`; override for tests or custom runtimes. |
|
|
65
|
+
| `onError` | — | Receives export and processor errors; errors are also reported through diagnostics. |
|
|
66
|
+
| `spanExporter` | — | Advanced/test seam replacing the built-in OTLP trace exporter. |
|
|
67
|
+
|
|
68
|
+
## Missing API key behavior
|
|
69
|
+
|
|
70
|
+
`new TelemetrySpanProcessor()` never throws. Without an API key, and without a `spanExporter` override, it becomes a no-op processor and emits only a debug-level diagnostic. This makes it safe to construct during local development or in environments where telemetry is disabled.
|
|
71
|
+
|
|
72
|
+
## Raw span exporter
|
|
73
|
+
|
|
74
|
+
Use `createTelemetrySpanExporter()` when you want to keep your own `BatchSpanProcessor` or `SimpleSpanProcessor` but send OTLP/HTTP protobuf payloads to telemetry.dev.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
78
|
+
import { createTelemetrySpanExporter } from "@telemetry-dev/otel";
|
|
79
|
+
|
|
80
|
+
const processor = new BatchSpanProcessor(createTelemetrySpanExporter());
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The exporter reads `apiKey`, `baseUrl`, `fetch`, and `onError` from the same option names and environment variables as `TelemetrySpanProcessor`.
|
|
84
|
+
|
|
85
|
+
## Correlation attributes
|
|
86
|
+
|
|
87
|
+
`propagateAttributes({ userId, sessionId, metadata }, fn)` works standalone with BYO providers. The function stores correlation attributes in the same OTel context key that `TelemetrySpanProcessor` reads on span start, so spans created inside `fn` receive `user.id`, `gen_ai.conversation.id`, and `td.metadata.*` attributes without installing `@telemetry-dev/sdk`.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { Attributes, Context, ContextManager } from "@opentelemetry/api";
|
|
2
|
+
import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
|
|
3
|
+
import { Resource } from "@opentelemetry/resources";
|
|
4
|
+
import { ReadableSpan, Span, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
5
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
+
import { LogRecordExporter } from "@opentelemetry/sdk-logs";
|
|
7
|
+
|
|
8
|
+
//#region src/attrs.d.ts
|
|
9
|
+
declare const SCOPE_NAME = "@telemetry-dev/sdk";
|
|
10
|
+
declare const SCOPE_VERSION = "0.0.0";
|
|
11
|
+
declare function omitUndefined(attributes: Attributes): Attributes;
|
|
12
|
+
declare function jsonAttr(value: unknown): string | undefined;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/config.d.ts
|
|
15
|
+
type LogLevel = "debug" | "info" | "warn" | "error";
|
|
16
|
+
type SdkLogLevel = LogLevel | "silent";
|
|
17
|
+
type ExportMode = "batched" | "immediate";
|
|
18
|
+
interface BatchOptions {
|
|
19
|
+
/** Spans per export batch. LLM spans are large; keep batches small. Default 64. */
|
|
20
|
+
maxExportBatchSize?: number;
|
|
21
|
+
/** Delay between batch exports in milliseconds. Default 1000. */
|
|
22
|
+
scheduledDelayMillis?: number;
|
|
23
|
+
/** Maximum spans buffered before drops. Default 2048. */
|
|
24
|
+
maxQueueSize?: number;
|
|
25
|
+
/** Per-export timeout in milliseconds. Default 30000. */
|
|
26
|
+
exportTimeoutMillis?: number;
|
|
27
|
+
}
|
|
28
|
+
declare const DEFAULT_BASE_URL = "https://ingest.telemetry.dev";
|
|
29
|
+
declare const DEFAULT_BATCH: Required<BatchOptions>;
|
|
30
|
+
declare function resolveEnv(): Record<string, string | undefined>;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/context.d.ts
|
|
33
|
+
declare const als: AsyncLocalStorage<Context> | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The active context: our AsyncLocalStorage when available, falling back to the OTel global
|
|
36
|
+
* context (which joins a host app's own OTel setup when one is registered).
|
|
37
|
+
*/
|
|
38
|
+
declare function activeContext(): Context;
|
|
39
|
+
/** Run `fn` with `ctx` active in both our ALS and the global OTel context manager. */
|
|
40
|
+
declare function withContext<T>(ctx: Context, fn: () => T): T;
|
|
41
|
+
declare const PROPAGATED_KEY: symbol;
|
|
42
|
+
interface PropagatedAttributes {
|
|
43
|
+
/** Stamped as user.id on every span and log record in scope. */
|
|
44
|
+
userId?: string;
|
|
45
|
+
/** Stamped as gen_ai.conversation.id on every span and log record in scope. */
|
|
46
|
+
sessionId?: string;
|
|
47
|
+
/** Stamped as td.metadata.<key> on every span and log record in scope. */
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
declare function buildPropagatedAttributes(attrs: PropagatedAttributes): Attributes;
|
|
51
|
+
declare function propagatedFromContext(ctx: Context): Attributes | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Stamp correlation attributes (user, session/conversation, metadata) on every span and log
|
|
54
|
+
* record created inside `fn`. Inner scopes merge over outer ones per key. Works before init().
|
|
55
|
+
*/
|
|
56
|
+
declare function propagateAttributes<T>(attributes: PropagatedAttributes, fn: () => T): T;
|
|
57
|
+
/** Minimal ContextManager over our AsyncLocalStorage, registered only for registerGlobal. */
|
|
58
|
+
declare class AlsContextManager implements ContextManager {
|
|
59
|
+
private readonly storage;
|
|
60
|
+
constructor(storage: AsyncLocalStorage<Context>);
|
|
61
|
+
active(): Context;
|
|
62
|
+
with<A extends unknown[], F extends (...args: A) => ReturnType<F>>(context: Context, fn: F, thisArg?: ThisParameterType<F>, ...args: A): ReturnType<F>;
|
|
63
|
+
bind<T>(context: Context, target: T): T;
|
|
64
|
+
enable(): this;
|
|
65
|
+
disable(): this;
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/debug.d.ts
|
|
69
|
+
declare function setLogLevel(level: SdkLogLevel): void;
|
|
70
|
+
declare const diag: {
|
|
71
|
+
debug: (...args: unknown[]) => void;
|
|
72
|
+
info: (...args: unknown[]) => void;
|
|
73
|
+
warn: (...args: unknown[]) => void;
|
|
74
|
+
error: (...args: unknown[]) => void;
|
|
75
|
+
};
|
|
76
|
+
/** Fail-open guard: SDK internals report through onError + diagnostics, never into user code. */
|
|
77
|
+
declare function reportError(onError: ((error: unknown) => void) | undefined, error: unknown): void;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/metrics.d.ts
|
|
80
|
+
declare const DURATION_BUCKETS: number[];
|
|
81
|
+
declare const TOKEN_BUCKETS: number[];
|
|
82
|
+
declare const DORMANT_INTERVAL_MS: number;
|
|
83
|
+
declare const BATCHED_METRIC_INTERVAL_MS = 60000;
|
|
84
|
+
interface MetricsPipeline {
|
|
85
|
+
record(span: ReadableSpan): void;
|
|
86
|
+
forceFlush(): Promise<void>;
|
|
87
|
+
shutdown(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
declare function createMetricsPipeline({
|
|
90
|
+
resource,
|
|
91
|
+
exporter,
|
|
92
|
+
exportIntervalMillis
|
|
93
|
+
}: {
|
|
94
|
+
resource: Resource;
|
|
95
|
+
exporter: PushMetricExporter;
|
|
96
|
+
exportIntervalMillis: number;
|
|
97
|
+
}): MetricsPipeline;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/otel.d.ts
|
|
100
|
+
interface TelemetrySpanProcessorOptions {
|
|
101
|
+
/** telemetry.dev ingest key. Falls back to `TELEMETRY_DEV_API_KEY`; absent ⇒ no-op processor. */
|
|
102
|
+
apiKey?: string;
|
|
103
|
+
/** Falls back to `TELEMETRY_DEV_BASE_URL`, then `https://ingest.telemetry.dev`. */
|
|
104
|
+
baseUrl?: string;
|
|
105
|
+
/** "batched" (default) or "immediate". */
|
|
106
|
+
exportMode?: ExportMode;
|
|
107
|
+
batch?: BatchOptions;
|
|
108
|
+
/** Default for BYO providers: export EVERY span (the user deliberately attached the processor). */
|
|
109
|
+
spanFilter?: (span: ReadableSpan) => boolean;
|
|
110
|
+
/** Record GenAI duration/token histograms for exported chat/agent/embedding/tool spans. Default true. */
|
|
111
|
+
metrics?: boolean;
|
|
112
|
+
/** service.name on the metrics resource. Falls back to `OTEL_SERVICE_NAME`. */
|
|
113
|
+
serviceName?: string;
|
|
114
|
+
/** deployment.environment.name on the metrics resource. Falls back to `TELEMETRY_DEV_ENVIRONMENT`. */
|
|
115
|
+
environment?: string;
|
|
116
|
+
fetch?: typeof fetch;
|
|
117
|
+
onError?: (error: unknown) => void;
|
|
118
|
+
/** Advanced/test seam: replaces the OTLP fetch exporter. */
|
|
119
|
+
spanExporter?: SpanExporter;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* BYO-OpenTelemetry surface: add this processor to your own TracerProvider (NodeSDK,
|
|
123
|
+
* registerOTel, …) to ship its spans to telemetry.dev. Also stamps propagateAttributes
|
|
124
|
+
* correlation attributes onto every span it sees.
|
|
125
|
+
*/
|
|
126
|
+
declare class TelemetrySpanProcessor implements SpanProcessor {
|
|
127
|
+
private readonly inner?;
|
|
128
|
+
private readonly metrics?;
|
|
129
|
+
constructor(options?: TelemetrySpanProcessorOptions);
|
|
130
|
+
onStart(span: Span, parentContext: Context): void;
|
|
131
|
+
onEnd(span: ReadableSpan): void;
|
|
132
|
+
forceFlush(): Promise<void>;
|
|
133
|
+
shutdown(): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
/** Raw OTLP/protobuf fetch exporter for users wiring their own BatchSpanProcessor. */
|
|
136
|
+
declare function createTelemetrySpanExporter(options?: {
|
|
137
|
+
apiKey?: string;
|
|
138
|
+
baseUrl?: string;
|
|
139
|
+
fetch?: typeof fetch;
|
|
140
|
+
onError?: (error: unknown) => void;
|
|
141
|
+
}): SpanExporter;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/processor.d.ts
|
|
144
|
+
interface StampingProcessorOptions {
|
|
145
|
+
exporter: SpanExporter;
|
|
146
|
+
exportMode: ExportMode;
|
|
147
|
+
batch: Required<BatchOptions>;
|
|
148
|
+
spanFilter?: (span: ReadableSpan) => boolean;
|
|
149
|
+
recordMetrics?: (span: ReadableSpan) => void;
|
|
150
|
+
onError?: (error: unknown) => void;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The vendor span processor: stamps propagated correlation attributes onto every span at start,
|
|
154
|
+
* then filters, records auto-metrics, and delegates to a Batch/SimpleSpanProcessor at end.
|
|
155
|
+
*/
|
|
156
|
+
declare class StampingSpanProcessor implements SpanProcessor {
|
|
157
|
+
private readonly options;
|
|
158
|
+
private readonly inner;
|
|
159
|
+
constructor(options: StampingProcessorOptions);
|
|
160
|
+
onStart(span: Span, parentContext: Context): void;
|
|
161
|
+
onEnd(span: ReadableSpan): void;
|
|
162
|
+
forceFlush(): Promise<void>;
|
|
163
|
+
shutdown(): Promise<void>;
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/transport.d.ts
|
|
167
|
+
interface Transport {
|
|
168
|
+
fetchImpl: typeof fetch;
|
|
169
|
+
onError?: (error: unknown) => void;
|
|
170
|
+
}
|
|
171
|
+
interface OtlpTarget {
|
|
172
|
+
url: string;
|
|
173
|
+
headers: Record<string, string>;
|
|
174
|
+
}
|
|
175
|
+
declare const postOtlp: ({
|
|
176
|
+
fetchImpl,
|
|
177
|
+
url,
|
|
178
|
+
headers,
|
|
179
|
+
body
|
|
180
|
+
}: {
|
|
181
|
+
fetchImpl: typeof fetch;
|
|
182
|
+
url: string;
|
|
183
|
+
headers: Record<string, string>;
|
|
184
|
+
body: Uint8Array;
|
|
185
|
+
}) => Promise<Response>;
|
|
186
|
+
declare function maybeGzip(body: Uint8Array): Promise<{
|
|
187
|
+
body: Uint8Array;
|
|
188
|
+
contentEncoding?: "gzip";
|
|
189
|
+
}>;
|
|
190
|
+
declare function createTraceExporter(target: OtlpTarget, transport: Transport): SpanExporter;
|
|
191
|
+
declare function createLogExporter(target: OtlpTarget, transport: Transport): LogRecordExporter;
|
|
192
|
+
declare function createMetricExporter(target: OtlpTarget, transport: Transport): PushMetricExporter;
|
|
193
|
+
declare function otlpHeaders(apiKey: string): Record<string, string>;
|
|
194
|
+
//#endregion
|
|
195
|
+
export { AlsContextManager, BATCHED_METRIC_INTERVAL_MS, type BatchOptions, DEFAULT_BASE_URL, DEFAULT_BATCH, DORMANT_INTERVAL_MS, DURATION_BUCKETS, type ExportMode, type LogLevel, type MetricsPipeline, type OtlpTarget, PROPAGATED_KEY, type PropagatedAttributes, SCOPE_NAME, SCOPE_VERSION, type SdkLogLevel, type StampingProcessorOptions, StampingSpanProcessor, TOKEN_BUCKETS, TelemetrySpanProcessor, type TelemetrySpanProcessorOptions, type Transport, activeContext, als, buildPropagatedAttributes, createLogExporter, createMetricExporter, createMetricsPipeline, createTelemetrySpanExporter, createTraceExporter, diag, jsonAttr, maybeGzip, omitUndefined, otlpHeaders, postOtlp, propagateAttributes, propagatedFromContext, reportError, resolveEnv, setLogLevel, withContext };
|