Logging for JavaScript (Node.js): Best Solutions
Logging is the foundation of monitoring and debugging any web application. In the Node.js world, there are several mature and popular libraries that allow flexible configuration of log collection, formatting, and storage. In this article, we will look at four best solutions: Winston, Pino, Bunyan, and Morgan. You will learn their features, strengths, and get practical usage examples.
Why do you need logging in Node.js?
Unlike browser JavaScript, a server-side application runs without direct access to the developer console. Logs allow you to:
- Track errors and exceptions in real time.
- Analyze performance (response time, requests).
- Record audit events (user login, data changes).
- Integrate with external monitoring systems (ELK, Datadog, Splunk).
1. Winston — The Universal Standard
Winston is the most popular logging library for Node.js. Its main advantage is transports (where to write logs) and formats (how to represent them).
Installation:
npm install winstonBasic setup example:
const winston = require('winston');
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'combined.log' }) ]});
logger.info('Server started on port 3000');logger.error('Database connection error', { db: 'main' });Key features:
- Support for multiple transports (console, file, HTTP, MongoDB, Elasticsearch).
- Custom formats (JSON, plain text, with timestamps).
- Filtering by level (error, warn, info, verbose, debug, silly).
- Creating child loggers for different modules.
When to choose: for complex projects that require flexible log routing and integration with external services.
2. Pino — Ultra-Fast Logger
Pino is built for maximum performance. It is 5-10 times faster than Winston due to the use of structured JSON and minimal overhead.
Installation:
npm install pinoUsage example:
const pino = require('pino');
const logger = pino({ level: 'info', transport: { target: 'pino-pretty', // for pretty console output options: { colorize: true } }});
logger.info({ user: 42 }, 'User logged in');logger.error(new Error('Failed to load file'));Features:
- Minimal CPU and memory consumption.
- Built-in support for
pino-prettyfor development. - Works with streams for integration with other tools.
- Automatic object serialization without performance loss.
When to choose: for high-load applications (APIs, microservices) where every millisecond counts.
3. Bunyan — Structured Logs from Joyent's Creators
Bunyan from Joyent (the creators of Node.js) focuses on a strict log structure. Each message is a JSON object with mandatory fields (hostname, pid, time).
Installation:
npm install bunyanExample:
const bunyan = require('bunyan');
const logger = bunyan.createLogger({ name: 'myapp', streams: [ { level: 'info', stream: process.stdout }, { level: 'error', path: 'error. Also in library
Object Serialization in Kotlin: Libraries and Code Examples
Internationalization (i18n) in Kotlin: A Complete Guide
Caching in Rust: Redis, Memcached, and Built-in Solutions