/** * Structured logger with request context support. * Uses AsyncLocalStorage to provide request-scoped context that's automatically isolated per async context. */ import { AsyncLocalStorage } from 'node:async_hooks' const asyncLocalStorage = new AsyncLocalStorage() /** * Run a function with logger context. Context is automatically isolated per async execution. * @param {string} reqId - Request ID * @param {string|null} uId - User ID (optional) * @param {Function} fn - Function to run with context * @returns {Promise} Result of the function */ export function runWithContext(reqId, uId, fn) { return asyncLocalStorage.run({ requestId: reqId, userId: uId }, fn) } /** * Set context for the current async context. Use runWithContext() instead for proper isolation. * @deprecated Use runWithContext() instead for proper async context isolation * @param {string} reqId - Request ID * @param {string|null} uId - User ID (optional) */ export function setContext(reqId, uId = null) { const store = asyncLocalStorage.getStore() if (store) { store.requestId = reqId store.userId = uId } } /** * Clear context for the current async context. * @deprecated Context is automatically cleared when async context ends. Use runWithContext() instead. */ export function clearContext() { const store = asyncLocalStorage.getStore() if (store) { store.requestId = null store.userId = null } } function getContext() { return asyncLocalStorage.getStore() || { requestId: null, userId: null } } function formatMessage(level, message, context = {}) { const { requestId, userId } = getContext() const timestamp = new Date().toISOString() const ctx = { timestamp, level, requestId, ...(userId && { userId }), ...context, } return `[${level.toUpperCase()}] ${JSON.stringify({ message, ...ctx })}` } export function info(message, context = {}) { console.log(formatMessage('info', message, context)) } export function error(message, context = {}) { const ctx = { ...context } if (context.error && context.error.stack) { ctx.stack = context.error.stack } console.error(formatMessage('error', message, ctx)) } export function warn(message, context = {}) { console.warn(formatMessage('warn', message, context)) } export function debug(message, context = {}) { if (process.env.NODE_ENV === 'development') { console.debug(formatMessage('debug', message, context)) } }