UNPKG

autotel

Version:
1,515 lines (1,510 loc) 60.1 kB
'use strict'; var chunkGML3FBOT_cjs = require('./chunk-GML3FBOT.cjs'); var chunkD5LMF53P_cjs = require('./chunk-D5LMF53P.cjs'); var chunkJSNUWSBH_cjs = require('./chunk-JSNUWSBH.cjs'); var chunkHZ3FYBJG_cjs = require('./chunk-HZ3FYBJG.cjs'); var chunk563EL6O6_cjs = require('./chunk-563EL6O6.cjs'); var chunkVH77IPJN_cjs = require('./chunk-VH77IPJN.cjs'); var chunkESLWRGAG_cjs = require('./chunk-ESLWRGAG.cjs'); var api = require('@opentelemetry/api'); var fs = require('fs'); var url = require('url'); // src/rate-limiter.ts var TokenBucketRateLimiter = class { tokens; maxTokens; refillRate; // tokens per millisecond lastRefill; constructor(config) { this.maxTokens = config.burstCapacity || config.maxEventsPerSecond * 2; this.tokens = this.maxTokens; this.refillRate = config.maxEventsPerSecond / 1e3; this.lastRefill = Date.now(); } /** * Try to consume a token (allow an event) * Returns true if allowed, false if rate limit exceeded */ tryConsume(count = 1) { this.refill(); if (this.tokens >= count) { this.tokens -= count; return true; } return false; } /** * Wait until a token is available (async rate limiting) * Returns a promise that resolves when the event can be processed */ async waitForToken(count = 1) { this.refill(); if (this.tokens >= count) { this.tokens -= count; return; } const tokensNeeded = count - this.tokens; const waitMs = Math.ceil(tokensNeeded / this.refillRate); await new Promise((resolve) => setTimeout(resolve, waitMs)); return this.waitForToken(count); } /** * Refill tokens based on elapsed time */ refill() { const now = Date.now(); const elapsed = now - this.lastRefill; const tokensToAdd = elapsed * this.refillRate; this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd); this.lastRefill = now; } /** * Get current available tokens (for testing/debugging) */ getAvailableTokens() { this.refill(); return Math.floor(this.tokens); } /** * Reset the rate limiter (for testing) */ reset() { this.tokens = this.maxTokens; this.lastRefill = Date.now(); } }; // src/event-queue.ts var DEFAULT_CONFIG = { maxSize: 5e4, batchSize: 100, flushInterval: 1e4, maxRetries: 3, rateLimit: { maxEventsPerSecond: 100, burstCapacity: 200 } }; function getSubscriberName(subscriber) { if (subscriber.name) { return subscriber.name.toLowerCase(); } const className = subscriber.constructor?.name || "unknown"; return className.replace(/Subscriber$/i, "").toLowerCase(); } var EventQueue = class { queue = []; flushTimer = null; config; subscribers; rateLimiter; flushPromise = null; isShuttingDown = false; // Metrics metrics = null; // Observable callback cleanup functions observableCleanups = []; // Subscriber health tracking (for observable gauges) subscriberHealthy = /* @__PURE__ */ new Map(); constructor(subscribers, config) { this.subscribers = subscribers; this.config = { ...DEFAULT_CONFIG, ...config }; this.rateLimiter = this.config.rateLimit ? new TokenBucketRateLimiter(this.config.rateLimit) : null; for (const subscriber of subscribers) { const name = getSubscriberName(subscriber); this.subscriberHealthy.set(name, true); } this.initMetrics(); } /** * Initialize OTel metrics for queue observability */ initMetrics() { const runtimeConfig = chunkESLWRGAG_cjs.getConfig(); const meter = runtimeConfig.meter; const queueSize = meter.createObservableGauge( "autotel.event_delivery.queue.size", { description: "Current number of events in the delivery queue", unit: "count" } ); const queueSizeCallback = (observableResult) => { observableResult.observe(this.queue.length); }; queueSize.addCallback(queueSizeCallback); this.observableCleanups.push( () => queueSize.removeCallback(queueSizeCallback) ); const oldestAge = meter.createObservableGauge( "autotel.event_delivery.queue.oldest_age_ms", { description: "Age of the oldest event in the queue in milliseconds", unit: "ms" } ); const oldestAgeCallback = (observableResult) => { if (this.queue.length > 0) { const oldest = this.queue[0]; const ageMs = Date.now() - oldest.timestamp; observableResult.observe(ageMs); } else { observableResult.observe(0); } }; oldestAge.addCallback(oldestAgeCallback); this.observableCleanups.push( () => oldestAge.removeCallback(oldestAgeCallback) ); const delivered = meter.createCounter( "autotel.event_delivery.queue.delivered", { description: "Number of events successfully delivered to subscribers", unit: "count" } ); const failed = meter.createCounter("autotel.event_delivery.queue.failed", { description: "Number of events that failed delivery after all retry attempts", unit: "count" }); const dropped = meter.createCounter( "autotel.event_delivery.queue.dropped", { description: "Number of events dropped from the queue", unit: "count" } ); const latency = meter.createHistogram( "autotel.event_delivery.queue.latency_ms", { description: "Event delivery latency from enqueue to successful send", unit: "ms" } ); const subscriberHealth = meter.createObservableGauge( "autotel.event_delivery.subscriber.health", { description: "Subscriber health status (1=healthy, 0=unhealthy)", unit: "1" } ); const subscriberHealthCallback = (observableResult) => { for (const [subscriberName, isHealthy] of this.subscriberHealthy) { observableResult.observe(isHealthy ? 1 : 0, { subscriber: subscriberName }); } }; subscriberHealth.addCallback(subscriberHealthCallback); this.observableCleanups.push( () => subscriberHealth.removeCallback(subscriberHealthCallback) ); this.metrics = { queueSize, oldestAge, delivered, failed, dropped, latency, subscriberHealth }; } /** * Record a dropped event with reason and emit debug breadcrumb */ recordDropped(reason, event, subscriberName) { const attrs = { reason }; if (subscriberName) { attrs.subscriber = subscriberName; } this.metrics?.dropped.add(1, attrs); const logLevel = reason === "payload_invalid" ? "error" : "warn"; const logger = chunk563EL6O6_cjs.getLogger(); if (logLevel === "error") { logger.error( { eventName: event?.name, subscriber: subscriberName, reason, correlationId: event?._correlationId, traceId: event?._traceId }, `[autotel] Event dropped: ${reason}` ); } else { logger.warn( { eventName: event?.name, subscriber: subscriberName, reason, correlationId: event?._correlationId, traceId: event?._traceId }, `[autotel] Event dropped: ${reason}` ); } } /** * Record permanent delivery failure (after all retries exhausted) * Increments failed counter and logs error */ recordFailed(event, subscriberName, error) { this.metrics?.failed.add(1, { subscriber: subscriberName }); this.subscriberHealthy.set(subscriberName, false); chunk563EL6O6_cjs.getLogger().error( { eventName: event.name, subscriber: subscriberName, correlationId: event._correlationId, traceId: event._traceId, err: error }, `[autotel] Event delivery failed after all retries` ); } /** * Mark subscriber as unhealthy on transient failure (without incrementing failed counter) * Used during retry attempts - only recordFailed should increment the counter */ markSubscriberUnhealthy(subscriberName) { this.subscriberHealthy.set(subscriberName, false); } /** * Record successful delivery */ recordDelivered(event, subscriberName, startTime) { const latencyMs = Date.now() - startTime; this.metrics?.delivered.add(1, { subscriber: subscriberName }); this.metrics?.latency.record(latencyMs, { subscriber: subscriberName }); this.subscriberHealthy.set(subscriberName, true); } /** * Enqueue an event for sending * * Backpressure policy: * - Drops oldest event and logs warning if queue is full (same behavior in all environments) */ enqueue(event) { if (this.isShuttingDown) { this.recordDropped("shutdown", event); return; } if (this.queue.length >= this.config.maxSize) { const droppedEvent = this.queue.shift(); this.recordDropped("rate_limit", droppedEvent); chunk563EL6O6_cjs.getLogger().warn( { droppedEvent: droppedEvent?.name }, `[autotel] Events queue full (${this.config.maxSize} events). Dropping oldest event. Events are being produced faster than they can be sent. Check your subscribers or reduce tracking frequency.` ); } const enrichedEvent = { ...event, _correlationId: event._correlationId || chunkJSNUWSBH_cjs.getOrCreateCorrelationId() }; this.queue.push(enrichedEvent); this.scheduleBatchFlush(); } /** * Schedule a batch flush if not already scheduled */ scheduleBatchFlush() { if (this.flushTimer || this.flushPromise) return; this.flushTimer = setTimeout(() => { this.flushTimer = null; void this.flushBatch(); }, this.config.flushInterval); } /** * Flush a batch of events * Uses promise-based concurrency control to prevent race conditions */ async flushBatch() { if (this.queue.length === 0) return; if (this.flushPromise) { await this.flushPromise; return; } this.flushPromise = this.doFlushBatch(); try { await this.flushPromise; } finally { this.flushPromise = null; if (this.queue.length > 0) { this.scheduleBatchFlush(); } } } /** * Internal flush implementation */ async doFlushBatch() { const batch = this.queue.splice(0, this.config.batchSize); await this.sendWithRetry(batch, this.config.maxRetries); } /** * Send events with exponential backoff retry * Tracks per-event, per-subscriber failures so failed counter reflects actual failed deliveries. * On retry, only failed (event, subscriber) pairs are re-sent to avoid double-counting delivered. */ async sendWithRetry(events, retriesLeft, subscribersByEventIndex) { const failedDeliveries = await this.sendToSubscribers( events, subscribersByEventIndex ); if (failedDeliveries.length > 0) { if (retriesLeft > 0) { const failedEventIndices = new Set( failedDeliveries.map((f) => f.eventIndex) ); const failedEventIndicesOrdered = [...failedEventIndices].sort( (a, b) => a - b ); const eventsToRetry = failedEventIndicesOrdered.map( (i) => events[i] ); const failedSubscribersByRetryIndex = /* @__PURE__ */ new Map(); for (let j = 0; j < failedEventIndicesOrdered.length; j++) { const origIndex = failedEventIndicesOrdered[j]; const set = /* @__PURE__ */ new Set(); for (const { eventIndex, subscriberName } of failedDeliveries) { if (eventIndex === origIndex) set.add(subscriberName); } failedSubscribersByRetryIndex.set(j, set); } const delay = Math.pow(2, this.config.maxRetries - retriesLeft) * 1e3; await new Promise((resolve) => setTimeout(resolve, delay)); return this.sendWithRetry( eventsToRetry, retriesLeft - 1, failedSubscribersByRetryIndex ); } else { for (const { eventIndex, subscriberName, error } of failedDeliveries) { const event = events[eventIndex]; if (event) this.recordFailed(event, subscriberName, error); } const failedSubscriberNames = [ ...new Set(failedDeliveries.map((f) => f.subscriberName)) ]; chunk563EL6O6_cjs.getLogger().error( { failedSubscribers: failedSubscriberNames, retriesAttempted: this.config.maxRetries }, "[autotel] Failed to send events after retries" ); } } } /** * Send events to configured subscribers with rate limiting and metrics. * When subscribersByEventIndex is provided (retry path), only those subscribers are tried per event. * Returns per-event, per-subscriber failures (empty if all succeeded). */ async sendToSubscribers(events, subscribersByEventIndex) { const failedDeliveries = []; const sendOne = async (event, eventIndex) => { const subscriberNames = subscribersByEventIndex?.get(eventIndex); const failures = await this.sendEventToSubscribers( event, subscriberNames ?? void 0 ); for (const failure of failures) { failedDeliveries.push({ eventIndex, subscriberName: failure.subscriberName, error: failure.error }); } }; if (!this.rateLimiter) { for (let i = 0; i < events.length; i++) { const event = events[i]; if (event) await sendOne(event, i); } return failedDeliveries; } for (let i = 0; i < events.length; i++) { await this.rateLimiter.waitForToken(); const event = events[i]; if (event) await sendOne(event, i); } return failedDeliveries; } /** * Send a single event to subscribers. * - When subscriberNames is undefined (initial attempt): send to all subscribers. * - When subscriberNames is provided (retry): send only to those subscribers (never re-send to healthy ones). * Returns list of subscribers that failed (empty if all succeeded). */ async sendEventToSubscribers(event, subscriberNames) { const startTime = event.timestamp; const failures = []; const subscribersToTry = subscriberNames === void 0 ? this.subscribers : this.subscribers.filter( (s) => subscriberNames.has(getSubscriberName(s)) ); const results = await Promise.allSettled( subscribersToTry.map(async (subscriber) => { const subscriberName = getSubscriberName(subscriber); try { await subscriber.trackEvent(event.name, event.attributes, { autotel: event.autotel }); this.recordDelivered(event, subscriberName, startTime); return { subscriberName, success: true }; } catch (error) { this.markSubscriberUnhealthy(subscriberName); return { subscriberName, success: false, error: error instanceof Error ? error : void 0 }; } }) ); for (const result of results) { if (result.status === "fulfilled" && !result.value.success) { failures.push({ subscriberName: result.value.subscriberName, error: result.value.error }); } } return failures; } /** * Flush all remaining events. Queue remains usable after flush (e.g. for * auto-flush at root span end). Use shutdown() when tearing down the queue. */ async flush() { if (this.flushTimer) { clearTimeout(this.flushTimer); this.flushTimer = null; } if (this.flushPromise) { await this.flushPromise; } while (this.queue.length > 0) { await this.doFlushBatch(); } } /** * Flush remaining events and permanently disable the queue (reject new events). * Use for process/SDK shutdown; use flush() for periodic or span-end drain. */ async shutdown() { this.isShuttingDown = true; await this.flush(); } /** * Cleanup observable metric callbacks to prevent memory leaks * Call this when destroying the EventQueue instance */ cleanup() { for (const cleanupFn of this.observableCleanups) { try { cleanupFn(); } catch { } } this.observableCleanups = []; } /** * Get queue size (for testing/debugging) */ size() { return this.queue.length; } /** * Get subscriber health status (for testing/debugging) */ getSubscriberHealth() { return new Map(this.subscriberHealthy); } /** * Check if a specific subscriber is healthy */ isSubscriberHealthy(subscriberName) { return this.subscriberHealthy.get(subscriberName.toLowerCase()) ?? true; } /** * Manually mark a subscriber as healthy or unhealthy * (used for circuit breaker integration) */ setSubscriberHealth(subscriberName, healthy) { this.subscriberHealthy.set(subscriberName.toLowerCase(), healthy); } }; // src/track.ts var eventsQueue = null; function buildAutotelContext(span2) { const eventsConfig = chunk563EL6O6_cjs.getEventsConfig(); const config = chunk563EL6O6_cjs.getConfig(); const correlationId = chunkJSNUWSBH_cjs.getOrCreateCorrelationId(); if (!eventsConfig?.includeTraceContext) { return { correlation_id: correlationId }; } const autotelContext = { correlation_id: correlationId }; const spanContext = span2?.spanContext(); if (spanContext) { autotelContext.trace_id = spanContext.traceId; autotelContext.span_id = spanContext.spanId; autotelContext.trace_flags = spanContext.traceFlags.toString(16).padStart(2, "0"); const traceState = spanContext.traceState; if (traceState) { try { if (typeof traceState.serialize === "function") { const traceStateStr = traceState.serialize(); if (traceStateStr) { autotelContext.trace_state = traceStateStr; } } } catch { } } if (eventsConfig.traceUrl && config) { const traceUrl = eventsConfig.traceUrl({ traceId: spanContext.traceId, spanId: spanContext.spanId, correlationId, serviceName: config.service, environment: config.environment }); if (traceUrl) { autotelContext.trace_url = traceUrl; } } } else { if (eventsConfig.traceUrl && config) { const traceUrl = eventsConfig.traceUrl({ correlationId, serviceName: config.service, environment: config.environment }); if (traceUrl) { autotelContext.trace_url = traceUrl; } } } return autotelContext; } function getOrCreateQueue() { if (!chunk563EL6O6_cjs.isInitialized()) { chunk563EL6O6_cjs.warnIfNotInitialized("track()"); return null; } if (!eventsQueue) { const config = chunk563EL6O6_cjs.getConfig(); if (!config?.subscribers || config.subscribers.length === 0) { return null; } eventsQueue = new EventQueue(config.subscribers); } return eventsQueue; } function track(event, data) { const queue = getOrCreateQueue(); if (!queue) return; const validationConfig = chunk563EL6O6_cjs.getValidationConfig(); const validated = chunkD5LMF53P_cjs.validateEvent(event, data, validationConfig || void 0); const span2 = api.trace.getActiveSpan(); const enrichedData = span2 ? { ...validated.attributes, traceId: span2.spanContext().traceId, spanId: span2.spanContext().spanId } : validated.attributes; const autotelContext = buildAutotelContext(span2); queue.enqueue({ name: validated.eventName, attributes: enrichedData, timestamp: Date.now(), autotel: autotelContext }); } function getEventQueue() { return eventsQueue; } function resetEventQueue() { eventsQueue = null; } var inferenceCache = /* @__PURE__ */ new Map(); var MAX_CACHE_SIZE = 50; function captureStackTrace() { const originalStackTraceLimit = Error.stackTraceLimit; Error.stackTraceLimit = 10; const err = new Error("Stack trace capture"); const stack = err.stack || ""; Error.stackTraceLimit = originalStackTraceLimit; return stack; } function parseCallLocation(stack) { const lines = stack.split("\n"); let skippedExternalFrame = false; for (const line of lines) { if (line.includes("variable-name-inference.ts") || line.includes("variable-name-inference.js") || line.includes("functional.ts") || line.includes("functional.js")) { continue; } const match = line.match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/) || line.match(/^.*?([^:]+):(\d+):(\d+)/); if (match) { let filePath = match[1].trim(); if (filePath.startsWith("file://")) { try { filePath = url.fileURLToPath(filePath); } catch { continue; } } if (!skippedExternalFrame) { skippedExternalFrame = true; continue; } return { file: filePath, line: Number.parseInt(match[2], 10), column: Number.parseInt(match[3], 10) }; } } return void 0; } function readSourceLine(filePath, lineNumber) { try { if (typeof fs.readFileSync !== "function") { return void 0; } const content = fs.readFileSync(filePath, "utf8"); const lines = content.split("\n"); return lines[lineNumber - 1]; } catch { return void 0; } } function extractVariableName(sourceLine) { const trimmed = sourceLine.trim(); const patterns = [ // export const varName = anyFunction( /export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/, // const varName = anyFunction( /const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/, // export let varName = anyFunction( /export\s+let\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/, // let varName = anyFunction( /let\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/, // export var varName = anyFunction( /export\s+var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/, // var varName = anyFunction( /var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/ ]; for (const pattern of patterns) { const match = trimmed.match(pattern); if (match && match[1]) { return match[1]; } } return void 0; } function cacheInference(key, value) { if (inferenceCache.size >= MAX_CACHE_SIZE) { const firstKey = inferenceCache.keys().next().value; if (firstKey) { inferenceCache.delete(firstKey); } } inferenceCache.set(key, value); } function inferVariableNameFromCallStack() { try { const stack = captureStackTrace(); const callLocation = parseCallLocation(stack); if (!callLocation) { return void 0; } const cacheKey = `${callLocation.file}:${callLocation.line}`; if (inferenceCache.has(cacheKey)) { return inferenceCache.get(cacheKey); } const sourceLine = readSourceLine(callLocation.file, callLocation.line); if (!sourceLine) { return void 0; } const variableName = extractVariableName(sourceLine); cacheInference(cacheKey, variableName); return variableName; } catch { return void 0; } } // src/functional.ts var FACTORY_NAME_HINTS = /* @__PURE__ */ new Set([ "ctx", "_ctx", "context", "tracecontext", "tracectx" ]); var TRACE_FACTORY_SET = /* @__PURE__ */ new WeakSet(); var SINGLE_LINE_COMMENT_REGEX = /\/\/.*$/gm; var MULTI_LINE_COMMENT_REGEX = /\/\*[\s\S]*?\*\//gm; var PARAM_TOKEN_SANITIZE_REGEX = new RegExp(String.raw`[{}\[\]\s]`, "g"); function markAsTraceFactory(fn) { TRACE_FACTORY_SET.add(fn); } function hasFactoryMark(fn) { return TRACE_FACTORY_SET.has(fn); } function sanitizeParameterToken(token) { const [firstToken] = token.split("="); return (firstToken ?? "").replaceAll(PARAM_TOKEN_SANITIZE_REGEX, "").trim(); } function getFirstParameterToken(fn) { let source = Function.prototype.toString.call(fn); source = source.replaceAll(MULTI_LINE_COMMENT_REGEX, "").replaceAll(SINGLE_LINE_COMMENT_REGEX, "").trim(); const arrowMatch = source.match( /^(?:async\s*)?(?:\(([^)]*)\)|([^=()]+))\s*=>/ ); if (arrowMatch) { const params = (arrowMatch[1] ?? arrowMatch[2] ?? "").split(","); const first = params[0]?.trim(); if (first) { return sanitizeParameterToken(first); } return null; } const functionMatch = source.match(/^[^(]*\(([^)]*)\)/); if (functionMatch) { const params = functionMatch[1]?.split(","); const first = params?.[0]?.trim(); if (first) { return sanitizeParameterToken(first); } } return null; } function looksLikeTraceFactory(fn) { if (hasFactoryMark(fn)) { return true; } if (fn.length === 0) { if (!isAsyncFunction(fn)) { try { const result = fn(); return typeof result === "function"; } catch { return false; } } return false; } const firstParam = getFirstParameterToken(fn); if (!firstParam) { return false; } const normalized = firstParam.toLowerCase(); if (FACTORY_NAME_HINTS.has(normalized) || normalized.startsWith("ctx") || normalized.startsWith("_ctx") || normalized.startsWith("trace") || normalized.endsWith("ctx") || // Match baseCtx, spanCtx, etc. normalized.includes("context")) { return true; } return false; } function isFactoryReturningFunction(fnWithCtx) { if (isAsyncFunction(fnWithCtx)) { return false; } try { const result = fnWithCtx(createDummyCtx()); return typeof result === "function"; } catch { return false; } } function isTraceFactoryFunction(fn) { if (typeof fn !== "function") { return false; } if (hasFactoryMark(fn)) { return true; } if (looksLikeTraceFactory(fn)) { markAsTraceFactory(fn); return true; } return false; } function ensureTraceFactory(fnOrFactory) { if (isTraceFactoryFunction(fnOrFactory)) { return fnOrFactory; } const plainFn = fnOrFactory; const factory = (ctx2) => { return plainFn; }; markAsTraceFactory(factory); return factory; } function wrapFactoryWithTracing(fnOrFactory, options, variableName) { const factory = ensureTraceFactory(fnOrFactory); const sampleFn = factory(createDummyCtx()); const innerFunctionName = inferFunctionName( sampleFn ); const callStackVariableName = innerFunctionName ? void 0 : inferVariableNameFromCallStack(); const factoryName = inferFunctionName(factory); const effectiveVariableName = variableName || innerFunctionName || callStackVariableName || factoryName; const useAsyncWrapper = isAsyncFunction(sampleFn); if (useAsyncWrapper) { return wrapWithTracing( factory, options, effectiveVariableName ); } return wrapWithTracingSync( factory, options, effectiveVariableName ); } var MAX_ERROR_MESSAGE_LENGTH = 500; function createDummyCtx() { return { traceId: "", spanId: "", correlationId: "", setAttribute: () => { }, setAttributes: () => { }, setStatus: () => { }, recordException: () => { }, addEvent: () => { }, addLink: () => { }, addLinks: () => { }, updateName: () => { }, isRecording: () => false, getBaggage: () => { }, setBaggage: () => "", deleteBaggage: () => { }, getAllBaggage: () => /* @__PURE__ */ new Map() }; } function isAsyncFunction(fn) { return typeof fn === "function" && fn.constructor?.name === "AsyncFunction"; } var INSTRUMENTED_SYMBOL = /* @__PURE__ */ Symbol.for("autotel.functional.instrumented"); function hasInstrumentationFlag(value) { return (typeof value === "function" || typeof value === "object") && value !== null && Boolean(value[INSTRUMENTED_SYMBOL]); } function truncateErrorMessage(message) { if (message.length <= MAX_ERROR_MESSAGE_LENGTH) { return message; } return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH)}... (truncated)`; } function inferFunctionName(fn) { const displayName = fn.displayName; if (displayName) { return displayName; } if (fn.name && fn.name !== "anonymous" && fn.name !== "") { return fn.name; } const source = Function.prototype.toString.call(fn); const match = source.match(/function\s+([^(\s]+)/); if (match && match[1] && match[1] !== "anonymous") { return match[1]; } return void 0; } function getSpanName(options, fn, variableName) { if (options.name) { return options.name; } let fnName = variableName || inferFunctionName(fn); fnName = fnName || "anonymous"; if (options.serviceName) { return `${options.serviceName}.${fnName}`; } if (fnName && fnName !== "anonymous") { return fnName; } return "unknown"; } function shouldSkip(key, fn, skip) { if (key.startsWith("_")) { return true; } if (!skip || skip.length === 0) { return false; } for (const rule of skip) { if (typeof rule === "string" && key === rule) { return true; } else if (rule instanceof RegExp && rule.test(key)) { return true; } else if (typeof rule === "function" && rule(key, fn)) { return true; } } return false; } function getCtxValue() { const activeSpan = api.trace.getActiveSpan(); if (!activeSpan) return null; return chunkHZ3FYBJG_cjs.createTraceContext(activeSpan); } var ctx = new Proxy( {}, { get(_target, prop) { const ctxValue = getCtxValue(); if (!ctxValue) { return; } return ctxValue[prop]; }, has(_target, prop) { const ctxValue = getCtxValue(); if (!ctxValue) { return false; } return prop in ctxValue; }, ownKeys() { const ctxValue = getCtxValue(); if (!ctxValue) { return []; } return Object.keys(ctxValue); }, getOwnPropertyDescriptor(_target, prop) { const ctxValue = getCtxValue(); if (!ctxValue) { return; } return Object.getOwnPropertyDescriptor(ctxValue, prop); } } ); function wrapWithTracing(fnFactory, options, variableName) { if (hasInstrumentationFlag(fnFactory)) ; const config = chunkESLWRGAG_cjs.getConfig(); const tracer = config.tracer; const meter = config.meter; const sampler = options.sampler || new chunkVH77IPJN_cjs.AlwaysSampler(); const tempFn = fnFactory(createDummyCtx()); const spanName = getSpanName(options, tempFn, variableName); const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, { description: `Call count for ${spanName}`, unit: "1" }) : void 0; const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, { description: `Duration for ${spanName}`, unit: "ms" }) : void 0; const wrappedFunction = async function wrappedFunction2(...args) { const samplingContext = { operationName: spanName, args, metadata: {} }; const shouldSample = sampler.shouldSample(samplingContext); const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false; if (!shouldSample && !needsTailSampling) { const fn = fnFactory(createDummyCtx()); return await fn.call(this, ...args); } const startTime = performance.now(); const isRootSpan = options.startNewRoot || api.trace.getActiveSpan() === void 0; const shouldAutoFlush = options.flushOnRootSpanEnd ?? chunk563EL6O6_cjs.getConfig()?.flushOnRootSpanEnd ?? true; const shouldAutoFlushSpans = chunk563EL6O6_cjs.getConfig()?.forceFlushOnShutdown ?? false; const flushIfNeeded = async () => { if (!shouldAutoFlush || !isRootSpan) return; try { const queue = getEventQueue(); if (queue && queue.size() > 0) { await queue.flush(); } if (shouldAutoFlushSpans) { const sdk = chunk563EL6O6_cjs.getSdk(); if (sdk) { try { const sdkAny = sdk; if (typeof sdkAny.getTracerProvider === "function") { const tracerProvider = sdkAny.getTracerProvider(); if (tracerProvider && typeof tracerProvider.forceFlush === "function") { await tracerProvider.forceFlush(); } } } catch { } } } } catch (error) { const initConfig = chunk563EL6O6_cjs.getConfig(); const logger = initConfig?.logger; if (logger?.error) { logger.error( { err: error instanceof Error ? error : void 0 }, `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}` ); } } }; const spanOptions = {}; if (options.startNewRoot) { spanOptions.root = true; } if (options.spanKind !== void 0) { spanOptions.kind = options.spanKind; } const parentContext = chunkHZ3FYBJG_cjs.getActiveContextWithBaggage(); return tracer.startActiveSpan( spanName, spanOptions, parentContext, async (span2) => { return chunkD5LMF53P_cjs.runInOperationContext(spanName, async () => { let shouldKeepSpan = true; chunkGML3FBOT_cjs.setSpanName(span2, spanName); const initialContext = api.context.active(); const contextStorage = chunkHZ3FYBJG_cjs.getContextStorage(); if (!contextStorage.getStore()) { chunkHZ3FYBJG_cjs.enterOrRun(contextStorage, initialContext); } const ctxValue = chunkHZ3FYBJG_cjs.createTraceContext(span2); const fn = fnFactory(ctxValue); const argsAttributes = options.attributesFromArgs ? options.attributesFromArgs(args) : {}; const handleTailSampling = (success, duration, error) => { if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") { shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, { success, duration, error }); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_KEEP, shouldKeepSpan); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_EVALUATED, true); } }; const onSuccess = async (result) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "success" }); durationHistogram?.record(duration, { operation: spanName, status: "success" }); const resultAttributes = options.attributesFromResult ? options.attributesFromResult(result) : {}; span2.setStatus({ code: api.SpanStatusCode.OK }); span2.setAttributes({ ...argsAttributes, ...resultAttributes, "operation.name": spanName, "code.function": spanName, "operation.duration": duration, "operation.success": true }); handleTailSampling(true, duration); span2.end(); await flushIfNeeded(); return result; }; const onError = async (error) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "error" }); durationHistogram?.record(duration, { operation: spanName, status: "error" }); const errorMessage = error instanceof Error ? error.message : "Unknown error"; const truncatedMessage = truncateErrorMessage(errorMessage); span2.setStatus({ code: api.SpanStatusCode.ERROR, message: truncatedMessage }); span2.setAttributes({ ...argsAttributes, "operation.name": spanName, "code.function": spanName, "operation.duration": duration, "operation.success": false, error: true, "exception.type": error instanceof Error ? error.constructor.name : "Error", "exception.message": truncatedMessage }); if (error instanceof Error && error.stack) { span2.setAttribute( "exception.stack", error.stack.slice(0, MAX_ERROR_MESSAGE_LENGTH) ); } span2.recordException( error instanceof Error ? error : new Error(String(error)) ); handleTailSampling(false, duration, error); span2.end(); await flushIfNeeded(); throw error; }; try { callCounter?.add(1, { operation: spanName, status: "started" }); const executeWithContext = async () => { const currentContext = chunkHZ3FYBJG_cjs.getActiveContextWithBaggage(); return api.context.with(currentContext, async () => { return fn.call(this, ...args); }); }; const result = await executeWithContext(); return await onSuccess(result); } catch (error) { await onError(error); throw error; } }); } ); }; wrappedFunction[INSTRUMENTED_SYMBOL] = true; Object.defineProperty(wrappedFunction, "name", { value: tempFn.name || "trace", configurable: true }); return wrappedFunction; } function wrapWithTracingSync(fnFactory, options, variableName) { if (hasInstrumentationFlag(fnFactory)) ; const config = chunkESLWRGAG_cjs.getConfig(); const tracer = config.tracer; const meter = config.meter; const sampler = options.sampler || new chunkVH77IPJN_cjs.AlwaysSampler(); const tempFn = fnFactory(createDummyCtx()); const spanName = getSpanName(options, tempFn, variableName); const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, { description: `Call count for ${spanName}`, unit: "1" }) : void 0; const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, { description: `Duration for ${spanName}`, unit: "ms" }) : void 0; function wrappedFunction(...args) { const samplingContext = { operationName: spanName, args, metadata: {} }; const shouldSample = sampler.shouldSample(samplingContext); const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false; if (!shouldSample && !needsTailSampling) { const fn = fnFactory(createDummyCtx()); return fn.call(this, ...args); } const startTime = performance.now(); const isRootSpan = options.startNewRoot || api.trace.getActiveSpan() === void 0; const shouldAutoFlush = options.flushOnRootSpanEnd ?? chunk563EL6O6_cjs.getConfig()?.flushOnRootSpanEnd ?? true; const shouldAutoFlushSpans = chunk563EL6O6_cjs.getConfig()?.forceFlushOnShutdown ?? false; const flushIfNeeded = () => { if (!shouldAutoFlush || !isRootSpan) return; const queue = getEventQueue(); if (queue && queue.size() > 0) { void queue.flush().catch((error) => { const initConfig = chunk563EL6O6_cjs.getConfig(); const logger = initConfig?.logger; if (logger?.error) { logger.error( { err: error instanceof Error ? error : void 0 }, `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}` ); } }); } if (shouldAutoFlushSpans) { const sdk = chunk563EL6O6_cjs.getSdk(); if (sdk) { try { const sdkAny = sdk; if (typeof sdkAny.getTracerProvider === "function") { const tracerProvider = sdkAny.getTracerProvider(); if (tracerProvider && typeof tracerProvider.forceFlush === "function") { void tracerProvider.forceFlush().catch((error) => { const initConfig = chunk563EL6O6_cjs.getConfig(); const logger = initConfig?.logger; if (logger?.error) { logger.error( { err: error instanceof Error ? error : void 0 }, `[autotel] Span flush failed${error instanceof Error ? "" : `: ${String(error)}`}` ); } }); } } } catch { } } } }; const spanOptions = {}; if (options.startNewRoot) { spanOptions.root = true; } if (options.spanKind !== void 0) { spanOptions.kind = options.spanKind; } const parentContext = chunkHZ3FYBJG_cjs.getActiveContextWithBaggage(); return tracer.startActiveSpan( spanName, spanOptions, parentContext, (span2) => { return chunkD5LMF53P_cjs.runInOperationContext(spanName, () => { let shouldKeepSpan = true; chunkGML3FBOT_cjs.setSpanName(span2, spanName); const ctxValue = chunkHZ3FYBJG_cjs.createTraceContext(span2); const fn = fnFactory(ctxValue); const argsAttributes = options.attributesFromArgs ? options.attributesFromArgs(args) : {}; const handleTailSampling = (success, duration, error) => { if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") { shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, { success, duration, error }); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_KEEP, shouldKeepSpan); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_EVALUATED, true); } }; const onSuccess = (result) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "success" }); durationHistogram?.record(duration, { operation: spanName, status: "success" }); const resultAttributes = options.attributesFromResult ? options.attributesFromResult(result) : {}; span2.setStatus({ code: api.SpanStatusCode.OK }); span2.setAttributes({ ...argsAttributes, ...resultAttributes, "operation.name": spanName, "code.function": spanName, "operation.duration": duration, "operation.success": true }); handleTailSampling(true, duration); span2.end(); void flushIfNeeded(); return result; }; const onError = (error) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "error" }); durationHistogram?.record(duration, { operation: spanName, status: "error" }); const errorMessage = error instanceof Error ? error.message : "Unknown error"; const truncatedMessage = truncateErrorMessage(errorMessage); span2.setStatus({ code: api.SpanStatusCode.ERROR, message: truncatedMessage }); span2.setAttributes({ ...argsAttributes, "operation.name": spanName, "code.function": spanName, "operation.duration": duration, "operation.success": false, error: true, "exception.type": error instanceof Error ? error.constructor.name : "Error", "exception.message": truncatedMessage }); span2.recordException( error instanceof Error ? error : new Error(String(error)) ); handleTailSampling(false, duration, error); span2.end(); void flushIfNeeded(); throw error; }; try { callCounter?.add(1, { operation: spanName, status: "started" }); const result = fn.call(this, ...args); if (result instanceof Promise) { return result.then(onSuccess, onError); } return onSuccess(result); } catch (error) { return onError(error); } }); } ); } wrappedFunction[INSTRUMENTED_SYMBOL] = true; Object.defineProperty(wrappedFunction, "name", { value: tempFn.name || "trace", configurable: true }); return wrappedFunction; } function executeImmediately(fn, options) { const config = chunkESLWRGAG_cjs.getConfig(); const tracer = config.tracer; const meter = config.meter; const sampler = options.sampler || new chunkVH77IPJN_cjs.AlwaysSampler(); const spanName = options.name || "anonymous"; const samplingContext = { operationName: spanName, args: [], metadata: {} }; const shouldSample = sampler.shouldSample(samplingContext); const needsTailSampling = "needsTailSampling" in sampler && typeof sampler.needsTailSampling === "function" ? sampler.needsTailSampling() : false; if (!shouldSample && !needsTailSampling) { return fn(createDummyCtx()); } const startTime = performance.now(); const isRootSpan = options.startNewRoot || api.trace.getActiveSpan() === void 0; const shouldAutoFlush = options.flushOnRootSpanEnd ?? chunk563EL6O6_cjs.getConfig()?.flushOnRootSpanEnd ?? true; const shouldAutoFlushSpans = chunk563EL6O6_cjs.getConfig()?.forceFlushOnShutdown ?? false; const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, { description: `Call count for ${spanName}`, unit: "1" }) : void 0; const durationHistogram = options.withMetrics ? meter.createHistogram(`${spanName}.duration`, { description: `Duration for ${spanName}`, unit: "ms" }) : void 0; const flushIfNeeded = async () => { if (!shouldAutoFlush || !isRootSpan) return; try { const queue = getEventQueue(); if (queue && queue.size() > 0) { await queue.flush(); } if (shouldAutoFlushSpans) { const sdk = chunk563EL6O6_cjs.getSdk(); if (sdk) { try { const sdkAny = sdk; if (typeof sdkAny.getTracerProvider === "function") { const tracerProvider = sdkAny.getTracerProvider(); if (tracerProvider && typeof tracerProvider.forceFlush === "function") { await tracerProvider.forceFlush(); } } } catch { } } } } catch (error) { const initConfig = chunk563EL6O6_cjs.getConfig(); const logger = initConfig?.logger; if (logger?.error) { logger.error( { err: error instanceof Error ? error : void 0 }, `[autotel] Auto-flush failed${error instanceof Error ? "" : `: ${String(error)}`}` ); } } }; const spanOptions = {}; if (options.startNewRoot) { spanOptions.root = true; } if (options.spanKind !== void 0) { spanOptions.kind = options.spanKind; } const parentContext = chunkHZ3FYBJG_cjs.getActiveContextWithBaggage(); return tracer.startActiveSpan( spanName, spanOptions, parentContext, (span2) => { return chunkD5LMF53P_cjs.runInOperationContext(spanName, () => { let shouldKeepSpan = true; chunkGML3FBOT_cjs.setSpanName(span2, spanName); const ctxValue = chunkHZ3FYBJG_cjs.createTraceContext(span2); const handleTailSampling = (success, duration, error) => { if (needsTailSampling && "shouldKeepTrace" in sampler && typeof sampler.shouldKeepTrace === "function") { shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, { success, duration, error }); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_KEEP, shouldKeepSpan); span2.setAttribute(chunkVH77IPJN_cjs.AUTOTEL_SAMPLING_TAIL_EVALUATED, true); } }; const onSuccessSync = (result) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "success" }); durationHistogram?.record(duration, { operation: spanName, status: "success" }); span2.setStatus({ code: api.SpanStatusCode.OK }); span2.setAttributes({ "operation.name": spanName, "code.function": spanName, "operation.duration": duration, "operation.success": true }); handleTailSampling(true, duration); span2.end(); void flushIfNeeded(); return result; }; const onErrorSync = (error) => { const duration = performance.now() - startTime; callCounter?.add(1, { operation: spanName, status: "error" }); durationHistogram?.record(duration, { operation: spanName, status: "error" }); const errorMessage = error instanceof Error ? error.message : "Unknown error"; const truncatedMessage = truncateErrorMessage(er