107 lines · 3.0 KB · TypeScript
RawDownload
import type * as Exit from "effect/Exit";import { Effect, Option, Tracer } from "effect"; import { EffectTraceRecord, spanToTraceRecord } from "./TraceRecord.ts";import { makeTraceSink, type TraceSink } from "./TraceSink.ts"; export interface LocalFileTracerOptions {  readonly filePath: string;  readonly maxBytes: number;  readonly maxFiles: number;  readonly batchWindowMs: number;  readonly delegate?: Tracer.Tracer;  readonly sink?: TraceSink;} class LocalFileSpan implements Tracer.Span {  readonly _tag = "Span";  readonly name: string;  readonly spanId: string;  readonly traceId: string;  readonly parent: Option.Option<Tracer.AnySpan>;  readonly annotations: Tracer.Span["annotations"];  readonly links: Array<Tracer.SpanLink>;  readonly sampled: boolean;  readonly kind: Tracer.SpanKind;   status: Tracer.SpanStatus;  attributes: Map<string, unknown>;  events: Array<[name: string, startTime: bigint, attributes: Record<string, unknown>]>;   constructor(    options: Parameters<Tracer.Tracer["span"]>[0],    private readonly delegate: Tracer.Span,    private readonly push: (record: EffectTraceRecord) => void,  ) {    this.name = delegate.name;    this.spanId = delegate.spanId;    this.traceId = delegate.traceId;    this.parent = options.parent;    this.annotations = options.annotations;    this.links = [...options.links];    this.sampled = delegate.sampled;    this.kind = delegate.kind;    this.status = {      _tag: "Started",      startTime: options.startTime,    };    this.attributes = new Map();    this.events = [];  }   end(endTime: bigint, exit: Exit.Exit<unknown, unknown>): void {    this.status = {      _tag: "Ended",      startTime: this.status.startTime,      endTime,      exit,    };    this.delegate.end(endTime, exit);     if (this.sampled) {      this.push(spanToTraceRecord(this));    }  }   attribute(key: string, value: unknown): void {    this.attributes.set(key, value);    this.delegate.attribute(key, value);  }   event(name: string, startTime: bigint, attributes?: Record<string, unknown>): void {    const nextAttributes = attributes ?? {};    this.events.push([name, startTime, nextAttributes]);    this.delegate.event(name, startTime, nextAttributes);  }   addLinks(links: ReadonlyArray<Tracer.SpanLink>): void {    this.links.push(...links);    this.delegate.addLinks(links);  }} export const makeLocalFileTracer = Effect.fn("makeLocalFileTracer")(function* (  options: LocalFileTracerOptions,) {  const sink =    options.sink ??    (yield* makeTraceSink({      filePath: options.filePath,      maxBytes: options.maxBytes,      maxFiles: options.maxFiles,      batchWindowMs: options.batchWindowMs,    }));   const delegate =    options.delegate ??    Tracer.make({      span: (spanOptions) => new Tracer.NativeSpan(spanOptions),    });   return Tracer.make({    span(spanOptions) {      return new LocalFileSpan(spanOptions, delegate.span(spanOptions), sink.push);    },    ...(delegate.context ? { context: delegate.context } : {}),  });});