UNPKG

autotel

Version:
1,124 lines (1,117 loc) 37 kB
import * as _opentelemetry_sdk_trace_base from '@opentelemetry/sdk-trace-base'; import { SpanProcessor, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { NodeSDKConfiguration, NodeSDK } from '@opentelemetry/sdk-node'; import { Resource } from '@opentelemetry/resources'; import { Sampler, SamplingPreset } from './sampling.cjs'; import { EventSubscriber } from './event-subscriber.cjs'; import { Logger } from './logger.cjs'; import { Attributes } from '@opentelemetry/api'; import { MetricReader } from '@opentelemetry/sdk-metrics'; import { LogRecordProcessor } from '@opentelemetry/sdk-logs'; import { SpanFilterPredicate } from './filtering-span-processor.cjs'; import { SpanNameNormalizerConfig } from './span-name-normalizer.cjs'; import { AttributeRedactorConfig, AttributeRedactorPreset } from './attribute-redacting-processor.cjs'; import { CanonicalLogLineOptions } from './processors.cjs'; /** * Input validation for events events and attributes * * Prevents: * - Invalid event names * - Oversized payloads * - Circular references * - Sensitive data leaks */ interface ValidationConfig { /** Max event name length (default: 100) */ maxEventNameLength: number; /** Max attribute key length (default: 100) */ maxAttributeKeyLength: number; /** Max attribute value length for strings (default: 1000) */ maxAttributeValueLength: number; /** Max total attributes per event (default: 50) */ maxAttributeCount: number; /** Max nesting depth for objects (default: 3) */ maxNestingDepth: number; /** Sensitive field patterns to redact */ sensitivePatterns: RegExp[]; } /** * Events configuration types for trace context, correlation IDs, and enrichment * * @example Basic usage * ```typescript * import { init } from 'autotel'; * * init({ * service: 'my-app', * events: { * includeTraceContext: true, * traceUrl: (ctx) => `https://grafana.internal/explore?traceId=${ctx.traceId}` * } * }); * ``` */ /** * Context passed to the traceUrl function for generating clickable trace URLs */ interface TraceUrlContext { /** Trace ID (32 hex chars) - may be undefined outside a trace */ traceId?: string; /** Span ID (16 hex chars) - may be undefined outside a trace */ spanId?: string; /** Correlation ID (always present, 16 hex chars) */ correlationId: string; /** Service name from init config */ serviceName: string; /** Environment from init config */ environment?: string; } /** * Per-key transform options for baggage enrichment */ type BaggageTransform = 'plain' | 'hash' | ((value: string) => string); /** * Baggage enrichment configuration with guardrails */ interface EnrichFromBaggageConfig { /** * Allowlist of baggage keys to include in events * Supports exact matches and patterns (e.g., 'tenant.*') */ allow: string[]; /** * Optional denylist of baggage keys to exclude * Takes precedence over allow list */ deny?: string[]; /** * Optional prefix to add to all enriched keys * @example 'ctx.' results in 'ctx.tenant.id' */ prefix?: string; /** * Maximum number of keys to include (default: 10) * Prevents payload bloat from excessive baggage */ maxKeys?: number; /** * Maximum total bytes for enriched values (default: 1024) * Prevents payload bloat from large baggage values */ maxBytes?: number; /** * Per-key transform options * - 'plain': Include value as-is * - 'hash': Hash the value (for PII protection) * - function: Custom transform function * * @example * ```typescript * transform: { * 'user.id': 'hash', // Hash user ID for privacy * 'tenant.id': 'plain', // Include tenant ID as-is * 'session.id': (v) => v.slice(0, 8) // Custom truncation * } * ``` */ transform?: Record<string, BaggageTransform>; } /** * Events configuration for trace context and enrichment */ interface EventsConfig { /** * Include trace context in events (default: false) * * When enabled, events automatically include: * - autotel.trace_id (32 hex chars) * - autotel.span_id (16 hex chars) * - autotel.trace_flags (2 hex chars) * - autotel.trace_state (raw tracestate string, if present) * - autotel.correlation_id (always present, 16 hex chars) * * Subscribers map these to platform-specific names: * - PostHog: $trace_id, $span_id * - Mixpanel: trace_id, span_id */ includeTraceContext?: boolean; /** * Include full array of linked trace IDs for batch/fan-in scenarios (default: false) * * When false (default), batch/fan-in events include: * - autotel.linked_trace_id_count: Number of linked parents * - autotel.linked_trace_id_hash: Stable hash of sorted IDs (keeps payload lean) * * When true, events also include: * - autotel.linked_trace_ids: Full array of linked trace IDs */ includeLinkedTraceIds?: boolean; /** * Generate clickable trace URL from context * * @param ctx - Trace context with traceId, spanId, correlationId, serviceName, environment * @returns URL string or undefined to skip * * @example Grafana Tempo * ```typescript * traceUrl: (ctx) => ctx.traceId * ? `https://grafana.internal/explore?traceId=${ctx.traceId}` * : undefined * ``` * * @example Datadog * ```typescript * traceUrl: (ctx) => ctx.traceId * ? `https://app.datadoghq.com/apm/traces?traceId=${ctx.traceId}` * : undefined * ``` * * @example Jaeger * ```typescript * traceUrl: (ctx) => ctx.traceId * ? `https://jaeger.internal/trace/${ctx.traceId}` * : undefined * ``` */ traceUrl?: (ctx: TraceUrlContext) => string | undefined; /** * Auto-enrich events from baggage with guardrails * * Automatically includes baggage entries in events without manual code. * Apply allow/deny lists and per-key transforms for PII protection. * * @example Basic allowlist * ```typescript * enrichFromBaggage: { * allow: ['tenant.id', 'user.id', 'request.id'] * } * // Events include: tenant.id, user.id, request.id from baggage * ``` * * @example With prefix and transforms * ```typescript * enrichFromBaggage: { * allow: ['tenant.id', 'user.id', 'user.email'], * deny: ['user.ssn'], * prefix: 'ctx.', * transform: { * 'user.id': 'hash', * 'user.email': 'hash' * } * } * // Events include: ctx.tenant.id, ctx.user.id (hashed), ctx.user.email (hashed) * ``` */ enrichFromBaggage?: EnrichFromBaggageConfig; } interface AutotelDevtoolsConfig { enabled?: boolean; endpoint?: string; embedded?: boolean; host?: string; port?: number; verbose?: boolean; } interface AutotelConfig { /** Service name (required) */ service: string; /** * Local developer UX for autotel-devtools. * * - `true`: send traces, metrics, and logs to `http://127.0.0.1:4318` * - `{ embedded: true }`: attempt to start `autotel-devtools` automatically * * When enabled: * - `endpoint` defaults to the local devtools URL * - `logs` default to `true` unless explicitly set * * This keeps production config unchanged while making local debugging * effectively zero-config. */ devtools?: boolean | AutotelDevtoolsConfig; /** Event subscribers - bring your own (PostHog, Mixpanel, etc.) */ subscribers?: EventSubscriber[]; /** * Additional OpenTelemetry instrumentations to register (raw OTel classes). * Useful when you need custom instrumentation configs or instrumentations * not covered by autoInstrumentations. * * **Important:** If you need custom instrumentation configs (like `requireParentSpan: false`), * use EITHER manual instrumentations OR autoInstrumentations, not both for the same library. * Manual instrumentations always take precedence over auto-instrumentations. * * @example Manual instrumentations with custom config * ```typescript * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb' * * init({ * service: 'my-app', * autoInstrumentations: false, // Disable auto-instrumentations * instrumentations: [ * new MongoDBInstrumentation({ * requireParentSpan: false // Custom config * }) * ] * }) * ``` * * @example Mix auto + manual (auto for most, manual for specific configs) * ```typescript * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb' * * init({ * service: 'my-app', * autoInstrumentations: ['http', 'express'], // Auto for these * instrumentations: [ * new MongoDBInstrumentation({ * requireParentSpan: false // Manual config for MongoDB * }) * ] * }) * ``` */ instrumentations?: NodeSDKConfiguration['instrumentations']; /** * Simple names for auto-instrumentation. * Uses @opentelemetry/auto-instrumentations-node (peer dependency). * * **Important:** If you provide manual instrumentations for the same library, * the manual config takes precedence and auto-instrumentation for that library is disabled. * * @example Enable all auto-instrumentations (simple approach) * ```typescript * init({ * service: 'my-app', * autoInstrumentations: true // Enable all with defaults * }) * ``` * * @example Enable specific auto-instrumentations * ```typescript * init({ * service: 'my-app', * autoInstrumentations: ['express', 'pino', 'http'] * }) * ``` * * @example Configure specific auto-instrumentations * ```typescript * init({ * service: 'my-app', * autoInstrumentations: { * express: { enabled: true }, * pino: { enabled: true }, * http: { enabled: false } * } * }) * ``` * * @example Manual config when you need custom settings * ```typescript * import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb' * * init({ * service: 'my-app', * autoInstrumentations: false, // Use manual control * instrumentations: [ * new MongoDBInstrumentation({ * requireParentSpan: false // Custom config not available with auto * }) * ] * }) * ``` */ autoInstrumentations?: string[] | boolean | Record<string, { enabled?: boolean; }>; /** * OTLP endpoint for traces/metrics/logs * Only used if you don't provide custom exporters/processors * @default process.env.OTLP_ENDPOINT || 'http://localhost:4318' */ endpoint?: string; /** * Custom span processors for traces (supports multiple processors) * Allows you to use any backend: Jaeger, Zipkin, Datadog, New Relic, etc. * If not provided, defaults to OTLP with tail sampling * * @example Multiple processors * ```typescript * import { JaegerExporter } from '@opentelemetry/exporter-jaeger' * import { BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base' * * init({ * service: 'my-app', * spanProcessors: [ * new BatchSpanProcessor(new JaegerExporter()), * new SimpleSpanProcessor(new ConsoleSpanExporter()) // Debug alongside production * ] * }) * ``` * * @example Single processor * ```typescript * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base' * * init({ * service: 'my-app', * spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())] * }) * ``` */ spanProcessors?: SpanProcessor[]; /** * Custom span exporters for traces (alternative to spanProcessors, supports multiple exporters) * Provide either spanProcessors OR spanExporters, not both * Each exporter will be wrapped in TailSamplingSpanProcessor + BatchSpanProcessor * * @example Multiple exporters * ```typescript * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' * import { JaegerExporter } from '@opentelemetry/exporter-jaeger' * * init({ * service: 'my-app', * spanExporters: [ * new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' }), * new JaegerExporter() // Send to multiple backends simultaneously * ] * }) * ``` * * @example Single exporter * ```typescript * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' * * init({ * service: 'my-app', * spanExporters: [new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })] * }) * ``` */ spanExporters?: SpanExporter[]; /** * Custom metric readers (supports multiple readers) * Allows sending metrics to multiple backends: OTLP, Prometheus, custom readers * Defaults to OTLP metrics exporter when metrics are enabled. * * @example Multiple metric readers * ```typescript * import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics' * import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http' * import { PrometheusExporter } from '@opentelemetry/exporter-prometheus' * * init({ * service: 'my-app', * metricReaders: [ * new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter() }), * new PrometheusExporter() // Export to multiple backends * ] * }) * ``` */ metricReaders?: MetricReader[]; /** * Custom log record processors. When omitted, logs are not configured. */ logRecordProcessors?: LogRecordProcessor[]; /** * PostHog integration - auto-configures OTLP log exporter. * * @example * ```typescript * init({ * service: 'my-app', * posthog: { url: 'https://us.i.posthog.com/i/v1/logs?token=phc_xxx' } * }); * ``` * * Also reads from POSTHOG_LOGS_URL environment variable as fallback. */ posthog?: { url: string; }; /** Additional resource attributes to merge with defaults. */ resourceAttributes?: Attributes; /** Provide a fully custom Resource to merge (advanced use case). */ resource?: Resource; /** * Headers for OTLP exporters. Accepts either an object map or * a "key=value" comma separated string. * * @example * ```typescript * init({ * service: 'my-app', * endpoint: 'https://api.honeycomb.io', * headers: { 'x-honeycomb-team': 'YOUR_API_KEY' } * }) * ``` */ headers?: Record<string, string> | string; /** * OTLP protocol to use for traces, metrics, and logs * - 'http': HTTP/protobuf (default, uses port 4318) * - 'grpc': gRPC (uses port 4317) * * Can be overridden with OTEL_EXPORTER_OTLP_PROTOCOL env var. * * Note: gRPC exporters are optional peer dependencies. Install them with: * ```bash * pnpm add @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc * ``` * * @example HTTP (default) * ```typescript * init({ * service: 'my-app', * protocol: 'http', // or omit (defaults to http) * endpoint: 'http://localhost:4318' * }) * ``` * * @example gRPC * ```typescript * init({ * service: 'my-app', * protocol: 'grpc', * endpoint: 'grpc://localhost:4317' * }) * ``` * * @default 'http' */ protocol?: 'http' | 'grpc'; /** * Optional factory to build a customised NodeSDK instance from our defaults. */ sdkFactory?: (defaults: Partial<NodeSDKConfiguration>) => NodeSDK; /** * Infrastructure metrics configuration * - true: always enabled (default) * - false: always disabled * - 'auto': always enabled (same as true) * * Can be overridden with AUTOTEL_METRICS=on|off env var */ metrics?: boolean | 'auto'; /** * OTLP logs configuration * - true: auto-configure OTLP log exporter from endpoint * - false: disabled (default) * - 'auto': same as false (opt-in only) * * When enabled and an endpoint is configured, autotel will automatically * create a BatchLogRecordProcessor with an OTLPLogExporter - no manual * imports needed. Works alongside logRecordProcessors (additive). * * Requires @opentelemetry/sdk-logs and @opentelemetry/exporter-logs-otlp-http * (or -grpc) as peer dependencies. * * Can be overridden with AUTOTEL_LOGS=on|off env var. * * @example * ```typescript * init({ * service: 'my-app', * endpoint: 'http://localhost:4318', * logs: true, * }); * ``` */ logs?: boolean | 'auto'; /** Sampling strategy - takes precedence over `sampling` preset */ sampler?: Sampler; /** * Sampling preset shorthand — resolves to a pre-configured sampler. * If both `sampler` and `sampling` are provided, `sampler` takes precedence. * * @default 'production' */ sampling?: SamplingPreset; /** Service version (default: auto-detect from package.json or '1.0.0') */ version?: string; /** Environment (default: process.env.NODE_ENV || 'development') */ environment?: string; /** * Logger instance for internal autotel diagnostic messages * * This logger is used by autotel internally to log initialization, warnings, * and debug information. Any logger with info/warn/error/debug methods works. * * **For OTel instrumentation of your application logs**, use the `autoInstrumentations` option: * - `autoInstrumentations: ['pino']` - Injects traceId/spanId into Pino logs * - `autoInstrumentations: ['winston']` - Injects traceId/spanId into Winston logs * * Default: silent logger (no-op) * * @example Pino with OTel instrumentation * ```typescript * import pino from 'pino' * import { init } from 'autotel' * * const logger = pino({ level: 'info' }) * init({ * service: 'my-app', * logger, // For autotel's internal logs * autoInstrumentations: ['pino'] // For OTel trace context in YOUR logs * }) * ``` * * @example Custom logger for autotel diagnostics * ```typescript * const logger = { * info: (msg, extra) => console.log(msg, extra), * warn: (msg, extra) => console.warn(msg, extra), * error: (msg, err, extra) => console.error(msg, err, extra), * debug: (msg, extra) => console.debug(msg, extra), * } * init({ service: 'my-app', logger }) * ``` */ logger?: Logger; /** * Flush events queue when root spans end * - true: Flush on root span completion (default) * - false: Use batching (events flush every 10 seconds automatically) * * Only flushes on root spans to avoid excessive network calls. * Default is true for serverless/short-lived processes. Set to false * for long-running services where batching is more efficient. */ flushOnRootSpanEnd?: boolean; /** * Force-flush OpenTelemetry spans on shutdown (default: false) * * When enabled, spans are force-flushed along with events on root * span completion. This is useful for serverless/short-lived processes where * spans may not export before the process ends. * * - true: Force-flush spans on root span completion (~50-200ms latency) * - false: Spans export via normal batch processor (default behavior) * * Only applies when flushOnRootSpanEnd is also enabled. * * Note: For edge runtimes (Cloudflare Workers, Vercel Edge), use the * 'autotel-edge' package instead, which handles this automatically. * * @example Serverless with force-flush * ```typescript * init({ * service: 'my-lambda', * flushOnRootSpanEnd: true, * forceFlushOnShutdown: true, // Force-flush spans * }); * ``` */ forceFlushOnShutdown?: boolean; /** * Automatically copy baggage entries to span attributes * * When enabled, all baggage entries are automatically added as span attributes, * making them visible in trace UIs (Jaeger, Grafana, DataDog, etc.) without * manually calling ctx.setAttribute() for each entry. * * - `true`: adds baggage with 'baggage.' prefix (e.g. baggage.tenant.id) * - `string`: uses custom prefix (e.g. 'ctx' → ctx.tenant.id, '' → tenant.id) * - `false` or omit: disabled (default) * * @default false * * @example Enable with default prefix * ```typescript * init({ * service: 'my-app', * baggage: true * }); * * // Now baggage automatically appears as span attributes * await withBaggage({ * baggage: { 'tenant.id': 't1', 'user.id': 'u1' }, * fn: async () => { * // Span has baggage.tenant.id and baggage.user.id attributes! * } * }); * ``` * * @example Custom prefix * ```typescript * init({ * service: 'my-app', * baggage: 'ctx' // Uses 'ctx.' prefix * }); * // Creates attributes: ctx.tenant.id, ctx.user.id * ``` * * @example No prefix * ```typescript * init({ * service: 'my-app', * baggage: '' // No prefix * }); * // Creates attributes: tenant.id, user.id * ``` */ baggage?: boolean | string; /** * Validation configuration for events events * - Override default sensitive field patterns for redaction * - Customize max lengths, nesting depth, etc. * * @example Disable redaction for development * ```typescript * init({ * service: 'my-app', * validation: { * sensitivePatterns: [] // Disable all redaction * } * }) * ``` * * @example Add custom patterns * ```typescript * init({ * service: 'my-app', * validation: { * sensitivePatterns: [ * /password/i, * /apiKey/i, * /customSecret/i // Your custom pattern * ] * } * }) * ``` */ validation?: Partial<ValidationConfig>; /** * Events configuration for trace context, correlation IDs, and enrichment * * Controls how product events integrate with distributed tracing: * - `includeTraceContext`: Automatically include trace context in events * - `includeLinkedTraceIds`: Include full array of linked trace IDs (for batch/fan-in) * - `traceUrl`: Generate clickable trace URLs in events * - `enrichFromBaggage`: Auto-enrich events from baggage with guardrails * * @example Basic trace context * ```typescript * init({ * service: 'my-app', * events: { * includeTraceContext: true * } * }); * // Events now include autotel.trace_id, autotel.span_id, autotel.correlation_id * ``` * * @example With clickable trace URLs * ```typescript * init({ * service: 'my-app', * events: { * includeTraceContext: true, * traceUrl: (ctx) => `https://grafana.internal/explore?traceId=${ctx.traceId}` * } * }); * ``` * * @example With baggage enrichment * ```typescript * init({ * service: 'my-app', * events: { * includeTraceContext: true, * enrichFromBaggage: { * allow: ['tenant.id', 'user.id'], * prefix: 'ctx.', * maxKeys: 10, * maxBytes: 1024 * } * } * }); * ``` */ events?: EventsConfig; /** * Debug mode for local span inspection. * Enables console output to help you see spans as they're created. * * - `true`: Raw JSON output (ConsoleSpanExporter) * - `'pretty'`: Colorized, hierarchical output (PrettyConsoleExporter) * - `false`/undefined: No console output (default) * * When enabled: Outputs spans to console AND sends to backend (if endpoint/exporter configured) * * Perfect for progressive development: * - Start with debug: 'pretty' (no endpoint) → see traces immediately with nice formatting * - Add endpoint later → console + backend, verify before choosing provider * - Remove debug in production → backend only, clean production config * * Can be overridden with AUTOTEL_DEBUG environment variable. * * @example Pretty debug output (recommended for development) * ```typescript * init({ * service: 'my-app', * debug: 'pretty' // Colorized, hierarchical output * }) * ``` * * @example Raw JSON output (verbose) * ```typescript * init({ * service: 'my-app', * debug: true // Raw ConsoleSpanExporter output * }) * ``` * * @example Environment variable * ```bash * AUTOTEL_DEBUG=pretty node server.js * AUTOTEL_DEBUG=true node server.js * ``` */ debug?: boolean | 'pretty'; /** * Filter predicate to drop unwanted spans before processing. * * Useful for filtering out noisy spans from specific instrumentations * (e.g., Next.js internal spans, health check endpoints). * * The filter runs on completed spans (onEnd), so you have access to: * - `span.name` - Span name * - `span.attributes` - All span attributes * - `span.instrumentationScope` - `{ name, version }` of the instrumentation * - `span.status` - Span status code and message * - `span.duration` - Span duration as `[seconds, nanoseconds]` * * Return `true` to keep the span, `false` to drop it. * * @example Filter out Next.js instrumentation spans * ```typescript * init({ * service: 'my-app', * spanFilter: (span) => span.instrumentationScope.name !== 'next.js' * }) * ``` * * @example Filter out health check spans * ```typescript * init({ * service: 'my-app', * spanFilter: (span) => !span.name.includes('/health') * }) * ``` * * @example Complex filtering (multiple conditions) * ```typescript * init({ * service: 'my-app', * spanFilter: (span) => { * // Drop Next.js internal spans * if (span.instrumentationScope.name === 'next.js') return false; * // Drop health checks * if (span.name.includes('/health')) return false; * // Drop very short spans (less than 1ms) * const [secs, nanos] = span.duration; * if (secs === 0 && nanos < 1_000_000) return false; * return true; * } * }) * ``` */ spanFilter?: SpanFilterPredicate; /** * Normalize span names to reduce cardinality from dynamic path segments. * * High-cardinality span names (e.g., `/users/123/posts/456`) cause issues: * - Cost explosions in observability backends * - Cardinality limits exceeded * - Poor UX when searching/filtering traces * * The normalizer transforms dynamic segments into placeholders: * - `/users/123` → `/users/:id` * - `/items/550e8400-e29b-...` → `/items/:uuid` * * Provide either a custom function or use a built-in preset: * - `'rest-api'` - Numeric IDs, UUIDs, ObjectIds, dates, timestamps, emails * - `'graphql'` - GraphQL operation name normalization * - `'minimal'` - Only numeric IDs and UUIDs * * @example Custom normalizer function * ```typescript * init({ * service: 'my-app', * spanNameNormalizer: (name) => { * return name * .replace(/\/[0-9]+/g, '/:id') * .replace(/\/[a-f0-9-]{36}/gi, '/:uuid'); * } * }) * ``` * * @example Using built-in preset * ```typescript * init({ * service: 'my-app', * spanNameNormalizer: 'rest-api' * }) * ``` * * @example Combining with spanFilter * ```typescript * init({ * service: 'my-app', * spanNameNormalizer: 'rest-api', * spanFilter: (span) => span.instrumentationScope.name !== 'next.js' * }) * ``` */ spanNameNormalizer?: SpanNameNormalizerConfig; /** * Automatically redact PII and sensitive data from span attributes before export. * Critical for compliance (GDPR, PCI-DSS, HIPAA) and data security. * * Can be a preset name or custom configuration: * - `'default'`: Emails, phones, SSNs, credit cards, sensitive keys (password, secret, token) * - `'strict'`: Default + Bearer tokens, JWTs, API keys in values * - `'pci-dss'`: Payment card industry focus (credit cards, CVV, card-related keys) * * @example Use default preset * ```typescript * init({ * service: 'my-app', * attributeRedactor: 'default' * }) * ``` * * @example Custom patterns * ```typescript * init({ * service: 'my-app', * attributeRedactor: { * keyPatterns: [/password/i, /secret/i], * valuePatterns: [ * { name: 'customerId', pattern: /CUST-\d{8}/g, replacement: 'CUST-***' } * ] * } * }) * ``` * * @example Custom redactor function * ```typescript * init({ * service: 'my-app', * attributeRedactor: { * redactor: (key, value) => { * if (key === 'user.email' && typeof value === 'string') { * return value.replace(/@.+/, '@[REDACTED]'); * } * return value; * } * } * }) * ``` */ attributeRedactor?: AttributeRedactorConfig | AttributeRedactorPreset; /** * OpenLLMetry integration for LLM observability. * Requires @traceloop/node-server-sdk as an optional peer dependency. * * @example Enable OpenLLMetry with default settings * ```typescript * init({ * service: 'my-app', * openllmetry: { enabled: true } * }) * ``` * * @example Enable with custom options * ```typescript * init({ * service: 'my-app', * openllmetry: { * enabled: true, * options: { * disableBatch: process.env.NODE_ENV !== 'production', * apiKey: process.env.TRACELOOP_API_KEY * } * } * }) * ``` */ openllmetry?: { enabled: boolean; options?: Record<string, unknown>; }; /** * Canonical log lines - automatically emit spans as wide events (canonical log lines) * * When enabled, each span (or root span only) is automatically emitted as a * comprehensive log record with ALL span attributes. This implements the * "canonical log line" pattern: one comprehensive event per request with all context. * * **Benefits:** * - One log line per request with all context (wide event) * - High-cardinality, high-dimensionality data for powerful queries * - Automatic - no manual logging needed * - Queryable as structured data instead of string search * * @example Basic usage (one canonical log line per request) * ```typescript * init({ * service: 'checkout-api', * canonicalLogLines: { * enabled: true, * rootSpansOnly: true, // One canonical log line per request * }, * }); * ``` * * @example With custom logger * ```typescript * import pino from 'pino'; * 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}]`; * }, * }, * }); * ``` */ canonicalLogLines?: { enabled: boolean; /** 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: _opentelemetry_sdk_trace_base.ReadableSpan) => string; /** Whether to include resource attributes (default: true) */ includeResourceAttributes?: boolean; /** Predicate to decide whether to emit (runs after event is built). */ shouldEmit?: CanonicalLogLineOptions['shouldEmit']; /** * Declarative tail sampling conditions (OR logic). * Ignored when `shouldEmit` is provided. * @example keep: [{ status: 500 }, { durationMs: 1000 }] */ keep?: CanonicalLogLineOptions['keep']; /** Callback invoked after emit for custom fan-out. */ drain?: CanonicalLogLineOptions['drain']; /** Handler for drain failures. */ onDrainError?: CanonicalLogLineOptions['onDrainError']; /** * Pretty-print canonical log lines to console. * Defaults to true when NODE_ENV is 'development'. */ pretty?: boolean; }; /** * Suppress console output while keeping OTel exporters running. * Useful for platforms like GCP Cloud Run / AWS Lambda where stdout * is managed externally by the platform's log collector. * * @default false */ silent?: boolean; /** * Minimum log level for internal autotel diagnostic messages. * Messages below this level are dropped before processing. * * @default 'info' */ minLevel?: 'debug' | 'info' | 'warn' | 'error'; } /** * Lock the logger to prevent further `init()` calls. * Use this when framework plugins set up instrumentation and you want * to prevent accidental re-initialization from user code. */ declare function lockLogger(): void; /** * Check if the logger has been locked. */ declare function isLoggerLocked(): boolean; /** * Initialize autotel - Write Once, Observe Everywhere * * Follows OpenTelemetry standards: opinionated defaults with full flexibility * Idempotent: multiple calls are safe, last one wins * * @example Minimal setup (OTLP default) * ```typescript * init({ service: 'my-app' }) * ``` * * @example With events (observe in PostHog, Mixpanel, etc.) * ```typescript * import { PostHogSubscriber } from 'autotel-subscribers/posthog'; * * init({ * service: 'my-app', * subscribers: [new PostHogSubscriber({ apiKey: '...' })] * }) * ``` * * @example Observe in Jaeger * ```typescript * import { JaegerExporter } from '@opentelemetry/exporter-jaeger' * * init({ * service: 'my-app', * spanExporter: new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces' }) * }) * ``` * * @example Observe in Zipkin * ```typescript * import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' * * init({ * service: 'my-app', * spanExporter: new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' }) * }) * ``` * * @example Observe in Datadog * ```typescript * import { DatadogSpanProcessor } from '@opentelemetry/exporter-datadog' * * init({ * service: 'my-app', * spanProcessor: new DatadogSpanProcessor({ ... }) * }) * ``` * * @example Console output (dev) * ```typescript * import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base' * * init({ * service: 'my-app', * spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter()) * }) * ``` */ declare function init(cfg: AutotelConfig): void; export { type AutotelConfig as A, isLoggerLocked as a, init as i, lockLogger as l };