85 lines
2.4 KiB
JavaScript
85 lines
2.4 KiB
JavaScript
/**
|
|
* 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<any>} 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))
|
|
}
|
|
}
|