Node.js Logging
Logging records what your app does while it runs. Logs help you find bugs, track errors, and understand user activity. A good log works like the black box of an airplane. After a problem, you read it to learn what happened.
Why console.log Is Not Enough
The console.log function suits quick tests. Production apps need more.
- Log levels separate important errors from small details.
- Timestamps show when events occurred.
- Files and services store logs for later review.
- Structured data makes searching easy.
Log Levels
A level shows how serious a message is. Levels let you turn detail up or down.
error Something failed. Act now. (most serious) warn Something looks wrong. info Normal events, such as a login. http Request records. debug Detail for developers. (least serious)
A production server often logs info and above. A developer laptop logs debug and above.
Install Winston
Winston is a popular Node.js logging library.
npm install winstonCreate a Logger
const winston = require("winston");
const logger = winston.createLogger({
level: "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
new winston.transports.File({ filename: "logs/combined.log" })
]
});
module.exports = logger;Understand the Pieces
- level sets the lowest level that the logger records.
- format shapes each log line.
- transports name the destinations, such as the screen or a file.
logger.error("Payment failed")
|
v
+-----------+
| Logger |
+-----------+
| | |
v v v
Console error.log combined.log
Write Log Messages
const logger = require("./logger");
logger.info("Server started on port 3000");
logger.warn("Disk space is low");
logger.error("Database connection failed", { host: "db1", retry: 3 });Each line prints as JSON.
{"level":"info","message":"Server started on port 3000","timestamp":"2026-10-07T10:15:30.120Z"}Log Requests with Morgan
Morgan records every web request.
npm install morgan
const morgan = require("morgan");
app.use(morgan("combined", {
stream: { write: (line) => logger.http(line.trim()) }
}));Each request creates a line with the method, path, status code, and response time.
Log Errors in Express
app.use((err, req, res, next) => {
logger.error(err.message, {
stack: err.stack,
path: req.originalUrl,
method: req.method
});
res.status(500).json({ error: "Something went wrong." });
});The visitor sees a short message. The log holds the full detail.
Rotate Log Files
One file grows without limit. Rotation starts a new file each day and removes old ones.
npm install winston-daily-rotate-file
require("winston-daily-rotate-file");
new winston.transports.DailyRotateFile({
filename: "logs/app-%DATE%.log",
datePattern: "YYYY-MM-DD",
maxFiles: "14d"
});Add Context to Every Line
Child loggers attach shared details such as a request id.
const requestLogger = logger.child({ requestId: "abc-123" });
requestLogger.info("User loaded cart");
requestLogger.error("Checkout failed");A search for one request id shows its whole story.
What Never to Log
- Passwords and password hashes.
- Credit card numbers.
- Access tokens and secret keys.
- Private personal details beyond what you need.
Mask sensitive values before you write them.
Send Logs to a Service
Large systems collect logs from many servers in one place. Tools such as the ELK stack, Grafana Loki, and cloud log services provide search and alerts. JSON lines work well with all of them.
Good Habits
- Write clear messages that name the event and the object.
- Choose the right level for each message.
- Keep one shared logger module for the whole app.
- Review logs on a regular schedule.
Practice Tasks
- Create a logger that writes errors to a file and everything to the console.
- Add Morgan to a small Express app and read the request lines.
- Log a failed login with the email but without the password.
