Logger

Logging is a foundational requirement for debugging, monitoring, and operating production-grade backend systems.

The ServerCN Logger component provides two interchangeable logging strategies so you can choose what best fits your application:

  • Winston — flexible, feature-rich, file-based logging
  • Pino — ultra-fast, JSON-first logging optimized for performance

Both approaches follow the same philosophy:

  • Centralized configuration
  • Environment-aware behavior
  • Structured log output
  • Production-safe defaults

Installation Guide

Install the component using the ServerCN CLI:

npx servercn add logger-pino
npx servercn add logger-winston

Choosing a Logger

| Logger  | Best For                 | Characteristics                                            |
|---------|--------------------------|------------------------------------------------------------|
| Winston | Traditional backend apps | Multiple transports, log rotation, readable console output |
| Pino    | High-performance APIs    | Extremely fast, JSON logs, low overhead                    |

Prerequisites

Ensure the following environment variables are defined before running the application:

PORT = "3000";
NODE_ENV = "development";
LOG_LEVEL = "info";

src/configs/env.ts

Ensure the following configuration are defined:

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

Winston is ideal if you want:

  • File-based logs
  • Daily rotation
  • Human-readable console output
  • Multiple log destinations

Winston Configuration

src/utils/logger.ts

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 database

Child Logger

const authLogger = logger.child({ module: "auth" });
 
authLogger.info("Login started");
authLogger.error({ userId }, "Invalid credentials");

Winston Installation

npx servercn add logger-winston

Pino is designed for speed and structured logging. It is the recommended choice for:

  • High-throughput APIs
  • Containerized deployments
  • Centralized log aggregation (ELK, Loki, Datadog)

Pino Configuration

src/utils/logger.ts

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"}

 
[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");

Pino Installation

npx servercn add logger-pino

File & Folder Structure

Select a file to view its contents

Installation

npx servercn add logger-pino