Logger
Structured and configurable logging for Node.js applications using Winston or Pino.
Installation Guide
Install the component using the servercn CLI:
npx servercn-cli@latest add logger You will be prompted to select a file upload provider:
? Select a logging library: » - Use arrow-keys. Return to submit.
> Pino (High-performance)
Winston (Flexible)The CLI will then automatically configure the component based on your selected library.
Prerequisites
interface Config {
PORT: number;
NODE_ENV: string;
LOG_LEVEL: string;
}
const config: Config = {
PORT: Number(process.env.PORT) || 3000,
NODE_ENV: process.env.NODE_ENV || "development",
LOG_LEVEL: process.env.LOG_LEVEL || "info"
};
export default config;Basic Implementation
1. Winston Logger
Minimal Configuration
import winston from "winston";
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [new winston.transports.Console()]
});Advanced Configuration
import env from "../configs/env";
import winston from "winston";
import DailyRotateFile from "winston-daily-rotate-file";
const { combine, timestamp, printf, colorize, errors } = winston.format;
const logFormat = printf(({ level, message, timestamp, stack }) => {
return `${timestamp} [${level}] : ${stack || message}`;
});
const transports: winston.transport[] = [];
/**
* Console logging (development / local)
*/
if (env.NODE_ENV !== "production") {
transports.push(
new winston.transports.Console({
format: combine(
colorize(),
timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
errors({ stack: true }),
logFormat
)
})
);
}
/**
* File logging (staging / production)
*/
if (env.NODE_ENV !== "development") {
transports.push(
new DailyRotateFile({
dirname: "logs/app",
filename: "app-%DATE%.log",
datePattern: "YYYY-MM-DD",
zippedArchive: true,
maxSize: "20m",
maxFiles: "14d",
level: "info"
})
);
transports.push(
new DailyRotateFile({
dirname: "logs/error",
filename: "errors-%DATE%.log",
datePattern: "YYYY-MM-DD",
zippedArchive: true,
maxSize: "20m",
maxFiles: "30d",
level: "error"
})
);
}
export const logger = winston.createLogger({
level: env.LOG_LEVEL,
format: combine(
timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
errors({ stack: true }),
logFormat
),
transports,
exitOnError: false
});Winston Usage Example
import { logger } from "../utils/logger";
logger.info("Server started successfully");
logger.warn("Disk space running low");
logger.error("Failed to connect to database");Winston Usage Output
2026-01-03 18:43:23 [info] : Server started successfully
2026-01-03 18:43:23 [warn] : Disk space running low
2026-01-03 18:43:23 [error] : Failed to connect to databaseChild Logger
const authLogger = logger.child({ module: "auth" });
authLogger.info("Login started");
authLogger.error({ userId }, "Invalid credentials");2. Pino Logger
Minimal Configuration
import pino from "pino";
import env from "../configs/env";
export const logger = pino({
level: env.LOG_LEVEL,
transport:
env.NODE_ENV !== "production"
? {
target: "pino-pretty",
options: {
colorize: true,
translateTime: "yyyy-mm-dd HH:MM:ss",
ignore: "pid,hostname"
}
}
: undefined
});Advanced Configuration
import pino from "pino";
import env from "../configs/env";
const isProduction = env.NODE_ENV !== "production";
export const logger = pino({
level: env.LOG_LEVEL || "info",
base: {
pid: process.pid
},
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level(label) {
return { level: label };
}
},
redact: {
paths: [
"req.headers.authorization",
"req.headers.cookie",
"password",
"token",
"refreshToken"
],
censor: "[REDACTED]"
},
...(isProduction
? {}
: {
transport: {
target: "pino-pretty",
options: {
colorize: true,
translateTime: "SYS:standard",
ignore: "pid,hostname"
}
}
})
});Pino Usage Example
import { logger } from "../utils/logger";
logger.info("Server started");
logger.warn({ userId: "123" }, "Suspicious activity detected");
logger.error(
{ err: "Database connection failed" },
"Database connection failed"
);Pino Usage Output
NODE_ENV: production
{"level":30,"time":1767447427459,"pid":95816,"hostname":"HOST_NAME","msg":"Server started"}
{"level":40,"time":1767447427460,"pid":95816,"hostname":"HOST_NAME","userId":"123","msg":"Suspicious activity detected"}
{"level":50,"time":1767447427461,"pid":95816,"hostname":"HOST_NAME","err":"Database connection failed","msg":"Database connection failed"}NODE_ENV: development
[2026-01-04 08:17:19] INFO: Server started
[2026-01-04 08:17:19] WARN: Suspicious activity detected
userId: "123"
[2026-01-04 08:17:19] ERROR: Database connection failed
err: "Database connection failed"Child Logger
const authLogger = logger.child({ module: "auth" });
authLogger.info("Login started");
authLogger.error({ userId }, "Invalid credentials");