Logging in JavaScript Applications: Best Practices for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Logging in JavaScript Applications: Best Practices for Beginners



Logging is the foundation of debugging and monitoring any application. Without quality logs, you'll be looking for a "needle in a haystack" when something goes wrong. In this article, we'll cover the best practices for logging in JavaScript (Node.js), suitable for both small projects and production environments.



1. Why You Shouldn't Rely on console.log?



Many beginner developers use console.log for everything. It's convenient during development, but in a production environment, this approach leads to problems:



  • No logging levels — you can't filter errors from informational messages.
  • Difficult to manage output — you can't disable logs for specific modules.
  • Poor performance — serializing objects in console.log can be costly.
  • Lack of structure — logs don't have a unified format (JSON, timestamp, level).


Instead, use specialized libraries. Here's a simple but correct example using Winston:



// logger.jsconst winston = require('winston');

const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ],});

if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple(), }));}

module.exports = logger;


2. Logging Levels: When to Use What



Standard logging levels (in increasing severity): debug, info, warn, error. It's important to understand when to apply each level:



  • debug — detailed information for developers (variable values, system state). Disabled in production.
  • info — normal application events (request processed, user registered).
  • warn — potentially dangerous situations (low memory, deprecated API).
  • error — errors that require intervention (exceptions, database failures).


Example of correct usage with the Pino library (the fastest for Node.js):



const pino = require('pino');

const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: { target: 'pino-pretty', options: { colorize: true } }});

// Usagelogger.info({ userId: 123 }, 'User logged in');logger.error(new Error('Database connection error'), 'Critical error');logger.debug('Variable x = 42'); // won't output if level = 'info'


3. Structured Logging: JSON and Context



Instead of text strings, use structured logs (usually JSON). This allows you to:



  • Easily parse logs with tools (ELK, Graylog).
  • Add context (request ID, user, version).
  • Filter and aggregate data.


Example of a structured log with context:



const logger = require('./logger');

app.use((req, res, next) => { req.requestId = uuidv4(); next();});

app.get('/api/users', async (req, res) => { logger.info({ requestId: req.requestId, method: req.method, url: req.originalUrl, ip: req.ip }, 'Incoming request');

try { const users = await getUsers(); logger.info({ requestId: req.requestId, count: users.length }, 'Users retrieved'); res.json(users); } catch (err) { logger.error({

Blogs

Book Recommendations