diff --git a/src/tracer/Tracer.ts b/src/tracer/Tracer.ts index 71d81d0..a82233b 100644 --- a/src/tracer/Tracer.ts +++ b/src/tracer/Tracer.ts @@ -1,54 +1,54 @@ -import type { SpanEvent } from './types.js'; -import { IdSortable, utils as idUtils } from '@matrixai/id'; +import type { SpanEvent, SpanId } from './types.js'; +import { IdSortable } from '@matrixai/id'; class Tracer { - protected activeSpans: Map = new Map(); + protected activeSpans: Map = new Map(); protected queue: Array = []; protected resolveWaitChunksP: (() => void) | undefined; protected ended: boolean = false; protected idGen = new IdSortable(); + protected shouldTrace = false; - protected nextId(): string { + protected nextId(): SpanId { const result = this.idGen.next(); if (result.done || result.value == null) { throw new Error('Unexpected end of id generator'); } - return idUtils.toMultibase(result.value, 'base64'); + return result.value.toMultibase('base32hex') as SpanId; } - protected queueSpanEvent(evt: SpanEvent) { + protected queueSpanEvent(evt: SpanEvent): void { + if (!this.shouldTrace) return; this.queue.push(evt); if (this.resolveWaitChunksP != null) this.resolveWaitChunksP(); } - public startSpan(name: string, parentSpanId?: string): string { + public startSpan(name: string, parentSpanId?: SpanId): SpanId { const spanId = this.nextId(); this.activeSpans.set(spanId, name); this.queueSpanEvent({ type: 'start', - id: this.nextId(), - spanId: spanId, - parentSpanId: parentSpanId, + id: spanId, + parentId: parentSpanId, name: name, }); return spanId; } - public endSpan(spanId: string): void { + public endSpan(spanId: SpanId): void { const name = this.activeSpans.get(spanId); if (!name) return; this.activeSpans.delete(spanId); this.queueSpanEvent({ - type: 'end', + type: 'stop', id: this.nextId(), - spanId: spanId, - name: name, + startId: spanId, }); } public async traced( name: string, - parentSpanId: string | undefined, + parentSpanId: SpanId | undefined, fn: () => T | Promise, ): Promise { const fnProm = async () => { @@ -77,6 +77,13 @@ class Tracer { yield value; } } + + public disableTracing(): void { + this.shouldTrace = false; + this.ended = true; + this.resolveWaitChunksP?.(); + this.queue = []; + } } export default Tracer; diff --git a/src/tracer/types.ts b/src/tracer/types.ts index 74b75d4..ab8550f 100644 --- a/src/tracer/types.ts +++ b/src/tracer/types.ts @@ -1,12 +1,26 @@ -type Span = { - spanId: string; - name: string; - parentSpanId?: string; +type SpanId = string & { readonly brand: unique symbol }; + +/** + * A span is a virtual concept, not an actual object. A span is made up of + * multiple events. Each event gets its own ID. Each start event must be + * associated with a stop event, otherwise the span is considered to be + * ongoing. + */ + +type SpanStart = { + type: 'start'; + id: SpanId; + parentId?: SpanId; + name?: string; }; -type SpanEvent = Span & { - type: 'start' | 'end'; - id: string; +type SpanStop = { + type: 'stop'; + id: SpanId; + startId: SpanId; + parentId?: SpanId; }; -export type { Span, SpanEvent }; +type SpanEvent = SpanStart | SpanStop; + +export type { SpanId, SpanEvent }; diff --git a/tests/asciinemaTest.ts b/tests/asciinemaTest.ts index 88b6e2c..2b268ca 100644 --- a/tests/asciinemaTest.ts +++ b/tests/asciinemaTest.ts @@ -1,10 +1,11 @@ +import type { SpanId } from '#tracer/index.js'; import fs from 'fs'; import * as fc from 'fast-check'; import tracer from '#tracer/index.js'; let parentIndex = 0; let step = 0; -let nestedIds: Array = []; +let nestedIds: Array = []; type Flags = { hasForkA: boolean; @@ -18,9 +19,9 @@ type Flags = { }; const current: { - parentId?: string; - forkAId?: string; - forkBId?: string; + parentId?: SpanId; + forkAId?: SpanId; + forkBId?: SpanId; flags: Flags; } = { flags: {