autotel
Version:
Write Once, Observe Anywhere
1 lines • 26.4 kB
Source Map (JSON)
{"version":3,"file":"canonical-log-line-processor--RlFDHhm.cjs","names":["logs","SeverityNumber"],"sources":["../src/pretty-log-formatter.ts","../src/processors/canonical-log-line-processor.ts"],"sourcesContent":["import type { CanonicalLogLineEvent } from './processors/canonical-log-line-processor';\n\nconst RESET = '\\x1b[0m';\nconst DIM = '\\x1b[2m';\nconst BOLD = '\\x1b[1m';\nconst RED = '\\x1b[31m';\nconst YELLOW = '\\x1b[33m';\nconst GREEN = '\\x1b[32m';\nconst CYAN = '\\x1b[36m';\nconst GRAY = '\\x1b[90m';\n\nconst LEVEL_COLORS: Record<string, string> = {\n debug: GRAY,\n info: GREEN,\n warn: YELLOW,\n error: RED,\n};\n\n/** Internal OTel attributes to skip in pretty output. */\nconst SKIP_PREFIXES = [\n 'telemetry.',\n 'otel.',\n 'process.',\n 'os.',\n 'host.',\n 'service.',\n 'autotel.',\n];\n\nconst SKIP_KEYS = new Set([\n 'operation',\n 'traceId',\n 'spanId',\n 'correlationId',\n 'duration_ms',\n 'duration',\n 'status_code',\n 'status_message',\n 'timestamp',\n 'http.request.method',\n 'url.path',\n 'http.route',\n 'http.response.status_code',\n]);\n\nfunction useColor(): boolean {\n if (typeof process !== 'undefined') {\n if (process.env.NO_COLOR) return false;\n if (process.env.FORCE_COLOR) return true;\n if (process.stdout?.isTTY) return true;\n }\n return false;\n}\n\nfunction c(color: string, text: string): string {\n return useColor() ? `${color}${text}${RESET}` : text;\n}\n\n/**\n * Format milliseconds into a human-readable duration string.\n *\n * @example\n * formatDuration(45) // \"45ms\"\n * formatDuration(1234) // \"1.2s\"\n * formatDuration(65000) // \"1m 5s\"\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60_000) {\n const seconds = ms / 1000;\n return seconds < 10 ? `${seconds.toFixed(1)}s` : `${Math.round(seconds)}s`;\n }\n const minutes = Math.floor(ms / 60_000);\n const seconds = Math.round((ms % 60_000) / 1000);\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n}\n\nfunction formatTime(iso: string): string {\n try {\n const d = new Date(iso);\n return d.toLocaleTimeString('en-GB', { hour12: false });\n } catch {\n return iso.slice(11, 19);\n }\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value);\n if (value == null) return '';\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Group flat dot-notation attributes into a nested tree for pretty display.\n * e.g. { 'user.id': '1', 'user.plan': 'pro' } → { user: { id: '1', plan: 'pro' } }\n */\nfunction groupAttributes(\n event: Record<string, unknown>,\n): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(event)) {\n if (SKIP_KEYS.has(key)) continue;\n if (SKIP_PREFIXES.some((p) => key.startsWith(p))) continue;\n if (value == null || value === '') continue;\n\n const parts = key.split('.');\n if (parts.length === 1) {\n tree[key] = value;\n } else {\n let current = tree;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n if (!(part in current) || typeof current[part] !== 'object') {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n current[parts[parts.length - 1]!] = value;\n }\n }\n\n return tree;\n}\n\nfunction renderTree(\n obj: Record<string, unknown>,\n indent: string,\n isLast: boolean[],\n): string[] {\n const lines: string[] = [];\n const entries = Object.entries(obj);\n\n entries.forEach(([key, value], idx) => {\n const last = idx === entries.length - 1;\n const connector = last ? '\\u2514\\u2500' : '\\u251c\\u2500';\n const prefix = indent + connector + ' ';\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const nested = value as Record<string, unknown>;\n const flatValues = Object.entries(nested).filter(\n ([, v]) => typeof v !== 'object' || v === null,\n );\n const nestedObjs = Object.entries(nested).filter(\n ([, v]) => typeof v === 'object' && v !== null && !Array.isArray(v),\n );\n\n if (nestedObjs.length === 0) {\n const inline = flatValues\n .map(([k, v]) => `${c(CYAN, k)}=${formatValue(v)}`)\n .join(' ');\n lines.push(`${prefix}${c(BOLD, key)}: ${inline}`);\n } else {\n lines.push(`${prefix}${c(BOLD, key)}:`);\n const nextIndent = indent + (last ? ' ' : '\\u2502 ');\n lines.push(...renderTree(nested, nextIndent, [...isLast, last]));\n }\n } else {\n lines.push(`${prefix}${c(CYAN, key)}: ${c(DIM, formatValue(value))}`);\n }\n });\n\n return lines;\n}\n\n/**\n * Format a canonical log line event as a pretty tree for development output.\n */\nexport function formatPrettyLogLine(ctx: CanonicalLogLineEvent): string {\n const { event, level, message } = ctx;\n\n const timestamp = formatTime(String(event.timestamp ?? ''));\n const service = event['service.name'] || event.service || '';\n const method = event['http.request.method'] || '';\n const path = event['http.route'] || event['url.path'] || '';\n const status = event['http.response.status_code'] || event.status_code || '';\n const durationMs = Number(event.duration_ms ?? 0);\n const duration = formatDuration(durationMs);\n\n const levelColor = LEVEL_COLORS[level] ?? '';\n const levelStr = c(levelColor, level.toUpperCase().padEnd(5));\n\n const parts = [c(DIM, timestamp), levelStr];\n if (service) parts.push(c(DIM, `[${service}]`));\n if (method) parts.push(c(BOLD, String(method)));\n if (path) parts.push(String(path));\n if (status) {\n const statusNum = Number(status);\n const statusColor =\n statusNum >= 500 ? RED : statusNum >= 400 ? YELLOW : GREEN;\n parts.push(c(statusColor, String(status)));\n }\n parts.push(c(DIM, `in ${duration}`));\n\n const header = parts.join(' ');\n\n const tree = groupAttributes(event);\n if (Object.keys(tree).length === 0) {\n return header;\n }\n\n const treeLines = renderTree(tree, ' ', []);\n return [header, ...treeLines].join('\\n');\n}\n","/**\n * Canonical Log Line Processor\n *\n * Automatically emits spans as canonical log lines (wide events) when they end.\n * Implements canonical log line\" pattern: one comprehensive\n * event per request with all context.\n *\n * When a span ends, this processor creates a log record with ALL span attributes,\n * making the span itself the canonical log line that can be queried like structured data.\n *\n * @example\n * ```typescript\n * import { init } from 'autotel';\n *\n * init({\n * service: 'my-app',\n * canonicalLogLines: {\n * enabled: true,\n * rootSpansOnly: true, // One canonical log line per request\n * },\n * });\n * ```\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Attributes, AttributeValue } from '@opentelemetry/api';\nimport { logs, SeverityNumber } from '@opentelemetry/api-logs';\nimport type { Logger } from '../logger';\nimport { formatPrettyLogLine, formatDuration } from '../pretty-log-formatter';\n\n/**\n * Function to redact sensitive attribute values\n */\nexport type AttributeRedactorFn = (\n key: string,\n value: AttributeValue,\n) => AttributeValue;\n\nexport interface CanonicalLogLineEvent {\n span: ReadableSpan;\n level: 'debug' | 'info' | 'warn' | 'error';\n message: string;\n event: Record<string, unknown>;\n}\n\nexport interface KeepCondition {\n /** Keep events where HTTP status >= this value. */\n status?: number;\n /** Keep events where duration_ms >= this value. */\n durationMs?: number;\n /** Keep events matching this path pattern (simple prefix match). */\n path?: string;\n}\n\nexport interface CanonicalLogLineOptions {\n /** Logger to use for emitting canonical log lines (defaults to OTel Logs API) */\n logger?: Logger;\n /** Only emit canonical log lines for root spans (default: false) */\n rootSpansOnly?: boolean;\n /** Minimum log level for canonical log lines (default: 'info') */\n minLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Custom message format (default: uses span name) */\n messageFormat?: (span: ReadableSpan) => string;\n /** Whether to include resource attributes (default: true) */\n includeResourceAttributes?: boolean;\n /**\n * Attribute redactor function to apply before logging.\n * This ensures sensitive data is redacted in canonical log lines,\n * matching the behavior of attributeRedactor in init().\n */\n attributeRedactor?: AttributeRedactorFn;\n /** Predicate to decide whether to emit (runs after event is built). */\n shouldEmit?: (ctx: CanonicalLogLineEvent) => boolean;\n /**\n * Declarative tail sampling conditions (OR logic). If any condition matches,\n * the event is kept. Ignored when `shouldEmit` is provided.\n *\n * @example\n * keep: [{ status: 500 }, { durationMs: 1000 }]\n */\n keep?: KeepCondition[];\n /** Callback invoked after emit for custom fan-out. */\n drain?: (ctx: CanonicalLogLineEvent) => void | Promise<void>;\n /** Handler for drain failures. */\n onDrainError?: (error: unknown, ctx: CanonicalLogLineEvent) => void;\n /**\n * Pretty-print canonical log lines to console in a tree format.\n * Defaults to true when NODE_ENV is 'development'.\n */\n pretty?: boolean;\n}\n\n/**\n * Span processor that automatically emits spans as canonical log lines\n *\n * When a span ends, this processor creates a log record with ALL span attributes.\n * This implements the \"canonical log line\" pattern: one comprehensive event\n * per request with all context, queryable as structured data.\n *\n * **Key Benefits:**\n * - One log line per request with all context (wide event)\n * - High-cardinality, high-dimensionality data for powerful queries\n * - Automatic - no manual logging needed\n * - Works with any logger or OTel Logs API\n *\n * @example Basic usage\n * ```typescript\n * import { init } from 'autotel';\n *\n * init({\n * service: 'checkout-api',\n * canonicalLogLines: {\n * enabled: true,\n * rootSpansOnly: true, // One canonical log line per request\n * },\n * });\n * ```\n *\n * @example With custom logger\n * ```typescript\n * import pino from 'pino';\n * import { init } from 'autotel';\n *\n * const logger = pino();\n * init({\n * service: 'my-app',\n * logger,\n * canonicalLogLines: {\n * enabled: true,\n * logger, // Use Pino for canonical log lines\n * rootSpansOnly: true,\n * },\n * });\n * ```\n *\n * @example Custom message format\n * ```typescript\n * init({\n * service: 'my-app',\n * canonicalLogLines: {\n * enabled: true,\n * messageFormat: (span) => {\n * const status = span.status.code === 2 ? 'ERROR' : 'SUCCESS';\n * return `${span.name} [${status}]`;\n * },\n * },\n * });\n * ```\n */\nexport class CanonicalLogLineProcessor implements SpanProcessor {\n private logger?: Logger;\n private rootSpansOnly: boolean;\n private minLevel: 'debug' | 'info' | 'warn' | 'error';\n private messageFormat: (span: ReadableSpan) => string;\n private includeResourceAttributes: boolean;\n private attributeRedactor?: AttributeRedactorFn;\n private shouldEmit?: (ctx: CanonicalLogLineEvent) => boolean;\n private drain?: (ctx: CanonicalLogLineEvent) => void | Promise<void>;\n private onDrainError?: (error: unknown, ctx: CanonicalLogLineEvent) => void;\n private pretty: boolean;\n private getOTelLogger: (() => ReturnType<typeof logs.getLogger>) | null =\n null;\n\n constructor(options: CanonicalLogLineOptions = {}) {\n this.logger = options.logger;\n this.rootSpansOnly = options.rootSpansOnly ?? false;\n this.minLevel = options.minLevel ?? 'info';\n this.messageFormat =\n options.messageFormat ?? ((span) => `[${span.name}] Request completed`);\n this.includeResourceAttributes = options.includeResourceAttributes ?? true;\n this.attributeRedactor = options.attributeRedactor;\n this.shouldEmit =\n options.shouldEmit ?? this.buildKeepPredicate(options.keep);\n this.drain = options.drain;\n this.onDrainError = options.onDrainError;\n this.pretty =\n options.pretty ??\n (typeof process !== 'undefined' &&\n process.env.NODE_ENV === 'development');\n\n if (!this.logger) {\n this.getOTelLogger = () => logs.getLogger('autotel.canonical-log-line');\n }\n }\n\n private buildKeepPredicate(\n keep?: KeepCondition[],\n ): ((ctx: CanonicalLogLineEvent) => boolean) | undefined {\n if (!keep || keep.length === 0) return undefined;\n\n return (ctx: CanonicalLogLineEvent) => {\n return keep.some((condition) => {\n if (condition.status !== undefined) {\n const httpStatus = Number(\n ctx.event['http.response.status_code'] ?? 0,\n );\n if (httpStatus >= condition.status) return true;\n }\n if (\n condition.durationMs !== undefined &&\n Number(ctx.event.duration_ms ?? 0) >= condition.durationMs\n ) {\n return true;\n }\n if (condition.path !== undefined) {\n const route = String(\n ctx.event['http.route'] ?? ctx.event['url.path'] ?? '',\n );\n if (route.startsWith(condition.path)) return true;\n }\n return false;\n });\n };\n }\n\n onStart(): void {\n // No-op\n }\n\n onEnd(span: ReadableSpan): void {\n if (\n this.rootSpansOnly &&\n span.parentSpanContext?.spanId &&\n !span.parentSpanContext.isRemote\n ) {\n return;\n }\n\n const level = this.getLogLevel(span);\n if (!this.shouldLog(level)) {\n return;\n }\n\n const canonicalLogLine = this.buildCanonicalLogLine(span);\n const message = this.messageFormat(span);\n const eventContext: CanonicalLogLineEvent = {\n span,\n level,\n message,\n event: canonicalLogLine,\n };\n\n if (this.shouldEmit && !this.shouldEmit(eventContext)) return;\n\n if (this.pretty) {\n console.log(formatPrettyLogLine(eventContext));\n }\n\n if (this.logger) {\n this.emitViaLogger(level, message, canonicalLogLine);\n } else if (this.getOTelLogger) {\n const otelLogger = this.getOTelLogger();\n this.emitViaOTel(level, message, canonicalLogLine, otelLogger);\n }\n\n if (this.drain) {\n Promise.resolve(this.drain(eventContext)).catch((error) => {\n if (this.onDrainError) {\n this.onDrainError(error, eventContext);\n return;\n }\n this.reportInternalWarning('canonicalLogLines.drain failed', error);\n });\n }\n }\n\n private buildCanonicalLogLine(span: ReadableSpan): Record<string, unknown> {\n const durationMs = span.duration[0] * 1000 + span.duration[1] / 1_000_000;\n const timestamp = new Date(\n span.startTime[0] * 1000 + span.startTime[1] / 1_000_000,\n ).toISOString();\n\n // Span attributes first so core metadata fields below take precedence\n const canonicalLogLine: Record<string, unknown> = {};\n const attributes = this.redactAttributes(span.attributes);\n Object.assign(canonicalLogLine, attributes);\n\n if (this.includeResourceAttributes) {\n const resourceAttrs = this.redactAttributes(\n span.resource.attributes as Attributes,\n );\n Object.assign(canonicalLogLine, resourceAttrs);\n }\n\n canonicalLogLine.operation = span.name;\n canonicalLogLine.traceId = span.spanContext().traceId;\n canonicalLogLine.spanId = span.spanContext().spanId;\n canonicalLogLine.correlationId = span.spanContext().traceId.slice(0, 16);\n canonicalLogLine.duration_ms = Math.round(durationMs * 100) / 100;\n canonicalLogLine.duration = formatDuration(durationMs);\n canonicalLogLine.status_code = span.status.code;\n canonicalLogLine.status_message = span.status.message || undefined;\n canonicalLogLine.timestamp = timestamp;\n\n return canonicalLogLine;\n }\n\n private redactAttributes(attributes: Attributes): Record<string, unknown> {\n if (!this.attributeRedactor) {\n return { ...attributes };\n }\n\n const redacted: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== undefined) {\n redacted[key] = this.attributeRedactor(key, value);\n }\n }\n return redacted;\n }\n\n private emitViaLogger(\n level: 'debug' | 'info' | 'warn' | 'error',\n message: string,\n canonicalLogLine: Record<string, unknown>,\n ): void {\n this.logger;\n }\n\n private emitViaOTel(\n level: 'debug' | 'info' | 'warn' | 'error',\n message: string,\n canonicalLogLine: Record<string, unknown>,\n otelLogger: ReturnType<typeof logs.getLogger>,\n ): void {\n const otelAttributes: Record<string, string | number | boolean> = {};\n for (const [key, value] of Object.entries(canonicalLogLine)) {\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n otelAttributes[key] = value;\n } else if (value !== null && value !== undefined) {\n otelAttributes[key] = String(value);\n }\n }\n otelLogger.emit({\n severityNumber: this.getSeverityNumber(level),\n severityText: level.toUpperCase(),\n body: message,\n attributes: otelAttributes,\n });\n }\n\n private getLogLevel(span: ReadableSpan): 'debug' | 'info' | 'warn' | 'error' {\n const explicitLevel = span.attributes['autotel.log.level'];\n if (\n explicitLevel === 'debug' ||\n explicitLevel === 'info' ||\n explicitLevel === 'warn' ||\n explicitLevel === 'error'\n ) {\n return explicitLevel;\n }\n\n if (span.status.code === 2) return 'error';\n return 'info';\n }\n\n private shouldLog(level: string): boolean {\n const levels = ['debug', 'info', 'warn', 'error'];\n return levels.indexOf(level) >= levels.indexOf(this.minLevel);\n }\n\n private getSeverityNumber(level: string): SeverityNumber {\n const mapping: Record<string, SeverityNumber> = {\n debug: SeverityNumber.DEBUG,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n };\n return mapping[level] ?? SeverityNumber.INFO;\n }\n\n private reportInternalWarning(message: string, error: unknown): void {\n const err =\n error instanceof Error ? error.message : String(error ?? 'unknown error');\n if (this.logger) {\n this.logger.warn({ error: err }, `[autotel] ${message}`);\n return;\n }\n console.warn(`[autotel] ${message}: ${err}`);\n }\n\n async forceFlush(): Promise<void> {\n // No-op\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n}\n"],"mappings":"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"}