abxbus 2.4.22 → 2.4.24

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.
@@ -53,7 +53,6 @@ export type OtelTracingMiddlewareOptions = {
53
53
  otlp_endpoint?: string
54
54
  service_name?: string
55
55
  instrumentation_name?: string
56
- root_span_name?: string | ((eventbus: EventBus, event: BaseEvent) => string)
57
56
  root_span_attributes?: SpanAttributes | ((eventbus: EventBus, event: BaseEvent) => SpanAttributes)
58
57
  }
59
58
 
@@ -62,10 +61,7 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
62
61
  private readonly trace_api: OpenTelemetryTraceApi
63
62
  private readonly span_factory?: OtelTracingSpanFactory
64
63
  private readonly span_provider?: OtelTracingSpanProvider
65
- private readonly root_span_name: OtelTracingMiddlewareOptions['root_span_name']
66
64
  private readonly root_span_attributes: OtelTracingMiddlewareOptions['root_span_attributes']
67
- private readonly root_spans = new Map<string, Span>()
68
- private readonly root_contexts = new Map<string, Context>()
69
65
  private readonly event_spans = new Map<string, Span>()
70
66
  private readonly event_contexts = new Map<string, Context>()
71
67
  private readonly handler_spans = new Map<string, Span>()
@@ -80,7 +76,6 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
80
76
  (this.span_provider
81
77
  ? createProviderSpanFactory(this.trace_api, this.span_provider, options.instrumentation_name ?? 'abxbus')
82
78
  : undefined)
83
- this.root_span_name = options.root_span_name
84
79
  this.root_span_attributes = options.root_span_attributes
85
80
  }
86
81
 
@@ -118,22 +113,14 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
118
113
  return existing
119
114
  }
120
115
 
121
- const parent_context = this.parentContextForEvent(event) ?? this.startRootSpan(eventbus, event)
116
+ const parent_context = this.parentContextForEvent(event) ?? ROOT_CONTEXT
122
117
  const start_time = dateFromIso(event.event_started_at)
123
118
  const span = this.tracer.startSpan(
124
- `abxbus.event ${event.event_type}`,
119
+ eventSpanName(eventbus, event),
125
120
  {
126
- attributes: compactAttributes({
127
- 'abxbus.bus.id': eventbus.id,
128
- 'abxbus.bus.name': eventbus.name,
129
- 'abxbus.event.id': event.event_id,
130
- 'abxbus.event.type': event.event_type,
131
- 'abxbus.event.version': event.event_version,
132
- 'abxbus.event.session_id': stringValue((event as { session_id?: unknown }).session_id),
133
- 'abxbus.event.parent_id': event.event_parent_id,
134
- 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,
135
- 'abxbus.event.path': event.event_path.join(' '),
136
- }),
121
+ attributes: event.event_parent_id
122
+ ? eventStartedSpanAttributes(eventbus, event)
123
+ : topLevelEventStartedSpanAttributes(this.root_span_attributes, eventbus, event),
137
124
  startTime: start_time,
138
125
  },
139
126
  parent_context
@@ -157,19 +144,13 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
157
144
  span.setStatus({ code: SpanStatusCode.OK })
158
145
  }
159
146
  span.setAttributes(
160
- compactAttributes({
161
- 'abxbus.event.status': event.event_status,
162
- 'abxbus.event.result_count': event.event_results.size,
163
- 'abxbus.event.error_count': event.event_errors.length,
164
- 'abxbus.event.child_count': event.event_children.length,
165
- })
147
+ event.event_parent_id ? eventSpanAttributes(eventbus, event) : topLevelEventSpanAttributes(this.root_span_attributes, eventbus, event)
166
148
  )
167
149
  const start_time = dateFromIso(event.event_started_at)
168
150
  const end_time = endTimeAfterStart(start_time, dateFromIso(event.event_completed_at))
169
151
  span.end(end_time)
170
152
  this.event_spans.delete(event.event_id)
171
153
  this.event_contexts.delete(event.event_id)
172
- this.completeRootSpan(event.event_id, start_time, end_time)
173
154
  }
174
155
 
175
156
  private startHandlerSpan(eventbus: EventBus, event: BaseEvent, event_result: EventResult): Span {
@@ -181,19 +162,9 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
181
162
  const parent_context =
182
163
  this.event_contexts.get(event.event_id) ?? this.trace_api.setSpan(ROOT_CONTEXT, this.startEventSpan(eventbus, event))
183
164
  const span = this.tracer.startSpan(
184
- `abxbus.handler ${event.event_type} ${event_result.handler_name}`,
165
+ handlerSpanName(event, event_result),
185
166
  {
186
- attributes: compactAttributes({
187
- 'abxbus.bus.id': eventbus.id,
188
- 'abxbus.bus.name': eventbus.name,
189
- 'abxbus.event.id': event.event_id,
190
- 'abxbus.event.type': event.event_type,
191
- 'abxbus.handler.id': event_result.handler_id,
192
- 'abxbus.handler.name': event_result.handler_name,
193
- 'abxbus.handler.file_path': event_result.handler_file_path,
194
- 'abxbus.handler.event_pattern': event_result.handler.event_pattern,
195
- 'abxbus.event_result.id': event_result.id,
196
- }),
167
+ attributes: handlerSpanAttributes(eventbus, event, event_result),
197
168
  startTime: dateFromIso(event_result.started_at),
198
169
  },
199
170
  parent_context
@@ -206,7 +177,6 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
206
177
 
207
178
  private completeHandlerSpan(eventbus: EventBus, event: BaseEvent, event_result: EventResult): void {
208
179
  if (this.span_factory) {
209
- this.completeHandlerSpanWithFactory(eventbus, event, event_result)
210
180
  return
211
181
  }
212
182
 
@@ -220,59 +190,12 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
220
190
  } else {
221
191
  span.setStatus({ code: SpanStatusCode.OK })
222
192
  }
223
- span.setAttributes(
224
- compactAttributes({
225
- 'abxbus.event_result.status': event_result.status,
226
- 'abxbus.handler.child_count': event_result.event_children.length,
227
- })
228
- )
193
+ span.setAttributes(handlerSpanAttributes(eventbus, event, event_result))
229
194
  span.end(endTimeAfterStart(dateFromIso(event_result.started_at), dateFromIso(event_result.completed_at)))
230
195
  this.handler_spans.delete(event_result.id)
231
196
  this.handler_contexts.delete(handlerSpanKey(event_result.event_id, event_result.handler_id))
232
197
  }
233
198
 
234
- private startRootSpan(eventbus: EventBus, event: BaseEvent): Context {
235
- const existing = this.root_contexts.get(event.event_id)
236
- if (existing) {
237
- return existing
238
- }
239
-
240
- const session_id = stringValue((event as { session_id?: unknown }).session_id)
241
- const root_attributes = resolveAttributes(this.root_span_attributes, eventbus, event)
242
- const root_span = this.tracer.startSpan(
243
- resolveRootSpanName(this.root_span_name, eventbus, event),
244
- {
245
- attributes: compactAttributes({
246
- ...root_attributes,
247
- 'abxbus.trace.root': true,
248
- 'abxbus.bus.id': eventbus.id,
249
- 'abxbus.bus.name': eventbus.name,
250
- 'abxbus.root_event.id': event.event_id,
251
- 'abxbus.root_event.type': event.event_type,
252
- 'abxbus.root_event.session_id': session_id,
253
- }),
254
- startTime: dateFromIso(event.event_started_at),
255
- },
256
- ROOT_CONTEXT
257
- )
258
- const root_context = this.trace_api.setSpan(ROOT_CONTEXT, root_span)
259
- this.root_spans.set(event.event_id, root_span)
260
- this.root_contexts.set(event.event_id, root_context)
261
- return root_context
262
- }
263
-
264
- private completeRootSpan(event_id: string, start_time: Date | undefined, end_time: Date | undefined): void {
265
- const root_span = this.root_spans.get(event_id)
266
- if (!root_span) {
267
- return
268
- }
269
-
270
- root_span.setStatus({ code: SpanStatusCode.OK })
271
- root_span.end(endTimeAfterStart(start_time, end_time))
272
- this.root_spans.delete(event_id)
273
- this.root_contexts.delete(event_id)
274
- }
275
-
276
199
  private parentContextForEvent(event: BaseEvent): Context | undefined {
277
200
  if (event.event_parent_id && event.event_emitted_by_handler_id) {
278
201
  const handler_context = this.handler_contexts.get(handlerSpanKey(event.event_parent_id, event.event_emitted_by_handler_id))
@@ -285,56 +208,77 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
285
208
  }
286
209
 
287
210
  private completeEventSpanWithFactory(eventbus: EventBus, event: BaseEvent): void {
288
- const root_event = rootEventForEvent(eventbus, event)
289
- const trace_id = traceIdForRootEvent(root_event.event_id)
290
- const event_context = eventSpanContext(trace_id, event.event_id)
291
- const start_time = dateFromIso(event.event_started_at)
292
- const end_time = endTimeAfterStart(start_time, dateFromIso(event.event_completed_at))
211
+ if (event.event_parent_id) {
212
+ return
213
+ }
293
214
 
294
- if (!event.event_parent_id) {
295
- const root_span = this.span_factory!({
296
- name: resolveRootSpanName(this.root_span_name, eventbus, event),
297
- span_context: rootSpanContext(trace_id, event.event_id),
298
- attributes: rootSpanAttributes(this.root_span_attributes, eventbus, event),
299
- start_time,
300
- })
301
- root_span.setStatus({ code: SpanStatusCode.OK })
302
- root_span.end(end_time)
215
+ const top_level_event = event._event_original ?? event
216
+ const trace_id = traceIdForRootEvent(top_level_event.event_id)
217
+ this.exportEventTreeWithFactory(eventbus, top_level_event, trace_id, undefined, new Set<string>())
218
+ }
219
+
220
+ private exportEventTreeWithFactory(
221
+ eventbus: EventBus,
222
+ event: BaseEvent,
223
+ trace_id: string,
224
+ parent_span_context: SpanContext | undefined,
225
+ visited_event_ids: Set<string>
226
+ ): void {
227
+ const original_event = event._event_original ?? event
228
+ if (visited_event_ids.has(original_event.event_id)) {
229
+ return
303
230
  }
231
+ visited_event_ids.add(original_event.event_id)
304
232
 
233
+ const start_time = dateFromIso(original_event.event_started_at)
234
+ const span_context = eventSpanContext(trace_id, original_event.event_id)
305
235
  const span = this.span_factory!({
306
- name: `abxbus.event ${event.event_type}`,
307
- span_context: event_context,
308
- parent_span_context: parentSpanContextForEvent(event, trace_id),
309
- attributes: eventSpanAttributes(eventbus, event),
236
+ name: eventSpanName(eventbus, original_event),
237
+ span_context,
238
+ parent_span_context,
239
+ attributes: original_event.event_parent_id
240
+ ? eventSpanAttributes(eventbus, original_event)
241
+ : topLevelEventSpanAttributes(this.root_span_attributes, eventbus, original_event),
310
242
  start_time,
311
243
  })
312
- if (event.event_errors.length > 0) {
313
- recordSpanError(span, event.event_errors[0])
244
+ if (original_event.event_errors.length > 0) {
245
+ recordSpanError(span, original_event.event_errors[0])
314
246
  } else {
315
247
  span.setStatus({ code: SpanStatusCode.OK })
316
248
  }
317
- span.end(end_time)
249
+ span.end(endTimeAfterStart(start_time, dateFromIso(original_event.event_completed_at)))
250
+
251
+ for (const event_result of original_event.event_results.values()) {
252
+ const handler_context = this.exportHandlerSpanWithFactory(eventbus, original_event, event_result, trace_id, span_context)
253
+ for (const child of event_result.event_children) {
254
+ this.exportEventTreeWithFactory(eventbus, child, trace_id, handler_context, visited_event_ids)
255
+ }
256
+ }
318
257
  }
319
258
 
320
- private completeHandlerSpanWithFactory(eventbus: EventBus, event: BaseEvent, event_result: EventResult): void {
321
- const root_event = rootEventForEvent(eventbus, event)
322
- const trace_id = traceIdForRootEvent(root_event.event_id)
259
+ private exportHandlerSpanWithFactory(
260
+ eventbus: EventBus,
261
+ event: BaseEvent,
262
+ event_result: EventResult,
263
+ trace_id: string,
264
+ parent_span_context: SpanContext
265
+ ): SpanContext {
323
266
  const start_time = dateFromIso(event_result.started_at)
267
+ const span_context = handlerSpanContext(trace_id, event_result.event_id, event_result.handler_id)
324
268
  const span = this.span_factory!({
325
- name: `abxbus.handler ${event.event_type} ${event_result.handler_name}`,
326
- span_context: handlerSpanContext(trace_id, event_result.event_id, event_result.handler_id),
327
- parent_span_context: eventSpanContext(trace_id, event.event_id),
269
+ name: handlerSpanName(event, event_result),
270
+ span_context,
271
+ parent_span_context,
328
272
  attributes: handlerSpanAttributes(eventbus, event, event_result),
329
273
  start_time,
330
274
  })
331
-
332
275
  if (event_result.error !== undefined) {
333
276
  recordSpanError(span, event_result.error)
334
277
  } else {
335
278
  span.setStatus({ code: SpanStatusCode.OK })
336
279
  }
337
280
  span.end(endTimeAfterStart(start_time, dateFromIso(event_result.completed_at)))
281
+ return span_context
338
282
  }
339
283
  }
340
284
 
@@ -342,6 +286,14 @@ function handlerSpanKey(event_id: string, handler_id: string): string {
342
286
  return `${event_id}:${handler_id}`
343
287
  }
344
288
 
289
+ function eventSpanName(eventbus: EventBus, event: BaseEvent): string {
290
+ return `${eventbus.name}.emit(${event.event_type})`
291
+ }
292
+
293
+ function handlerSpanName(event: BaseEvent, event_result: EventResult): string {
294
+ return `${event_result.handler_name}(${event.event_type})`
295
+ }
296
+
345
297
  function createOtlpSpanProvider(options: OtelTracingMiddlewareOptions): OtelTracingSpanProvider {
346
298
  return new BasicTracerProvider({
347
299
  resource: resourceFromAttributes({
@@ -395,92 +347,64 @@ function normalizeOtlpTracesEndpoint(endpoint: string): string {
395
347
  return trimmed.endsWith('/v1/traces') ? trimmed : `${trimmed}/v1/traces`
396
348
  }
397
349
 
350
+ function eventStartedSpanAttributes(eventbus: EventBus, event: BaseEvent): SpanAttributes {
351
+ return compactAttributes({
352
+ 'abxbus.event_bus.id': eventbus.id,
353
+ 'abxbus.event_bus.name': eventbus.name,
354
+ 'abxbus.event_id': event.event_id,
355
+ 'abxbus.event_type': event.event_type,
356
+ 'abxbus.event_version': event.event_version,
357
+ 'abxbus.session_id': stringValue((event as { session_id?: unknown }).session_id),
358
+ 'abxbus.event_parent_id': event.event_parent_id,
359
+ 'abxbus.event_emitted_by_handler_id': event.event_emitted_by_handler_id,
360
+ 'abxbus.event_path': event.event_path.join(' '),
361
+ })
362
+ }
363
+
398
364
  function eventSpanAttributes(eventbus: EventBus, event: BaseEvent): SpanAttributes {
399
365
  return compactAttributes({
400
- 'abxbus.bus.id': eventbus.id,
401
- 'abxbus.bus.name': eventbus.name,
402
- 'abxbus.event.id': event.event_id,
403
- 'abxbus.event.type': event.event_type,
404
- 'abxbus.event.version': event.event_version,
405
- 'abxbus.event.session_id': stringValue((event as { session_id?: unknown }).session_id),
406
- 'abxbus.event.parent_id': event.event_parent_id,
407
- 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,
408
- 'abxbus.event.path': event.event_path.join(' '),
409
- 'abxbus.event.status': event.event_status,
410
- 'abxbus.event.result_count': event.event_results.size,
411
- 'abxbus.event.error_count': event.event_errors.length,
412
- 'abxbus.event.child_count': event.event_children.length,
366
+ ...eventStartedSpanAttributes(eventbus, event),
367
+ 'abxbus.event_status': event.event_status,
413
368
  })
414
369
  }
415
370
 
416
371
  function handlerSpanAttributes(eventbus: EventBus, event: BaseEvent, event_result: EventResult): SpanAttributes {
417
372
  return compactAttributes({
418
- 'abxbus.bus.id': eventbus.id,
419
- 'abxbus.bus.name': eventbus.name,
420
- 'abxbus.event.id': event.event_id,
421
- 'abxbus.event.type': event.event_type,
422
- 'abxbus.handler.id': event_result.handler_id,
423
- 'abxbus.handler.name': event_result.handler_name,
424
- 'abxbus.handler.file_path': event_result.handler_file_path,
425
- 'abxbus.handler.event_pattern': event_result.handler.event_pattern,
426
- 'abxbus.event_result.id': event_result.id,
427
- 'abxbus.event_result.status': event_result.status,
428
- 'abxbus.handler.child_count': event_result.event_children.length,
373
+ 'abxbus.event_bus.id': eventbus.id,
374
+ 'abxbus.event_bus.name': eventbus.name,
375
+ 'abxbus.event_id': event.event_id,
376
+ 'abxbus.event_type': event.event_type,
377
+ 'abxbus.handler_id': event_result.handler_id,
378
+ 'abxbus.handler_name': event_result.handler_name,
379
+ 'abxbus.handler_file_path': event_result.handler_file_path,
380
+ 'abxbus.handler_event_pattern': event_result.handler.event_pattern,
381
+ 'abxbus.event_result_id': event_result.id,
382
+ 'abxbus.event_result_status': event_result.status,
429
383
  })
430
384
  }
431
385
 
432
- function rootSpanAttributes(
386
+ function topLevelEventStartedSpanAttributes(
433
387
  root_span_attributes: OtelTracingMiddlewareOptions['root_span_attributes'],
434
388
  eventbus: EventBus,
435
389
  event: BaseEvent
436
390
  ): SpanAttributes {
437
- const session_id = stringValue((event as { session_id?: unknown }).session_id)
438
391
  return compactAttributes({
392
+ ...eventStartedSpanAttributes(eventbus, event),
439
393
  ...resolveAttributes(root_span_attributes, eventbus, event),
440
394
  'abxbus.trace.root': true,
441
- 'abxbus.bus.id': eventbus.id,
442
- 'abxbus.bus.name': eventbus.name,
443
- 'abxbus.root_event.id': event.event_id,
444
- 'abxbus.root_event.type': event.event_type,
445
- 'abxbus.root_event.session_id': session_id,
446
- 'abxbus.root_event.status': event.event_status,
447
- 'abxbus.root_event.error_count': event.event_errors.length,
448
- 'abxbus.root_event.child_count': event.event_children.length,
449
395
  })
450
396
  }
451
397
 
452
- function rootEventForEvent(eventbus: EventBus, event: BaseEvent): BaseEvent {
453
- let current = event._event_original ?? event
454
- const seen = new Set<string>()
455
- while (current.event_parent_id && !seen.has(current.event_id)) {
456
- seen.add(current.event_id)
457
- const parent = eventbus.findEventById(current.event_parent_id)
458
- if (!parent) {
459
- break
460
- }
461
- current = parent._event_original ?? parent
462
- }
463
- return current
464
- }
465
-
466
- function parentSpanContextForEvent(event: BaseEvent, trace_id: string): SpanContext {
467
- if (!event.event_parent_id) {
468
- return rootSpanContext(trace_id, event.event_id)
469
- }
470
-
471
- if (event.event_emitted_by_handler_id) {
472
- return handlerSpanContext(trace_id, event.event_parent_id, event.event_emitted_by_handler_id)
473
- }
474
-
475
- return eventSpanContext(trace_id, event.event_parent_id)
476
- }
477
-
478
- function rootSpanContext(trace_id: string, event_id: string): SpanContext {
479
- return {
480
- traceId: trace_id,
481
- spanId: deterministicSpanId(`abxbus.root:${event_id}`),
482
- traceFlags: 1,
483
- }
398
+ function topLevelEventSpanAttributes(
399
+ root_span_attributes: OtelTracingMiddlewareOptions['root_span_attributes'],
400
+ eventbus: EventBus,
401
+ event: BaseEvent
402
+ ): SpanAttributes {
403
+ return compactAttributes({
404
+ ...eventSpanAttributes(eventbus, event),
405
+ ...resolveAttributes(root_span_attributes, eventbus, event),
406
+ 'abxbus.trace.root': true,
407
+ })
484
408
  }
485
409
 
486
410
  function eventSpanContext(trace_id: string, event_id: string): SpanContext {
@@ -537,13 +461,6 @@ function endTimeAfterStart(start_time: Date | undefined, end_time: Date | undefi
537
461
  return end_time.getTime() > start_time.getTime() ? end_time : new Date(start_time.getTime() + 1)
538
462
  }
539
463
 
540
- function resolveRootSpanName(root_span_name: OtelTracingMiddlewareOptions['root_span_name'], eventbus: EventBus, event: BaseEvent): string {
541
- if (typeof root_span_name === 'function') {
542
- return root_span_name(eventbus, event)
543
- }
544
- return root_span_name ?? `abxbus.trace ${eventbus.name}`
545
- }
546
-
547
464
  function resolveAttributes(
548
465
  attributes: OtelTracingMiddlewareOptions['root_span_attributes'],
549
466
  eventbus: EventBus,