UNPKG

autotel

Version:
1,031 lines (1,023 loc) 35.1 kB
import { loadYamlConfig } from './chunk-3SDILILG.js'; import { TailSamplingSpanProcessor } from './chunk-A4E5AQFK.js'; import { FilteringSpanProcessor } from './chunk-WGWSHJ2N.js'; import { SpanNameNormalizingProcessor } from './chunk-GYR5K654.js'; import { REDACTOR_PRESETS, normalizeAttributeRedactorConfig, AttributeRedactingProcessor } from './chunk-TDNKIHKT.js'; import { PrettyConsoleExporter } from './chunk-6UQRVUN3.js'; import { CanonicalLogLineProcessor } from './chunk-3QXBFGKP.js'; import { requireModule, safeRequire } from './chunk-33WTKH7X.js'; import { resolveSamplingPreset, samplingPresets } from './chunk-DPSA4QLA.js'; import { propagation, context } from '@opentelemetry/api'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter, SamplingDecision, ParentBasedSampler, TraceIdRatioBasedSampler, AlwaysOffSampler, AlwaysOnSampler } from '@opentelemetry/sdk-trace-base'; import { resourceFromAttributes } from '@opentelemetry/resources'; import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'; var BaggageSpanProcessor = class { prefix; constructor(options = {}) { this.prefix = options.prefix ?? "baggage."; } onStart(span, parentContext) { let baggage = propagation.getBaggage(parentContext); if (!baggage) { baggage = propagation.getBaggage(context.active()); } if (!baggage) { try { const { getActiveContextWithBaggage } = requireModule("./trace-context"); const storedContext = getActiveContextWithBaggage(); baggage = propagation.getBaggage(storedContext); } catch { } } if (!baggage) return; for (const [key, entry] of baggage.getAllEntries()) { span.setAttribute(`${this.prefix}${key}`, entry.value); } } // eslint-disable-next-line @typescript-eslint/no-unused-vars onEnd(_span) { } async shutdown() { } async forceFlush() { } }; // src/redact-values.ts function createStringRedactor(config2) { const resolved = typeof config2 === "string" ? REDACTOR_PRESETS[config2] : config2; const valuePatterns = resolved.valuePatterns ?? []; const defaultReplacement = resolved.replacement ?? "[REDACTED]"; return (value) => { let result = value; for (const { pattern, replacement } of valuePatterns) { pattern.lastIndex = 0; result = result.replaceAll(pattern, replacement ?? defaultReplacement); } return result; }; } // src/posthog-logs.ts var RedactingLogRecordProcessor = class { constructor(wrapped, redact) { this.wrapped = wrapped; this.redact = redact; } wrapped; redact; onEmit(logRecord, context) { if (logRecord.body && typeof logRecord.body === "string") { logRecord.body = this.redact(logRecord.body); } if (logRecord.attributes) { for (const [key, value] of Object.entries(logRecord.attributes)) { if (typeof value === "string") { logRecord.attributes[key] = this.redact(value); } else if (Array.isArray(value)) { logRecord.attributes[key] = value.map( (item) => typeof item === "string" ? this.redact(item) : item ); } } } this.wrapped.onEmit(logRecord, context); } shutdown() { return this.wrapped.shutdown(); } forceFlush() { return this.wrapped.forceFlush(); } }; function buildPostHogLogProcessors(config2, stringRedactor) { const url = config2?.url || process.env.POSTHOG_LOGS_URL; if (!url) return []; const sdkLogs = safeRequire("@opentelemetry/sdk-logs"); const exporterModule = safeRequire("@opentelemetry/exporter-logs-otlp-http"); if (!sdkLogs || !exporterModule) return []; const exporter = new exporterModule.OTLPLogExporter({ url }); let processor = new sdkLogs.BatchLogRecordProcessor( exporter ); if (stringRedactor) { processor = new RedactingLogRecordProcessor(processor, stringRedactor); } return [processor]; } function isValidUrl(urlString) { try { const url = new URL(urlString); return url.protocol === "http:" || url.protocol === "https:"; } catch { return false; } } function resolveOtelEnv() { const env = {}; if (process.env.OTEL_SERVICE_NAME) { const value = process.env.OTEL_SERVICE_NAME.trim(); if (value) { env.OTEL_SERVICE_NAME = value; } } if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) { const value = process.env.OTEL_EXPORTER_OTLP_ENDPOINT.trim(); if (value && isValidUrl(value)) { env.OTEL_EXPORTER_OTLP_ENDPOINT = value; } } if (process.env.OTEL_EXPORTER_OTLP_HEADERS) { const value = process.env.OTEL_EXPORTER_OTLP_HEADERS.trim(); if (value) { env.OTEL_EXPORTER_OTLP_HEADERS = value; } } if (process.env.OTEL_RESOURCE_ATTRIBUTES) { const value = process.env.OTEL_RESOURCE_ATTRIBUTES.trim(); if (value) { env.OTEL_RESOURCE_ATTRIBUTES = value; } } if (process.env.OTEL_EXPORTER_OTLP_PROTOCOL) { const value = process.env.OTEL_EXPORTER_OTLP_PROTOCOL.trim().toLowerCase(); if (value === "http" || value === "grpc") { env.OTEL_EXPORTER_OTLP_PROTOCOL = value; } } if (process.env.OTEL_TRACES_SAMPLER) { const value = process.env.OTEL_TRACES_SAMPLER.trim(); if (value) { env.OTEL_TRACES_SAMPLER = value; } } if (process.env.OTEL_TRACES_SAMPLER_ARG) { const value = process.env.OTEL_TRACES_SAMPLER_ARG.trim(); if (value) { env.OTEL_TRACES_SAMPLER_ARG = value; } } return env; } function parseRatioSamplerArg(samplerName, samplerArg) { if (samplerArg === void 0) { return 1; } const ratio = Number(samplerArg); if (!Number.isFinite(ratio) || ratio < 0 || ratio > 1) { console.error( `[autotel] Invalid OTEL_TRACES_SAMPLER_ARG="${samplerArg}" for ${samplerName}. Expected a number in [0..1]. Falling back to 1.0.` ); return 1; } return ratio; } function warnOnUnusedSamplerArg(samplerName, samplerArg) { if (samplerArg !== void 0) { console.error( `[autotel] OTEL_TRACES_SAMPLER_ARG is not used by OTEL_TRACES_SAMPLER="${samplerName}". Ignoring value "${samplerArg}".` ); } } function createSamplerFromEnv(env) { const samplerName = env.OTEL_TRACES_SAMPLER; if (!samplerName) { return void 0; } switch (samplerName) { case "always_on": warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG); return new AlwaysOnSampler(); case "always_off": warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG); return new AlwaysOffSampler(); case "traceidratio": return new TraceIdRatioBasedSampler( parseRatioSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG) ); case "parentbased_always_on": warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG); return new ParentBasedSampler({ root: new AlwaysOnSampler() }); case "parentbased_always_off": warnOnUnusedSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG); return new ParentBasedSampler({ root: new AlwaysOffSampler() }); case "parentbased_traceidratio": return new ParentBasedSampler({ root: new TraceIdRatioBasedSampler( parseRatioSamplerArg(samplerName, env.OTEL_TRACES_SAMPLER_ARG) ) }); case "jaeger_remote": case "parentbased_jaeger_remote": case "xray": console.error( `[autotel] OTEL_TRACES_SAMPLER="${samplerName}" is not supported yet by autotel. Falling back to the next sampler source.` ); return void 0; default: console.error( `[autotel] Unknown OTEL_TRACES_SAMPLER="${samplerName}". Falling back to the next sampler source.` ); return void 0; } } function parseResourceAttributes(input) { if (!input || input.trim() === "") { return {}; } const attributes = {}; const pairs = input.split(","); for (const pair of pairs) { const trimmedPair = pair.trim(); if (!trimmedPair) continue; const equalIndex = trimmedPair.indexOf("="); if (equalIndex === -1) { continue; } const key = trimmedPair.slice(0, equalIndex).trim(); const value = trimmedPair.slice(equalIndex + 1).trim(); if (key && value) { attributes[key] = value; } } return attributes; } function parseOtlpHeaders(input) { if (!input || input.trim() === "") { return {}; } const headers = {}; const pairs = input.split(","); for (const pair of pairs) { const trimmedPair = pair.trim(); if (!trimmedPair) continue; const equalIndex = trimmedPair.indexOf("="); if (equalIndex === -1) { continue; } const key = trimmedPair.slice(0, equalIndex).trim(); const value = trimmedPair.slice(equalIndex + 1).trim(); if (key && value) { headers[key] = value; } } return headers; } function envToConfig(env) { const config2 = {}; if (env.OTEL_SERVICE_NAME) { config2.service = env.OTEL_SERVICE_NAME; } if (env.OTEL_EXPORTER_OTLP_ENDPOINT) { config2.endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT; } if (env.OTEL_EXPORTER_OTLP_PROTOCOL) { config2.protocol = env.OTEL_EXPORTER_OTLP_PROTOCOL; } if (env.OTEL_EXPORTER_OTLP_HEADERS) { config2.headers = parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS); } const resourceAttrs = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES); if (Object.keys(resourceAttrs).length > 0) { config2.resourceAttributes = resourceAttrs; } const sampler = createSamplerFromEnv(env); if (sampler) { config2.otelSampler = sampler; } return config2; } function resolveConfigFromEnv() { const env = resolveOtelEnv(); return envToConfig(env); } // src/devtools.ts var defaultHost = "127.0.0.1"; var defaultPort = 4318; function resolveDevtoolsConfig(config2) { if (!config2) { return { enabled: false, endpoint: void 0, embedded: false, host: defaultHost, port: defaultPort, verbose: false }; } if (config2 === true) { return { enabled: true, endpoint: `http://${defaultHost}:${defaultPort}`, embedded: false, host: defaultHost, port: defaultPort, verbose: false }; } const enabled = config2.enabled ?? true; const host = config2.host ?? defaultHost; const port = config2.port ?? defaultPort; const endpoint = config2.endpoint ?? `http://${host}:${port}`; return { enabled, endpoint: enabled ? endpoint : void 0, embedded: enabled && (config2.embedded ?? false), host, port, verbose: config2.verbose ?? false }; } // src/init.ts var silentLogger = { info: () => { }, warn: () => { }, error: () => { }, debug: () => { } }; function toOtelSampler(sampler) { return { shouldSample(_context, _traceId, spanName, _spanKind, _attributes, links) { const shouldTrace = sampler.shouldSample({ operationName: spanName, args: [], links }); return { decision: shouldTrace ? SamplingDecision.RECORD_AND_SAMPLED : SamplingDecision.NOT_RECORD }; }, toString() { return `AutotelSamplerAdapter`; } }; } var OTLPTraceExporterGRPC; var OTLPMetricExporterGRPC; var OTLPLogExporterGRPC; function loadGRPCTraceExporter() { if (OTLPTraceExporterGRPC) return OTLPTraceExporterGRPC; try { const grpcModule = requireModule("@opentelemetry/exporter-trace-otlp-grpc"); OTLPTraceExporterGRPC = grpcModule.OTLPTraceExporter; return OTLPTraceExporterGRPC; } catch { throw new Error( "gRPC trace exporter not found. Install @opentelemetry/exporter-trace-otlp-grpc" ); } } function loadGRPCMetricExporter() { if (OTLPMetricExporterGRPC) return OTLPMetricExporterGRPC; try { const grpcModule = requireModule("@opentelemetry/exporter-metrics-otlp-grpc"); OTLPMetricExporterGRPC = grpcModule.OTLPMetricExporter; return OTLPMetricExporterGRPC; } catch { throw new Error( "gRPC metric exporter not found. Install @opentelemetry/exporter-metrics-otlp-grpc" ); } } function createTraceExporter(protocol, config2) { if (protocol === "grpc") { const Exporter = loadGRPCTraceExporter(); return new Exporter(config2); } return new OTLPTraceExporter(config2); } function createMetricExporter(protocol, config2) { if (protocol === "grpc") { const Exporter = loadGRPCMetricExporter(); return new Exporter(config2); } return new OTLPMetricExporter(config2); } function loadGRPCLogExporter() { if (OTLPLogExporterGRPC) return OTLPLogExporterGRPC; try { const grpcModule = requireModule("@opentelemetry/exporter-logs-otlp-grpc"); OTLPLogExporterGRPC = grpcModule.OTLPLogExporter; return OTLPLogExporterGRPC; } catch { throw new Error( "gRPC log exporter not found. Install @opentelemetry/exporter-logs-otlp-grpc" ); } } function createLogExporter(protocol, config2) { if (protocol === "grpc") { const Exporter = loadGRPCLogExporter(); return new Exporter(config2); } return new OTLPLogExporter(config2); } function resolveProtocol(configProtocol) { if (configProtocol === "grpc" || configProtocol === "http") { return configProtocol; } const envProtocol = process.env.OTEL_EXPORTER_OTLP_PROTOCOL; if (envProtocol === "grpc") return "grpc"; if (envProtocol === "http/protobuf" || envProtocol === "http") return "http"; return "http"; } function formatEndpointUrl(endpoint, signal, protocol) { if (protocol === "grpc") { return endpoint.replace(/\/(v1\/)?(traces|metrics|logs)$/, ""); } if (!endpoint.endsWith(`/v1/${signal}`)) { return `${endpoint}/v1/${signal}`; } return endpoint; } var initialized = false; var locked = false; var config = null; var sdk = null; var warnedOnce = false; var logger = silentLogger; var validationConfig = null; var eventsConfig = null; var _stringRedactor = null; var _optionalRequire = safeRequire; var _devtoolsClose = null; var LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 }; function lockLogger() { locked = true; } function isLoggerLocked() { return locked; } function createSilentLogger() { return { info: () => { }, warn: () => { }, error: () => { }, debug: () => { } }; } function wrapLogger(base, silent, minLevel) { if (silent) return createSilentLogger(); const threshold = LOG_LEVELS[minLevel]; const wrap = (fn, level) => { if (LOG_LEVELS[level] < threshold) { return (() => { }); } return ((...args) => fn(...args)); }; return { debug: wrap(base.debug, "debug"), info: wrap(base.info, "info"), warn: wrap(base.warn, "warn"), error: wrap(base.error, "error") }; } function detectEnvironmentAttributes() { const attrs = {}; const commitSha = process.env.COMMIT_SHA || process.env.GITHUB_SHA || process.env.VERCEL_GIT_COMMIT_SHA || process.env.CF_PAGES_COMMIT_SHA || process.env.AWS_CODEPIPELINE_EXECUTION_ID; if (commitSha) attrs["service.commit.sha"] = commitSha; const region = process.env.VERCEL_REGION || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || process.env.FLY_REGION || process.env.CF_REGION || process.env.GOOGLE_CLOUD_REGION; if (region) attrs["service.region"] = region; const version = process.env.APP_VERSION || process.env.HEROKU_RELEASE_VERSION || process.env.VERCEL_GIT_COMMIT_REF; if (version) attrs["service.deploy.version"] = version; return attrs; } function resolveMetricsFlag(configFlag = "auto") { const envFlag = process.env.AUTOTEL_METRICS; if (envFlag === "on" || envFlag === "true") return true; if (envFlag === "off" || envFlag === "false") return false; if (configFlag === true) return true; if (configFlag === false) return false; return true; } function resolveLogsFlag(configFlag = "auto") { const envFlag = process.env.AUTOTEL_LOGS; if (envFlag === "on" || envFlag === "true") return true; if (envFlag === "off" || envFlag === "false") return false; if (configFlag === true) return true; if (configFlag === false) return false; return false; } function resolveDebugFlag(configFlag) { const envFlag = process.env.AUTOTEL_DEBUG; if (envFlag === "pretty") return "pretty"; if (envFlag === "true" || envFlag === "1") return true; if (envFlag === "false" || envFlag === "0") return false; return configFlag ?? false; } function normalizeOtlpHeaders(headers) { if (!headers) return void 0; if (typeof headers !== "string") return headers; const parsed = {}; for (const pair of headers.split(",")) { const [key, ...valueParts] = pair.split("="); if (!key || valueParts.length === 0) continue; parsed[key.trim()] = valueParts.join("=").trim(); } return parsed; } function init(cfg) { if (locked) { return; } const envConfig = resolveConfigFromEnv(); const yamlConfig = loadYamlConfig() ?? {}; const mergedConfig = { ...envConfig, // Environment variables (lowest priority) ...yamlConfig, // YAML file (middle priority) ...cfg, // Explicit config (highest priority) // Deep merge for resourceAttributes resourceAttributes: { ...envConfig.resourceAttributes, ...yamlConfig.resourceAttributes, ...detectEnvironmentAttributes(), ...cfg.resourceAttributes }, // Handle headers merge (can be string or object) headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers }; if (mergedConfig.attributeRedactor !== void 0) { const normalizedRedactor = normalizeAttributeRedactorConfig( mergedConfig.attributeRedactor ); if (!normalizedRedactor) { throw new Error("Invalid attributeRedactor config"); } mergedConfig.attributeRedactor = normalizedRedactor; } const devtoolsConfig = resolveDevtoolsConfig(mergedConfig.devtools); if (devtoolsConfig.enabled && mergedConfig.logs === void 0) { mergedConfig.logs = true; } const silent = mergedConfig.silent ?? false; const minLevel = mergedConfig.minLevel ?? "info"; const baseLogger = mergedConfig.logger || silentLogger; logger = wrapLogger(baseLogger, silent, minLevel); if (initialized) { logger.warn( {}, "[autotel] init() called again - last config wins. This may cause unexpected behavior." ); } config = mergedConfig; validationConfig = mergedConfig.validation || null; eventsConfig = mergedConfig.events || null; let endpoint = mergedConfig.endpoint ?? devtoolsConfig.endpoint; const otlpHeaders = normalizeOtlpHeaders(mergedConfig.headers); const version = mergedConfig.version || detectVersion(); const environment = mergedConfig.environment || process.env.NODE_ENV || "development"; const metricsEnabled = resolveMetricsFlag(mergedConfig.metrics); const logsEnabled = resolveLogsFlag(mergedConfig.logs); if (devtoolsConfig.enabled && devtoolsConfig.embedded) { const devtoolsModule = _optionalRequire("autotel-devtools"); if (devtoolsModule?.createDevtools) { const devtoolsInstance = devtoolsModule.createDevtools({ port: devtoolsConfig.port, host: devtoolsConfig.host, verbose: devtoolsConfig.verbose }); _devtoolsClose = devtoolsInstance.close; endpoint = `http://${devtoolsConfig.host}:${devtoolsInstance.port}`; logger.info( {}, `[autotel] autotel-devtools embedded server started at ${endpoint}` ); } else { logger.warn( {}, "[autotel] devtools.embedded requested but autotel-devtools is not installed. Falling back to endpoint-only mode." ); } } const hostname = detectHostname(); let resource = resourceFromAttributes({ [ATTR_SERVICE_NAME]: mergedConfig.service, [ATTR_SERVICE_VERSION]: version, // Support both old and new OpenTelemetry semantic conventions for environment "deployment.environment": environment, // Deprecated but widely supported "deployment.environment.name": environment // OTel v1.27.0+ standard }); if (hostname) { resource = resource.merge( resourceFromAttributes({ "host.name": hostname, // OpenTelemetry standard "datadog.host.name": hostname // Datadog-specific, highest priority for Datadog }) ); } if (mergedConfig.resource) { resource = resource.merge(mergedConfig.resource); } if (mergedConfig.resourceAttributes) { resource = resource.merge( resourceFromAttributes(mergedConfig.resourceAttributes) ); } const protocol = resolveProtocol(mergedConfig.protocol); let spanProcessors = []; if (mergedConfig.spanProcessors && mergedConfig.spanProcessors.length > 0) { spanProcessors.push(...mergedConfig.spanProcessors); } else if (mergedConfig.spanExporters && mergedConfig.spanExporters.length > 0) { for (const exporter of mergedConfig.spanExporters) { spanProcessors.push( new TailSamplingSpanProcessor(new BatchSpanProcessor(exporter)) ); } } else if (endpoint) { const traceExporter = createTraceExporter(protocol, { url: formatEndpointUrl(endpoint, "traces", protocol), headers: otlpHeaders }); spanProcessors.push( new TailSamplingSpanProcessor(new BatchSpanProcessor(traceExporter)) ); } if (mergedConfig.baggage) { const prefix = typeof mergedConfig.baggage === "string" ? mergedConfig.baggage ? `${mergedConfig.baggage}.` : "" : "baggage."; spanProcessors.push(new BaggageSpanProcessor({ prefix })); } const debugMode = resolveDebugFlag(mergedConfig.debug); if (debugMode === "pretty") { spanProcessors.push(new SimpleSpanProcessor(new PrettyConsoleExporter())); } else if (debugMode === true) { spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter())); } if (mergedConfig.canonicalLogLines?.enabled) { const canonicalOptions = { logger: mergedConfig.canonicalLogLines.logger || mergedConfig.logger, rootSpansOnly: mergedConfig.canonicalLogLines.rootSpansOnly, minLevel: mergedConfig.canonicalLogLines.minLevel, messageFormat: mergedConfig.canonicalLogLines.messageFormat, includeResourceAttributes: mergedConfig.canonicalLogLines.includeResourceAttributes, shouldEmit: mergedConfig.canonicalLogLines.shouldEmit, keep: mergedConfig.canonicalLogLines.keep, drain: mergedConfig.canonicalLogLines.drain, onDrainError: mergedConfig.canonicalLogLines.onDrainError, pretty: mergedConfig.canonicalLogLines.pretty }; spanProcessors.push(new CanonicalLogLineProcessor(canonicalOptions)); } if (mergedConfig.attributeRedactor && spanProcessors.length > 0) { spanProcessors = spanProcessors.map( (processor) => new AttributeRedactingProcessor(processor, { redactor: mergedConfig.attributeRedactor }) ); } if (mergedConfig.attributeRedactor) { _stringRedactor = createStringRedactor(mergedConfig.attributeRedactor); } if (_stringRedactor && mergedConfig.subscribers) { for (const subscriber of mergedConfig.subscribers) { if ("setStringRedactor" in subscriber && typeof subscriber.setStringRedactor === "function") { subscriber.setStringRedactor(_stringRedactor); } } } if (mergedConfig.spanNameNormalizer && spanProcessors.length > 0) { spanProcessors = spanProcessors.map( (processor) => new SpanNameNormalizingProcessor(processor, { normalizer: mergedConfig.spanNameNormalizer }) ); } if (mergedConfig.spanFilter && spanProcessors.length > 0) { spanProcessors = spanProcessors.map( (processor) => new FilteringSpanProcessor(processor, { filter: mergedConfig.spanFilter }) ); } const metricReaders = []; if (mergedConfig.metricReaders && mergedConfig.metricReaders.length > 0) { metricReaders.push(...mergedConfig.metricReaders); } else if (metricsEnabled && endpoint) { const metricExporter = createMetricExporter(protocol, { url: formatEndpointUrl(endpoint, "metrics", protocol), headers: otlpHeaders }); metricReaders.push( new PeriodicExportingMetricReader({ exporter: metricExporter }) ); } let logRecordProcessors; if (mergedConfig.logRecordProcessors && mergedConfig.logRecordProcessors.length > 0) { logRecordProcessors = [...mergedConfig.logRecordProcessors]; } if (logsEnabled && endpoint) { const logExporter = createLogExporter(protocol, { url: formatEndpointUrl(endpoint, "logs", protocol), headers: otlpHeaders }); let processor = new BatchLogRecordProcessor( logExporter ); if (_stringRedactor) { processor = new RedactingLogRecordProcessor(processor, _stringRedactor); } if (!logRecordProcessors) { logRecordProcessors = []; } logRecordProcessors.push(processor); logger.info({}, "[autotel] OTLP log exporter configured"); } const posthogProcessors = buildPostHogLogProcessors( mergedConfig.posthog, _stringRedactor ); if (posthogProcessors.length > 0) { if (!logRecordProcessors) { logRecordProcessors = []; } logRecordProcessors.push(...posthogProcessors); logger.info({}, "[autotel] PostHog OTLP logs configured"); } let finalInstrumentations = mergedConfig.instrumentations ? [...mergedConfig.instrumentations] : []; if (mergedConfig.autoInstrumentations !== void 0 && mergedConfig.autoInstrumentations !== false) { const isESM = isESMMode(); if (isESM) { logger.info( {}, "[autotel] ESM mode detected. For auto-instrumentation to work:\n 1. Install @opentelemetry/auto-instrumentations-node as a direct dependency\n 2. Import autotel/register FIRST in your instrumentation file\n 3. Use getNodeAutoInstrumentations() directly instead of autoInstrumentations\n See: https://github.com/jagreehal/autotel#esm-setup" ); } try { const manualInstrumentationNames = getInstrumentationNames( mergedConfig.instrumentations ?? [] ); if (manualInstrumentationNames.size > 0) { const manualNames = [...manualInstrumentationNames].join(", "); logger.info( {}, `[autotel] Detected manual instrumentations (${manualNames}). These will take precedence over auto-instrumentations. Tip: Set autoInstrumentations:false if you want full manual control, or remove manual configs to use auto-instrumentations.` ); } const autoInstrumentations = getAutoInstrumentations( mergedConfig.autoInstrumentations, manualInstrumentationNames ); if (autoInstrumentations && autoInstrumentations.length > 0) { finalInstrumentations = [ ...finalInstrumentations, ...autoInstrumentations ]; } } catch (error) { logger.warn( {}, `[autotel] Failed to configure auto-instrumentations: ${error instanceof Error ? error.message : String(error)}` ); } } const autotelSampler = mergedConfig.sampler ?? (mergedConfig.sampling ? resolveSamplingPreset(mergedConfig.sampling) : void 0); if (autotelSampler) { mergedConfig.sampler = autotelSampler; } const sampler = autotelSampler ? toOtelSampler(autotelSampler) : envConfig.otelSampler ?? toOtelSampler(samplingPresets.production()); const sdkOptions = { resource, sampler, instrumentations: finalInstrumentations }; if (spanProcessors.length > 0) { sdkOptions.spanProcessors = spanProcessors; } if (metricReaders.length > 0) { sdkOptions.metricReaders = metricReaders; } if (logRecordProcessors && logRecordProcessors.length > 0) { sdkOptions.logRecordProcessors = logRecordProcessors; } sdk = mergedConfig.sdkFactory ? mergedConfig.sdkFactory(sdkOptions) : new NodeSDK(sdkOptions); if (!sdk) { throw new Error("[autotel] sdkFactory must return a NodeSDK instance"); } sdk.start(); if (mergedConfig.openllmetry?.enabled) { const traceloop = _optionalRequire("@traceloop/node-server-sdk"); if (traceloop) { const initOptions = { ...mergedConfig.openllmetry.options }; try { const tracerProvider = sdk.getTracerProvider(); initOptions.tracerProvider = tracerProvider; } catch { } if (mergedConfig.spanExporters?.[0]) { initOptions.exporter = mergedConfig.spanExporters[0]; } if (typeof traceloop.initialize === "function") { traceloop.initialize(initOptions); logger.info({}, "[autotel] OpenLLMetry initialized successfully"); } else { logger.warn( {}, "[autotel] OpenLLMetry initialize function not found. Check @traceloop/node-server-sdk version." ); } } else { logger.warn( {}, "[autotel] OpenLLMetry enabled but @traceloop/node-server-sdk is not installed. Install it as a peer dependency to use OpenLLMetry integration." ); } } initialized = true; } function getInstrumentationNames(instrumentations) { const names = /* @__PURE__ */ new Set(); if (!instrumentations) return names; for (const instrumentation of instrumentations) { if (instrumentation && typeof instrumentation === "object") { names.add(instrumentation.constructor.name); } } return names; } var INSTRUMENTATION_CLASS_TO_PACKAGE = { HttpInstrumentation: "@opentelemetry/instrumentation-http", HttpsInstrumentation: "@opentelemetry/instrumentation-http", ExpressInstrumentation: "@opentelemetry/instrumentation-express", FastifyInstrumentation: "@opentelemetry/instrumentation-fastify", MongoDBInstrumentation: "@opentelemetry/instrumentation-mongodb", MongooseInstrumentation: "@opentelemetry/instrumentation-mongoose", PrismaInstrumentation: "@opentelemetry/instrumentation-prisma", PinoInstrumentation: "@opentelemetry/instrumentation-pino", WinstonInstrumentation: "@opentelemetry/instrumentation-winston", RedisInstrumentation: "@opentelemetry/instrumentation-redis", GraphQLInstrumentation: "@opentelemetry/instrumentation-graphql", GrpcInstrumentation: "@opentelemetry/instrumentation-grpc", IORedisInstrumentation: "@opentelemetry/instrumentation-ioredis", KnexInstrumentation: "@opentelemetry/instrumentation-knex", NestJsInstrumentation: "@opentelemetry/instrumentation-nestjs-core", PgInstrumentation: "@opentelemetry/instrumentation-pg", MySQLInstrumentation: "@opentelemetry/instrumentation-mysql", MySQL2Instrumentation: "@opentelemetry/instrumentation-mysql2" }; function isESMMode() { try { const fs = requireModule("node:fs"); try { const pkg = JSON.parse( fs.readFileSync(`${process.cwd()}/package.json`, "utf8") ); return pkg.type === "module"; } catch { return false; } } catch { return false; } } function loadNodeAutoInstrumentations() { try { const mod = requireModule("@opentelemetry/auto-instrumentations-node"); return mod.getNodeAutoInstrumentations; } catch { const isESM = isESMMode(); const baseMessage = "@opentelemetry/auto-instrumentations-node not found."; if (isESM) { throw new Error( `${baseMessage} ESM Setup Required: 1. Install as a direct dependency: pnpm add @opentelemetry/auto-instrumentations-node 2. Create instrumentation.mjs with: import 'autotel/register'; // MUST be first! import { init } from 'autotel'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; init({ service: "my-app", instrumentations: getNodeAutoInstrumentations() }); 3. Run with: tsx --import ./instrumentation.mjs src/index.ts See: https://github.com/jagreehal/autotel#esm-setup` ); } throw new Error( `${baseMessage} Install it: pnpm add @opentelemetry/auto-instrumentations-node` ); } } function getAutoInstrumentations(integrations, manualInstrumentationNames = /* @__PURE__ */ new Set()) { if (integrations === false) { return []; } const getNodeAutoInstrumentations = loadNodeAutoInstrumentations(); const exclusionConfig = {}; for (const className of manualInstrumentationNames) { const packageName = INSTRUMENTATION_CLASS_TO_PACKAGE[className]; if (packageName) { exclusionConfig[packageName] = { enabled: false }; } } if (integrations === true) { if (Object.keys(exclusionConfig).length > 0) { return getNodeAutoInstrumentations(exclusionConfig); } return getNodeAutoInstrumentations(); } if (Array.isArray(integrations)) { const config3 = { ...exclusionConfig }; for (const name of integrations) { const packageName = `@opentelemetry/instrumentation-${name}`; if (!exclusionConfig[packageName]) { config3[packageName] = { enabled: true }; } } return getNodeAutoInstrumentations(config3); } const config2 = { ...exclusionConfig, ...integrations }; for (const packageName of Object.keys(exclusionConfig)) { const integrationsKey = Object.keys(integrations).find( (key) => packageName.includes(key) ); if (integrationsKey) { config2[packageName] = { enabled: false }; } } return getNodeAutoInstrumentations(config2); } function isInitialized() { return initialized; } function getConfig() { return config; } function getLogger() { return logger; } function getValidationConfig() { return validationConfig; } function getEventsConfig() { return eventsConfig; } function warnIfNotInitialized(context) { if (!initialized && !warnedOnce) { logger.warn( {}, `[autotel] ${context} used before init() called. Call init({ service: "..." }) first. See: https://docs.autotel.dev/quickstart` ); warnedOnce = true; } } function detectVersion() { try { const fs = requireModule("node:fs"); const pkg = JSON.parse( fs.readFileSync(`${process.cwd()}/package.json`, "utf8") ); return pkg.version || "1.0.0"; } catch { return "1.0.0"; } } function detectHostname() { if (process.env.DD_HOSTNAME) { return process.env.DD_HOSTNAME; } if (process.env.HOSTNAME) { return process.env.HOSTNAME; } try { const os = requireModule("node:os"); return os.hostname(); } catch { return void 0; } } async function _closeEmbeddedDevtools() { if (_devtoolsClose) { await _devtoolsClose(); _devtoolsClose = null; } } function getSdk() { return sdk; } export { BaggageSpanProcessor, _closeEmbeddedDevtools, createStringRedactor, getConfig, getEventsConfig, getLogger, getSdk, getValidationConfig, init, isInitialized, isLoggerLocked, lockLogger, warnIfNotInitialized }; //# sourceMappingURL=chunk-W35FVJBC.js.map //# sourceMappingURL=chunk-W35FVJBC.js.map