Tired of Configuring Winston? Read This.
When you’re building a modern Node.js app, here’s the truth:
Logging isn’t optional — it’s everything. Think about it.
- In development, it helps you debug issues fast.
- In production, it’s how you track requests across multiple services without tearing your hair out. A good logging setup can literally save you hours, if not days.
While working on microservices, I noticed developers (including myself) often repeated the same steps:
- Configure Winston with multiple transports
- Write middleware to log HTTP requests
- Inject unique
requestIds to trace distributed logs - Sanitize sensitive fields (
password,token, etc.) - Pipe logs into Elasticsearch for analysis
- Maintain consistency across different services
Every project, same boilerplate.
That’s exactly why I built np-express-winston-logger. My goal was simple: create a centralized, type-safe, configurable logger that just works. No more reinventing the wheel every time you spin up a new service.
So, what can it do?
Let me break it down for you:
- Centralized logger — a single place to handle both application logs and HTTP request logs.
- Express middleware (httpLogger) — automatically logs every request, with dynamic log levels based on status codes.
- RequestId injection — every log gets a unique ID, making it easy to trace requests across distributed systems.
- Sensitive data sanitization — automatically masks passwords, tokens, and any custom fields you specify.
- Elasticsearch integration — send logs to Elasticsearch for search, visualization, and analysis.
- Environment-aware logging — adjust behavior depending on whether you’re in dev, prod, stage, or test.
- TypeScript support — fully typed for IDE autocompletion and fewer errors.
In short, it takes care of all the repetitive, error-prone setup so you can focus on building features instead of debugging your logging system.
Installation
npm i np-express-winston-logger
# or
yarn add np-express-winston-loggerYour first setup
Here’s what it looks like when you use it in a brand-new Express app:
import express from "express";
import { createLogger } from "np-express-winston-logger";
const { logger, httpLogger } = createLogger({
serviceName: "product-service",
});
const app = express();
// Attach HTTP logger
app.use(httpLogger);
app.get("/", (_req, res) => {
const user = { id: 1, email: "john@example.com", password: "secret" };
logger.info("User:", { user });
res.send("OK");
});
app.listen(3000, () => {
logger.info("Server started successfully");
});That’s it.
Get Nazar Pashazade’s stories in your inbox
Join Medium for free to get updates from this writer.
Start your app, and you’ll see logs like this in the console:
Notice something?
- password is already masked (sanitized) for you.
2025-09-07T08:41:01.431Z [info] product-service: Server started successfully
2025-09-07T08:20:18.456Z [info] product-service: User: { "id": 1, "email": "john@example.com", "password": "****" }
2025-09-07T08:20:18.457Z [http] product-service: GET / 200 5msWant to sanitize custom fields?
By default, fields like password and token are masked. But if you want to add your own, just pass them in:
const { logger } = createLogger({
serviceName: "auth-service",
sanitize: { sensitiveFields: ["email"] },
});Now email will also be hidden in logs.
Need requestId injection for each request?
import { requestIdMiddleware } from "np-express-winston-logger";app.use(requestIdMiddleware); // ✅ Make sure requestIdMiddleware comes BEFORE httpLoggerapp.use(httpLogger);
From now on, every log entry will include a reqId, so you can trace logs across your services without manually passing IDs around:
2025-09-07T08:20:18.456Z [info] product-service: [reqId=77d4eb31...] User: { "id": 1, "email": "john@example.com", "password": "****" }
2025-09-07T08:20:18.457Z [http] product-service: [reqId=77d4eb31...] GET / 200 5msNeed Elasticsearch integration?
Logging to the console is fine, but in production you want logs centralized. That’s where Elasticsearch comes in:
const { logger } = createLogger({
serviceName: "product-service",
elasticsearch: { url: "http://localhost:9200" },
});Done. Your logs will be searchable, structured, and ready for Kibana dashboards.
Controlling log levels
Not everything deserves to be logged in production. With minLevel, you decide what gets through:
const { logger } = createLogger({
serviceName: "billing-service",
env: "prod",
minLevel: "info", // ignores debug/verbose
});So logger.debug(...) is ignored in prod, but logger.info(...), logger.warn(...), and logger.error(...) still show up.
When should you use this?
- You’re building microservices and want consistent logs across all of them
- You need request-level tracing without writing custom middleware
- You want to mask sensitive fields automatically
- You’re sending logs to Elasticsearch (or planning to)
Basically: anytime you don’t want to waste a day configuring Winston by hand.
Summary:
np-express-winston-logger gives you a production-ready logging setup in minutes. No boilerplate, no fragile hacks — just a logger that works the way you expect.
👉 Check the full docs on GitHub
And if it saves you time, I’d love it if you ⭐ the repo and share it with your team.








