@autofleet/logger 4.4.0-beta.1 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +32 -0
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["levelToSeverity: Record<LevelOrString, string>","ddMode: DDMode","AsyncLocalStorage","#contextMiddlewares","context","#logger","stdSerializers","newMetadata: Record<string, unknown>"],"sources":["../src/gcpLogOptions.ts","../src/datadogClient.ts","../src/context.ts","../src/index.ts"],"sourcesContent":["// Based on https://github.com/simenandre/pino-cloud-logging/blob/7816b2e4dec3dd18ef34c0f3f241d88a4cab0b90/src/main.ts\n// The repo is licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0\n// Extracted since the repo no longer receives updates\n\nimport type { LevelOrString, LoggerOptions } from 'pino';\n\n// Map Pino levels to Google Cloud Logging severity levels\n// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity\nconst levelToSeverity: Record<LevelOrString, string> = {\n trace: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n fatal: 'CRITICAL',\n};\n\nexport interface ServiceContext {\n serviceName?: string;\n version?: string;\n tags?: Record<string, string>;\n}\n\nexport function gcpLogOptions(\n options?: LoggerOptions,\n context: ServiceContext = {},\n): LoggerOptions {\n const { serviceName, version, tags } = context;\n\n const baseObj = {\n ...(serviceName && version && { serviceContext: { service: serviceName, version } }),\n ...tags,\n };\n const base = Object.keys(baseObj).length ? baseObj : null;\n\n return {\n // https://cloud.google.com/error-reporting/docs/formatting-error-messages#json_representation\n base,\n formatters: {\n level: (label: string) => ({\n severity: levelToSeverity[label] ?? levelToSeverity.info,\n // `@type` property tells Error Reporting to track even if there is no `stack_trace`\n ...(['error', 'fatal'].includes(label) && { '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' }),\n }),\n\n log(object) {\n const logObject = object as { err?: Error; error?: Error; e?: Error; };\n const stackTrace = logObject.err?.stack ?? logObject.error?.stack ?? logObject.e?.stack;\n // eslint-disable-next-line camelcase\n return { ...object, ...(stackTrace && { stack_trace: stackTrace }) };\n },\n },\n messageKey: 'message',\n ...options,\n };\n}\n","import { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\n\ninterface DDTracer {\n dogstatsd: {\n increment: (metric: string, value: number, tags?: Record<string, string | number>) => void;\n };\n}\n\nconst tryRequire = (path: string): DDTracer | null => {\n try {\n const t = _require(path) as DDTracer;\n return t?.dogstatsd ? t : null;\n } catch {\n return null;\n }\n};\n\nconst tracer = tryRequire('dd-trace') ?? tryRequire('/opt/datadog/apm/library/js/node_modules/dd-trace');\nconst isDebugLevel = ['debug', 'trace'].includes(process.env.LOG_LEVEL || process.env.PINO_LOG_LEVEL || '');\n\nconst ddSite = process.env.DD_SITE || 'datadoghq.eu';\nconst ddApiKey = !tracer ? process.env.DD_API_KEY : null;\n\nexport type DDMode = 'dd-trace' | 'api' | 'none';\nexport const ddMode: DDMode = tracer ? 'dd-trace' : ddApiKey ? 'api' : 'none';\nprocess.stdout.write(`[logger] Datadog metrics mode initialized: ${ddMode}\\n`);\n\nexport const sendDDEvent = (eventName: string, data: Record<string, unknown>): void => {\n const service = process.env.DD_SERVICE || process.env.SERVICE_NAME || 'unknown';\n const env = process.env.DD_ENV || process.env.ENV_NAME || 'local';\n\n const primitiveData = Object.fromEntries(\n Object.entries(data).filter(([, v]) => typeof v === 'string' || typeof v === 'number'),\n ) as Record<string, string | number>;\n\n if (tracer) {\n tracer.dogstatsd.increment(eventName, 1, { service, env, ...primitiveData });\n return;\n }\n\n if (!ddApiKey) return;\n\n const tags = [\n `service:${service}`,\n `env:${env}`,\n ...Object.entries(primitiveData).map(([k, v]) => `${k}:${String(v)}`),\n ];\n\n fetch(`https://api.${ddSite}/api/v2/series`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'DD-API-KEY': ddApiKey },\n body: JSON.stringify({\n series: [{\n metric: eventName,\n type: 1,\n points: [{ timestamp: Math.floor(Date.now() / 1000), value: 1 }],\n tags,\n }],\n }),\n }).then((res) => {\n if (!res.ok && isDebugLevel) {\n res.text().then((body) => {\n process.stderr.write(`[logger] DD API error ${res.status}: ${body}\\n`);\n }).catch(() => undefined);\n }\n }).catch((err) => {\n if (isDebugLevel) {\n process.stderr.write(`[logger] DD API fetch failed: ${String(err)}\\n`);\n }\n });\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\ntype LogContext = Record<string, string | number>;\n\nconst storage = new AsyncLocalStorage<LogContext>();\n\nexport function addToContext(context: Record<string, string | number>): void {\n const store = storage.getStore();\n if (!store) return;\n for (const [key, value] of Object.entries(context)) {\n if (typeof value !== 'string' && typeof value !== 'number') continue;\n store[key] = value;\n }\n}\n\nexport function runWithLoggerContext<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\nexport function loggerContextMiddleware(_req: unknown, _res: unknown, next: () => void): void {\n storage.run({}, next);\n}\n\nexport function getContext(): LogContext {\n return storage.getStore() ?? {};\n}\n","import Pino, { type LoggerOptions, stdSerializers } from 'pino';\nimport { gcpLogOptions, type ServiceContext } from './gcpLogOptions';\nimport { sendDDEvent } from './datadogClient';\nimport { getContext, addToContext, runWithLoggerContext, loggerContextMiddleware } from './context';\n\nexport { type ServiceContext };\n\nexport enum LogLevel {\n trace = 'trace',\n debug = 'debug',\n info = 'info',\n warn = 'warn',\n error = 'error',\n fatal = 'fatal',\n}\ntype LazyLogFn = () => [string] | [string, unknown];\n\nconst getLevel = (logLevel?: LogLevel): LogLevel => {\n if (logLevel) return logLevel;\n if (process.env.LOG_LEVEL) return process.env.LOG_LEVEL as LogLevel;\n if (process.env.NODE_ENV && ['production', 'staging', 'test'].includes(process.env.NODE_ENV)) return LogLevel.info;\n if (process.env.NODE_ENV === 'development') return LogLevel.debug;\n return LogLevel.debug;\n};\n\nconst createLoggerInstance = (level: LogLevel, options: Omit<LoggerOptions, 'transport'>, context?: ServiceContext): Pino.Logger => {\n let loggerInstance;\n if (process.env.NODE_ENV === 'production') {\n loggerInstance = Pino(gcpLogOptions({\n level: process.env.PINO_LOG_LEVEL || 'info',\n ...options,\n } as object, context));\n } else {\n loggerInstance = Pino({\n level: process.env.PINO_LOG_LEVEL || 'info',\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true,\n },\n },\n ...options,\n });\n }\n loggerInstance.level = level;\n return loggerInstance;\n};\n\ntype MiddlewareFunction = () => Record<string, unknown>;\n\nexport class LoggerInstanceManager {\n #contextMiddlewares: MiddlewareFunction[] = [getContext];\n\n #logger: Pino.Logger;\n\n constructor(logLevel?: LogLevel, context?: ServiceContext) {\n this.#logger = createLoggerInstance(getLevel(logLevel), {\n mixin: () => this.addMetadata(),\n serializers: {\n ...stdSerializers,\n e: stdSerializers.errWithCause,\n err: stdSerializers.errWithCause,\n error: stdSerializers.errWithCause,\n errors(val) {\n if (Array.isArray(val)) {\n return val.map(stdSerializers.errWithCause);\n }\n return stdSerializers.errWithCause(val);\n },\n },\n }, context);\n }\n\n // To support winston like logging in pino\n private winstonLikeLoggerCall(logLevel: LogLevel, message: string, meta: unknown): void {\n if (message && typeof message === 'object' && typeof meta === 'string') {\n // Fastify sends the message as the second parameter when logging (which is how pino actually works),\n this.#logger[logLevel](message as object, meta);\n } else if (meta) {\n this.#logger[logLevel](meta, message);\n } else {\n this.#logger[logLevel](message);\n }\n }\n\n private lazyLoggerCall(logLevel: LogLevel, fn: LazyLogFn): void {\n if (!this.#logger.isLevelEnabled(logLevel)) {\n return;\n }\n const [message, meta] = fn();\n this.winstonLikeLoggerCall(logLevel, message, meta);\n }\n\n private addMetadata(): Record<string, unknown> {\n const newMetadata: Record<string, unknown> = {};\n this.#contextMiddlewares.forEach((middleware) => {\n const metadata = middleware();\n Object.assign(newMetadata, metadata);\n });\n return newMetadata;\n }\n\n addContextMiddleware = (middleware: MiddlewareFunction): number => this.#contextMiddlewares.push(middleware);\n\n addToContext = (context: Record<string, string | number>): void => addToContext(context);\n\n trace = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.trace, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the trace level is enabled */\n traceLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.trace, fn);\n\n debug = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.debug, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the debug level is enabled */\n debugLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.debug, fn);\n\n info = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.info, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the info level is enabled */\n infoLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.info, fn);\n\n warn = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.warn, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the warn level is enabled */\n warnLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.warn, fn);\n\n error = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.error, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the error level is enabled */\n errorLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.error, fn);\n\n fatal = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.fatal, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the fatal level is enabled */\n fatalLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.fatal, fn);\n\n /** No-op logging method for silent level (required for Fastify compatibility) */\n silent = (_message: string, _meta?: unknown): void => {\n // no-op\n };\n\n trackEvent = (eventName: string, data: Record<string, unknown> = {}): void => {\n sendDDEvent(eventName, data);\n };\n\n child: OverloadedChildLoggerMethod = (...args: [metadata: Record<string, unknown>] | [instanceNamePrefix: string, metadata: Record<string, unknown>]) => {\n const childLoggerInstanceManager = new LoggerInstanceManager();\n childLoggerInstanceManager.#contextMiddlewares = [...this.#contextMiddlewares];\n\n if (typeof args[0] === 'string') {\n // Overload: child(instanceNamePrefix: string, metadata: Record<string, unknown>)\n const [instanceNamePrefix, metadata] = args;\n childLoggerInstanceManager.#logger = this.#logger.child(metadata!, { msgPrefix: `[${instanceNamePrefix}] ` });\n } else if (typeof args[0] === 'object') {\n // Overload: child(metadata: Record<string, unknown>)\n childLoggerInstanceManager.#logger = this.#logger.child(args[0]);\n } else {\n throw new Error('Invalid arguments to child logger');\n }\n return childLoggerInstanceManager;\n };\n\n get level(): Pino.LevelWithSilentOrString {\n return this.#logger.level;\n }\n}\n\ninterface OverloadedChildLoggerMethod {\n (metadata: Record<string, unknown>): LoggerInstanceManager;\n (instanceNamePrefix: string, metadata: Record<string, unknown>): LoggerInstanceManager;\n}\n\nexport { addToContext, runWithLoggerContext, loggerContextMiddleware };\n\nexport default (loglevel?: `${LogLevel}`, context?: ServiceContext): LoggerInstanceManager => new LoggerInstanceManager(loglevel as LogLevel, context);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["levelToSeverity: Record<LevelOrString, string>","ddMode: DDMode","AsyncLocalStorage","#contextMiddlewares","context","#logger","stdSerializers","newMetadata: Record<string, unknown>"],"sources":["../src/gcpLogOptions.ts","../src/datadogClient.ts","../src/context.ts","../src/index.ts"],"sourcesContent":["// Based on https://github.com/simenandre/pino-cloud-logging/blob/7816b2e4dec3dd18ef34c0f3f241d88a4cab0b90/src/main.ts\n// The repo is licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0\n// Extracted since the repo no longer receives updates\n\nimport type { LevelOrString, LoggerOptions } from 'pino';\n\n// Map Pino levels to Google Cloud Logging severity levels\n// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity\nconst levelToSeverity: Record<LevelOrString, string> = {\n trace: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n fatal: 'CRITICAL',\n};\n\nexport interface ServiceContext {\n serviceName?: string;\n version?: string;\n tags?: Record<string, string>;\n}\n\nexport function gcpLogOptions(\n options?: LoggerOptions,\n context: ServiceContext = {},\n): LoggerOptions {\n const { serviceName, version, tags } = context;\n\n const baseObj = {\n ...(serviceName && version && { serviceContext: { service: serviceName, version } }),\n ...tags,\n };\n const base = Object.keys(baseObj).length ? baseObj : null;\n\n return {\n // https://cloud.google.com/error-reporting/docs/formatting-error-messages#json_representation\n base,\n formatters: {\n level: (label: string) => ({\n severity: levelToSeverity[label] ?? levelToSeverity.info,\n // `@type` property tells Error Reporting to track even if there is no `stack_trace`\n ...(['error', 'fatal'].includes(label) && { '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' }),\n }),\n\n log(object) {\n const logObject = object as { err?: Error; error?: Error; e?: Error; };\n const stackTrace = logObject.err?.stack ?? logObject.error?.stack ?? logObject.e?.stack;\n // eslint-disable-next-line camelcase\n return { ...object, ...(stackTrace && { stack_trace: stackTrace }) };\n },\n },\n messageKey: 'message',\n ...options,\n };\n}\n","import { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\n\ninterface DDTracer {\n dogstatsd: {\n increment: (metric: string, value: number, tags?: Record<string, string | number>) => void;\n };\n}\n\nconst tryRequire = (path: string): DDTracer | null => {\n try {\n const t = _require(path) as DDTracer;\n return t?.dogstatsd ? t : null;\n } catch {\n return null;\n }\n};\n\nconst tracer = tryRequire('dd-trace') ?? tryRequire('/opt/datadog/apm/library/js/node_modules/dd-trace');\nconst isDebugLevel = ['debug', 'trace'].includes(process.env.LOG_LEVEL || process.env.PINO_LOG_LEVEL || '');\n\nconst ddSite = process.env.DD_SITE || 'datadoghq.eu';\nconst ddApiKey = !tracer ? process.env.DD_API_KEY : null;\n\nexport type DDMode = 'dd-trace' | 'api' | 'none';\nexport const ddMode: DDMode = tracer ? 'dd-trace' : ddApiKey ? 'api' : 'none';\nprocess.stdout.write(`[logger] Datadog metrics mode initialized: ${ddMode}\\n`);\n\nexport const sendDDEvent = (eventName: string, data: Record<string, unknown>): void => {\n const service = process.env.DD_SERVICE || process.env.SERVICE_NAME || 'unknown';\n const env = process.env.DD_ENV || process.env.ENV_NAME || 'local';\n\n const primitiveData = Object.fromEntries(\n Object.entries(data).filter(([, v]) => typeof v === 'string' || typeof v === 'number'),\n ) as Record<string, string | number>;\n\n if (tracer) {\n tracer.dogstatsd.increment(eventName, 1, { service, env, ...primitiveData });\n return;\n }\n\n if (!ddApiKey) return;\n\n const tags = [\n `service:${service}`,\n `env:${env}`,\n ...Object.entries(primitiveData).map(([k, v]) => `${k}:${String(v)}`),\n ];\n\n fetch(`https://api.${ddSite}/api/v2/series`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'DD-API-KEY': ddApiKey },\n body: JSON.stringify({\n series: [{\n metric: eventName,\n type: 1,\n points: [{ timestamp: Math.floor(Date.now() / 1000), value: 1 }],\n tags,\n }],\n }),\n }).then((res) => {\n if (!res.ok && isDebugLevel) {\n res.text().then((body) => {\n process.stderr.write(`[logger] DD API error ${res.status}: ${body}\\n`);\n }).catch(() => undefined);\n }\n }).catch((err) => {\n if (isDebugLevel) {\n process.stderr.write(`[logger] DD API fetch failed: ${String(err)}\\n`);\n }\n });\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\ntype LogContext = Record<string, string | number>;\n\nconst storage = new AsyncLocalStorage<LogContext>();\n\nexport function addToContext(context: Record<string, string | number>): void {\n const store = storage.getStore();\n if (!store) return;\n for (const [key, value] of Object.entries(context)) {\n if (typeof value !== 'string' && typeof value !== 'number') continue;\n store[key] = value;\n }\n}\n\nexport function runWithLoggerContext<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\nexport function loggerContextMiddleware(_req: unknown, _res: unknown, next: () => void): void {\n storage.run({}, next);\n}\n\nexport function getContext(): LogContext {\n return storage.getStore() ?? {};\n}\n","import Pino, { type LoggerOptions, stdSerializers } from 'pino';\nimport { gcpLogOptions, type ServiceContext } from './gcpLogOptions';\nimport { sendDDEvent } from './datadogClient';\nimport { getContext, addToContext, runWithLoggerContext, loggerContextMiddleware } from './context';\n\nexport { type ServiceContext };\nexport { addToContext, runWithLoggerContext, loggerContextMiddleware };\n\nexport enum LogLevel {\n trace = 'trace',\n debug = 'debug',\n info = 'info',\n warn = 'warn',\n error = 'error',\n fatal = 'fatal',\n}\ntype LazyLogFn = () => [string] | [string, unknown];\n\nconst getLevel = (logLevel?: LogLevel): LogLevel => {\n if (logLevel) return logLevel;\n if (process.env.LOG_LEVEL) return process.env.LOG_LEVEL as LogLevel;\n if (process.env.NODE_ENV && ['production', 'staging', 'test'].includes(process.env.NODE_ENV)) return LogLevel.info;\n if (process.env.NODE_ENV === 'development') return LogLevel.debug;\n return LogLevel.debug;\n};\n\nconst createLoggerInstance = (level: LogLevel, options: Omit<LoggerOptions, 'transport'>, context?: ServiceContext): Pino.Logger => {\n let loggerInstance;\n if (process.env.NODE_ENV === 'production') {\n loggerInstance = Pino(gcpLogOptions({\n level: process.env.PINO_LOG_LEVEL || 'info',\n ...options,\n } as object, context));\n } else {\n loggerInstance = Pino({\n level: process.env.PINO_LOG_LEVEL || 'info',\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true,\n },\n },\n ...options,\n });\n }\n loggerInstance.level = level;\n return loggerInstance;\n};\n\ntype MiddlewareFunction = () => Record<string, unknown>;\n\nexport class LoggerInstanceManager {\n #contextMiddlewares: MiddlewareFunction[] = [getContext];\n\n #logger: Pino.Logger;\n\n constructor(logLevel?: LogLevel, context?: ServiceContext) {\n this.#logger = createLoggerInstance(getLevel(logLevel), {\n mixin: () => this.addMetadata(),\n serializers: {\n ...stdSerializers,\n e: stdSerializers.errWithCause,\n err: stdSerializers.errWithCause,\n error: stdSerializers.errWithCause,\n errors(val) {\n if (Array.isArray(val)) {\n return val.map(stdSerializers.errWithCause);\n }\n return stdSerializers.errWithCause(val);\n },\n },\n }, context);\n }\n\n // To support winston like logging in pino\n private winstonLikeLoggerCall(logLevel: LogLevel, message: string, meta: unknown): void {\n if (message && typeof message === 'object' && typeof meta === 'string') {\n // Fastify sends the message as the second parameter when logging (which is how pino actually works),\n this.#logger[logLevel](message as object, meta);\n } else if (meta) {\n this.#logger[logLevel](meta, message);\n } else {\n this.#logger[logLevel](message);\n }\n }\n\n private lazyLoggerCall(logLevel: LogLevel, fn: LazyLogFn): void {\n if (!this.#logger.isLevelEnabled(logLevel)) {\n return;\n }\n const [message, meta] = fn();\n this.winstonLikeLoggerCall(logLevel, message, meta);\n }\n\n private addMetadata(): Record<string, unknown> {\n const newMetadata: Record<string, unknown> = {};\n this.#contextMiddlewares.forEach((middleware) => {\n const metadata = middleware();\n Object.assign(newMetadata, metadata);\n });\n return newMetadata;\n }\n\n addContextMiddleware = (middleware: MiddlewareFunction): number => this.#contextMiddlewares.push(middleware);\n\n addToContext = (context: Record<string, string | number>): void => addToContext(context);\n\n trace = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.trace, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the trace level is enabled */\n traceLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.trace, fn);\n\n debug = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.debug, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the debug level is enabled */\n debugLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.debug, fn);\n\n info = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.info, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the info level is enabled */\n infoLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.info, fn);\n\n warn = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.warn, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the warn level is enabled */\n warnLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.warn, fn);\n\n error = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.error, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the error level is enabled */\n errorLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.error, fn);\n\n fatal = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.fatal, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the fatal level is enabled */\n fatalLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.fatal, fn);\n\n /** No-op logging method for silent level (required for Fastify compatibility) */\n silent = (_message: string, _meta?: unknown): void => {\n // no-op\n };\n\n trackEvent = (eventName: string, data: Record<string, unknown> = {}): void => {\n sendDDEvent(eventName, data);\n };\n\n child: OverloadedChildLoggerMethod = (...args: [metadata: Record<string, unknown>] | [instanceNamePrefix: string, metadata: Record<string, unknown>]) => {\n const childLoggerInstanceManager = new LoggerInstanceManager();\n childLoggerInstanceManager.#contextMiddlewares = [...this.#contextMiddlewares];\n\n if (typeof args[0] === 'string') {\n // Overload: child(instanceNamePrefix: string, metadata: Record<string, unknown>)\n const [instanceNamePrefix, metadata] = args;\n childLoggerInstanceManager.#logger = this.#logger.child(metadata!, { msgPrefix: `[${instanceNamePrefix}] ` });\n } else if (typeof args[0] === 'object') {\n // Overload: child(metadata: Record<string, unknown>)\n childLoggerInstanceManager.#logger = this.#logger.child(args[0]);\n } else {\n throw new Error('Invalid arguments to child logger');\n }\n return childLoggerInstanceManager;\n };\n\n get level(): Pino.LevelWithSilentOrString {\n return this.#logger.level;\n }\n}\n\ninterface OverloadedChildLoggerMethod {\n (metadata: Record<string, unknown>): LoggerInstanceManager;\n (instanceNamePrefix: string, metadata: Record<string, unknown>): LoggerInstanceManager;\n}\n\nexport default (loglevel?: `${LogLevel}`, context?: ServiceContext): LoggerInstanceManager => new LoggerInstanceManager(loglevel as LogLevel, context);\n"],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["levelToSeverity: Record<LevelOrString, string>","ddMode: DDMode","#contextMiddlewares","context","#logger","newMetadata: Record<string, unknown>"],"sources":["../src/gcpLogOptions.ts","../src/datadogClient.ts","../src/context.ts","../src/index.ts"],"sourcesContent":["// Based on https://github.com/simenandre/pino-cloud-logging/blob/7816b2e4dec3dd18ef34c0f3f241d88a4cab0b90/src/main.ts\n// The repo is licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0\n// Extracted since the repo no longer receives updates\n\nimport type { LevelOrString, LoggerOptions } from 'pino';\n\n// Map Pino levels to Google Cloud Logging severity levels\n// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity\nconst levelToSeverity: Record<LevelOrString, string> = {\n trace: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n fatal: 'CRITICAL',\n};\n\nexport interface ServiceContext {\n serviceName?: string;\n version?: string;\n tags?: Record<string, string>;\n}\n\nexport function gcpLogOptions(\n options?: LoggerOptions,\n context: ServiceContext = {},\n): LoggerOptions {\n const { serviceName, version, tags } = context;\n\n const baseObj = {\n ...(serviceName && version && { serviceContext: { service: serviceName, version } }),\n ...tags,\n };\n const base = Object.keys(baseObj).length ? baseObj : null;\n\n return {\n // https://cloud.google.com/error-reporting/docs/formatting-error-messages#json_representation\n base,\n formatters: {\n level: (label: string) => ({\n severity: levelToSeverity[label] ?? levelToSeverity.info,\n // `@type` property tells Error Reporting to track even if there is no `stack_trace`\n ...(['error', 'fatal'].includes(label) && { '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' }),\n }),\n\n log(object) {\n const logObject = object as { err?: Error; error?: Error; e?: Error; };\n const stackTrace = logObject.err?.stack ?? logObject.error?.stack ?? logObject.e?.stack;\n // eslint-disable-next-line camelcase\n return { ...object, ...(stackTrace && { stack_trace: stackTrace }) };\n },\n },\n messageKey: 'message',\n ...options,\n };\n}\n","import { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\n\ninterface DDTracer {\n dogstatsd: {\n increment: (metric: string, value: number, tags?: Record<string, string | number>) => void;\n };\n}\n\nconst tryRequire = (path: string): DDTracer | null => {\n try {\n const t = _require(path) as DDTracer;\n return t?.dogstatsd ? t : null;\n } catch {\n return null;\n }\n};\n\nconst tracer = tryRequire('dd-trace') ?? tryRequire('/opt/datadog/apm/library/js/node_modules/dd-trace');\nconst isDebugLevel = ['debug', 'trace'].includes(process.env.LOG_LEVEL || process.env.PINO_LOG_LEVEL || '');\n\nconst ddSite = process.env.DD_SITE || 'datadoghq.eu';\nconst ddApiKey = !tracer ? process.env.DD_API_KEY : null;\n\nexport type DDMode = 'dd-trace' | 'api' | 'none';\nexport const ddMode: DDMode = tracer ? 'dd-trace' : ddApiKey ? 'api' : 'none';\nprocess.stdout.write(`[logger] Datadog metrics mode initialized: ${ddMode}\\n`);\n\nexport const sendDDEvent = (eventName: string, data: Record<string, unknown>): void => {\n const service = process.env.DD_SERVICE || process.env.SERVICE_NAME || 'unknown';\n const env = process.env.DD_ENV || process.env.ENV_NAME || 'local';\n\n const primitiveData = Object.fromEntries(\n Object.entries(data).filter(([, v]) => typeof v === 'string' || typeof v === 'number'),\n ) as Record<string, string | number>;\n\n if (tracer) {\n tracer.dogstatsd.increment(eventName, 1, { service, env, ...primitiveData });\n return;\n }\n\n if (!ddApiKey) return;\n\n const tags = [\n `service:${service}`,\n `env:${env}`,\n ...Object.entries(primitiveData).map(([k, v]) => `${k}:${String(v)}`),\n ];\n\n fetch(`https://api.${ddSite}/api/v2/series`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'DD-API-KEY': ddApiKey },\n body: JSON.stringify({\n series: [{\n metric: eventName,\n type: 1,\n points: [{ timestamp: Math.floor(Date.now() / 1000), value: 1 }],\n tags,\n }],\n }),\n }).then((res) => {\n if (!res.ok && isDebugLevel) {\n res.text().then((body) => {\n process.stderr.write(`[logger] DD API error ${res.status}: ${body}\\n`);\n }).catch(() => undefined);\n }\n }).catch((err) => {\n if (isDebugLevel) {\n process.stderr.write(`[logger] DD API fetch failed: ${String(err)}\\n`);\n }\n });\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\ntype LogContext = Record<string, string | number>;\n\nconst storage = new AsyncLocalStorage<LogContext>();\n\nexport function addToContext(context: Record<string, string | number>): void {\n const store = storage.getStore();\n if (!store) return;\n for (const [key, value] of Object.entries(context)) {\n if (typeof value !== 'string' && typeof value !== 'number') continue;\n store[key] = value;\n }\n}\n\nexport function runWithLoggerContext<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\nexport function loggerContextMiddleware(_req: unknown, _res: unknown, next: () => void): void {\n storage.run({}, next);\n}\n\nexport function getContext(): LogContext {\n return storage.getStore() ?? {};\n}\n","import Pino, { type LoggerOptions, stdSerializers } from 'pino';\nimport { gcpLogOptions, type ServiceContext } from './gcpLogOptions';\nimport { sendDDEvent } from './datadogClient';\nimport { getContext, addToContext, runWithLoggerContext, loggerContextMiddleware } from './context';\n\nexport { type ServiceContext };\n\nexport enum LogLevel {\n trace = 'trace',\n debug = 'debug',\n info = 'info',\n warn = 'warn',\n error = 'error',\n fatal = 'fatal',\n}\ntype LazyLogFn = () => [string] | [string, unknown];\n\nconst getLevel = (logLevel?: LogLevel): LogLevel => {\n if (logLevel) return logLevel;\n if (process.env.LOG_LEVEL) return process.env.LOG_LEVEL as LogLevel;\n if (process.env.NODE_ENV && ['production', 'staging', 'test'].includes(process.env.NODE_ENV)) return LogLevel.info;\n if (process.env.NODE_ENV === 'development') return LogLevel.debug;\n return LogLevel.debug;\n};\n\nconst createLoggerInstance = (level: LogLevel, options: Omit<LoggerOptions, 'transport'>, context?: ServiceContext): Pino.Logger => {\n let loggerInstance;\n if (process.env.NODE_ENV === 'production') {\n loggerInstance = Pino(gcpLogOptions({\n level: process.env.PINO_LOG_LEVEL || 'info',\n ...options,\n } as object, context));\n } else {\n loggerInstance = Pino({\n level: process.env.PINO_LOG_LEVEL || 'info',\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true,\n },\n },\n ...options,\n });\n }\n loggerInstance.level = level;\n return loggerInstance;\n};\n\ntype MiddlewareFunction = () => Record<string, unknown>;\n\nexport class LoggerInstanceManager {\n #contextMiddlewares: MiddlewareFunction[] = [getContext];\n\n #logger: Pino.Logger;\n\n constructor(logLevel?: LogLevel, context?: ServiceContext) {\n this.#logger = createLoggerInstance(getLevel(logLevel), {\n mixin: () => this.addMetadata(),\n serializers: {\n ...stdSerializers,\n e: stdSerializers.errWithCause,\n err: stdSerializers.errWithCause,\n error: stdSerializers.errWithCause,\n errors(val) {\n if (Array.isArray(val)) {\n return val.map(stdSerializers.errWithCause);\n }\n return stdSerializers.errWithCause(val);\n },\n },\n }, context);\n }\n\n // To support winston like logging in pino\n private winstonLikeLoggerCall(logLevel: LogLevel, message: string, meta: unknown): void {\n if (message && typeof message === 'object' && typeof meta === 'string') {\n // Fastify sends the message as the second parameter when logging (which is how pino actually works),\n this.#logger[logLevel](message as object, meta);\n } else if (meta) {\n this.#logger[logLevel](meta, message);\n } else {\n this.#logger[logLevel](message);\n }\n }\n\n private lazyLoggerCall(logLevel: LogLevel, fn: LazyLogFn): void {\n if (!this.#logger.isLevelEnabled(logLevel)) {\n return;\n }\n const [message, meta] = fn();\n this.winstonLikeLoggerCall(logLevel, message, meta);\n }\n\n private addMetadata(): Record<string, unknown> {\n const newMetadata: Record<string, unknown> = {};\n this.#contextMiddlewares.forEach((middleware) => {\n const metadata = middleware();\n Object.assign(newMetadata, metadata);\n });\n return newMetadata;\n }\n\n addContextMiddleware = (middleware: MiddlewareFunction): number => this.#contextMiddlewares.push(middleware);\n\n addToContext = (context: Record<string, string | number>): void => addToContext(context);\n\n trace = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.trace, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the trace level is enabled */\n traceLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.trace, fn);\n\n debug = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.debug, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the debug level is enabled */\n debugLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.debug, fn);\n\n info = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.info, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the info level is enabled */\n infoLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.info, fn);\n\n warn = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.warn, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the warn level is enabled */\n warnLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.warn, fn);\n\n error = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.error, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the error level is enabled */\n errorLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.error, fn);\n\n fatal = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.fatal, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the fatal level is enabled */\n fatalLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.fatal, fn);\n\n /** No-op logging method for silent level (required for Fastify compatibility) */\n silent = (_message: string, _meta?: unknown): void => {\n // no-op\n };\n\n trackEvent = (eventName: string, data: Record<string, unknown> = {}): void => {\n sendDDEvent(eventName, data);\n };\n\n child: OverloadedChildLoggerMethod = (...args: [metadata: Record<string, unknown>] | [instanceNamePrefix: string, metadata: Record<string, unknown>]) => {\n const childLoggerInstanceManager = new LoggerInstanceManager();\n childLoggerInstanceManager.#contextMiddlewares = [...this.#contextMiddlewares];\n\n if (typeof args[0] === 'string') {\n // Overload: child(instanceNamePrefix: string, metadata: Record<string, unknown>)\n const [instanceNamePrefix, metadata] = args;\n childLoggerInstanceManager.#logger = this.#logger.child(metadata!, { msgPrefix: `[${instanceNamePrefix}] ` });\n } else if (typeof args[0] === 'object') {\n // Overload: child(metadata: Record<string, unknown>)\n childLoggerInstanceManager.#logger = this.#logger.child(args[0]);\n } else {\n throw new Error('Invalid arguments to child logger');\n }\n return childLoggerInstanceManager;\n };\n\n get level(): Pino.LevelWithSilentOrString {\n return this.#logger.level;\n }\n}\n\ninterface OverloadedChildLoggerMethod {\n (metadata: Record<string, unknown>): LoggerInstanceManager;\n (instanceNamePrefix: string, metadata: Record<string, unknown>): LoggerInstanceManager;\n}\n\nexport { addToContext, runWithLoggerContext, loggerContextMiddleware };\n\nexport default (loglevel?: `${LogLevel}`, context?: ServiceContext): LoggerInstanceManager => new LoggerInstanceManager(loglevel as LogLevel, context);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["levelToSeverity: Record<LevelOrString, string>","ddMode: DDMode","#contextMiddlewares","context","#logger","newMetadata: Record<string, unknown>"],"sources":["../src/gcpLogOptions.ts","../src/datadogClient.ts","../src/context.ts","../src/index.ts"],"sourcesContent":["// Based on https://github.com/simenandre/pino-cloud-logging/blob/7816b2e4dec3dd18ef34c0f3f241d88a4cab0b90/src/main.ts\n// The repo is licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0\n// Extracted since the repo no longer receives updates\n\nimport type { LevelOrString, LoggerOptions } from 'pino';\n\n// Map Pino levels to Google Cloud Logging severity levels\n// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity\nconst levelToSeverity: Record<LevelOrString, string> = {\n trace: 'DEBUG',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARNING',\n error: 'ERROR',\n fatal: 'CRITICAL',\n};\n\nexport interface ServiceContext {\n serviceName?: string;\n version?: string;\n tags?: Record<string, string>;\n}\n\nexport function gcpLogOptions(\n options?: LoggerOptions,\n context: ServiceContext = {},\n): LoggerOptions {\n const { serviceName, version, tags } = context;\n\n const baseObj = {\n ...(serviceName && version && { serviceContext: { service: serviceName, version } }),\n ...tags,\n };\n const base = Object.keys(baseObj).length ? baseObj : null;\n\n return {\n // https://cloud.google.com/error-reporting/docs/formatting-error-messages#json_representation\n base,\n formatters: {\n level: (label: string) => ({\n severity: levelToSeverity[label] ?? levelToSeverity.info,\n // `@type` property tells Error Reporting to track even if there is no `stack_trace`\n ...(['error', 'fatal'].includes(label) && { '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' }),\n }),\n\n log(object) {\n const logObject = object as { err?: Error; error?: Error; e?: Error; };\n const stackTrace = logObject.err?.stack ?? logObject.error?.stack ?? logObject.e?.stack;\n // eslint-disable-next-line camelcase\n return { ...object, ...(stackTrace && { stack_trace: stackTrace }) };\n },\n },\n messageKey: 'message',\n ...options,\n };\n}\n","import { createRequire } from 'node:module';\n\nconst _require = createRequire(import.meta.url);\n\ninterface DDTracer {\n dogstatsd: {\n increment: (metric: string, value: number, tags?: Record<string, string | number>) => void;\n };\n}\n\nconst tryRequire = (path: string): DDTracer | null => {\n try {\n const t = _require(path) as DDTracer;\n return t?.dogstatsd ? t : null;\n } catch {\n return null;\n }\n};\n\nconst tracer = tryRequire('dd-trace') ?? tryRequire('/opt/datadog/apm/library/js/node_modules/dd-trace');\nconst isDebugLevel = ['debug', 'trace'].includes(process.env.LOG_LEVEL || process.env.PINO_LOG_LEVEL || '');\n\nconst ddSite = process.env.DD_SITE || 'datadoghq.eu';\nconst ddApiKey = !tracer ? process.env.DD_API_KEY : null;\n\nexport type DDMode = 'dd-trace' | 'api' | 'none';\nexport const ddMode: DDMode = tracer ? 'dd-trace' : ddApiKey ? 'api' : 'none';\nprocess.stdout.write(`[logger] Datadog metrics mode initialized: ${ddMode}\\n`);\n\nexport const sendDDEvent = (eventName: string, data: Record<string, unknown>): void => {\n const service = process.env.DD_SERVICE || process.env.SERVICE_NAME || 'unknown';\n const env = process.env.DD_ENV || process.env.ENV_NAME || 'local';\n\n const primitiveData = Object.fromEntries(\n Object.entries(data).filter(([, v]) => typeof v === 'string' || typeof v === 'number'),\n ) as Record<string, string | number>;\n\n if (tracer) {\n tracer.dogstatsd.increment(eventName, 1, { service, env, ...primitiveData });\n return;\n }\n\n if (!ddApiKey) return;\n\n const tags = [\n `service:${service}`,\n `env:${env}`,\n ...Object.entries(primitiveData).map(([k, v]) => `${k}:${String(v)}`),\n ];\n\n fetch(`https://api.${ddSite}/api/v2/series`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'DD-API-KEY': ddApiKey },\n body: JSON.stringify({\n series: [{\n metric: eventName,\n type: 1,\n points: [{ timestamp: Math.floor(Date.now() / 1000), value: 1 }],\n tags,\n }],\n }),\n }).then((res) => {\n if (!res.ok && isDebugLevel) {\n res.text().then((body) => {\n process.stderr.write(`[logger] DD API error ${res.status}: ${body}\\n`);\n }).catch(() => undefined);\n }\n }).catch((err) => {\n if (isDebugLevel) {\n process.stderr.write(`[logger] DD API fetch failed: ${String(err)}\\n`);\n }\n });\n};\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\ntype LogContext = Record<string, string | number>;\n\nconst storage = new AsyncLocalStorage<LogContext>();\n\nexport function addToContext(context: Record<string, string | number>): void {\n const store = storage.getStore();\n if (!store) return;\n for (const [key, value] of Object.entries(context)) {\n if (typeof value !== 'string' && typeof value !== 'number') continue;\n store[key] = value;\n }\n}\n\nexport function runWithLoggerContext<T>(fn: () => T): T {\n return storage.run({}, fn);\n}\n\nexport function loggerContextMiddleware(_req: unknown, _res: unknown, next: () => void): void {\n storage.run({}, next);\n}\n\nexport function getContext(): LogContext {\n return storage.getStore() ?? {};\n}\n","import Pino, { type LoggerOptions, stdSerializers } from 'pino';\nimport { gcpLogOptions, type ServiceContext } from './gcpLogOptions';\nimport { sendDDEvent } from './datadogClient';\nimport { getContext, addToContext, runWithLoggerContext, loggerContextMiddleware } from './context';\n\nexport { type ServiceContext };\nexport { addToContext, runWithLoggerContext, loggerContextMiddleware };\n\nexport enum LogLevel {\n trace = 'trace',\n debug = 'debug',\n info = 'info',\n warn = 'warn',\n error = 'error',\n fatal = 'fatal',\n}\ntype LazyLogFn = () => [string] | [string, unknown];\n\nconst getLevel = (logLevel?: LogLevel): LogLevel => {\n if (logLevel) return logLevel;\n if (process.env.LOG_LEVEL) return process.env.LOG_LEVEL as LogLevel;\n if (process.env.NODE_ENV && ['production', 'staging', 'test'].includes(process.env.NODE_ENV)) return LogLevel.info;\n if (process.env.NODE_ENV === 'development') return LogLevel.debug;\n return LogLevel.debug;\n};\n\nconst createLoggerInstance = (level: LogLevel, options: Omit<LoggerOptions, 'transport'>, context?: ServiceContext): Pino.Logger => {\n let loggerInstance;\n if (process.env.NODE_ENV === 'production') {\n loggerInstance = Pino(gcpLogOptions({\n level: process.env.PINO_LOG_LEVEL || 'info',\n ...options,\n } as object, context));\n } else {\n loggerInstance = Pino({\n level: process.env.PINO_LOG_LEVEL || 'info',\n transport: {\n target: 'pino-pretty',\n options: {\n colorize: true,\n },\n },\n ...options,\n });\n }\n loggerInstance.level = level;\n return loggerInstance;\n};\n\ntype MiddlewareFunction = () => Record<string, unknown>;\n\nexport class LoggerInstanceManager {\n #contextMiddlewares: MiddlewareFunction[] = [getContext];\n\n #logger: Pino.Logger;\n\n constructor(logLevel?: LogLevel, context?: ServiceContext) {\n this.#logger = createLoggerInstance(getLevel(logLevel), {\n mixin: () => this.addMetadata(),\n serializers: {\n ...stdSerializers,\n e: stdSerializers.errWithCause,\n err: stdSerializers.errWithCause,\n error: stdSerializers.errWithCause,\n errors(val) {\n if (Array.isArray(val)) {\n return val.map(stdSerializers.errWithCause);\n }\n return stdSerializers.errWithCause(val);\n },\n },\n }, context);\n }\n\n // To support winston like logging in pino\n private winstonLikeLoggerCall(logLevel: LogLevel, message: string, meta: unknown): void {\n if (message && typeof message === 'object' && typeof meta === 'string') {\n // Fastify sends the message as the second parameter when logging (which is how pino actually works),\n this.#logger[logLevel](message as object, meta);\n } else if (meta) {\n this.#logger[logLevel](meta, message);\n } else {\n this.#logger[logLevel](message);\n }\n }\n\n private lazyLoggerCall(logLevel: LogLevel, fn: LazyLogFn): void {\n if (!this.#logger.isLevelEnabled(logLevel)) {\n return;\n }\n const [message, meta] = fn();\n this.winstonLikeLoggerCall(logLevel, message, meta);\n }\n\n private addMetadata(): Record<string, unknown> {\n const newMetadata: Record<string, unknown> = {};\n this.#contextMiddlewares.forEach((middleware) => {\n const metadata = middleware();\n Object.assign(newMetadata, metadata);\n });\n return newMetadata;\n }\n\n addContextMiddleware = (middleware: MiddlewareFunction): number => this.#contextMiddlewares.push(middleware);\n\n addToContext = (context: Record<string, string | number>): void => addToContext(context);\n\n trace = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.trace, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the trace level is enabled */\n traceLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.trace, fn);\n\n debug = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.debug, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the debug level is enabled */\n debugLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.debug, fn);\n\n info = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.info, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the info level is enabled */\n infoLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.info, fn);\n\n warn = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.warn, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the warn level is enabled */\n warnLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.warn, fn);\n\n error = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.error, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the error level is enabled */\n errorLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.error, fn);\n\n fatal = (message: string, meta?: unknown): void => this.winstonLikeLoggerCall(LogLevel.fatal, message, meta);\n /** A lazy version of trace that only evaluates the message and metadata only if the fatal level is enabled */\n fatalLazy = (fn: LazyLogFn): void => this.lazyLoggerCall(LogLevel.fatal, fn);\n\n /** No-op logging method for silent level (required for Fastify compatibility) */\n silent = (_message: string, _meta?: unknown): void => {\n // no-op\n };\n\n trackEvent = (eventName: string, data: Record<string, unknown> = {}): void => {\n sendDDEvent(eventName, data);\n };\n\n child: OverloadedChildLoggerMethod = (...args: [metadata: Record<string, unknown>] | [instanceNamePrefix: string, metadata: Record<string, unknown>]) => {\n const childLoggerInstanceManager = new LoggerInstanceManager();\n childLoggerInstanceManager.#contextMiddlewares = [...this.#contextMiddlewares];\n\n if (typeof args[0] === 'string') {\n // Overload: child(instanceNamePrefix: string, metadata: Record<string, unknown>)\n const [instanceNamePrefix, metadata] = args;\n childLoggerInstanceManager.#logger = this.#logger.child(metadata!, { msgPrefix: `[${instanceNamePrefix}] ` });\n } else if (typeof args[0] === 'object') {\n // Overload: child(metadata: Record<string, unknown>)\n childLoggerInstanceManager.#logger = this.#logger.child(args[0]);\n } else {\n throw new Error('Invalid arguments to child logger');\n }\n return childLoggerInstanceManager;\n };\n\n get level(): Pino.LevelWithSilentOrString {\n return this.#logger.level;\n }\n}\n\ninterface OverloadedChildLoggerMethod {\n (metadata: Record<string, unknown>): LoggerInstanceManager;\n (instanceNamePrefix: string, metadata: Record<string, unknown>): LoggerInstanceManager;\n}\n\nexport default (loglevel?: `${LogLevel}`, context?: ServiceContext): LoggerInstanceManager => new LoggerInstanceManager(loglevel as LogLevel, context);\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -15,6 +15,38 @@ logger.error('errors displayed in logs/error.log', new Error('test error'));
|
|
|
15
15
|
logger.debug('debug with object', { a: 5 });
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Request-scoped context
|
|
19
|
+
|
|
20
|
+
Use `addToContext` to attach fields that automatically appear on every log within the same async request chain, isolated per concurrent request.
|
|
21
|
+
|
|
22
|
+
**HTTP (Express / super-express)** — add `loggerContextMiddleware` once at the app level:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import Logger, { loggerContextMiddleware } from '@autofleet/logger';
|
|
26
|
+
|
|
27
|
+
const logger = Logger();
|
|
28
|
+
app.use(loggerContextMiddleware);
|
|
29
|
+
|
|
30
|
+
// Then anywhere in the request lifecycle:
|
|
31
|
+
logger.addToContext({ requestId: req.id }); // in middleware
|
|
32
|
+
logger.addToContext({ userId }); // after auth
|
|
33
|
+
logger.addToContext({ orderId: order.id }); // in route handler
|
|
34
|
+
|
|
35
|
+
logger.info('order created'); // → { requestId, userId, orderId, message: 'order created', ... }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**RabbitMQ / Kafka consumers** — wrap the handler with `runWithLoggerContext`:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import Logger, { runWithLoggerContext } from '@autofleet/logger';
|
|
42
|
+
|
|
43
|
+
runWithLoggerContext(() => processMessage(msg));
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Values must be `string` or `number` — arrays and objects are silently skipped
|
|
47
|
+
- Calling `addToContext` without an active context is a silent no-op
|
|
48
|
+
- Context is automatically cleaned up when the request/handler finishes (no memory leaks)
|
|
49
|
+
|
|
18
50
|
|
|
19
51
|
# Migration to V4
|
|
20
52
|
|