Migrating from Winston
Complete guide to migrating from Winston to cenglu
Migrating from Winston
This guide helps you migrate from Winston to cenglu with code examples and feature mappings.
Quick Comparison
| Feature | Winston | Cenglu |
|---|---|---|
| Dependencies | Many | Zero |
| Bundle Size | ~1MB | < 500KB |
| Performance | Slow | Fast (5x faster) |
| Built-in Redaction | ❌ | ✅ |
| TypeScript | ⚠️ Partial | ✅ Full |
| Configuration | Complex | Simple |
Basic Migration
Creating a Logger
Winston:
import winston from "winston";
const logger = winston.createLogger({
level: "info",
format: winston.format.json(),
defaultMeta: { service: "my-app" },
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "app.log" }),
],
});Cenglu:
import { createLogger, createFileTransport } from "cenglu";
const logger = createLogger({
level: "info",
service: "my-app",
structured: { type: "json" }, // JSON format built-in
transports: [
createFileTransport({ dir: "./logs" }),
],
});Logging Methods
Winston:
logger.info("User logged in");
logger.info("User logged in", { userId: 123 });
logger.error("Failed", { error: new Error("Oops") });Cenglu:
logger.info("User logged in");
logger.info("User logged in", { userId: 123 });
logger.error("Failed", new Error("Oops")); // Error as second argumentChild Loggers
Winston:
const child = logger.child({ requestId: "abc-123" });
child.info("Processing request");Cenglu:
const child = logger.child({ requestId: "abc-123" });
child.info("Processing request");✅ Same API
API Mapping
Log Levels
| Winston | Cenglu | Notes |
|---|---|---|
error | error | ✅ Same |
warn | warn | ✅ Same |
info | info | ✅ Same |
http | info | Map to info |
verbose | debug | Map to debug |
debug | debug | ✅ Same |
silly | trace | Map to trace |
Configuration
| Winston Option | Cenglu Equivalent |
|---|---|
level | level |
levels | Not needed (standardized) |
format | structured.type |
defaultMeta | bindings |
transports | transports |
exitOnError | Handle manually |
silent | console.enabled: false |
Format Migration
JSON Format
Winston:
const logger = winston.createLogger({
format: winston.format.json(),
});Cenglu:
const logger = createLogger({
structured: { type: "json" }, // Default
});Pretty Format
Winston:
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
});Cenglu:
const logger = createLogger({
pretty: { enabled: true }, // Built-in
});Custom Format
Winston:
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(info => {
return `${info.timestamp} [${info.level}]: ${info.message}`;
})
),
});Cenglu:
const logger = createLogger({
pretty: {
enabled: true,
formatter: (record) => {
return `${new Date(record.time).toISOString()} [${record.level}]: ${record.msg}`;
},
},
});Transport Migration
Console Transport
Winston:
new winston.transports.Console({
level: "info",
format: winston.format.simple(),
})Cenglu:
import { createConsoleTransport } from "cenglu";
createConsoleTransport({
enabled: true,
stream: process.stdout,
})File Transport
Winston:
new winston.transports.File({
filename: "error.log",
level: "error",
maxsize: 10485760, // 10MB
maxFiles: 5,
})Cenglu:
import { createRotatingFileTransport } from "cenglu";
createRotatingFileTransport({
dir: "./logs",
separateErrors: true,
rotation: {
maxBytes: 10485760, // 10MB
maxFiles: 5,
compress: "gzip",
},
})Daily Rotate File
Winston:
import DailyRotateFile from "winston-daily-rotate-file";
new DailyRotateFile({
filename: "app-%DATE%.log",
datePattern: "YYYY-MM-DD",
maxSize: "20m",
maxFiles: "14d",
})Cenglu:
createRotatingFileTransport({
dir: "./logs",
filename: ({ date }) => `app-${date.toISOString().split("T")[0]}.log`,
rotation: {
intervalDays: 1,
maxBytes: 20971520, // 20MB
retentionDays: 14,
},
})HTTP Transport
Winston:
new winston.transports.Http({
host: "logs.example.com",
port: 443,
path: "/logs",
})Cenglu:
const logger = createLogger({
adapters: [
{
name: "http",
handle: async (record) => {
await fetch("https://logs.example.com/logs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(record),
});
},
},
],
});Feature Migration
Metadata
Winston:
const logger = winston.createLogger({
defaultMeta: {
service: "my-app",
version: "1.0.0",
},
});
logger.info("Message", { userId: 123 });
// Output includes service, version, userIdCenglu:
const logger = createLogger({
service: "my-app",
version: "1.0.0",
bindings: {
region: "us-east-1",
},
});
logger.info("Message", { userId: 123 });
// Output includes all fieldsLogging Errors
Winston:
try {
throw new Error("Something went wrong");
} catch (error) {
logger.error("Error occurred", { error });
}Cenglu:
try {
throw new Error("Something went wrong");
} catch (error) {
logger.error("Error occurred", error); // Error as second argument
}Filters
Winston:
const logger = winston.createLogger({
format: winston.format.combine(
winston.format((info) => {
if (info.level === "http") {
return false; // Drop
}
return info;
})()
),
});Cenglu:
import { filterPlugin } from "cenglu";
const logger = createLogger({
plugins: [
filterPlugin({
filter: (record) => {
return record.level !== "debug"; // Keep all except debug
},
}),
],
});Exception Handling
Winston:
const logger = winston.createLogger({
exceptionHandlers: [
new winston.transports.File({ filename: "exceptions.log" }),
],
rejectionHandlers: [
new winston.transports.File({ filename: "rejections.log" }),
],
});Cenglu:
const logger = createLogger({ service: "app" });
process.on("uncaughtException", (error) => {
logger.fatal("Uncaught exception", error);
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
logger.fatal("Unhandled rejection", reason as Error, {
promise: String(promise),
});
});Express Integration
Winston:
import express from "express";
import expressWinston from "express-winston";
const app = express();
app.use(expressWinston.logger({
transports: [new winston.transports.Console()],
format: winston.format.json(),
meta: true,
msg: "HTTP {{req.method}} {{req.url}}",
}));Cenglu:
import express from "express";
import { createLogger, expressMiddleware } from "cenglu";
const app = express();
const logger = createLogger({ service: "api" });
app.use(expressMiddleware(logger, {
logRequests: true,
logResponses: true,
}));
// Access via req.logger
app.get("/users", (req, res) => {
req.logger.info("Fetching users");
res.json({ users: [] });
});Advanced Features
Log Streaming
Winston:
const stream = logger.stream({
level: "info",
});
stream.on("log", (info) => {
console.log(info);
});Cenglu:
// Use adapters for streaming
const logger = createLogger({
adapters: [
{
name: "stream",
handle: (record) => {
myStream.write(record);
},
},
],
});Query Logs
Winston supports querying logs from transports. In cenglu, query your log storage directly (files, databases, etc.).
Winston:
logger.query({ limit: 10 }, (err, results) => {
console.log(results);
});Cenglu:
// Query your log storage directly
import fs from "fs/promises";
const logs = await fs.readFile("./logs/app.log", "utf-8");
const lines = logs.split("\n")
.filter(Boolean)
.map(line => JSON.parse(line))
.slice(-10); // Last 10 logsProfiling
Winston:
logger.profile("test");
// ... do work
logger.profile("test"); // Logs durationCenglu:
const done = logger.time("test");
// ... do work
done(); // Logs durationCommon Pitfalls
1. Format Combiners
Winston uses format combiners:
// Winston
winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
// Cenglu: simpler
{ structured: { type: "json" } }2. Transport Arrays
Winston:
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "app.log" }),
]Cenglu:
transports: [
createConsoleTransport(),
createFileTransport({ dir: "./logs" }),
]3. Custom Levels
Winston allows custom levels:
// Winston
levels: {
error: 0,
warn: 1,
info: 2,
http: 3,
debug: 4,
}Cenglu uses standardized levels. Map your custom levels to standard ones.
Migration Checklist
- Replace
winstonwithcengluin package.json - Update logger creation
- Simplify format configuration
- Replace transport instantiation
- Update error logging (error as second arg)
- Migrate exception handlers to process events
- Replace expressWinston with expressMiddleware
- Update profiling to use
logger.time() - Remove format combiners
- Test thoroughly
Gradual Migration
Run both loggers during migration:
import winston from "winston";
import { createLogger } from "cenglu";
const winstonLogger = winston.createLogger({...});
const cengluLogger = createLogger({...});
// Wrapper
function log(level: string, message: string, meta?: any) {
winstonLogger[level](message, meta);
cengluLogger[level](message, meta);
}
// Use wrapper during migration
log("info", "Test", { data: 123 });
// Gradually replace with cengluLogger.info()Why Migrate?
- 5x Faster: Significantly better performance
- Zero Dependencies: Smaller bundle, fewer vulnerabilities
- Built-in Features: Redaction, middleware, TypeScript
- Simpler API: Less configuration, more conventions
- Better DX: Full TypeScript support, better error messages