@moostjs/otel 0.6.49 → 0.6.50

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 CHANGED
@@ -184,6 +184,29 @@ function readHandlerOtelMeta(controller, method) {
184
184
  }
185
185
  return meta;
186
186
  }
187
+ /** Root spans already owned by a Moost event — never attached to (renamed, re-attributed) twice. */ const claimedSpans = /* @__PURE__ */ new WeakSet();
188
+ /**
189
+ * Whether the active span is the server span of the http instrumentation, not yet owned by a
190
+ * Moost event. Anything else active (a lifecycle span of a running event, a client span) is a
191
+ * parent, not the request's own span.
192
+ */ function isInstrumentationServerSpan(span) {
193
+ return !!span && !claimedSpans.has(span) && span.kind === _opentelemetry_api.SpanKind.SERVER;
194
+ }
195
+ /**
196
+ * The controller context of the current event, or `undefined` while no handler is resolved yet
197
+ * (an unmatched route reports `Handler:not_found` before any controller context exists).
198
+ */ function readControllerContext() {
199
+ const cc = (0, moost.useControllerContext)();
200
+ try {
201
+ return {
202
+ controller: cc.getController(),
203
+ method: cc.getMethod(),
204
+ route: cc.getRoute()
205
+ };
206
+ } catch {
207
+ return;
208
+ }
209
+ }
187
210
  /** Context injector that wraps Moost lifecycle hooks with OpenTelemetry spans and records metrics. */ var SpanInjector = class extends moost.ContextInjector {
188
211
  with(name, attributes, cb) {
189
212
  const fn = typeof attributes === "function" ? attributes : cb;
@@ -206,45 +229,39 @@ function readHandlerOtelMeta(controller, method) {
206
229
  const res = this.getResponse();
207
230
  if (res) {
208
231
  const originalWriteHead = res.writeHead;
209
- Object.assign(res, { writeHead: (arg0, arg1, arg2) => {
210
- res._statusCode = arg0;
211
- const headers = typeof arg2 === "object" ? arg2 : typeof arg1 === "object" ? arg1 : void 0;
212
- res._contentLength = headers?.["content-length"] ? Number(headers["content-length"]) : 0;
213
- return originalWriteHead.apply(res, [
214
- arg0,
215
- arg1,
216
- arg2
217
- ]);
218
- } });
232
+ res.writeHead = (...args) => {
233
+ res._statusCode = args[0];
234
+ return originalWriteHead.apply(res, args);
235
+ };
219
236
  }
220
237
  }
221
238
  startEvent(eventType, cb) {
222
239
  if (eventType === "init") return cb();
223
240
  const { registerSpan } = useOtelContext();
224
- let span = _opentelemetry_api.trace.getActiveSpan();
225
- if (eventType === _moostjs_event_http.httpKind.kind) this.patchRsponse();
226
- else span = tracer.startSpan(`${eventType} Event`);
227
- if (span) {
228
- registerSpan(span);
229
- return this.withSpan(span, cb, {
230
- withMetrics: true,
231
- endSpan: eventType !== _moostjs_event_http.httpKind.kind
232
- });
233
- }
234
- return cb();
241
+ const isHttp = eventType === _moostjs_event_http.httpKind.name;
242
+ if (isHttp) this.patchRsponse();
243
+ const active = isHttp ? _opentelemetry_api.trace.getActiveSpan() : void 0;
244
+ const attached = isInstrumentationServerSpan(active) ? active : void 0;
245
+ const span = attached ?? tracer.startSpan(`${eventType} Event`, isHttp && !active ? { kind: _opentelemetry_api.SpanKind.SERVER } : void 0);
246
+ claimedSpans.add(span);
247
+ registerSpan(span);
248
+ return this.withSpan(span, cb, {
249
+ withMetrics: true,
250
+ endSpan: !attached
251
+ });
235
252
  }
236
253
  getEventType() {
237
254
  const ctx = (0, moost.current)();
238
255
  return ctx.has(moost.eventTypeKey) ? ctx.get(moost.eventTypeKey) : void 0;
239
256
  }
240
257
  getIgnoreSpan() {
241
- const { getController, getMethod } = (0, moost.useControllerContext)();
242
- return readHandlerOtelMeta(getController(), getMethod()).ignoreSpan;
258
+ const cc = readControllerContext();
259
+ return readHandlerOtelMeta(cc?.controller, cc?.method).ignoreSpan;
243
260
  }
244
261
  getControllerHandlerMeta() {
245
- const { getMethod, getController, getRoute } = (0, moost.useControllerContext)();
246
- const methodName = getMethod();
247
- const meta = readHandlerOtelMeta(getController(), methodName);
262
+ const cc = readControllerContext();
263
+ const methodName = cc?.method;
264
+ const meta = readHandlerOtelMeta(cc?.controller, methodName);
248
265
  return {
249
266
  ignoreMeter: meta.ignoreMeter,
250
267
  ignoreSpan: meta.ignoreSpan,
@@ -255,7 +272,7 @@ function readHandlerOtelMeta(controller, method) {
255
272
  "moost.handler_label": meta.label,
256
273
  "moost.handler_id": meta.id,
257
274
  "moost.ignore": meta.ignoreSpan,
258
- "moost.route": getRoute(),
275
+ "moost.route": cc?.route,
259
276
  "moost.event_type": this.getEventType()
260
277
  }
261
278
  };
@@ -273,10 +290,7 @@ function readHandlerOtelMeta(controller, method) {
273
290
  const chm = this.getControllerHandlerMeta();
274
291
  const span = getSpan();
275
292
  if (span) {
276
- if (this.getEventType() === _moostjs_event_http.httpKind.kind) {
277
- const req = this.getRequest();
278
- span.updateName(`${req?.method || ""} ${req?.url}`);
279
- }
293
+ if (this.getEventType() === _moostjs_event_http.httpKind.name) span.updateName(this.getRequest()?.method || "HTTP");
280
294
  }
281
295
  this.startEventMetrics(chm.attrs, route);
282
296
  } else if (name === "Controller:registered") {
@@ -286,7 +300,7 @@ function readHandlerOtelMeta(controller, method) {
286
300
  const span = getSpan();
287
301
  if (span) {
288
302
  span.setAttributes(chm.attrs);
289
- if (chm.attrs["moost.event_type"] === _moostjs_event_http.httpKind.kind) span.updateName(`${this.getRequest()?.method || ""} ${_route || "<unresolved>"}`);
303
+ if (chm.attrs["moost.event_type"] === _moostjs_event_http.httpKind.name) span.updateName(`${this.getRequest()?.method || ""} ${_route || "<unresolved>"}`);
290
304
  else span.updateName(`${chm.attrs["moost.event_type"]} ${_route || "<unresolved>"}`);
291
305
  }
292
306
  }
@@ -321,8 +335,7 @@ function readHandlerOtelMeta(controller, method) {
321
335
  "moost.event_type": a["moost.event_type"],
322
336
  "moost.is_error": error ? 1 : 0
323
337
  };
324
- if (a["moost.event_type"] === _moostjs_event_http.httpKind.kind) {
325
- if (!attrs.route) attrs.route = this.getRequest()?.url || "";
338
+ if (a["moost.event_type"] === _moostjs_event_http.httpKind.name) {
326
339
  attrs["http.status_code"] = this.getResponse()?._statusCode || 0;
327
340
  attrs["moost.is_error"] = attrs["moost.is_error"] || attrs["http.status_code"] > 399 ? 1 : 0;
328
341
  }
package/dist/index.d.ts CHANGED
@@ -135,7 +135,7 @@ declare class SpanInjector extends ContextInjector<TContextInjectorHook> {
135
135
  'moost.handler_label': string | undefined;
136
136
  'moost.handler_id': string | undefined;
137
137
  'moost.ignore': boolean | undefined;
138
- 'moost.route': string;
138
+ 'moost.route': string | undefined;
139
139
  'moost.event_type': string | undefined;
140
140
  };
141
141
  };
@@ -149,7 +149,6 @@ declare class SpanInjector extends ContextInjector<TContextInjectorHook> {
149
149
  getRequest(): http.IncomingMessage | undefined;
150
150
  getResponse(): (ServerResponse<http.IncomingMessage> & {
151
151
  _statusCode?: number;
152
- _contentLength?: number;
153
152
  }) | undefined;
154
153
  }
155
154
 
package/dist/index.mjs CHANGED
@@ -183,6 +183,29 @@ function readHandlerOtelMeta(controller, method) {
183
183
  }
184
184
  return meta;
185
185
  }
186
+ /** Root spans already owned by a Moost event — never attached to (renamed, re-attributed) twice. */ const claimedSpans = /* @__PURE__ */ new WeakSet();
187
+ /**
188
+ * Whether the active span is the server span of the http instrumentation, not yet owned by a
189
+ * Moost event. Anything else active (a lifecycle span of a running event, a client span) is a
190
+ * parent, not the request's own span.
191
+ */ function isInstrumentationServerSpan(span) {
192
+ return !!span && !claimedSpans.has(span) && span.kind === SpanKind.SERVER;
193
+ }
194
+ /**
195
+ * The controller context of the current event, or `undefined` while no handler is resolved yet
196
+ * (an unmatched route reports `Handler:not_found` before any controller context exists).
197
+ */ function readControllerContext() {
198
+ const cc = useControllerContext();
199
+ try {
200
+ return {
201
+ controller: cc.getController(),
202
+ method: cc.getMethod(),
203
+ route: cc.getRoute()
204
+ };
205
+ } catch {
206
+ return;
207
+ }
208
+ }
186
209
  /** Context injector that wraps Moost lifecycle hooks with OpenTelemetry spans and records metrics. */ var SpanInjector = class extends ContextInjector {
187
210
  with(name, attributes, cb) {
188
211
  const fn = typeof attributes === "function" ? attributes : cb;
@@ -205,45 +228,39 @@ function readHandlerOtelMeta(controller, method) {
205
228
  const res = this.getResponse();
206
229
  if (res) {
207
230
  const originalWriteHead = res.writeHead;
208
- Object.assign(res, { writeHead: (arg0, arg1, arg2) => {
209
- res._statusCode = arg0;
210
- const headers = typeof arg2 === "object" ? arg2 : typeof arg1 === "object" ? arg1 : void 0;
211
- res._contentLength = headers?.["content-length"] ? Number(headers["content-length"]) : 0;
212
- return originalWriteHead.apply(res, [
213
- arg0,
214
- arg1,
215
- arg2
216
- ]);
217
- } });
231
+ res.writeHead = (...args) => {
232
+ res._statusCode = args[0];
233
+ return originalWriteHead.apply(res, args);
234
+ };
218
235
  }
219
236
  }
220
237
  startEvent(eventType, cb) {
221
238
  if (eventType === "init") return cb();
222
239
  const { registerSpan } = useOtelContext();
223
- let span = trace.getActiveSpan();
224
- if (eventType === httpKind.kind) this.patchRsponse();
225
- else span = tracer.startSpan(`${eventType} Event`);
226
- if (span) {
227
- registerSpan(span);
228
- return this.withSpan(span, cb, {
229
- withMetrics: true,
230
- endSpan: eventType !== httpKind.kind
231
- });
232
- }
233
- return cb();
240
+ const isHttp = eventType === httpKind.name;
241
+ if (isHttp) this.patchRsponse();
242
+ const active = isHttp ? trace.getActiveSpan() : void 0;
243
+ const attached = isInstrumentationServerSpan(active) ? active : void 0;
244
+ const span = attached ?? tracer.startSpan(`${eventType} Event`, isHttp && !active ? { kind: SpanKind.SERVER } : void 0);
245
+ claimedSpans.add(span);
246
+ registerSpan(span);
247
+ return this.withSpan(span, cb, {
248
+ withMetrics: true,
249
+ endSpan: !attached
250
+ });
234
251
  }
235
252
  getEventType() {
236
253
  const ctx = current();
237
254
  return ctx.has(eventTypeKey) ? ctx.get(eventTypeKey) : void 0;
238
255
  }
239
256
  getIgnoreSpan() {
240
- const { getController, getMethod } = useControllerContext();
241
- return readHandlerOtelMeta(getController(), getMethod()).ignoreSpan;
257
+ const cc = readControllerContext();
258
+ return readHandlerOtelMeta(cc?.controller, cc?.method).ignoreSpan;
242
259
  }
243
260
  getControllerHandlerMeta() {
244
- const { getMethod, getController, getRoute } = useControllerContext();
245
- const methodName = getMethod();
246
- const meta = readHandlerOtelMeta(getController(), methodName);
261
+ const cc = readControllerContext();
262
+ const methodName = cc?.method;
263
+ const meta = readHandlerOtelMeta(cc?.controller, methodName);
247
264
  return {
248
265
  ignoreMeter: meta.ignoreMeter,
249
266
  ignoreSpan: meta.ignoreSpan,
@@ -254,7 +271,7 @@ function readHandlerOtelMeta(controller, method) {
254
271
  "moost.handler_label": meta.label,
255
272
  "moost.handler_id": meta.id,
256
273
  "moost.ignore": meta.ignoreSpan,
257
- "moost.route": getRoute(),
274
+ "moost.route": cc?.route,
258
275
  "moost.event_type": this.getEventType()
259
276
  }
260
277
  };
@@ -272,10 +289,7 @@ function readHandlerOtelMeta(controller, method) {
272
289
  const chm = this.getControllerHandlerMeta();
273
290
  const span = getSpan();
274
291
  if (span) {
275
- if (this.getEventType() === httpKind.kind) {
276
- const req = this.getRequest();
277
- span.updateName(`${req?.method || ""} ${req?.url}`);
278
- }
292
+ if (this.getEventType() === httpKind.name) span.updateName(this.getRequest()?.method || "HTTP");
279
293
  }
280
294
  this.startEventMetrics(chm.attrs, route);
281
295
  } else if (name === "Controller:registered") {
@@ -285,7 +299,7 @@ function readHandlerOtelMeta(controller, method) {
285
299
  const span = getSpan();
286
300
  if (span) {
287
301
  span.setAttributes(chm.attrs);
288
- if (chm.attrs["moost.event_type"] === httpKind.kind) span.updateName(`${this.getRequest()?.method || ""} ${_route || "<unresolved>"}`);
302
+ if (chm.attrs["moost.event_type"] === httpKind.name) span.updateName(`${this.getRequest()?.method || ""} ${_route || "<unresolved>"}`);
289
303
  else span.updateName(`${chm.attrs["moost.event_type"]} ${_route || "<unresolved>"}`);
290
304
  }
291
305
  }
@@ -320,8 +334,7 @@ function readHandlerOtelMeta(controller, method) {
320
334
  "moost.event_type": a["moost.event_type"],
321
335
  "moost.is_error": error ? 1 : 0
322
336
  };
323
- if (a["moost.event_type"] === httpKind.kind) {
324
- if (!attrs.route) attrs.route = this.getRequest()?.url || "";
337
+ if (a["moost.event_type"] === httpKind.name) {
325
338
  attrs["http.status_code"] = this.getResponse()?._statusCode || 0;
326
339
  attrs["moost.is_error"] = attrs["moost.is_error"] || attrs["http.status_code"] > 399 ? 1 : 0;
327
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/otel",
3
- "version": "0.6.49",
3
+ "version": "0.6.50",
4
4
  "description": "@moostjs/otel",
5
5
  "keywords": [
6
6
  "composables",
@@ -46,8 +46,8 @@
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@wooksjs/event-core": "^0.7.28",
49
- "@moostjs/event-http": "^0.6.49",
50
- "moost": "^0.6.49"
49
+ "@moostjs/event-http": "^0.6.50",
50
+ "moost": "^0.6.50"
51
51
  },
52
52
  "scripts": {
53
53
  "pub": "pnpm publish --access public",