autotel
Version:
Write Once, Observe Anywhere
1 lines • 29.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/autotel-logger.ts","../src/logger.ts"],"names":["createContextKey","api_context","trace","err","rest","errorAttrs","getConfig","SpanStatusCode"],"mappings":"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","file":"chunk-FU6R566Y.cjs","sourcesContent":["/**\n * Zero-dependency structured logger for autotel\n *\n * This logger provides:\n * - Structured JSON logging (production) or pretty print (development)\n * - Auto trace context injection (traceId, spanId, correlationId)\n * - Dynamic log level control (per-request via OTel context)\n * - Level support (debug, info, warn, error, none)\n * - Zero additional dependencies (uses @opentelemetry/api, already a dep)\n *\n * Uses Pino-compatible signature supporting both patterns:\n * - `log.info('simple message')` - string-only\n * - `log.info({ userId: '123' }, 'message')` - object first with optional message\n *\n * Used as the default fallback when users don't provide Pino/Bunyan.\n * Can also be used directly: import { createBuiltinLogger } from 'autotel/logger'\n *\n * @example\n * ```typescript\n * import { createBuiltinLogger, runWithLogLevel } from 'autotel/logger';\n *\n * const log = createBuiltinLogger('my-service');\n *\n * // Simple message (no metadata)\n * log.info('Server started');\n *\n * // With metadata (Pino-style: object first, message second)\n * log.info({ userId: '123' }, 'User created');\n * // Output: {\"level\":\"info\",\"service\":\"my-service\",\"msg\":\"User created\",\"userId\":\"123\",\"traceId\":\"...\"}\n *\n * // Dynamic log level per-request\n * runWithLogLevel('debug', () => {\n * log.debug('This will log even if default level is \"info\"');\n * });\n * ```\n */\n\nimport {\n trace,\n context as api_context,\n createContextKey,\n} from '@opentelemetry/api';\nimport type { Logger } from './logger';\n\nexport type BuiltinLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\n/**\n * Context key for storing active log level (enables per-request log levels)\n */\nconst LOG_LEVEL_KEY = createContextKey('autotel-log-level');\n\n/**\n * Get the active log level from context (if set)\n * Falls back to undefined if no log level is set in context\n */\nexport function getActiveLogLevel(): BuiltinLogLevel | undefined {\n return api_context.active().getValue(LOG_LEVEL_KEY) as\n | BuiltinLogLevel\n | undefined;\n}\n\n/**\n * Run a function with a specific log level\n * The log level is stored in OpenTelemetry context and applies to all logger calls within the callback\n *\n * @example\n * ```typescript\n * // Enable debug logging for a specific request\n * runWithLogLevel('debug', () => {\n * log.debug('This will be logged');\n * processRequest();\n * });\n *\n * // Disable logging temporarily\n * runWithLogLevel('none', () => {\n * log.info('This will NOT be logged');\n * });\n * ```\n */\nexport function runWithLogLevel<T>(\n level: BuiltinLogLevel,\n callback: () => T,\n): T {\n const ctx = api_context.active().setValue(LOG_LEVEL_KEY, level);\n return api_context.with(ctx, callback);\n}\n\n/**\n * Get current trace context from active span\n * Returns null if no active span exists\n */\nfunction getTraceContextInternal(): {\n traceId: string;\n spanId: string;\n correlationId: string;\n} | null {\n const span = trace.getActiveSpan();\n if (!span) return null;\n\n const ctx = span.spanContext();\n return {\n traceId: ctx.traceId,\n spanId: ctx.spanId,\n correlationId: ctx.traceId.slice(0, 16), // First 16 chars for grouping\n };\n}\n\n/**\n * Helper to get trace context (useful for BYOL - Bring Your Own Logger)\n *\n * @example\n * ```typescript\n * import bunyan from 'bunyan';\n * import { getTraceContext } from 'autotel/logger';\n *\n * const bunyanLogger = bunyan.createLogger({ name: 'myapp' });\n * const ctx = getTraceContext();\n * bunyanLogger.info({ ...ctx, email: 'test@example.com' }, 'Creating user');\n * ```\n */\nexport function getTraceContext() {\n return getTraceContextInternal();\n}\n\nexport interface BuiltinLoggerOptions {\n /** Minimum log level. Default: 'info' */\n level?: BuiltinLogLevel;\n /** Pretty print for development. Default: false (JSON output) */\n pretty?: boolean;\n}\n\n/**\n * Create a lightweight structured logger\n *\n * @param service - Service name for logging\n * @param options - Optional configuration\n *\n * @example\n * ```typescript\n * const log = createBuiltinLogger('user-service');\n *\n * log.info('Creating user', { email: 'test@example.com' });\n * // Output: {\"level\":\"info\",\"service\":\"user-service\",\"msg\":\"Creating user\",\n * // \"email\":\"test@example.com\",\"traceId\":\"...\",\"spanId\":\"...\"}\n *\n * // Dynamic log level control per-request\n * runWithLogLevel('debug', () => {\n * log.debug('This will be logged even if logger was created with level: \"info\"');\n * });\n * ```\n */\nexport function createBuiltinLogger(\n service: string,\n options?: BuiltinLoggerOptions,\n): Logger {\n const defaultLevel = options?.level || 'info';\n const pretty = options?.pretty || false;\n\n const levelPriority: Record<BuiltinLogLevel, number> = {\n none: -1,\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n const shouldLog = (level: BuiltinLogLevel): boolean => {\n // Priority: context level > options level > 'info' default\n const activeLevel = getActiveLogLevel() ?? defaultLevel;\n\n // 'none' means suppress all logging\n if (activeLevel === 'none') return false;\n\n return levelPriority[level] >= levelPriority[activeLevel];\n };\n\n const log = (\n level: 'info' | 'error' | 'warn' | 'debug',\n msg: string,\n attrs?: Record<string, unknown>,\n ) => {\n if (!shouldLog(level)) return;\n\n const ctx = getTraceContextInternal();\n const logEntry: Record<string, unknown> = {\n level,\n service,\n msg,\n ...attrs,\n ...ctx, // Auto-inject traceId, spanId, correlationId\n timestamp: new Date().toISOString(),\n };\n\n if (pretty) {\n // Pretty print for development\n const traceInfo = ctx\n ? ` [${ctx.traceId.slice(0, 8)}.../${ctx.spanId.slice(0, 8)}...]`\n : '';\n console.log(\n `[${level.toUpperCase()}]${traceInfo} ${service}: ${msg}`,\n attrs || '',\n );\n } else {\n // Structured JSON for production\n console.log(JSON.stringify(logEntry));\n }\n };\n\n // Pino-compatible signature: supports both:\n // - logger.info('message') - string-only\n // - logger.info({ extra }, 'message') - Pino style with metadata\n // Also auto-detects and handles legacy Winston-style: logger.info('message', { extra })\n const createLogMethod = (level: 'info' | 'warn' | 'debug') => {\n return (\n extraOrMessage: Record<string, unknown> | string,\n message?: string | Record<string, unknown>,\n ) => {\n if (typeof extraOrMessage === 'string') {\n // First arg is string - could be:\n // 1. String-only: logger.info('message')\n // 2. Legacy Winston-style: logger.info('message', { extra })\n if (message !== undefined && typeof message === 'object') {\n // Legacy Winston-style detected - auto-swap for backward compatibility\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[autotel] Legacy logger pattern detected: logger.${level}('message', metadata). ` +\n `Autotel recommends Pino signature: logger.${level}({ ...metadata }, 'message'). ` +\n `Auto-swapping arguments for compatibility.`,\n );\n }\n // Swap: treat first arg as message, second as metadata\n log(level, extraOrMessage, message as Record<string, unknown>);\n } else {\n // Pure string-only call: logger.info('message')\n log(level, extraOrMessage);\n }\n } else {\n // Pino style: logger.info({ extra }, 'message')\n log(level, (message as string) || '', extraOrMessage);\n }\n };\n };\n\n return {\n info: createLogMethod('info'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n\n error: (\n extraOrMessage: Record<string, unknown> | string,\n message?: string | Record<string, unknown> | Error,\n ) => {\n if (typeof extraOrMessage === 'string') {\n // First arg is string - could be:\n // 1. String-only: logger.error('message')\n // 2. Legacy: logger.error('message', error) - Error as second arg\n // 3. Legacy: logger.error('message', { extra }) - object as second arg\n\n // Handle legacy logger.error('message', error) pattern\n if (message instanceof Error) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[autotel] Legacy logger pattern detected: logger.error('message', error). ` +\n `Autotel recommends Pino signature: logger.error({ err: error }, 'message'). ` +\n `Auto-swapping arguments for compatibility.`,\n );\n }\n log('error', extraOrMessage, {\n error: message.message,\n stack: message.stack,\n name: message.name,\n });\n return;\n }\n\n // Handle legacy logger.error('message', { extra }) pattern\n if (message !== undefined && typeof message === 'object') {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[autotel] Legacy logger pattern detected: logger.error('message', metadata). ` +\n `Autotel recommends Pino signature: logger.error({ ...metadata }, 'message'). ` +\n `Auto-swapping arguments for compatibility.`,\n );\n }\n // Swap: treat first arg as message, second as metadata (handle err extraction)\n const extra = message as Record<string, unknown>;\n const { err, ...rest } = extra as Record<string, unknown> & {\n err?: unknown;\n };\n let errorAttrs: Record<string, unknown> = rest;\n if (err instanceof Error) {\n errorAttrs = {\n error: err.message,\n stack: err.stack,\n name: err.name,\n ...rest,\n };\n } else if (err !== undefined) {\n errorAttrs = { err, ...rest };\n }\n log('error', extraOrMessage, errorAttrs);\n return;\n }\n\n // Pure string-only call: logger.error('message')\n log('error', extraOrMessage);\n return;\n }\n\n // Pino style: logger.error({ err, ...extra }, 'message')\n // Extract err from extra if present (Pino convention)\n const { err, ...rest } = extraOrMessage as Record<string, unknown> & {\n err?: unknown;\n };\n let errorAttrs: Record<string, unknown> = rest;\n if (err instanceof Error) {\n // err is an Error - extract message, stack, name for structured logging\n errorAttrs = {\n error: err.message,\n stack: err.stack,\n name: err.name,\n ...rest,\n };\n } else if (err !== undefined) {\n // err is not an Error but exists - preserve it as-is\n errorAttrs = {\n err,\n ...rest,\n };\n }\n log('error', (message as string) || '', errorAttrs);\n },\n };\n}\n\n/**\n * Pino-like factory function for creating an autotel logger\n *\n * @example\n * ```typescript\n * import { autotelLogger } from 'autotel/logger';\n *\n * const log = autotelLogger();\n *\n * // Simple message\n * log.info('Server started');\n *\n * // With metadata (Pino-style: object first, message second)\n * log.info({ userId: '123' }, 'User created');\n *\n * // With options\n * const devLog = autotelLogger({ service: 'my-app', level: 'debug', pretty: true });\n * ```\n */\nexport function autotelLogger(options?: {\n service?: string;\n level?: BuiltinLogLevel;\n pretty?: boolean;\n}): Logger {\n return createBuiltinLogger(options?.service || 'app', {\n level: options?.level,\n pretty: options?.pretty,\n });\n}\n","/**\n * Logger types and utilities for autotel\n *\n * **Zero-Config Option:** Don't provide a logger to `init()` and autotel uses\n * a built-in structured JSON logger with automatic trace context injection.\n *\n * **BYOL (Bring Your Own Logger):** Pass Pino or Bunyan to `init()` for\n * automatic instrumentation with trace context and OTLP log export.\n *\n * ## Logger Signature\n *\n * Autotel v2.10+ uses **Pino's signature**: `logger.info({ metadata }, 'message')`.\n *\n * ### Backward Compatibility\n *\n * The built-in logger auto-detects legacy Winston-style calls and swaps arguments:\n * ```typescript\n * // Legacy (auto-detected and handled)\n * logger.info('User created', { userId: '123' });\n * // → Internally treated as: logger.info({ userId: '123' }, 'User created')\n * // → Logs warning in development, works silently in production\n * ```\n *\n * ### Recommended Usage\n *\n * ```typescript\n * // ✅ Pino-style (preferred)\n * logger.info({ userId: '123' }, 'User created');\n *\n * // ✅ Simple message (no metadata)\n * logger.info('Server started');\n * ```\n *\n * **Note:** If you BYOL (bring your own logger), it must use Pino signature.\n * Winston and other `(message, meta)` loggers are NOT compatible.\n * For Winston, use `@opentelemetry/instrumentation-winston` instead.\n *\n * @example Zero-config (uses built-in logger)\n * ```typescript\n * import { init } from 'autotel';\n *\n * init({ service: 'my-app' });\n * // Internal logs: {\"level\":\"info\",\"service\":\"my-app\",\"msg\":\"...\",\"traceId\":\"...\"}\n * ```\n *\n * @example Using built-in logger directly\n * ```typescript\n * import { createBuiltinLogger, runWithLogLevel } from 'autotel/logger';\n *\n * const log = createBuiltinLogger('my-service');\n *\n * // Simple message (no metadata)\n * log.info('Server started');\n *\n * // With metadata (Pino-style: object first, message second)\n * log.info({ userId: '123' }, 'User created');\n * // Output: {\"level\":\"info\",\"service\":\"my-service\",\"msg\":\"User created\",\"userId\":\"123\",\"traceId\":\"...\"}\n *\n * // Dynamic log level per-request\n * runWithLogLevel('debug', () => {\n * log.debug('Debug info for this request only');\n * });\n * ```\n *\n * @example Using Pino (recommended for production, auto-instrumented)\n * ```typescript\n * import pino from 'pino'; // npm install pino\n * import { init } from 'autotel';\n *\n * const logger = pino({ level: 'info' });\n * init({ service: 'my-app', logger });\n *\n * // Logs automatically include traceId/spanId and export via OTLP!\n * logger.info({ userId: '123' }, 'User created');\n * ```\n *\n * @example Using Bunyan (auto-instrumented, same signature as Pino)\n * ```typescript\n * import bunyan from 'bunyan'; // npm install bunyan @opentelemetry/instrumentation-bunyan\n * import { init } from 'autotel';\n * import { BunyanInstrumentation } from '@opentelemetry/instrumentation-bunyan';\n *\n * const logger = bunyan.createLogger({ name: 'my-app' });\n * init({\n * service: 'my-app',\n * logger,\n * instrumentations: [new BunyanInstrumentation()]\n * });\n * ```\n *\n * @example Custom logger (MUST use Pino-compatible signature)\n * ```typescript\n * // ⚠️ Your custom logger MUST accept (object, message?) signature\n * const logger = {\n * info: (extra, msg) => console.log(msg || '', extra),\n * warn: (extra, msg) => console.warn(msg || '', extra),\n * error: (extra, msg) => console.error(msg || '', extra),\n * debug: (extra, msg) => console.debug(msg || '', extra),\n * };\n * init({ service: 'my-app', logger });\n * ```\n *\n * @example BYOL helper: inject trace context into any logger\n * ```typescript\n * import bunyan from 'bunyan';\n * import { getTraceContext } from 'autotel/logger';\n *\n * const bunyanLogger = bunyan.createLogger({ name: 'myapp' });\n * const ctx = getTraceContext();\n * bunyanLogger.info({ ...ctx, userId: '123' }, 'Creating user');\n * ```\n */\n\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { getConfig } from './config';\n\n// ============================================================================\n// Logger Types\n// ============================================================================\n\n/**\n * Log level constants\n */\nexport const LOG_LEVEL = {\n DEBUG: 'debug',\n INFO: 'info',\n WARN: 'warn',\n ERROR: 'error',\n} as const;\n\nexport type LogLevel = (typeof LOG_LEVEL)[keyof typeof LOG_LEVEL];\n\n/**\n * Logger configuration (for reference - not needed with BYOL approach)\n */\nexport interface LoggerConfig {\n service: string;\n level?: LogLevel;\n pretty?: boolean;\n redact?: string[] | false;\n}\n\n/**\n * Pino-compatible log function signature\n *\n * Matches Pino's actual LogFn type which supports:\n * - `(msg: string)` - simple string message\n * - `(obj: object, msg?: string)` - object first with optional message\n *\n * @example\n * ```typescript\n * logger.info('User logged in');\n * logger.info({ userId: '123' }, 'User created');\n * logger.error({ err: error }, 'Operation failed');\n * ```\n */\nexport interface LogFn {\n (msg: string): void;\n (obj: Record<string, unknown>, msg?: string): void;\n}\n\n/**\n * Simple logger interface - Pino/Bunyan-compatible\n *\n * Uses Pino's LogFn signature which supports both:\n * - `logger.info('message')` - simple string message\n * - `logger.info({ extra }, 'message')` - object first with optional message\n *\n * This is compatible with Pino, Bunyan, and any logger following this pattern.\n *\n * @example Using Pino (just works!)\n * ```typescript\n * import pino from 'pino';\n * const logger = pino({ level: 'info' });\n * init({ service: 'my-app', logger });\n * ```\n *\n * @example Direct usage\n * ```typescript\n * logger.info('Simple message');\n * logger.info({ userId: '123' }, 'User created');\n * logger.error({ err: error }, 'Operation failed');\n * ```\n */\nexport interface Logger {\n info: LogFn;\n warn: LogFn;\n error: LogFn;\n debug: LogFn;\n}\n\n/**\n * Alias for Logger interface (backwards compatibility)\n * @deprecated Use Logger instead\n */\nexport type ILogger = Logger;\n\n/**\n * Pino logger type - re-exported for convenience\n *\n * Note: This is a type-only export. To use Pino, install it as a peer dependency:\n * `npm install pino`\n */\nexport type { Logger as PinoLogger } from 'pino';\n\n// ============================================================================\n// LoggedOperation Decorator\n// ============================================================================\n\nexport interface LoggedOperationOptions {\n /** Operation name for tracing (e.g., 'user.createUser') */\n operationName: string;\n}\n\n/**\n * TS5+ Standard Decorator for logging and tracing operations\n * Uses TC39 Stage 3 decorator syntax\n *\n * This is the traditional per-method decorator approach.\n * For zero-boilerplate solution, see @Instrumented class decorator.\n *\n * @example\n * // Simple usage (Pino-style: object first, message second)\n * class OrderService {\n * constructor(private readonly deps: { log: Logger }) {}\n *\n * @LoggedOperation('order.create')\n * async createOrder(data: CreateOrderData) {\n * // ✅ Correct Pino-style logging\n * this.deps.log.info({ orderId: data.id }, 'Creating order');\n * }\n * }\n *\n * // Advanced usage (future-proof for options)\n * @LoggedOperation({ operationName: 'order.create' })\n * async createOrder(data: CreateOrderData) { }\n */\nexport function LoggedOperation(\n operationNameOrOptions: string | LoggedOperationOptions,\n) {\n const operationName =\n typeof operationNameOrOptions === 'string'\n ? operationNameOrOptions\n : operationNameOrOptions.operationName;\n\n return function <This, Args extends unknown[], Return>(\n originalMethod: (this: This, ...args: Args) => Promise<Return>,\n context: ClassMethodDecoratorContext<\n This,\n (this: This, ...args: Args) => Promise<Return>\n >,\n ) {\n const methodName = String(context.name);\n\n return async function (this: This, ...args: Args): Promise<Return> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const log = (this as any).deps?.log;\n const startTime = performance.now();\n\n const config = getConfig();\n const tracer = config.tracer;\n\n return tracer.startActiveSpan(operationName, async (span) => {\n try {\n log?.info(\n {\n operation: operationName,\n method: methodName,\n args,\n },\n 'Operation started',\n );\n\n const result = await originalMethod.apply(this, args);\n\n const duration = performance.now() - startTime;\n log?.info(\n {\n operation: operationName,\n method: methodName,\n duration,\n },\n 'Operation completed',\n );\n\n span.setStatus({ code: SpanStatusCode.OK });\n span.setAttributes({\n 'operation.name': operationName,\n 'operation.method': methodName,\n 'operation.duration': duration,\n 'operation.success': true,\n });\n\n return result;\n } catch (error) {\n const duration = performance.now() - startTime;\n log?.error(\n {\n err: error instanceof Error ? error : undefined,\n operation: operationName,\n method: methodName,\n duration,\n },\n 'Operation failed',\n );\n\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : 'Unknown error',\n });\n span.setAttributes({\n 'operation.name': operationName,\n 'operation.method': methodName,\n 'operation.duration': duration,\n 'operation.success': false,\n 'error.type':\n error instanceof Error ? error.constructor.name : 'Unknown',\n });\n\n throw error;\n } finally {\n span.end();\n }\n });\n };\n };\n}\n\n// ============================================================================\n// Built-in Logger (re-exports)\n// ============================================================================\n\nexport {\n autotelLogger,\n createBuiltinLogger,\n runWithLogLevel,\n getTraceContext,\n getActiveLogLevel,\n type BuiltinLogLevel,\n type BuiltinLoggerOptions,\n} from './autotel-logger';\n"]}