abxbus 2.4.23 → 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
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 {
@@ -183,17 +164,7 @@ export class OtelTracingMiddleware implements EventBusMiddleware {
183
164
  const span = this.tracer.startSpan(
184
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
+ }
214
+
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
+ }
293
219
 
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)
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: eventSpanName(eventbus, event),
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
269
  name: handlerSpanName(event, event_result),
326
- span_context: handlerSpanContext(trace_id, event_result.event_id, event_result.handler_id),
327
- parent_span_context: eventSpanContext(trace_id, event.event_id),
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
 
@@ -403,92 +347,64 @@ function normalizeOtlpTracesEndpoint(endpoint: string): string {
403
347
  return trimmed.endsWith('/v1/traces') ? trimmed : `${trimmed}/v1/traces`
404
348
  }
405
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
+
406
364
  function eventSpanAttributes(eventbus: EventBus, event: BaseEvent): SpanAttributes {
407
365
  return compactAttributes({
408
- 'abxbus.bus.id': eventbus.id,
409
- 'abxbus.bus.name': eventbus.name,
410
- 'abxbus.event.id': event.event_id,
411
- 'abxbus.event.type': event.event_type,
412
- 'abxbus.event.version': event.event_version,
413
- 'abxbus.event.session_id': stringValue((event as { session_id?: unknown }).session_id),
414
- 'abxbus.event.parent_id': event.event_parent_id,
415
- 'abxbus.event.emitted_by_handler_id': event.event_emitted_by_handler_id,
416
- 'abxbus.event.path': event.event_path.join(' '),
417
- 'abxbus.event.status': event.event_status,
418
- 'abxbus.event.result_count': event.event_results.size,
419
- 'abxbus.event.error_count': event.event_errors.length,
420
- 'abxbus.event.child_count': event.event_children.length,
366
+ ...eventStartedSpanAttributes(eventbus, event),
367
+ 'abxbus.event_status': event.event_status,
421
368
  })
422
369
  }
423
370
 
424
371
  function handlerSpanAttributes(eventbus: EventBus, event: BaseEvent, event_result: EventResult): SpanAttributes {
425
372
  return compactAttributes({
426
- 'abxbus.bus.id': eventbus.id,
427
- 'abxbus.bus.name': eventbus.name,
428
- 'abxbus.event.id': event.event_id,
429
- 'abxbus.event.type': event.event_type,
430
- 'abxbus.handler.id': event_result.handler_id,
431
- 'abxbus.handler.name': event_result.handler_name,
432
- 'abxbus.handler.file_path': event_result.handler_file_path,
433
- 'abxbus.handler.event_pattern': event_result.handler.event_pattern,
434
- 'abxbus.event_result.id': event_result.id,
435
- 'abxbus.event_result.status': event_result.status,
436
- '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,
437
383
  })
438
384
  }
439
385
 
440
- function rootSpanAttributes(
386
+ function topLevelEventStartedSpanAttributes(
441
387
  root_span_attributes: OtelTracingMiddlewareOptions['root_span_attributes'],
442
388
  eventbus: EventBus,
443
389
  event: BaseEvent
444
390
  ): SpanAttributes {
445
- const session_id = stringValue((event as { session_id?: unknown }).session_id)
446
391
  return compactAttributes({
392
+ ...eventStartedSpanAttributes(eventbus, event),
447
393
  ...resolveAttributes(root_span_attributes, eventbus, event),
448
394
  'abxbus.trace.root': true,
449
- 'abxbus.bus.id': eventbus.id,
450
- 'abxbus.bus.name': eventbus.name,
451
- 'abxbus.root_event.id': event.event_id,
452
- 'abxbus.root_event.type': event.event_type,
453
- 'abxbus.root_event.session_id': session_id,
454
- 'abxbus.root_event.status': event.event_status,
455
- 'abxbus.root_event.error_count': event.event_errors.length,
456
- 'abxbus.root_event.child_count': event.event_children.length,
457
395
  })
458
396
  }
459
397
 
460
- function rootEventForEvent(eventbus: EventBus, event: BaseEvent): BaseEvent {
461
- let current = event._event_original ?? event
462
- const seen = new Set<string>()
463
- while (current.event_parent_id && !seen.has(current.event_id)) {
464
- seen.add(current.event_id)
465
- const parent = eventbus.findEventById(current.event_parent_id)
466
- if (!parent) {
467
- break
468
- }
469
- current = parent._event_original ?? parent
470
- }
471
- return current
472
- }
473
-
474
- function parentSpanContextForEvent(event: BaseEvent, trace_id: string): SpanContext {
475
- if (!event.event_parent_id) {
476
- return rootSpanContext(trace_id, event.event_id)
477
- }
478
-
479
- if (event.event_emitted_by_handler_id) {
480
- return handlerSpanContext(trace_id, event.event_parent_id, event.event_emitted_by_handler_id)
481
- }
482
-
483
- return eventSpanContext(trace_id, event.event_parent_id)
484
- }
485
-
486
- function rootSpanContext(trace_id: string, event_id: string): SpanContext {
487
- return {
488
- traceId: trace_id,
489
- spanId: deterministicSpanId(`abxbus.root:${event_id}`),
490
- traceFlags: 1,
491
- }
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
+ })
492
408
  }
493
409
 
494
410
  function eventSpanContext(trace_id: string, event_id: string): SpanContext {
@@ -545,13 +461,6 @@ function endTimeAfterStart(start_time: Date | undefined, end_time: Date | undefi
545
461
  return end_time.getTime() > start_time.getTime() ? end_time : new Date(start_time.getTime() + 1)
546
462
  }
547
463
 
548
- function resolveRootSpanName(root_span_name: OtelTracingMiddlewareOptions['root_span_name'], eventbus: EventBus, event: BaseEvent): string {
549
- if (typeof root_span_name === 'function') {
550
- return root_span_name(eventbus, event)
551
- }
552
- return root_span_name ?? `abxbus.trace ${eventbus.name}`
553
- }
554
-
555
464
  function resolveAttributes(
556
465
  attributes: OtelTracingMiddlewareOptions['root_span_attributes'],
557
466
  eventbus: EventBus,