@nodii/telemetry 0.21.1 → 0.22.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.
@@ -0,0 +1,126 @@
1
+ import { type RedactionConfig } from "../redaction.js";
2
+ import type { ActorType } from "../types.js";
3
+ import { type AttrValue, type OtlpResource } from "./otlp-http.js";
4
+ export type { AttrValue } from "./otlp-http.js";
5
+ export type { RedactionConfig } from "../redaction.js";
6
+ /**
7
+ * The explicit per-request context threaded into each emit — the workerd-safe
8
+ * analogue of the Node `NodiiContext` (15-telemetry § 6.2 / D41). The Workers
9
+ * `fetch` handler builds one per request (from inbound headers + auth) and
10
+ * passes it to every `log.*` / `counter.inc` call. All fields optional so a
11
+ * pre-context emit (cold boot / unauthenticated probe) still emits — the
12
+ * envelope substitutes empty-string `tenant_id`/`request_id` exactly as the
13
+ * Node logger does when no context is active.
14
+ */
15
+ export interface WorkerContext {
16
+ /** Bare `tenant_id` per D20. */
17
+ tenant_id?: string;
18
+ user_id?: string | null;
19
+ actor_type?: ActorType;
20
+ on_behalf_of?: string | null;
21
+ request_id?: string;
22
+ intent_id?: string | null;
23
+ /** Active trace id (hex), if a trace context was propagated in. */
24
+ trace_id?: string | null;
25
+ /** Active span id (hex), if a trace context was propagated in. */
26
+ span_id?: string | null;
27
+ }
28
+ /**
29
+ * The `fetch` signature the adapter POSTs OTLP/HTTP with. Defaults to the
30
+ * runtime-global `fetch` (present on workerd + Bun + Node ≥18). Injectable so
31
+ * tests capture the request WITHOUT a live collector (R1 — a real capturing
32
+ * fake, not a Noop default).
33
+ */
34
+ export type FetchLike = (input: string, init: {
35
+ method: string;
36
+ headers: Record<string, string>;
37
+ body: string;
38
+ }) => Promise<{
39
+ ok: boolean;
40
+ status: number;
41
+ }>;
42
+ /** Config for {@link createWorkerTelemetry}. All values are passed in. */
43
+ export interface WorkerTelemetryConfig {
44
+ /**
45
+ * OTLP/HTTP collector BASE url (Workers env binding), e.g.
46
+ * `https://otel.dev.nucleus-cloud.in`. The adapter appends the standard
47
+ * OTLP/HTTP signal paths (`/v1/logs`, `/v1/metrics`). A url that already ends
48
+ * in a signal path is used verbatim.
49
+ */
50
+ collectorUrl: string;
51
+ /**
52
+ * Static headers attached to every OTLP/HTTP POST (Workers env binding),
53
+ * e.g. `{ authorization: "Bearer <aud=nodii-obs JWT>" }` for the
54
+ * `oidcauthextension` ingress (15-telemetry § 8 / D95). `content-type:
55
+ * application/json` is set by the adapter and need not be supplied.
56
+ */
57
+ headers?: Record<string, string>;
58
+ /** D60 resource attr — the service id (e.g. `nodii-pager`). */
59
+ serviceName: string;
60
+ /** D60 resource attr — deployment env (`dev` | `uat` | `prod`). */
61
+ env: string;
62
+ /** D60 resource attr — optional service version. */
63
+ serviceVersion?: string;
64
+ /** Free-form extra resource attributes (D60). */
65
+ resourceAttributes?: Record<string, AttrValue>;
66
+ /**
67
+ * Injectable `fetch` (defaults to the runtime global). Supply a capturing
68
+ * fake in tests to assert the OTLP/HTTP request without a live collector.
69
+ */
70
+ fetchImpl?: FetchLike;
71
+ /**
72
+ * Called with a one-line diagnostic when an OTLP/HTTP POST fails (non-2xx or
73
+ * network throw). Telemetry export failure MUST NOT break Workers business
74
+ * work (15-telemetry § 4.1 / pillar 5 / R1) — the emit path swallows the
75
+ * error and routes it here. Defaults to `console.warn`.
76
+ */
77
+ onExportError?: (message: string, cause: unknown) => void;
78
+ /**
79
+ * Extra D15 redaction config, additive on top of the canonical D2 PII-field
80
+ * list + regex/PAN passes that ALWAYS run on the LOG path (15-telemetry § 7.3,
81
+ * fail-closed). Mirrors the Node logger's `.redact()` — `extraPaths` masks
82
+ * additional field-name keys; `scrubPan` toggles the Luhn PAN scrub (default
83
+ * on). Omit for the canonical passes only.
84
+ */
85
+ redaction?: RedactionConfig;
86
+ }
87
+ /** A structured logger over OTLP/HTTP for Workers. */
88
+ export interface WorkerLogger {
89
+ debug(msg: string, opts?: EmitOpts): Promise<void>;
90
+ info(msg: string, opts?: EmitOpts): Promise<void>;
91
+ warn(msg: string, opts?: EmitOpts): Promise<void>;
92
+ error(msg: string, opts?: EmitOpts): Promise<void>;
93
+ }
94
+ /** Per-emit options: the explicit context + extra structured fields. */
95
+ export interface EmitOpts {
96
+ /** The explicit per-request context (workerd-safe; no ALS). */
97
+ context?: WorkerContext;
98
+ /** Extra structured fields merged into the log-record attributes. */
99
+ fields?: Record<string, AttrValue | null | undefined>;
100
+ }
101
+ /** A monotonic counter handle over OTLP/HTTP for Workers. */
102
+ export interface WorkerCounter {
103
+ /** Increment by 1 (or `by`), stamping the D26 trio + labels from `opts`. */
104
+ inc(opts?: CounterOpts): Promise<void>;
105
+ inc(by: number, opts?: CounterOpts): Promise<void>;
106
+ }
107
+ /** Per-increment options: the explicit context + extra labels. */
108
+ export interface CounterOpts {
109
+ context?: WorkerContext;
110
+ labels?: Record<string, AttrValue | null | undefined>;
111
+ }
112
+ /** The handle returned by {@link createWorkerTelemetry}. */
113
+ export interface WorkerTelemetry {
114
+ logger: WorkerLogger;
115
+ /** Register (or fetch) a monotonic counter by name. */
116
+ counter(name: string): WorkerCounter;
117
+ /** The resource attrs stamped on every export (for introspection/tests). */
118
+ readonly resource: OtlpResource;
119
+ }
120
+ /**
121
+ * Build the workerd-runnable telemetry handle. Config is passed in from Workers
122
+ * env bindings (NOT `process.env`). Emits structured logs + counters over
123
+ * OTLP/HTTP via `fetch`.
124
+ */
125
+ export declare function createWorkerTelemetry(config: WorkerTelemetryConfig): WorkerTelemetry;
126
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker-lite/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ // `@nodii/telemetry/worker-lite` — the minimal Cloudflare Workers (workerd)
2
+ // emit adapter.
3
+ //
4
+ // WHY THIS EXISTS (15-telemetry-doctrine § 11 Open Question 3, LIVE):
5
+ // > "Fourth-substrate (Cloudflare Workers) emit gap. `nodii-pager` (D170)
6
+ // > runs on Workers; the Workers-OTLP exporter / minimal `@nodii/telemetry`
7
+ // > Workers adapter is TBD and out of current roster scope."
8
+ //
9
+ // The main package (`.`) + the `./worker` subpath boot the Node OTel SDK with
10
+ // gRPC OTLP exporters (`src/otlp-adapter.ts`) and the BullMQ/Redis-Streams
11
+ // worker substrate (`src/worker/`) — both pull `@grpc/grpc-js` + `node:*`, which
12
+ // CANNOT run on workerd. `nodii-pager` (D170, Cloudflare-resident) needs the
13
+ // minimal slice: structured LOGS + COUNTERS emitted over OTLP/HTTP via `fetch`.
14
+ // This module is that slice — NO `node:*`, NO `@opentelemetry/*`, NO gRPC, NO
15
+ // BullMQ. Only `fetch` + Web-standard APIs (workerd-runnable).
16
+ //
17
+ // DOCTRINE CONFORMANCE:
18
+ // - OTLP is the wire (15-telemetry § 2 / D11). We use OTLP/HTTP (JSON) instead
19
+ // of OTLP/gRPC because gRPC is Node-only; the collector's `otlphttp` receiver
20
+ // accepts the same payload tree (`otlp-http.ts`).
21
+ // - Logs carry the canonical D13 11-field envelope (15-telemetry § 3 →
22
+ // 03-logging § 2), SDK-injected from the supplied context + config — the
23
+ // SAME `LogEnvelope` type the Node logger forms, so Workers logs are
24
+ // wire-consistent with Node ones.
25
+ // - The mandatory correlation trio `tenant_id` / `request_id` / `intent_id`
26
+ // (15-telemetry § 4.2 / D26) rides every log envelope AND every counter
27
+ // datapoint. `tenant_id` is the bare key (§ 5.3 / D20) — same key across
28
+ // signals.
29
+ // - `service` + `deployment.environment.name` + bare `env` resource attrs on
30
+ // every export (§ 7.2 / D60).
31
+ // - D15 SDK-side redaction (§ 7.3 / § 7.5, LOCKED, fail-closed) runs on the
32
+ // LOG path before serialization — the load-bearing PII pass § 7.1/D41 says
33
+ // can't be bypassed per-service. On a redaction failure the record is
34
+ // DROPPED + `telemetry.sdk.redaction_failure_total` increments (drop, not
35
+ // leak). Reuses the pure (workerd-safe) `src/redaction.ts`.
36
+ //
37
+ // CONTEXT MECHANISM (deliverable requirement 4):
38
+ // Workers has NO AsyncLocalStorage guarantee (15-telemetry § 6.1 / D40 fixes
39
+ // ALS for Node/Bun; workerd does not guarantee it, and pulling `node:async_hooks`
40
+ // would break workerd-safety). So the Workers adapter takes an EXPLICIT
41
+ // `WorkerContext` argument on every emit (`log.info(msg, { context, ... })`,
42
+ // `counter.inc({ context, ... })`) rather than an implicit ambient store. The
43
+ // caller threads its per-request context (already available on the Workers
44
+ // `fetch(request, env, ctx)` handler) into each emit. This is the workerd-safe
45
+ // analogue of the Node ALS `NodiiContext` — same fields, explicitly passed.
46
+ //
47
+ // CONFIG (deliverable requirement 5):
48
+ // All config is PASSED IN (Workers env bindings), never read from
49
+ // `process.env`. `createWorkerTelemetry({ collectorUrl, headers, serviceName,
50
+ // env, ... })`.
51
+ // D15 SDK-side redaction (15-telemetry § 7.3 / § 7.5, LOCKED). `redaction.ts` is
52
+ // PURE (zero imports → workerd-safe); we import only its functions + type. This
53
+ // is the load-bearing, fail-closed PII pass that § 7.1/D41 says "can't be
54
+ // bypassed per-service" — so the Workers LOG path runs it exactly like the Node
55
+ // logger (`src/logger.ts`), NOT a per-service bypass.
56
+ import { attemptRedactFields, attemptRedactString, } from "../redaction.js";
57
+ import { buildLogsExportBody, buildMetricsExportBody, buildResource, } from "./otlp-http.js";
58
+ const SCOPE_NAME = "@nodii/telemetry/worker-lite";
59
+ /**
60
+ * Build the workerd-runnable telemetry handle. Config is passed in from Workers
61
+ * env bindings (NOT `process.env`). Emits structured logs + counters over
62
+ * OTLP/HTTP via `fetch`.
63
+ */
64
+ export function createWorkerTelemetry(config) {
65
+ const resource = buildResource({
66
+ serviceName: config.serviceName,
67
+ env: config.env,
68
+ serviceVersion: config.serviceVersion,
69
+ extra: config.resourceAttributes,
70
+ });
71
+ // Resolve `fetch` at build time. Default to the runtime global (workerd/Bun/
72
+ // Node ≥18). We reference `globalThis.fetch` (present on all three) rather
73
+ // than a bare `fetch` so bundlers don't tree-shake it away.
74
+ const doFetch = config.fetchImpl ?? globalThis.fetch;
75
+ const onExportError = config.onExportError ??
76
+ ((message, cause) => {
77
+ // eslint-disable-next-line no-console
78
+ console.warn(`[@nodii/telemetry/worker-lite] ${message}: ${cause instanceof Error ? cause.message : String(cause)}`);
79
+ });
80
+ const headers = {
81
+ "content-type": "application/json",
82
+ ...config.headers,
83
+ };
84
+ const logsUrl = signalUrl(config.collectorUrl, "logs");
85
+ const metricsUrl = signalUrl(config.collectorUrl, "metrics");
86
+ async function post(url, body) {
87
+ try {
88
+ const res = await doFetch(url, {
89
+ method: "POST",
90
+ headers,
91
+ body: JSON.stringify(body),
92
+ });
93
+ if (!res.ok) {
94
+ onExportError(`OTLP/HTTP export to ${url} returned ${res.status}`, new Error(`status ${res.status}`));
95
+ }
96
+ }
97
+ catch (err) {
98
+ // Export failure must NEVER break business work (§ 4.1 / R1).
99
+ onExportError(`OTLP/HTTP export to ${url} failed`, err);
100
+ }
101
+ }
102
+ /**
103
+ * Emit a single monotonic DELTA counter datapoint over OTLP/HTTP. Shared by
104
+ * the public `counter().inc()` AND the internal D15 redaction-failure emit
105
+ * (workerd has no persistent SDK metric registry, so the fail-closed
106
+ * `telemetry.sdk.redaction_failure_total` is emitted through this same
107
+ * fetch-based path — best-effort, § 7.3).
108
+ */
109
+ async function emitCounter(name, by, attributes) {
110
+ const nowNano = `${BigInt(Date.now()) * 1000000n}`;
111
+ const point = {
112
+ name,
113
+ value: by,
114
+ attributes,
115
+ // Delta window: for a stateless Workers emit each increment IS its own
116
+ // window, so start == time (a point-in-time delta of `by`).
117
+ startTimeUnixNano: nowNano,
118
+ timeUnixNano: nowNano,
119
+ };
120
+ await post(metricsUrl, buildMetricsExportBody(resource, SCOPE_NAME, [point]));
121
+ }
122
+ async function emitLog(level, msg, opts) {
123
+ // D15 SDK-side redaction (15-telemetry § 7.3 / § 7.5, LOCKED), run BEFORE
124
+ // the OTLP record is built — exactly like the Node logger (`src/logger.ts`
125
+ // emit). Two passes: the regex/PAN scrub on the free-text `msg` and the D2
126
+ // PII-field-name key match on the structured `fields`. FAIL-CLOSED: if
127
+ // either pass returns `{ok:false}` (redaction threw), the record is DROPPED
128
+ // (never emitted unredacted) and `telemetry.sdk.redaction_failure_total`
129
+ // increments. Better to drop than leak. § 7.1/D41: this pass can't be
130
+ // bypassed per-service — so the Workers adapter runs it too.
131
+ //
132
+ // SCOPING: redaction runs on the LOG path ONLY. Counters carry just the
133
+ // controlled D26 trio (tenant_id/request_id/intent_id — low-cardinality IDs,
134
+ // no free text) + caller labels, and worker-lite has NO spans — consistent
135
+ // with § 7.3 ("logs AND span attributes"), so no counter/span redaction is
136
+ // needed here.
137
+ const msgResult = attemptRedactString(msg, config.redaction);
138
+ if (!msgResult.ok) {
139
+ await emitRedactionFailure(msgResult.reason);
140
+ return; // DROP — do not emit the record.
141
+ }
142
+ const rawFields = opts?.fields ?? {};
143
+ const hasFields = Object.keys(rawFields).length > 0;
144
+ const fieldsResult = hasFields
145
+ ? attemptRedactFields(rawFields, config.redaction)
146
+ : { ok: true, redacted: {} };
147
+ if (!fieldsResult.ok) {
148
+ await emitRedactionFailure(fieldsResult.reason);
149
+ return; // DROP — do not emit the record.
150
+ }
151
+ const envelope = buildEnvelope(level, msgResult.redacted, config.serviceName, config.env, opts?.context);
152
+ const record = {
153
+ envelope,
154
+ // The redacted fields are a plain record; narrow to the OTLP attr value
155
+ // type the encoder accepts (it drops any non-primitive / null defensively).
156
+ fields: fieldsResult.redacted,
157
+ };
158
+ await post(logsUrl, buildLogsExportBody(resource, SCOPE_NAME, [record]));
159
+ }
160
+ /**
161
+ * Emit the D15 fail-closed counter (`telemetry.sdk.redaction_failure_total`)
162
+ * with the `reason` label. Best-effort — a failure to emit this counter must
163
+ * never itself throw out of the log path (§ 4.1 / R1); `emitCounter` routes
164
+ * any export error through `onExportError`.
165
+ */
166
+ async function emitRedactionFailure(reason) {
167
+ await emitCounter("telemetry.sdk.redaction_failure_total", 1, { reason });
168
+ }
169
+ const logger = {
170
+ debug: (msg, opts) => emitLog("debug", msg, opts),
171
+ info: (msg, opts) => emitLog("info", msg, opts),
172
+ warn: (msg, opts) => emitLog("warn", msg, opts),
173
+ error: (msg, opts) => emitLog("error", msg, opts),
174
+ };
175
+ function counter(name) {
176
+ function increment(by, opts) {
177
+ // D26 trio + caller labels. The trio derives from the explicit context;
178
+ // bare `tenant_id` per D20.
179
+ return emitCounter(name, by, {
180
+ ...contextTrio(opts?.context),
181
+ ...opts?.labels,
182
+ });
183
+ }
184
+ return {
185
+ inc(byOrOpts, maybeOpts) {
186
+ if (typeof byOrOpts === "number") {
187
+ return increment(byOrOpts, maybeOpts);
188
+ }
189
+ return increment(1, byOrOpts);
190
+ },
191
+ };
192
+ }
193
+ return { logger, counter, resource };
194
+ }
195
+ /**
196
+ * Form the D13 11-field log envelope from the explicit context + config. Mirror
197
+ * of the Node logger's envelope construction (`src/logger.ts` `emit`). The `msg`
198
+ * passed here is ALREADY D15-redacted by the caller (`emitLog`); this function
199
+ * only shapes the envelope. Empty-string `tenant_id`/`request_id` substitution
200
+ * matches the Node logger when no context is active.
201
+ */
202
+ function buildEnvelope(level, msg, service, env, ctx) {
203
+ return {
204
+ level,
205
+ ts: new Date().toISOString(),
206
+ msg,
207
+ service,
208
+ env,
209
+ tenant_id: ctx?.tenant_id ?? "",
210
+ user_id: ctx?.user_id ?? null,
211
+ on_behalf_of: ctx?.on_behalf_of ?? null,
212
+ actor_type: ctx?.actor_type,
213
+ request_id: ctx?.request_id ?? "",
214
+ trace_id: ctx?.trace_id ?? null,
215
+ span_id: ctx?.span_id ?? null,
216
+ intent_id: ctx?.intent_id ?? null,
217
+ };
218
+ }
219
+ /**
220
+ * The D26 mandatory span-attribute trio as counter attributes. Bare `tenant_id`
221
+ * (D20). Only present keys are emitted; the OTLP encoder drops null/undefined.
222
+ */
223
+ function contextTrio(ctx) {
224
+ return {
225
+ tenant_id: ctx?.tenant_id,
226
+ request_id: ctx?.request_id,
227
+ intent_id: ctx?.intent_id,
228
+ };
229
+ }
230
+ /**
231
+ * Resolve the OTLP/HTTP signal endpoint. If `base` already ends in a `/v1/*`
232
+ * signal path it is used verbatim; otherwise the standard path is appended
233
+ * (`<base>/v1/logs`, `<base>/v1/metrics`) per the OTLP/HTTP spec.
234
+ */
235
+ function signalUrl(base, signal) {
236
+ const trimmed = base.replace(/\/+$/, "");
237
+ if (/\/v1\/(logs|metrics|traces)$/.test(trimmed))
238
+ return trimmed;
239
+ return `${trimmed}/v1/${signal}`;
240
+ }
241
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/worker-lite/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import type { LogEnvelope } from "../types.js";
2
+ /** A primitive attribute value the OTLP KeyValue encoder accepts. */
3
+ export type AttrValue = string | number | boolean;
4
+ /** OTLP `KeyValue` (JSON mapping). */
5
+ export interface OtlpKeyValue {
6
+ key: string;
7
+ value: OtlpAnyValue;
8
+ }
9
+ /** OTLP `AnyValue` (JSON mapping) — the subset we emit. */
10
+ export type OtlpAnyValue = {
11
+ stringValue: string;
12
+ } | {
13
+ intValue: string;
14
+ } | {
15
+ doubleValue: number;
16
+ } | {
17
+ boolValue: boolean;
18
+ };
19
+ /**
20
+ * Resource attributes stamped on the `resource` of every export (D60). The
21
+ * adapter builds this once from its config; the builders below take it as-is.
22
+ */
23
+ export interface OtlpResource {
24
+ attributes: OtlpKeyValue[];
25
+ }
26
+ /** Encode a single primitive into an OTLP AnyValue. */
27
+ export declare function toAnyValue(v: AttrValue): OtlpAnyValue;
28
+ /** Encode a flat attribute record into the OTLP KeyValue list. */
29
+ export declare function toKeyValues(attrs: Record<string, AttrValue | null | undefined>): OtlpKeyValue[];
30
+ /**
31
+ * Build the OTLP `resource` from the adapter's resource attributes (D60). The
32
+ * bare `env` key AND the `deployment.environment.name` semantic key are BOTH
33
+ * stamped — mirroring `src/otlp-adapter.ts` so the collector's env-filter OTTL
34
+ * gate (infra request 63337357) resolves Workers signals identically to Node
35
+ * ones. `service.name` is the OTel semantic-convention key.
36
+ */
37
+ export declare function buildResource(attrs: {
38
+ serviceName: string;
39
+ env: string;
40
+ serviceVersion?: string;
41
+ extra?: Record<string, AttrValue>;
42
+ }): OtlpResource;
43
+ /**
44
+ * Flatten a D13 `LogEnvelope` + caller fields into the OTLP log-record
45
+ * attribute list. Mirrors `src/otlp-adapter.ts` `buildLogAttributes` so a
46
+ * Workers log row is shaped identically to a Node one: bare `tenant_id` (D20),
47
+ * `request_id`, and the optional identity/correlation keys.
48
+ */
49
+ export declare function buildLogAttributes(envelope: LogEnvelope, fields: Record<string, AttrValue | null | undefined>): OtlpKeyValue[];
50
+ /** A single log record to encode (envelope + already-formed extra fields). */
51
+ export interface WorkerLogRecord {
52
+ envelope: LogEnvelope;
53
+ fields: Record<string, AttrValue | null | undefined>;
54
+ }
55
+ /**
56
+ * Build an OTLP/HTTP `ExportLogsServiceRequest` JSON body from a batch of log
57
+ * records under one resource. `timeUnixNano` is derived from the envelope `ts`
58
+ * ISO-8601 timestamp (the log emit time), matching the Node adapter's
59
+ * `timestamp: Date.now()` intent but preserving the envelope's own instant.
60
+ */
61
+ export declare function buildLogsExportBody(resource: OtlpResource, scopeName: string, records: readonly WorkerLogRecord[]): {
62
+ resourceLogs: unknown[];
63
+ };
64
+ /** A single counter datapoint to encode. */
65
+ export interface WorkerCounterPoint {
66
+ name: string;
67
+ /** Monotonic-sum increment for this export window. */
68
+ value: number;
69
+ attributes: Record<string, AttrValue | null | undefined>;
70
+ /** Emission instant, unix-nanos as a decimal string. */
71
+ timeUnixNano: string;
72
+ /** Start of the delta window, unix-nanos as a decimal string. */
73
+ startTimeUnixNano: string;
74
+ }
75
+ /**
76
+ * Build an OTLP/HTTP `ExportMetricsServiceRequest` JSON body for a batch of
77
+ * counter datapoints under one resource. Every counter is emitted as a
78
+ * monotonic `sum` with **DELTA** temporality (`aggregationTemporality: 1`) —
79
+ * matching D21 (delta temporality for counters) and the Node adapter's
80
+ * `AggregationTemporality.DELTA`. One OTLP `metric` per distinct counter name;
81
+ * datapoints with the same name group under it.
82
+ */
83
+ export declare function buildMetricsExportBody(resource: OtlpResource, scopeName: string, points: readonly WorkerCounterPoint[]): {
84
+ resourceMetrics: unknown[];
85
+ };
86
+ /**
87
+ * Convert an ISO-8601 timestamp to unix-nanoseconds as a decimal string (the
88
+ * OTLP proto-JSON encoding for `fixed64` time fields). Millisecond precision
89
+ * (JS `Date` granularity) padded to nanos. Falls back to the current instant
90
+ * for an unparseable input rather than emitting `NaN`.
91
+ */
92
+ export declare function isoToUnixNano(iso: string): string;
93
+ //# sourceMappingURL=otlp-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otlp-http.d.ts","sourceRoot":"","sources":["../../src/worker-lite/otlp-http.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,WAAW,EAAY,MAAM,aAAa,CAAC;AAEzD,qEAAqE;AACrE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GACpB;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GACvB;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpB;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GACvB;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3B;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,YAAY,EAAE,CAAC;CAC5B;AAeD,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,YAAY,CAQrD;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,GAClD,YAAY,EAAE,CAOhB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACnC,GAAG,YAAY,CAYf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,GACnD,YAAY,EAAE,CAehB;AAED,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC;IAAE,YAAY,EAAE,OAAO,EAAE,CAAA;CAAE,CAsB7B;AAED,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,kBAAkB,EAAE,GACpC;IAAE,eAAe,EAAE,OAAO,EAAE,CAAA;CAAE,CA6BhC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjD"}
@@ -0,0 +1,179 @@
1
+ // OTLP/HTTP (JSON) payload builders for the Cloudflare Workers adapter.
2
+ //
3
+ // workerd (Cloudflare Workers) CANNOT run the Node OTel SDK gRPC exporters
4
+ // (`@opentelemetry/exporter-*-otlp-grpc`) that `src/otlp-adapter.ts` wires —
5
+ // those pull `@grpc/grpc-js` + `node:*`. The Workers runtime exposes only
6
+ // `fetch` + Web-standard APIs, so this module hand-builds the OTLP/HTTP JSON
7
+ // envelope (the same wire the collector's `otlphttp` receiver accepts) and the
8
+ // adapter POSTs it with `fetch`. NO `@opentelemetry/*` import, NO `node:*`
9
+ // import — purely data-shaping over plain objects.
10
+ //
11
+ // The JSON shape follows the OTLP/HTTP spec's ExportLogsServiceRequest /
12
+ // ExportMetricsServiceRequest (opentelemetry-proto → JSON mapping): a
13
+ // `resourceLogs[]` / `resourceMetrics[]` tree with a `resource.attributes`
14
+ // KeyValue list, one `scopeLogs` / `scopeMetrics` entry, and the records.
15
+ //
16
+ // The log record shape reuses the canonical D13 11-field `LogEnvelope`
17
+ // (15-telemetry § 3 → 03-logging § 2) EXACTLY so Workers logs are wire-consistent
18
+ // with the Node adapter's logs (`src/otlp-adapter.ts` buildLogAttributes) — the
19
+ // same bare `tenant_id` key (D20), the same `request_id` / `intent_id` / `trace_id`
20
+ // correlation keys (D26/D3), the same `service` + `deployment.environment.name`
21
+ // resource attrs (D60). A counter datapoint carries the D26 mandatory
22
+ // span-attribute trio (`tenant_id`, `request_id`, `intent_id`) as its attributes
23
+ // when a context is supplied, so Workers metrics correlate per-tenant too (D20).
24
+ /**
25
+ * SeverityNumber per the OTLP logs spec (same numeric mapping the Node adapter
26
+ * derives via `@opentelemetry/api-logs` SeverityNumber): DEBUG=5, INFO=9,
27
+ * WARN=13, ERROR=17. Kept as a local constant so this module adds NO
28
+ * `@opentelemetry/*` dependency (workerd-safety).
29
+ */
30
+ const SEVERITY_NUMBER = {
31
+ debug: 5,
32
+ info: 9,
33
+ warn: 13,
34
+ error: 17,
35
+ };
36
+ /** Encode a single primitive into an OTLP AnyValue. */
37
+ export function toAnyValue(v) {
38
+ if (typeof v === "boolean")
39
+ return { boolValue: v };
40
+ if (typeof v === "number") {
41
+ // Integers → intValue (string per proto-JSON int64 rule); non-integers →
42
+ // doubleValue. Matches how the collector round-trips numeric attributes.
43
+ return Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v };
44
+ }
45
+ return { stringValue: v };
46
+ }
47
+ /** Encode a flat attribute record into the OTLP KeyValue list. */
48
+ export function toKeyValues(attrs) {
49
+ const out = [];
50
+ for (const [key, v] of Object.entries(attrs)) {
51
+ if (v === null || v === undefined)
52
+ continue;
53
+ out.push({ key, value: toAnyValue(v) });
54
+ }
55
+ return out;
56
+ }
57
+ /**
58
+ * Build the OTLP `resource` from the adapter's resource attributes (D60). The
59
+ * bare `env` key AND the `deployment.environment.name` semantic key are BOTH
60
+ * stamped — mirroring `src/otlp-adapter.ts` so the collector's env-filter OTTL
61
+ * gate (infra request 63337357) resolves Workers signals identically to Node
62
+ * ones. `service.name` is the OTel semantic-convention key.
63
+ */
64
+ export function buildResource(attrs) {
65
+ const kv = {
66
+ "service.name": attrs.serviceName,
67
+ "deployment.environment.name": attrs.env,
68
+ // Bare `env` — the collector env-filter OTTL gate keys on this exact name.
69
+ env: attrs.env,
70
+ ...(attrs.serviceVersion
71
+ ? { "service.version": attrs.serviceVersion }
72
+ : {}),
73
+ ...attrs.extra,
74
+ };
75
+ return { attributes: toKeyValues(kv) };
76
+ }
77
+ /**
78
+ * Flatten a D13 `LogEnvelope` + caller fields into the OTLP log-record
79
+ * attribute list. Mirrors `src/otlp-adapter.ts` `buildLogAttributes` so a
80
+ * Workers log row is shaped identically to a Node one: bare `tenant_id` (D20),
81
+ * `request_id`, and the optional identity/correlation keys.
82
+ */
83
+ export function buildLogAttributes(envelope, fields) {
84
+ const attrs = {
85
+ "service.name": envelope.service,
86
+ "deployment.environment.name": envelope.env,
87
+ tenant_id: envelope.tenant_id,
88
+ request_id: envelope.request_id,
89
+ ...(envelope.user_id ? { user_id: envelope.user_id } : {}),
90
+ ...(envelope.actor_type ? { actor_type: envelope.actor_type } : {}),
91
+ ...(envelope.on_behalf_of ? { on_behalf_of: envelope.on_behalf_of } : {}),
92
+ ...(envelope.intent_id ? { intent_id: envelope.intent_id } : {}),
93
+ ...(envelope.trace_id ? { "telemetry.trace_id": envelope.trace_id } : {}),
94
+ ...(envelope.span_id ? { "telemetry.span_id": envelope.span_id } : {}),
95
+ ...fields,
96
+ };
97
+ return toKeyValues(attrs);
98
+ }
99
+ /**
100
+ * Build an OTLP/HTTP `ExportLogsServiceRequest` JSON body from a batch of log
101
+ * records under one resource. `timeUnixNano` is derived from the envelope `ts`
102
+ * ISO-8601 timestamp (the log emit time), matching the Node adapter's
103
+ * `timestamp: Date.now()` intent but preserving the envelope's own instant.
104
+ */
105
+ export function buildLogsExportBody(resource, scopeName, records) {
106
+ const logRecords = records.map((r) => {
107
+ const timeUnixNano = isoToUnixNano(r.envelope.ts);
108
+ return {
109
+ timeUnixNano,
110
+ observedTimeUnixNano: timeUnixNano,
111
+ severityNumber: SEVERITY_NUMBER[r.envelope.level],
112
+ severityText: r.envelope.level,
113
+ body: { stringValue: r.envelope.msg },
114
+ attributes: buildLogAttributes(r.envelope, r.fields),
115
+ ...(r.envelope.trace_id ? { traceId: r.envelope.trace_id } : {}),
116
+ ...(r.envelope.span_id ? { spanId: r.envelope.span_id } : {}),
117
+ };
118
+ });
119
+ return {
120
+ resourceLogs: [
121
+ {
122
+ resource,
123
+ scopeLogs: [{ scope: { name: scopeName }, logRecords }],
124
+ },
125
+ ],
126
+ };
127
+ }
128
+ /**
129
+ * Build an OTLP/HTTP `ExportMetricsServiceRequest` JSON body for a batch of
130
+ * counter datapoints under one resource. Every counter is emitted as a
131
+ * monotonic `sum` with **DELTA** temporality (`aggregationTemporality: 1`) —
132
+ * matching D21 (delta temporality for counters) and the Node adapter's
133
+ * `AggregationTemporality.DELTA`. One OTLP `metric` per distinct counter name;
134
+ * datapoints with the same name group under it.
135
+ */
136
+ export function buildMetricsExportBody(resource, scopeName, points) {
137
+ const byName = new Map();
138
+ for (const p of points) {
139
+ const bucket = byName.get(p.name);
140
+ if (bucket)
141
+ bucket.push(p);
142
+ else
143
+ byName.set(p.name, [p]);
144
+ }
145
+ const metrics = [...byName.entries()].map(([name, pts]) => ({
146
+ name,
147
+ sum: {
148
+ // 1 = AGGREGATION_TEMPORALITY_DELTA (OTLP metrics proto enum). D21.
149
+ aggregationTemporality: 1,
150
+ isMonotonic: true,
151
+ dataPoints: pts.map((p) => ({
152
+ asInt: String(Math.trunc(p.value)),
153
+ startTimeUnixNano: p.startTimeUnixNano,
154
+ timeUnixNano: p.timeUnixNano,
155
+ attributes: toKeyValues(p.attributes),
156
+ })),
157
+ },
158
+ }));
159
+ return {
160
+ resourceMetrics: [
161
+ {
162
+ resource,
163
+ scopeMetrics: [{ scope: { name: scopeName }, metrics }],
164
+ },
165
+ ],
166
+ };
167
+ }
168
+ /**
169
+ * Convert an ISO-8601 timestamp to unix-nanoseconds as a decimal string (the
170
+ * OTLP proto-JSON encoding for `fixed64` time fields). Millisecond precision
171
+ * (JS `Date` granularity) padded to nanos. Falls back to the current instant
172
+ * for an unparseable input rather than emitting `NaN`.
173
+ */
174
+ export function isoToUnixNano(iso) {
175
+ const ms = Date.parse(iso);
176
+ const epochMs = Number.isNaN(ms) ? Date.now() : ms;
177
+ return `${BigInt(epochMs) * 1000000n}`;
178
+ }
179
+ //# sourceMappingURL=otlp-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otlp-http.js","sourceRoot":"","sources":["../../src/worker-lite/otlp-http.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/telemetry",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Substrate observability library for the Nodii microservice stack — OTel wrapper, log envelope, intent lifecycle, audit signal, agent registry, context propagation. Spec: planning hub docKey=telemetry.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,6 +34,10 @@
34
34
  "./worker": {
35
35
  "types": "./dist/worker/index.d.ts",
36
36
  "default": "./dist/worker/index.js"
37
+ },
38
+ "./worker-lite": {
39
+ "types": "./dist/worker-lite/index.d.ts",
40
+ "default": "./dist/worker-lite/index.js"
37
41
  }
38
42
  },
39
43
  "files": [