@moostjs/otel 0.4.14 → 0.4.16

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
@@ -117,6 +117,49 @@ function getMoostMetrics() {
117
117
  return moostMetrics;
118
118
  }
119
119
 
120
+ function withSpan(span, cb, postProcess) {
121
+ const _span = typeof span.name === 'string' && !span.spanContext
122
+ ? span
123
+ : api.trace
124
+ .getTracer('default')
125
+ .startSpan(span.name, span.options);
126
+ let result = undefined;
127
+ const finalizeSpan = (e, r) => {
128
+ if (e) {
129
+ _span.recordException(e);
130
+ }
131
+ if (postProcess) {
132
+ postProcess(_span, e, r);
133
+ }
134
+ else {
135
+ _span.end();
136
+ }
137
+ };
138
+ api.context.with(api.trace.setSpan(api.context.active(), _span), () => {
139
+ try {
140
+ result = cb();
141
+ }
142
+ catch (error) {
143
+ finalizeSpan(error, undefined);
144
+ throw error;
145
+ }
146
+ if (result instanceof Promise) {
147
+ result
148
+ .then(r => {
149
+ finalizeSpan(undefined, r);
150
+ return r;
151
+ })
152
+ .catch(error => {
153
+ finalizeSpan(error, undefined);
154
+ });
155
+ }
156
+ else {
157
+ finalizeSpan(undefined, result);
158
+ }
159
+ });
160
+ return result;
161
+ }
162
+
120
163
  const tracer = api.trace.getTracer('moost-tracer');
121
164
  class SpanInjector extends moost.ContextInjector {
122
165
  constructor() {
@@ -135,10 +178,11 @@ class SpanInjector extends moost.ContextInjector {
135
178
  }
136
179
  else {
137
180
  const span = tracer.startSpan(name, {
181
+ kind: api.SpanKind.INTERNAL,
138
182
  attributes: attrs,
139
183
  });
140
184
  return this.withSpan(span, fn, {
141
- rootSpan: false,
185
+ withMetrics: false,
142
186
  endSpan: true,
143
187
  });
144
188
  }
@@ -174,7 +218,7 @@ class SpanInjector extends moost.ContextInjector {
174
218
  if (span) {
175
219
  registerSpan(span);
176
220
  return this.withSpan(span, cb, {
177
- rootSpan: true,
221
+ withMetrics: true,
178
222
  endSpan: eventType !== 'HTTP',
179
223
  });
180
224
  }
@@ -217,8 +261,17 @@ class SpanInjector extends moost.ContextInjector {
217
261
  if (method === '__SYSTEM__') {
218
262
  return;
219
263
  }
264
+ const { getSpan } = useOtelContext();
220
265
  if (name === 'Handler:not_found') {
221
266
  const chm = this.getControllerHandlerMeta();
267
+ const span = getSpan();
268
+ if (span) {
269
+ const eventType = this.getEventType();
270
+ if (eventType === 'HTTP') {
271
+ const req = this.getRequest();
272
+ span.updateName(`${req?.method || ''} ${req?.url}`);
273
+ }
274
+ }
222
275
  this.startEventMetrics(chm.attrs, route);
223
276
  }
224
277
  else if (name === 'Controller:registered') {
@@ -227,7 +280,6 @@ class SpanInjector extends moost.ContextInjector {
227
280
  if (!chm.ignoreMeter) {
228
281
  this.startEventMetrics(chm.attrs, _route);
229
282
  }
230
- const { getSpan } = useOtelContext();
231
283
  const span = getSpan();
232
284
  if (span) {
233
285
  span.setAttributes(chm.attrs);
@@ -244,48 +296,20 @@ class SpanInjector extends moost.ContextInjector {
244
296
  }
245
297
  }
246
298
  withSpan(span, cb, opts) {
247
- let result;
248
- let exception;
249
- const endSpan = (error) => {
250
- if (error) {
251
- span.recordException(error);
299
+ return withSpan(span, cb, (_span, exception, result) => {
300
+ if (result instanceof Error) {
301
+ _span.recordException(result);
252
302
  }
253
- if (opts.rootSpan) {
303
+ if (opts.withMetrics) {
254
304
  const chm = this.getControllerHandlerMeta();
255
305
  if (!chm.ignoreMeter) {
256
- this.endEventMetrics(chm.attrs, error);
306
+ this.endEventMetrics(chm.attrs, result instanceof Error ? result : exception);
257
307
  }
258
308
  }
259
309
  if (opts.endSpan) {
260
- span.end();
261
- }
262
- };
263
- api.context.with(api.trace.setSpan(api.context.active(), span), () => {
264
- try {
265
- result = cb();
266
- }
267
- catch (error) {
268
- exception = error;
269
- endSpan(exception);
310
+ _span.end();
270
311
  }
271
312
  });
272
- const ret = result;
273
- if (!exception) {
274
- if (ret && ret instanceof Promise) {
275
- ret
276
- .then(r => {
277
- endSpan(r instanceof Error ? r : undefined);
278
- return r;
279
- })
280
- .catch(error => {
281
- endSpan(error);
282
- });
283
- }
284
- else {
285
- endSpan(ret instanceof Error ? ret : undefined);
286
- }
287
- }
288
- return ret;
289
313
  }
290
314
  startEventMetrics(a, route) {
291
315
  if (a['moost.event_type'] === 'HTTP') {
@@ -1212,3 +1236,4 @@ exports.useOtelContext = useOtelContext;
1212
1236
  exports.useOtelPropagation = useOtelPropagation;
1213
1237
  exports.useSpan = useSpan;
1214
1238
  exports.useTrace = useTrace;
1239
+ exports.withSpan = withSpan;
package/dist/index.d.ts CHANGED
@@ -230,7 +230,7 @@ declare class SpanInjector extends ContextInjector<TContextInjectorHook> {
230
230
  };
231
231
  hook(method: string, name: 'Handler:not_found' | 'Handler:routed' | 'Controller:registered', route?: string): void;
232
232
  withSpan<T>(span: Span, cb: () => T, opts: {
233
- rootSpan: boolean;
233
+ withMetrics: boolean;
234
234
  endSpan: boolean;
235
235
  }): T;
236
236
  startEventMetrics(a: Record<string, string | number | boolean | undefined>, route?: string): void;
@@ -242,4 +242,23 @@ declare class SpanInjector extends ContextInjector<TContextInjectorHook> {
242
242
  }) | undefined;
243
243
  }
244
244
 
245
- export { MoostBatchSpanProcessor, MoostSimpleSpanProcessor, OtelIgnoreMeter, OtelIgnoreSpan, SpanInjector, type TOtelContext, type TOtelMate, enableOtelForMoost, getOtelMate, shouldSpanBeIgnored, useOtelContext, useOtelPropagation, useSpan, useTrace };
245
+ type TPostSpanProcessFn<T> = (span: Span, exception: Error | undefined, result: Awaited<T> | undefined) => void;
246
+ interface TSpanInput {
247
+ name: string;
248
+ options?: SpanOptions;
249
+ }
250
+ /**
251
+ * Starts or continues a span, executes the callback within the span's context,
252
+ * and handles span completion and error recording. Supports both synchronous and asynchronous callbacks.
253
+ * An optional post-processing callback can be used to enrich the span before it ends.
254
+ *
255
+ * @template T
256
+ * @param {TSpanInput | Span} span - The span input containing name and options, or an existing span.
257
+ * @param {() => T} cb - The callback function to execute within the span's context.
258
+ * @param {TPostSpanProcessFn<T>=} postProcess - An optional post-processing callback to enrich the span before it ends. **CAUTION: When used, you must end the span yourself `span.end()`.**
259
+ * @returns {T} The result of the callback function.
260
+ * @throws {Error} Will throw an error if the callback function throws.
261
+ */
262
+ declare function withSpan<T>(span: TSpanInput | Span, cb: () => T, postProcess?: TPostSpanProcessFn<T>): T;
263
+
264
+ export { MoostBatchSpanProcessor, MoostSimpleSpanProcessor, OtelIgnoreMeter, OtelIgnoreSpan, SpanInjector, type TOtelContext, type TOtelMate, type TPostSpanProcessFn, type TSpanInput, enableOtelForMoost, getOtelMate, shouldSpanBeIgnored, useOtelContext, useOtelPropagation, useSpan, useTrace, withSpan };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { trace, context, metrics } from '@opentelemetry/api';
1
+ import { trace, context, metrics, SpanKind } from '@opentelemetry/api';
2
2
  import { useAsyncEventContext as useAsyncEventContext$1, ContextInjector, useControllerContext, getConstructor as getConstructor$2, replaceContextInjector } from 'moost';
3
3
  import { randomUUID } from 'crypto';
4
4
  import { AsyncLocalStorage } from 'node:async_hooks';
@@ -115,6 +115,49 @@ function getMoostMetrics() {
115
115
  return moostMetrics;
116
116
  }
117
117
 
118
+ function withSpan(span, cb, postProcess) {
119
+ const _span = typeof span.name === 'string' && !span.spanContext
120
+ ? span
121
+ : trace
122
+ .getTracer('default')
123
+ .startSpan(span.name, span.options);
124
+ let result = undefined;
125
+ const finalizeSpan = (e, r) => {
126
+ if (e) {
127
+ _span.recordException(e);
128
+ }
129
+ if (postProcess) {
130
+ postProcess(_span, e, r);
131
+ }
132
+ else {
133
+ _span.end();
134
+ }
135
+ };
136
+ context.with(trace.setSpan(context.active(), _span), () => {
137
+ try {
138
+ result = cb();
139
+ }
140
+ catch (error) {
141
+ finalizeSpan(error, undefined);
142
+ throw error;
143
+ }
144
+ if (result instanceof Promise) {
145
+ result
146
+ .then(r => {
147
+ finalizeSpan(undefined, r);
148
+ return r;
149
+ })
150
+ .catch(error => {
151
+ finalizeSpan(error, undefined);
152
+ });
153
+ }
154
+ else {
155
+ finalizeSpan(undefined, result);
156
+ }
157
+ });
158
+ return result;
159
+ }
160
+
118
161
  const tracer = trace.getTracer('moost-tracer');
119
162
  class SpanInjector extends ContextInjector {
120
163
  constructor() {
@@ -133,10 +176,11 @@ class SpanInjector extends ContextInjector {
133
176
  }
134
177
  else {
135
178
  const span = tracer.startSpan(name, {
179
+ kind: SpanKind.INTERNAL,
136
180
  attributes: attrs,
137
181
  });
138
182
  return this.withSpan(span, fn, {
139
- rootSpan: false,
183
+ withMetrics: false,
140
184
  endSpan: true,
141
185
  });
142
186
  }
@@ -172,7 +216,7 @@ class SpanInjector extends ContextInjector {
172
216
  if (span) {
173
217
  registerSpan(span);
174
218
  return this.withSpan(span, cb, {
175
- rootSpan: true,
219
+ withMetrics: true,
176
220
  endSpan: eventType !== 'HTTP',
177
221
  });
178
222
  }
@@ -215,8 +259,17 @@ class SpanInjector extends ContextInjector {
215
259
  if (method === '__SYSTEM__') {
216
260
  return;
217
261
  }
262
+ const { getSpan } = useOtelContext();
218
263
  if (name === 'Handler:not_found') {
219
264
  const chm = this.getControllerHandlerMeta();
265
+ const span = getSpan();
266
+ if (span) {
267
+ const eventType = this.getEventType();
268
+ if (eventType === 'HTTP') {
269
+ const req = this.getRequest();
270
+ span.updateName(`${req?.method || ''} ${req?.url}`);
271
+ }
272
+ }
220
273
  this.startEventMetrics(chm.attrs, route);
221
274
  }
222
275
  else if (name === 'Controller:registered') {
@@ -225,7 +278,6 @@ class SpanInjector extends ContextInjector {
225
278
  if (!chm.ignoreMeter) {
226
279
  this.startEventMetrics(chm.attrs, _route);
227
280
  }
228
- const { getSpan } = useOtelContext();
229
281
  const span = getSpan();
230
282
  if (span) {
231
283
  span.setAttributes(chm.attrs);
@@ -242,48 +294,20 @@ class SpanInjector extends ContextInjector {
242
294
  }
243
295
  }
244
296
  withSpan(span, cb, opts) {
245
- let result;
246
- let exception;
247
- const endSpan = (error) => {
248
- if (error) {
249
- span.recordException(error);
297
+ return withSpan(span, cb, (_span, exception, result) => {
298
+ if (result instanceof Error) {
299
+ _span.recordException(result);
250
300
  }
251
- if (opts.rootSpan) {
301
+ if (opts.withMetrics) {
252
302
  const chm = this.getControllerHandlerMeta();
253
303
  if (!chm.ignoreMeter) {
254
- this.endEventMetrics(chm.attrs, error);
304
+ this.endEventMetrics(chm.attrs, result instanceof Error ? result : exception);
255
305
  }
256
306
  }
257
307
  if (opts.endSpan) {
258
- span.end();
259
- }
260
- };
261
- context.with(trace.setSpan(context.active(), span), () => {
262
- try {
263
- result = cb();
264
- }
265
- catch (error) {
266
- exception = error;
267
- endSpan(exception);
308
+ _span.end();
268
309
  }
269
310
  });
270
- const ret = result;
271
- if (!exception) {
272
- if (ret && ret instanceof Promise) {
273
- ret
274
- .then(r => {
275
- endSpan(r instanceof Error ? r : undefined);
276
- return r;
277
- })
278
- .catch(error => {
279
- endSpan(error);
280
- });
281
- }
282
- else {
283
- endSpan(ret instanceof Error ? ret : undefined);
284
- }
285
- }
286
- return ret;
287
311
  }
288
312
  startEventMetrics(a, route) {
289
313
  if (a['moost.event_type'] === 'HTTP') {
@@ -1198,4 +1222,4 @@ class MoostSimpleSpanProcessor extends SimpleSpanProcessor {
1198
1222
  }
1199
1223
  }
1200
1224
 
1201
- export { MoostBatchSpanProcessor, MoostSimpleSpanProcessor, OtelIgnoreMeter, OtelIgnoreSpan, SpanInjector, enableOtelForMoost, getOtelMate, shouldSpanBeIgnored, useOtelContext, useOtelPropagation, useSpan, useTrace };
1225
+ export { MoostBatchSpanProcessor, MoostSimpleSpanProcessor, OtelIgnoreMeter, OtelIgnoreSpan, SpanInjector, enableOtelForMoost, getOtelMate, shouldSpanBeIgnored, useOtelContext, useOtelPropagation, useSpan, useTrace, withSpan };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/otel",
3
- "version": "0.4.14",
3
+ "version": "0.4.16",
4
4
  "description": "@moostjs/otel",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -39,6 +39,6 @@
39
39
  "dependencies": {
40
40
  "@opentelemetry/api": "^1.9.0",
41
41
  "@opentelemetry/sdk-trace-base": "^1.25.1",
42
- "moost": "0.4.14"
42
+ "moost": "0.4.16"
43
43
  }
44
44
  }