Skip to content

Instantly share code, notes, and snippets.

@Avi-E-Koenig
Created January 3, 2025 13:51
Show Gist options
  • Save Avi-E-Koenig/5f4578bb3fea272aaba67ba5fb53f871 to your computer and use it in GitHub Desktop.
Save Avi-E-Koenig/5f4578bb3fea272aaba67ba5fb53f871 to your computer and use it in GitHub Desktop.
Winston file logger
import winston from 'winston';
import asyncLocalStorage from './request-context.js';
// Custom format to handle errors properly
const errorStackFormat = winston.format((info) => {
if (info instanceof Error) {
return Object.assign({}, info, {
stack: info.stack,
message: info.message,
});
}
return info;
});
// Custom format to add request context
const addRequestContext = winston.format((info) => {
const context = asyncLocalStorage.getStore();
if (context?.uuid) {
info.requestId = context.uuid;
}
return info;
});
// Winston Logger Configuration
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
errorStackFormat(),
addRequestContext(),
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json(),
winston.format.printf(({ timestamp, level, message, stack, ...metadata }) => {
// Remove internal Winston properties
delete metadata.splat;
delete metadata.level;
delete metadata.timestamp;
// Create metadata string if there are properties
const metaStr = Object.keys(metadata).length ? `\n${JSON.stringify(metadata, null, 2)}` : '';
if (stack) {
return `${timestamp} [${level.toUpperCase()}]: ${message}${metaStr}\n${stack}\n`;
}
return `${timestamp} [${level.toUpperCase()}]: ${message}${metaStr}\n`;
})
),
transports: [
// File transport with rotation
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5,
tailable: true,
}),
new winston.transports.File({
filename: 'logs/combined.log',
maxsize: 5242880, // 5MB
maxFiles: 5,
tailable: true,
}),
],
});
// Add console transport in non-production environments
if (process.env.NODE_ENV !== 'production') {
logger.add(
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, stack, ...metadata }) => {
// Remove internal Winston properties
delete metadata.splat;
delete metadata.level;
delete metadata.timestamp;
// Create metadata string if there are properties
const metaStr = Object.keys(metadata).length ? ` ${JSON.stringify(metadata)}` : '';
if (stack) {
// Include stack trace if present
return `${timestamp} [${level}]: ${message}${metaStr}\n${stack}`;
}
// Standard log format
return `${timestamp} [${level}]: ${message}${metaStr}`;
})
),
})
);
}
// Handling uncaught exceptions and unhandled rejections
logger.exceptions.handle(new winston.transports.File({ filename: 'logs/exceptions.log' }));
process.on('unhandledRejection', (error) => {
logger.error('Unhandled Rejection:', error);
});
export default logger;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment