@nifrajs/otel 3.5.0 → 4.0.2
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 +90 -3
- package/dist/cache.d.ts +46 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +78 -0
- package/dist/cache.js.map +1 -0
- package/dist/context-trace.d.ts +8 -0
- package/dist/context-trace.d.ts.map +1 -0
- package/dist/context-trace.js +16 -0
- package/dist/context-trace.js.map +1 -0
- package/dist/effects.d.ts.map +1 -1
- package/dist/effects.js +2 -0
- package/dist/effects.js.map +1 -1
- package/dist/events.d.ts +69 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +106 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +56 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +98 -0
- package/dist/jobs.js.map +1 -0
- package/dist/lifecycle.d.ts +13 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +12 -3
- package/dist/lifecycle.js.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +0 -0
- package/dist/metrics.js.map +1 -1
- package/dist/otlp.d.ts.map +1 -1
- package/dist/otlp.js +17 -1
- package/dist/otlp.js.map +1 -1
- package/dist/route-template.d.ts +14 -0
- package/dist/route-template.d.ts.map +1 -0
- package/dist/route-template.js +38 -0
- package/dist/route-template.js.map +1 -0
- package/dist/sdk-bridge.d.ts +107 -0
- package/dist/sdk-bridge.d.ts.map +1 -0
- package/dist/sdk-bridge.js +243 -0
- package/dist/sdk-bridge.js.map +1 -0
- package/dist/span.d.ts +8 -1
- package/dist/span.d.ts.map +1 -1
- package/dist/span.js +1 -0
- package/dist/span.js.map +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/tracing.js +11 -2
- package/dist/tracing.js.map +1 -1
- package/package.json +33 -3
- package/src/cache.ts +115 -0
- package/src/context-trace.ts +14 -0
- package/src/effects.ts +2 -0
- package/src/events.ts +170 -0
- package/src/index.ts +2 -0
- package/src/jobs.ts +141 -0
- package/src/lifecycle.ts +26 -3
- package/src/metrics.ts +0 -0
- package/src/otlp.ts +18 -2
- package/src/route-template.ts +43 -0
- package/src/sdk-bridge.ts +375 -0
- package/src/span.ts +9 -1
- package/src/tracing.ts +11 -2
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bridge between nifra spans and the OpenTelemetry SDK. The app passes its own `@opentelemetry/api`;
|
|
3
|
+
* nifra takes no dependency on it.
|
|
4
|
+
*
|
|
5
|
+
* import * as api from "@opentelemetry/api"
|
|
6
|
+
* import { otelBridge } from "@nifrajs/otel/sdk-bridge"
|
|
7
|
+
*
|
|
8
|
+
* const bridge = otelBridge({ api })
|
|
9
|
+
* app.use(tracing({ adapters: [bridge.adapter] })).use(bridge.plugin)
|
|
10
|
+
* // a pg / undici span started inside a handler is now a child of the request span
|
|
11
|
+
*
|
|
12
|
+
* Context propagation needs an async context manager registered with the SDK (on Node, Bun and Deno the
|
|
13
|
+
* AsyncLocalStorage one from `@opentelemetry/context-async-hooks`; on Workers, `nodejs_compat`).
|
|
14
|
+
*/
|
|
15
|
+
import { defineIdentityPlugin } from "@nifrajs/core/server";
|
|
16
|
+
import { traceOfContext } from "./context-trace.js";
|
|
17
|
+
import { generateSpanId, generateTraceId } from "./traceparent.js";
|
|
18
|
+
// `SpanKind` of @opentelemetry/api, stable since 1.0. A kind is caller data, so anything unknown
|
|
19
|
+
// starts as server, the kind of the spans that predate the field.
|
|
20
|
+
function otelKind(kind) {
|
|
21
|
+
switch (kind) {
|
|
22
|
+
case "internal":
|
|
23
|
+
return 0;
|
|
24
|
+
case "client":
|
|
25
|
+
return 2;
|
|
26
|
+
case "producer":
|
|
27
|
+
return 3;
|
|
28
|
+
case "consumer":
|
|
29
|
+
return 4;
|
|
30
|
+
default:
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function otelLink(link) {
|
|
35
|
+
const out = {
|
|
36
|
+
context: { traceId: link.traceId, spanId: link.spanId, traceFlags: 0, isRemote: true },
|
|
37
|
+
};
|
|
38
|
+
if (link.attributes !== undefined)
|
|
39
|
+
out.attributes = { ...link.attributes };
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
// `SpanStatusCode` of @opentelemetry/api, stable since 1.0.
|
|
43
|
+
const STATUS_OK = 1;
|
|
44
|
+
const STATUS_ERROR = 2;
|
|
45
|
+
const SAMPLED = 1;
|
|
46
|
+
const INVALID = Object.freeze({
|
|
47
|
+
traceId: "00000000000000000000000000000000",
|
|
48
|
+
spanId: "0000000000000000",
|
|
49
|
+
traceFlags: 0,
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* A span that only carries a context. Instrumentations read the active span's `spanContext()` to pick
|
|
53
|
+
* their parent; nifra owns the span's lifecycle, so every other call is a no-op.
|
|
54
|
+
*/
|
|
55
|
+
class ContextSpan {
|
|
56
|
+
resolve;
|
|
57
|
+
constructor(resolve) {
|
|
58
|
+
this.resolve = resolve;
|
|
59
|
+
}
|
|
60
|
+
spanContext() {
|
|
61
|
+
return this.resolve();
|
|
62
|
+
}
|
|
63
|
+
setAttribute() {
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
setAttributes() {
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
addEvent() {
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
addLink() {
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
addLinks() {
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
setStatus() {
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
updateName() {
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
end() { }
|
|
85
|
+
isRecording() {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
recordException() { }
|
|
89
|
+
}
|
|
90
|
+
/** Build the bridge. See {@link OtelBridge} for what each part does. */
|
|
91
|
+
export function otelBridge(options) {
|
|
92
|
+
const api = options.api;
|
|
93
|
+
const maxActive = options.maxActive ?? 10_000;
|
|
94
|
+
const maxActiveAgeMs = options.maxActiveAgeMs ?? 5 * 60_000;
|
|
95
|
+
if (!Number.isSafeInteger(maxActive) || maxActive < 1) {
|
|
96
|
+
throw new RangeError("otelBridge maxActive must be a positive safe integer");
|
|
97
|
+
}
|
|
98
|
+
if (!Number.isSafeInteger(maxActiveAgeMs) || maxActiveAgeMs < 1) {
|
|
99
|
+
throw new RangeError("otelBridge maxActiveAgeMs must be a positive safe integer");
|
|
100
|
+
}
|
|
101
|
+
const now = options.now ?? Date.now;
|
|
102
|
+
let tracer = options.tracer;
|
|
103
|
+
const tracerOf = () => {
|
|
104
|
+
tracer ??= api.trace.getTracer("@nifrajs/otel");
|
|
105
|
+
return tracer;
|
|
106
|
+
};
|
|
107
|
+
let warned = false;
|
|
108
|
+
const onIdMismatch = options.onIdMismatch ??
|
|
109
|
+
(() => console.warn("[nifra/otel] mirrored spans got new ids from the SDK; pass `idGenerator: bridge.idGenerator` to the tracer provider so forwarded traceparents name real spans"));
|
|
110
|
+
// The ids the next SDK span must take. Set and cleared around one synchronous `startSpan` call.
|
|
111
|
+
let pending;
|
|
112
|
+
const idGenerator = {
|
|
113
|
+
generateTraceId() {
|
|
114
|
+
const id = pending?.traceId;
|
|
115
|
+
if (pending !== undefined)
|
|
116
|
+
pending.traceId = undefined;
|
|
117
|
+
return id ?? generateTraceId();
|
|
118
|
+
},
|
|
119
|
+
generateSpanId() {
|
|
120
|
+
const id = pending?.spanId;
|
|
121
|
+
if (pending !== undefined)
|
|
122
|
+
pending.spanId = undefined;
|
|
123
|
+
return id ?? generateSpanId();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const active = new Map();
|
|
127
|
+
let nextSweepAt = Number.POSITIVE_INFINITY;
|
|
128
|
+
const evict = (spanId) => {
|
|
129
|
+
const entry = active.get(spanId);
|
|
130
|
+
if (entry === undefined)
|
|
131
|
+
return;
|
|
132
|
+
active.delete(spanId);
|
|
133
|
+
try {
|
|
134
|
+
entry.span.setAttributes({ "nifra.bridge.evicted": true });
|
|
135
|
+
entry.span.end();
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// A failing SDK cannot break the request that triggered the eviction.
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
// Same retention shape as effectTracing: insertion order is age order, so a sweep stops at the
|
|
142
|
+
// first live entry and costs O(evicted).
|
|
143
|
+
const sweep = () => {
|
|
144
|
+
const at = now();
|
|
145
|
+
if (!Number.isFinite(at) || at < nextSweepAt)
|
|
146
|
+
return at;
|
|
147
|
+
nextSweepAt = Number.POSITIVE_INFINITY;
|
|
148
|
+
for (const [spanId, entry] of active) {
|
|
149
|
+
const expiresAt = entry.at + maxActiveAgeMs;
|
|
150
|
+
if (at >= expiresAt) {
|
|
151
|
+
evict(spanId);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
nextSweepAt = expiresAt;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return at;
|
|
159
|
+
};
|
|
160
|
+
const contextOf = (trace) => active.get(trace.spanId)?.span.spanContext() ?? {
|
|
161
|
+
traceId: trace.traceId,
|
|
162
|
+
spanId: trace.spanId,
|
|
163
|
+
traceFlags: trace.sampled ? SAMPLED : 0,
|
|
164
|
+
isRemote: false,
|
|
165
|
+
};
|
|
166
|
+
const contextWith = (active, spanContext) => api.trace.setSpan(active, new ContextSpan(spanContext));
|
|
167
|
+
const adapter = {
|
|
168
|
+
onStart(span) {
|
|
169
|
+
const at = sweep();
|
|
170
|
+
while (active.size >= maxActive) {
|
|
171
|
+
const oldest = active.keys().next().value;
|
|
172
|
+
if (oldest === undefined)
|
|
173
|
+
break;
|
|
174
|
+
evict(oldest);
|
|
175
|
+
}
|
|
176
|
+
const parentSpanId = span.parentSpanId;
|
|
177
|
+
let parent = api.ROOT_CONTEXT;
|
|
178
|
+
if (parentSpanId !== undefined) {
|
|
179
|
+
const mirroredParent = active.get(parentSpanId)?.span;
|
|
180
|
+
const parentSpan = mirroredParent ??
|
|
181
|
+
new ContextSpan(() => ({
|
|
182
|
+
traceId: span.traceId,
|
|
183
|
+
spanId: parentSpanId,
|
|
184
|
+
traceFlags: span.sampled ? SAMPLED : 0,
|
|
185
|
+
isRemote: true,
|
|
186
|
+
}));
|
|
187
|
+
parent = api.trace.setSpan(parent, parentSpan);
|
|
188
|
+
}
|
|
189
|
+
pending = { traceId: span.traceId, spanId: span.spanId };
|
|
190
|
+
let mirrored;
|
|
191
|
+
try {
|
|
192
|
+
const start = {
|
|
193
|
+
kind: otelKind(span.kind),
|
|
194
|
+
startTime: span.startTime,
|
|
195
|
+
attributes: { ...span.attributes },
|
|
196
|
+
};
|
|
197
|
+
if (span.links !== undefined)
|
|
198
|
+
start.links = span.links.map(otelLink);
|
|
199
|
+
mirrored = tracerOf().startSpan(span.name, start, parent);
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
pending = undefined;
|
|
203
|
+
}
|
|
204
|
+
if (!warned && mirrored.isRecording() && mirrored.spanContext().spanId !== span.spanId) {
|
|
205
|
+
warned = true;
|
|
206
|
+
onIdMismatch();
|
|
207
|
+
}
|
|
208
|
+
active.set(span.spanId, Object.freeze({ span: mirrored, at: Number.isFinite(at) ? at : 0 }));
|
|
209
|
+
nextSweepAt = Math.min(nextSweepAt, (Number.isFinite(at) ? at : 0) + maxActiveAgeMs);
|
|
210
|
+
},
|
|
211
|
+
onEnd(span) {
|
|
212
|
+
const entry = active.get(span.spanId);
|
|
213
|
+
if (entry === undefined)
|
|
214
|
+
return;
|
|
215
|
+
active.delete(span.spanId);
|
|
216
|
+
entry.span.setAttributes(span.attributes);
|
|
217
|
+
if (span.status !== "unset")
|
|
218
|
+
entry.span.setStatus({ code: span.status === "error" ? STATUS_ERROR : STATUS_OK });
|
|
219
|
+
entry.span.end(span.endTime);
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
const scope = (trace, run) => api.context.with(contextWith(api.context.active(), () => contextOf(trace)), run);
|
|
223
|
+
const apply = (app) => {
|
|
224
|
+
app.around((c, next) => {
|
|
225
|
+
const active = api.context.active();
|
|
226
|
+
const outer = api.trace.getSpan(active);
|
|
227
|
+
// The request span opens in `tracing()`'s derive, which runs inside this wrapper, so the
|
|
228
|
+
// context is resolved lazily when an instrumentation starts a child.
|
|
229
|
+
return api.context.with(contextWith(active, () => {
|
|
230
|
+
const trace = traceOfContext(c);
|
|
231
|
+
return trace === null ? (outer?.spanContext() ?? INVALID) : contextOf(trace);
|
|
232
|
+
}), next);
|
|
233
|
+
});
|
|
234
|
+
return app;
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
adapter,
|
|
238
|
+
plugin: defineIdentityPlugin("nifra:otel-sdk-bridge", apply),
|
|
239
|
+
scope,
|
|
240
|
+
idGenerator,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=sdk-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-bridge.js","sourceRoot":"","sources":["../src/sdk-bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/span.d.ts
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export type SpanStatus = "unset" | "ok" | "error";
|
|
9
9
|
export type AttributeValue = string | number | boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The OTel span kind. A span without one is exported as `server`, the kind of the request spans that
|
|
12
|
+
* predate this field.
|
|
13
|
+
*/
|
|
14
|
+
export type SpanKind = "server" | "client" | "producer" | "consumer" | "internal";
|
|
10
15
|
/** A non-parent causal relationship to a span in another trace (the OTel `Link` model). */
|
|
11
16
|
export interface ObservationLink {
|
|
12
17
|
readonly traceId: string;
|
|
@@ -23,8 +28,10 @@ export interface NifraSpan {
|
|
|
23
28
|
readonly parentSpanId?: string;
|
|
24
29
|
/** Whether the trace is sampled (the W3C flag). */
|
|
25
30
|
readonly sampled: boolean;
|
|
26
|
-
/** Span name - `"<METHOD> <path>"
|
|
31
|
+
/** Span name - `"<METHOD> <path>"` for a request. */
|
|
27
32
|
readonly name: string;
|
|
33
|
+
/** OTel span kind. Absent means `server`. */
|
|
34
|
+
readonly kind?: SpanKind;
|
|
28
35
|
/** Wall-clock start (epoch ms). */
|
|
29
36
|
readonly startTime: number;
|
|
30
37
|
/** Wall-clock end (epoch ms) - set on completion. */
|
package/dist/span.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAA;AACjD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAA;AACjD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AACtD;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAA;AAEjF,2FAA2F;AAC3F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;CAC/D;AAED,8DAA8D;AAC9D,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;IACxB,mCAAmC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,UAAU,CAAA;IAClB,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IACnD,iGAAiG;IACjG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;CAC5C;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IAC/B,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;CAC7B;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GACtC,kBAAkB,CAqBpB;AAED;kDACkD;AAClD,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAEtB,GACA,kBAAkB,CAiBpB"}
|
package/dist/span.js
CHANGED
package/dist/span.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"span.js","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"span.js","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsDH;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAuC;IAEvC,OAAO;QACL,OAAO,CAAC,IAAI;YACV,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,wCAAwC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI;YACR,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACrB,CAAC;gBAAC,MAAM,CAAC;oBACP,wCAAwC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED;kDACkD;AAClD,MAAM,UAAU,mBAAmB,CACjC,MAA8B,CAAC,CAAC,EAAE,EAAE;IAClC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAChB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,IAAI;YACR,GAAG,CACD,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,GAAG,IAAI,CAAC,UAAU;aACnB,CAAC,CACH,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/tracing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAKvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAKvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAExE,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,kBAAkB,CAAA;AAE7C,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAA;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACjD,uDAAuD;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QACnB,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAA;QACrC,sDAAsD;QACtD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;QAC3B;;;WAGG;QACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,KACtB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAChC,CAAA;CACF;AAMD;uDACuD;AACvD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,SAAS,CAAC,EAAE,gBAAgB,GAC3B;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAKrE;AAUD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAAC,cAAc,CAAC,CAkHnF"}
|
package/dist/tracing.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { causalityHeaders, continueCausality, readCausalityHeaders, startCausality, } from "@nifrajs/core/causality";
|
|
8
8
|
import { defineContextPlugin } from "@nifrajs/core/server";
|
|
9
9
|
import { createObservationLifecycle, } from "./lifecycle.js";
|
|
10
|
+
import { methodLabel, routeTemplateOf } from "./route-template.js";
|
|
10
11
|
import { consoleSpanExporter } from "./span.js";
|
|
11
12
|
/** Spread into an outgoing `fetch`/`ctx.api` call's headers to continue the trace downstream:
|
|
12
13
|
* `fetch(url, { headers: traceHeaders(c.trace) })`. */
|
|
@@ -53,14 +54,22 @@ export function tracing(options = {}) {
|
|
|
53
54
|
return defineContextPlugin("tracing", (app) => {
|
|
54
55
|
for (const stop of stopHooks)
|
|
55
56
|
app.onStop(stop);
|
|
57
|
+
const routeOf = routeTemplateOf(app);
|
|
56
58
|
return app
|
|
57
59
|
.derive((c) => {
|
|
58
60
|
const path = pathOf(c.req.url);
|
|
61
|
+
const method = methodLabel(c.req.method);
|
|
62
|
+
// Named `{method} {route template}` per the HTTP conventions: the raw path would make every
|
|
63
|
+
// id its own span name, and carry whatever a path holds into every trace backend's index.
|
|
64
|
+
const route = routeOf(c.req.method, path);
|
|
59
65
|
const observation = lifecycle.start({
|
|
60
|
-
name: `${
|
|
66
|
+
name: `${method === "_OTHER" ? "HTTP" : method}${route === undefined ? "" : ` ${route}`}`,
|
|
67
|
+
kind: "server",
|
|
61
68
|
traceparent: c.req.headers.get("traceparent"),
|
|
62
69
|
attributes: {
|
|
63
|
-
"http.request.method":
|
|
70
|
+
"http.request.method": method,
|
|
71
|
+
...(method === "_OTHER" ? { "http.request.method_original": c.req.method } : {}),
|
|
72
|
+
...(route === undefined ? {} : { "http.route": route }),
|
|
64
73
|
"url.path": path,
|
|
65
74
|
...(serviceName === undefined ? {} : { "service.name": serviceName }),
|
|
66
75
|
},
|
package/dist/tracing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAsB,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EAEL,0BAA0B,GAE3B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAA2B,MAAM,WAAW,CAAA;AA+CxE;uDACuD;AACvD,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,SAA4B;IAE5B,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;KAChE,CAAA;AACH,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,GAAW,EAAU,EAAE;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAA;IACZ,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,UAA0B,EAAE;IAClD,MAAM,QAAQ,GAAyB;QACrC,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;YAClE,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;YACzB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;gBAC9B,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzB,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;KAC5B,CAAA;IACD,MAAM,SAAS,GAAG,0BAA0B,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1D,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3D,MAAM,QAAQ,GAAI,OAAkC,CAAC,QAAQ,CAAA;QAC7D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC9F,CAAC,CAAC,CAAA;IACF,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAA;IACvC,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA8B,CAAA;IAE1D,OAAO,mBAAmB,CAAiB,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QAC5D,KAAK,MAAM,IAAI,IAAI,SAAS;YAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC9C,OAAO,GAAG;aACP,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACZ,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC9B,MAAM,WAAW,GAAG,SAAS,CAAC,KAAK,CAAC;gBAClC,IAAI,EAAE,GAAG,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/otel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Distributed tracing for nifra - W3C traceparent/tracestate propagation + OpenTelemetry-semantic-convention spans via a pluggable exporter. Bridge to the OpenTelemetry SDK or log spans directly; no SDK bundled. Edge-safe.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"w3c-traceparent"
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
|
+
"nifra": {
|
|
20
|
+
"environment": "library"
|
|
21
|
+
},
|
|
19
22
|
"main": "./dist/index.js",
|
|
20
23
|
"module": "./dist/index.js",
|
|
21
24
|
"types": "./dist/index.d.ts",
|
|
@@ -34,6 +37,26 @@
|
|
|
34
37
|
"bun": "./src/effects.ts",
|
|
35
38
|
"types": "./dist/effects.d.ts",
|
|
36
39
|
"default": "./dist/effects.js"
|
|
40
|
+
},
|
|
41
|
+
"./cache": {
|
|
42
|
+
"bun": "./src/cache.ts",
|
|
43
|
+
"types": "./dist/cache.d.ts",
|
|
44
|
+
"default": "./dist/cache.js"
|
|
45
|
+
},
|
|
46
|
+
"./jobs": {
|
|
47
|
+
"bun": "./src/jobs.ts",
|
|
48
|
+
"types": "./dist/jobs.d.ts",
|
|
49
|
+
"default": "./dist/jobs.js"
|
|
50
|
+
},
|
|
51
|
+
"./events": {
|
|
52
|
+
"bun": "./src/events.ts",
|
|
53
|
+
"types": "./dist/events.d.ts",
|
|
54
|
+
"default": "./dist/events.js"
|
|
55
|
+
},
|
|
56
|
+
"./sdk-bridge": {
|
|
57
|
+
"bun": "./src/sdk-bridge.ts",
|
|
58
|
+
"types": "./dist/sdk-bridge.d.ts",
|
|
59
|
+
"default": "./dist/sdk-bridge.js"
|
|
37
60
|
}
|
|
38
61
|
},
|
|
39
62
|
"files": [
|
|
@@ -49,10 +72,17 @@
|
|
|
49
72
|
"access": "public"
|
|
50
73
|
},
|
|
51
74
|
"peerDependencies": {
|
|
52
|
-
"@nifrajs/core": "^
|
|
75
|
+
"@nifrajs/core": "^4.0.2"
|
|
53
76
|
},
|
|
54
77
|
"devDependencies": {
|
|
55
|
-
"@nifrajs/
|
|
78
|
+
"@nifrajs/cache": "workspace:*",
|
|
79
|
+
"@nifrajs/core": "workspace:*",
|
|
80
|
+
"@nifrajs/events": "workspace:*",
|
|
81
|
+
"@nifrajs/jobs": "workspace:*",
|
|
82
|
+
"@opentelemetry/api": "^1.9.1",
|
|
83
|
+
"@opentelemetry/context-async-hooks": "^2.11.0",
|
|
84
|
+
"@opentelemetry/instrumentation-undici": "^0.32.0",
|
|
85
|
+
"@opentelemetry/sdk-trace-base": "^2.11.0"
|
|
56
86
|
},
|
|
57
87
|
"repository": {
|
|
58
88
|
"type": "git",
|
package/src/cache.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spans for `@nifrajs/cache` operations. `cacheTracing()` returns the cache's `observer`: every operation
|
|
3
|
+
* on a view bound with `cache.for(c)` becomes a child span of `c.trace` (or of `JobContext.trace` inside
|
|
4
|
+
* a traced job). Raw keys never leave the process unless you say so with `keyAttribute`.
|
|
5
|
+
*
|
|
6
|
+
* import { cacheTracing } from "@nifrajs/otel/cache"
|
|
7
|
+
* const cache = createCache({ observer: cacheTracing({ exporter }) })
|
|
8
|
+
* await cache.for(c).wrap(`user:${id}`, load) // span "cache wrap", nifra.cache.key_prefix = "user"
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { traceOfContext } from "./context-trace.ts"
|
|
12
|
+
import { createObservationLifecycle } from "./lifecycle.ts"
|
|
13
|
+
import type { AttributeValue, ObservationAdapter } from "./span.ts"
|
|
14
|
+
|
|
15
|
+
/** The `CacheEvent` of `@nifrajs/cache`, declared structurally so this package does not depend on it. */
|
|
16
|
+
export interface CacheTracingEvent {
|
|
17
|
+
readonly op: string
|
|
18
|
+
readonly outcome: string
|
|
19
|
+
readonly startedAt: number
|
|
20
|
+
readonly durationMs: number
|
|
21
|
+
readonly key: string | undefined
|
|
22
|
+
readonly tag: string | undefined
|
|
23
|
+
readonly tagCount: number
|
|
24
|
+
readonly context: object | undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* What of a key (or an invalidated tag) a span carries:
|
|
29
|
+
* - `"prefix"` (default): the leading token before the first `:`, exported as `nifra.cache.key_prefix`
|
|
30
|
+
* only when it matches `^[a-z][a-z0-9_-]{0,31}$` - `user:42` gives `user`, a key with no `:` gives
|
|
31
|
+
* nothing;
|
|
32
|
+
* - `"none"`: nothing;
|
|
33
|
+
* - a function: its result is exported as `nifra.cache.key` (return `undefined` to omit). It runs on
|
|
34
|
+
* raw keys, so it owns the redaction.
|
|
35
|
+
*/
|
|
36
|
+
export type CacheKeyAttribute = "prefix" | "none" | ((key: string) => string | undefined)
|
|
37
|
+
|
|
38
|
+
export interface CacheTracingOptions {
|
|
39
|
+
readonly exporter?: ObservationAdapter
|
|
40
|
+
readonly adapters?: readonly ObservationAdapter[]
|
|
41
|
+
readonly keyAttribute?: CacheKeyAttribute
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Pass as `createCache({ observer })`. */
|
|
45
|
+
export type CacheTracingObserver = (event: CacheTracingEvent) => void
|
|
46
|
+
|
|
47
|
+
const KEY_PREFIX = /^[a-z][a-z0-9_-]{0,31}$/
|
|
48
|
+
|
|
49
|
+
function keyPrefix(key: string): string | undefined {
|
|
50
|
+
const end = key.indexOf(":")
|
|
51
|
+
if (end < 1 || end > 32) return undefined
|
|
52
|
+
const prefix = key.slice(0, end)
|
|
53
|
+
return KEY_PREFIX.test(prefix) ? prefix : undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Spans for cache operations on views bound with `for(context)`. A bound operation becomes a child of
|
|
58
|
+
* the context's trace; an operation on the unbound cache, or under a context with no trace, is not
|
|
59
|
+
* traced. A stale `wrap` that starts a background refresh gets a separate `cache revalidate` span in its
|
|
60
|
+
* own trace, LINKED to the request span: the refresh outlives the request, so it is not its child.
|
|
61
|
+
*/
|
|
62
|
+
export function cacheTracing(options: CacheTracingOptions = {}): CacheTracingObserver {
|
|
63
|
+
const adapters = [
|
|
64
|
+
...(options.exporter === undefined ? [] : [options.exporter]),
|
|
65
|
+
...(options.adapters ?? []),
|
|
66
|
+
]
|
|
67
|
+
const lifecycle = createObservationLifecycle({ adapters })
|
|
68
|
+
const keyAttribute = options.keyAttribute ?? "prefix"
|
|
69
|
+
|
|
70
|
+
const keyAttributes = (
|
|
71
|
+
attributes: Record<string, AttributeValue>,
|
|
72
|
+
value: string,
|
|
73
|
+
prefixName: string,
|
|
74
|
+
fullName: string,
|
|
75
|
+
): void => {
|
|
76
|
+
if (keyAttribute === "none") return
|
|
77
|
+
if (keyAttribute === "prefix") {
|
|
78
|
+
const prefix = keyPrefix(value)
|
|
79
|
+
if (prefix !== undefined) attributes[prefixName] = prefix
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const exported = keyAttribute(value)
|
|
84
|
+
if (typeof exported === "string") attributes[fullName] = exported
|
|
85
|
+
} catch {
|
|
86
|
+
// A throwing key mapper drops the attribute, never the span.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (event) => {
|
|
91
|
+
const trace = traceOfContext(event.context)
|
|
92
|
+
if (trace === null) return
|
|
93
|
+
const attributes: Record<string, AttributeValue> = {
|
|
94
|
+
"nifra.cache.operation": event.op,
|
|
95
|
+
"nifra.cache.outcome": event.outcome,
|
|
96
|
+
"nifra.cache.tag_count": event.tagCount,
|
|
97
|
+
}
|
|
98
|
+
if (event.key !== undefined)
|
|
99
|
+
keyAttributes(attributes, event.key, "nifra.cache.key_prefix", "nifra.cache.key")
|
|
100
|
+
if (event.tag !== undefined)
|
|
101
|
+
keyAttributes(attributes, event.tag, "nifra.cache.tag_prefix", "nifra.cache.tag")
|
|
102
|
+
const revalidation = event.op === "revalidate"
|
|
103
|
+
lifecycle
|
|
104
|
+
.start({
|
|
105
|
+
name: `cache ${event.op}`,
|
|
106
|
+
kind: "internal",
|
|
107
|
+
startTime: event.startedAt,
|
|
108
|
+
attributes,
|
|
109
|
+
...(revalidation
|
|
110
|
+
? { parent: null, links: [{ traceId: trace.traceId, spanId: trace.spanId }] }
|
|
111
|
+
: { parent: trace }),
|
|
112
|
+
})
|
|
113
|
+
.end({ status: event.outcome === "error" ? "error" : "ok", durationMs: event.durationMs })
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ParsedTraceparent, parseTraceparent } from "./traceparent.ts"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The trace a bound context carries (`c.trace` from `tracing()`, `JobContext.trace` from `jobTracing()`),
|
|
5
|
+
* read structurally and validated through its W3C `traceparent`, so a context of any other shape is
|
|
6
|
+
* simply untraced.
|
|
7
|
+
*/
|
|
8
|
+
export function traceOfContext(context: unknown): ParsedTraceparent | null {
|
|
9
|
+
if (typeof context !== "object" || context === null || !("trace" in context)) return null
|
|
10
|
+
const trace = context.trace
|
|
11
|
+
if (typeof trace !== "object" || trace === null || !("traceparent" in trace)) return null
|
|
12
|
+
const traceparent = trace.traceparent
|
|
13
|
+
return typeof traceparent === "string" ? parseTraceparent(traceparent) : null
|
|
14
|
+
}
|
package/src/effects.ts
CHANGED
|
@@ -110,6 +110,7 @@ export function effectTracing(options: EffectTracingOptions = {}): EffectTracing
|
|
|
110
110
|
Object.freeze({
|
|
111
111
|
observation: lifecycle.start({
|
|
112
112
|
name: `nifra.effect.${event.stage}`,
|
|
113
|
+
kind: "internal",
|
|
113
114
|
parent: event.trace ?? null,
|
|
114
115
|
attributes: attributesOf(event),
|
|
115
116
|
}),
|
|
@@ -124,6 +125,7 @@ export function effectTracing(options: EffectTracingOptions = {}): EffectTracing
|
|
|
124
125
|
if (entry === undefined) {
|
|
125
126
|
observation = lifecycle.start({
|
|
126
127
|
name: `nifra.effect.${event.stage}`,
|
|
128
|
+
kind: "internal",
|
|
127
129
|
parent: event.trace ?? null,
|
|
128
130
|
attributes: attributesOf(event),
|
|
129
131
|
})
|