UNPKG

autotel

Version:
172 lines (168 loc) 5.46 kB
import { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base'; export { BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { AttributeValue } from '@opentelemetry/api'; import { Logger } from './logger.cjs'; import 'pino'; /** * Canonical Log Line Processor * * Automatically emits spans as canonical log lines (wide events) when they end. * Implements canonical log line" pattern: one comprehensive * event per request with all context. * * When a span ends, this processor creates a log record with ALL span attributes, * making the span itself the canonical log line that can be queried like structured data. * * @example * ```typescript * import { init } from 'autotel'; * * init({ * service: 'my-app', * canonicalLogLines: { * enabled: true, * rootSpansOnly: true, // One canonical log line per request * }, * }); * ``` */ /** * Function to redact sensitive attribute values */ type AttributeRedactorFn = (key: string, value: AttributeValue) => AttributeValue; interface CanonicalLogLineEvent { span: ReadableSpan; level: 'debug' | 'info' | 'warn' | 'error'; message: string; event: Record<string, unknown>; } interface KeepCondition { /** Keep events where HTTP status >= this value. */ status?: number; /** Keep events where duration_ms >= this value. */ durationMs?: number; /** Keep events matching this path pattern (simple prefix match). */ path?: string; } interface CanonicalLogLineOptions { /** Logger to use for emitting canonical log lines (defaults to OTel Logs API) */ logger?: Logger; /** Only emit canonical log lines for root spans (default: false) */ rootSpansOnly?: boolean; /** Minimum log level for canonical log lines (default: 'info') */ minLevel?: 'debug' | 'info' | 'warn' | 'error'; /** Custom message format (default: uses span name) */ messageFormat?: (span: ReadableSpan) => string; /** Whether to include resource attributes (default: true) */ includeResourceAttributes?: boolean; /** * Attribute redactor function to apply before logging. * This ensures sensitive data is redacted in canonical log lines, * matching the behavior of attributeRedactor in init(). */ attributeRedactor?: AttributeRedactorFn; /** Predicate to decide whether to emit (runs after event is built). */ shouldEmit?: (ctx: CanonicalLogLineEvent) => boolean; /** * Declarative tail sampling conditions (OR logic). If any condition matches, * the event is kept. Ignored when `shouldEmit` is provided. * * @example * keep: [{ status: 500 }, { durationMs: 1000 }] */ keep?: KeepCondition[]; /** Callback invoked after emit for custom fan-out. */ drain?: (ctx: CanonicalLogLineEvent) => void | Promise<void>; /** Handler for drain failures. */ onDrainError?: (error: unknown, ctx: CanonicalLogLineEvent) => void; /** * Pretty-print canonical log lines to console in a tree format. * Defaults to true when NODE_ENV is 'development'. */ pretty?: boolean; } /** * Span processor that automatically emits spans as canonical log lines * * When a span ends, this processor creates a log record with ALL span attributes. * This implements the "canonical log line" pattern: one comprehensive event * per request with all context, queryable as structured data. * * **Key Benefits:** * - One log line per request with all context (wide event) * - High-cardinality, high-dimensionality data for powerful queries * - Automatic - no manual logging needed * - Works with any logger or OTel Logs API * * @example Basic usage * ```typescript * import { init } from 'autotel'; * * init({ * service: 'checkout-api', * canonicalLogLines: { * enabled: true, * rootSpansOnly: true, // One canonical log line per request * }, * }); * ``` * * @example With custom logger * ```typescript * import pino from 'pino'; * import { init } from 'autotel'; * * const logger = pino(); * init({ * service: 'my-app', * logger, * canonicalLogLines: { * enabled: true, * logger, // Use Pino for canonical log lines * rootSpansOnly: true, * }, * }); * ``` * * @example Custom message format * ```typescript * init({ * service: 'my-app', * canonicalLogLines: { * enabled: true, * messageFormat: (span) => { * const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS'; * return `${span.name} [${status}]`; * }, * }, * }); * ``` */ declare class CanonicalLogLineProcessor implements SpanProcessor { private logger?; private rootSpansOnly; private minLevel; private messageFormat; private includeResourceAttributes; private attributeRedactor?; private shouldEmit?; private drain?; private onDrainError?; private pretty; private getOTelLogger; constructor(options?: CanonicalLogLineOptions); private buildKeepPredicate; onStart(): void; onEnd(span: ReadableSpan): void; private buildCanonicalLogLine; private redactAttributes; private emitViaLogger; private emitViaOTel; private getLogLevel; private shouldLog; private getSeverityNumber; private reportInternalWarning; forceFlush(): Promise<void>; shutdown(): Promise<void>; } export { type CanonicalLogLineOptions, CanonicalLogLineProcessor };