@moostjs/otel 0.6.50 → 0.6.52
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/dist/index.cjs +57 -13
- package/dist/index.d.ts +3 -4
- package/dist/index.mjs +57 -13
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
3
3
|
let moost = require("moost");
|
|
4
|
+
let _opentelemetry_core = require("@opentelemetry/core");
|
|
4
5
|
let _moostjs_event_http = require("@moostjs/event-http");
|
|
6
|
+
let net = require("net");
|
|
5
7
|
let _opentelemetry_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
6
8
|
|
|
7
9
|
//#region packages/otel/src/context.ts
|
|
@@ -185,12 +187,39 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
185
187
|
return meta;
|
|
186
188
|
}
|
|
187
189
|
/** Root spans already owned by a Moost event — never attached to (renamed, re-attributed) twice. */ const claimedSpans = /* @__PURE__ */ new WeakSet();
|
|
190
|
+
const otelHostSpanKey = (0, moost.globalKey)("otel.hostSpan");
|
|
188
191
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
* The server span of the http instrumentation (`@opentelemetry/instrumentation-http`) for this
|
|
193
|
+
* network request, when no Moost event owns it yet.
|
|
194
|
+
*
|
|
195
|
+
* The instrumentation publishes it as the request's RPC metadata, so it is found even when a host
|
|
196
|
+
* app's middleware span (Express instrumentation) is active, not the server span itself. Without
|
|
197
|
+
* RPC metadata only an active SERVER span qualifies — anything else active (a lifecycle span of a
|
|
198
|
+
* running event, a client span) is a parent, not the request's own span. An in-process request
|
|
199
|
+
* (`fetch()` / `request()` on the HTTP app) runs on a fake socket and never attaches: it is not
|
|
200
|
+
* the network request the server span stands for.
|
|
201
|
+
*/ function getHostServerSpan(req) {
|
|
202
|
+
if (!(req?.socket instanceof net.Socket)) return;
|
|
203
|
+
const rpc = (0, _opentelemetry_core.getRPCMetadata)(_opentelemetry_api.context.active());
|
|
204
|
+
if (rpc?.type === _opentelemetry_core.RPCType.HTTP && rpc.span) return claimedSpans.has(rpc.span) ? void 0 : {
|
|
205
|
+
span: rpc.span,
|
|
206
|
+
rpc,
|
|
207
|
+
mount: getMountRoute(req, rpc)
|
|
208
|
+
};
|
|
209
|
+
const active = _opentelemetry_api.trace.getActiveSpan();
|
|
210
|
+
return active?.kind === _opentelemetry_api.SpanKind.SERVER && !claimedSpans.has(active) ? {
|
|
211
|
+
span: active,
|
|
212
|
+
mount: ""
|
|
213
|
+
} : void 0;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The route template a host app mounted Moost under, when it stripped that path from `req.url`
|
|
217
|
+
* (`app.use('/api', moostMiddleware)`): the route the host's own instrumentation reported
|
|
218
|
+
* for the request so far. Empty when Moost sees the whole path.
|
|
219
|
+
*/ function getMountRoute(req, rpc) {
|
|
220
|
+
const path = (url) => url?.split("?")[0];
|
|
221
|
+
if (!req.originalUrl || path(req.originalUrl) === path(req.url) || !rpc.route) return "";
|
|
222
|
+
return rpc.route.replace(/\/+$/, "");
|
|
194
223
|
}
|
|
195
224
|
/**
|
|
196
225
|
* The controller context of the current event, or `undefined` while no handler is resolved yet
|
|
@@ -239,16 +268,23 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
239
268
|
if (eventType === "init") return cb();
|
|
240
269
|
const { registerSpan } = useOtelContext();
|
|
241
270
|
const isHttp = eventType === _moostjs_event_http.httpKind.name;
|
|
271
|
+
const req = isHttp ? this.getRequest() : void 0;
|
|
242
272
|
if (isHttp) this.patchRsponse();
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
const span =
|
|
273
|
+
const host = getHostServerSpan(req);
|
|
274
|
+
const active = _opentelemetry_api.trace.getActiveSpan();
|
|
275
|
+
const span = host?.span ?? tracer.startSpan(isHttp ? req?.method || "HTTP" : `${eventType} Event`, isHttp && !active ? { kind: _opentelemetry_api.SpanKind.SERVER } : void 0);
|
|
246
276
|
claimedSpans.add(span);
|
|
247
277
|
registerSpan(span);
|
|
248
|
-
|
|
278
|
+
if (host) (0, moost.current)().set(otelHostSpanKey, host);
|
|
279
|
+
const opts = {
|
|
249
280
|
withMetrics: true,
|
|
250
|
-
endSpan: !
|
|
251
|
-
}
|
|
281
|
+
endSpan: !host
|
|
282
|
+
};
|
|
283
|
+
if (host && active && active !== host.span) {
|
|
284
|
+
const outer = _opentelemetry_api.context.active();
|
|
285
|
+
return this.withSpan(span, () => _opentelemetry_api.context.with(outer, cb), opts);
|
|
286
|
+
}
|
|
287
|
+
return this.withSpan(span, cb, opts);
|
|
252
288
|
}
|
|
253
289
|
getEventType() {
|
|
254
290
|
const ctx = (0, moost.current)();
|
|
@@ -300,8 +336,15 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
300
336
|
const span = getSpan();
|
|
301
337
|
if (span) {
|
|
302
338
|
span.setAttributes(chm.attrs);
|
|
303
|
-
if (chm.attrs["moost.event_type"] === _moostjs_event_http.httpKind.name)
|
|
304
|
-
|
|
339
|
+
if (chm.attrs["moost.event_type"] === _moostjs_event_http.httpKind.name) {
|
|
340
|
+
const host = ctx.has(otelHostSpanKey) ? ctx.get(otelHostSpanKey) : void 0;
|
|
341
|
+
const httpRoute = _route ? `${host?.mount || ""}${_route}` : void 0;
|
|
342
|
+
span.updateName(`${this.getRequest()?.method || ""} ${httpRoute || "<unresolved>"}`);
|
|
343
|
+
if (httpRoute) {
|
|
344
|
+
span.setAttribute("http.route", httpRoute);
|
|
345
|
+
if (host?.rpc) host.rpc.route = httpRoute;
|
|
346
|
+
}
|
|
347
|
+
} else span.updateName(`${chm.attrs["moost.event_type"]} ${_route || "<unresolved>"}`);
|
|
305
348
|
}
|
|
306
349
|
}
|
|
307
350
|
if (name !== "Controller:registered") ctx.set(otelRouteKey, route);
|
|
@@ -328,6 +371,7 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
328
371
|
const ctx = (0, moost.current)();
|
|
329
372
|
const route = ctx.has(otelRouteKey) ? ctx.get(otelRouteKey) : void 0;
|
|
330
373
|
const startTime = ctx.has(otelStartTimeKey) ? ctx.get(otelStartTimeKey) : void 0;
|
|
374
|
+
if (startTime === void 0 && route === void 0 && a["moost.event_type"] === _moostjs_event_http.httpKind.name) return;
|
|
331
375
|
const duration = Date.now() - (startTime || Date.now() - 1);
|
|
332
376
|
const attrs = {
|
|
333
377
|
...ctx.has(customMetricAttrsKey) ? ctx.get(customMetricAttrsKey) : {},
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,7 @@ import * as moost from 'moost';
|
|
|
4
4
|
import { Mate, TMoostMetadata, TMateParamMeta, ContextInjector, TContextInjectorHook } from 'moost';
|
|
5
5
|
import { EventContext } from '@wooksjs/event-core';
|
|
6
6
|
import { BatchSpanProcessor, ReadableSpan, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
7
|
-
import
|
|
8
|
-
import { ServerResponse } from 'http';
|
|
7
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
9
8
|
|
|
10
9
|
/** Event context key storing the root OpenTelemetry span. */
|
|
11
10
|
declare const otelSpanKey: moost.Key<Span | undefined>;
|
|
@@ -146,8 +145,8 @@ declare class SpanInjector extends ContextInjector<TContextInjectorHook> {
|
|
|
146
145
|
}): T;
|
|
147
146
|
startEventMetrics(a: Record<string, string | number | boolean | undefined>, route?: string): void;
|
|
148
147
|
endEventMetrics(a: Record<string, string | number | boolean | undefined>, error?: Error): void;
|
|
149
|
-
getRequest():
|
|
150
|
-
getResponse(): (ServerResponse<
|
|
148
|
+
getRequest(): IncomingMessage | undefined;
|
|
149
|
+
getResponse(): (ServerResponse<IncomingMessage> & {
|
|
151
150
|
_statusCode?: number;
|
|
152
151
|
}) | undefined;
|
|
153
152
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SpanKind, SpanStatusCode, context, metrics, trace } from "@opentelemetry/api";
|
|
2
2
|
import { ContextInjector, current, eventTypeKey, getConstructor, getMoostMate, globalKey, replaceContextInjector, useControllerContext } from "moost";
|
|
3
|
+
import { RPCType, getRPCMetadata } from "@opentelemetry/core";
|
|
3
4
|
import { httpKind } from "@moostjs/event-http";
|
|
5
|
+
import { Socket } from "net";
|
|
4
6
|
import { BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
5
7
|
|
|
6
8
|
//#region packages/otel/src/context.ts
|
|
@@ -184,12 +186,39 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
184
186
|
return meta;
|
|
185
187
|
}
|
|
186
188
|
/** Root spans already owned by a Moost event — never attached to (renamed, re-attributed) twice. */ const claimedSpans = /* @__PURE__ */ new WeakSet();
|
|
189
|
+
const otelHostSpanKey = globalKey("otel.hostSpan");
|
|
187
190
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
* The server span of the http instrumentation (`@opentelemetry/instrumentation-http`) for this
|
|
192
|
+
* network request, when no Moost event owns it yet.
|
|
193
|
+
*
|
|
194
|
+
* The instrumentation publishes it as the request's RPC metadata, so it is found even when a host
|
|
195
|
+
* app's middleware span (Express instrumentation) is active, not the server span itself. Without
|
|
196
|
+
* RPC metadata only an active SERVER span qualifies — anything else active (a lifecycle span of a
|
|
197
|
+
* running event, a client span) is a parent, not the request's own span. An in-process request
|
|
198
|
+
* (`fetch()` / `request()` on the HTTP app) runs on a fake socket and never attaches: it is not
|
|
199
|
+
* the network request the server span stands for.
|
|
200
|
+
*/ function getHostServerSpan(req) {
|
|
201
|
+
if (!(req?.socket instanceof Socket)) return;
|
|
202
|
+
const rpc = getRPCMetadata(context.active());
|
|
203
|
+
if (rpc?.type === RPCType.HTTP && rpc.span) return claimedSpans.has(rpc.span) ? void 0 : {
|
|
204
|
+
span: rpc.span,
|
|
205
|
+
rpc,
|
|
206
|
+
mount: getMountRoute(req, rpc)
|
|
207
|
+
};
|
|
208
|
+
const active = trace.getActiveSpan();
|
|
209
|
+
return active?.kind === SpanKind.SERVER && !claimedSpans.has(active) ? {
|
|
210
|
+
span: active,
|
|
211
|
+
mount: ""
|
|
212
|
+
} : void 0;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The route template a host app mounted Moost under, when it stripped that path from `req.url`
|
|
216
|
+
* (`app.use('/api', moostMiddleware)`): the route the host's own instrumentation reported
|
|
217
|
+
* for the request so far. Empty when Moost sees the whole path.
|
|
218
|
+
*/ function getMountRoute(req, rpc) {
|
|
219
|
+
const path = (url) => url?.split("?")[0];
|
|
220
|
+
if (!req.originalUrl || path(req.originalUrl) === path(req.url) || !rpc.route) return "";
|
|
221
|
+
return rpc.route.replace(/\/+$/, "");
|
|
193
222
|
}
|
|
194
223
|
/**
|
|
195
224
|
* The controller context of the current event, or `undefined` while no handler is resolved yet
|
|
@@ -238,16 +267,23 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
238
267
|
if (eventType === "init") return cb();
|
|
239
268
|
const { registerSpan } = useOtelContext();
|
|
240
269
|
const isHttp = eventType === httpKind.name;
|
|
270
|
+
const req = isHttp ? this.getRequest() : void 0;
|
|
241
271
|
if (isHttp) this.patchRsponse();
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
const span =
|
|
272
|
+
const host = getHostServerSpan(req);
|
|
273
|
+
const active = trace.getActiveSpan();
|
|
274
|
+
const span = host?.span ?? tracer.startSpan(isHttp ? req?.method || "HTTP" : `${eventType} Event`, isHttp && !active ? { kind: SpanKind.SERVER } : void 0);
|
|
245
275
|
claimedSpans.add(span);
|
|
246
276
|
registerSpan(span);
|
|
247
|
-
|
|
277
|
+
if (host) current().set(otelHostSpanKey, host);
|
|
278
|
+
const opts = {
|
|
248
279
|
withMetrics: true,
|
|
249
|
-
endSpan: !
|
|
250
|
-
}
|
|
280
|
+
endSpan: !host
|
|
281
|
+
};
|
|
282
|
+
if (host && active && active !== host.span) {
|
|
283
|
+
const outer = context.active();
|
|
284
|
+
return this.withSpan(span, () => context.with(outer, cb), opts);
|
|
285
|
+
}
|
|
286
|
+
return this.withSpan(span, cb, opts);
|
|
251
287
|
}
|
|
252
288
|
getEventType() {
|
|
253
289
|
const ctx = current();
|
|
@@ -299,8 +335,15 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
299
335
|
const span = getSpan();
|
|
300
336
|
if (span) {
|
|
301
337
|
span.setAttributes(chm.attrs);
|
|
302
|
-
if (chm.attrs["moost.event_type"] === httpKind.name)
|
|
303
|
-
|
|
338
|
+
if (chm.attrs["moost.event_type"] === httpKind.name) {
|
|
339
|
+
const host = ctx.has(otelHostSpanKey) ? ctx.get(otelHostSpanKey) : void 0;
|
|
340
|
+
const httpRoute = _route ? `${host?.mount || ""}${_route}` : void 0;
|
|
341
|
+
span.updateName(`${this.getRequest()?.method || ""} ${httpRoute || "<unresolved>"}`);
|
|
342
|
+
if (httpRoute) {
|
|
343
|
+
span.setAttribute("http.route", httpRoute);
|
|
344
|
+
if (host?.rpc) host.rpc.route = httpRoute;
|
|
345
|
+
}
|
|
346
|
+
} else span.updateName(`${chm.attrs["moost.event_type"]} ${_route || "<unresolved>"}`);
|
|
304
347
|
}
|
|
305
348
|
}
|
|
306
349
|
if (name !== "Controller:registered") ctx.set(otelRouteKey, route);
|
|
@@ -327,6 +370,7 @@ function readHandlerOtelMeta(controller, method) {
|
|
|
327
370
|
const ctx = current();
|
|
328
371
|
const route = ctx.has(otelRouteKey) ? ctx.get(otelRouteKey) : void 0;
|
|
329
372
|
const startTime = ctx.has(otelStartTimeKey) ? ctx.get(otelStartTimeKey) : void 0;
|
|
373
|
+
if (startTime === void 0 && route === void 0 && a["moost.event_type"] === httpKind.name) return;
|
|
330
374
|
const duration = Date.now() - (startTime || Date.now() - 1);
|
|
331
375
|
const attrs = {
|
|
332
376
|
...ctx.has(customMetricAttrsKey) ? ctx.get(customMetricAttrsKey) : {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moostjs/otel",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.52",
|
|
4
4
|
"description": "@moostjs/otel",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"composables",
|
|
@@ -39,15 +39,16 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@opentelemetry/api": "^1.9.1",
|
|
42
|
+
"@opentelemetry/core": "^2.10.0",
|
|
42
43
|
"@opentelemetry/sdk-trace-base": "^2.10.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"vitest": "3.2.7"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|
|
48
|
-
"@wooksjs/event-core": "^0.7.
|
|
49
|
-
"@moostjs/event-http": "^0.6.
|
|
50
|
-
"moost": "^0.6.
|
|
49
|
+
"@wooksjs/event-core": "^0.7.30",
|
|
50
|
+
"@moostjs/event-http": "^0.6.52",
|
|
51
|
+
"moost": "^0.6.52"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
54
|
"pub": "pnpm publish --access public",
|