Logging básico para APIs Node.js

Capítulo 9

Tiempo estimado de lectura: 6 minutos

+ Ejercicio

Qué es “logging útil y mínimo” en una API

El objetivo del logging en una API no es “guardar todo”, sino dejar un rastro suficiente para responder rápido a preguntas típicas: ¿qué request falló?, ¿cuánto tardó?, ¿qué endpoint fue?, ¿con qué código respondió?, ¿qué error ocurrió?, ¿se repite el patrón?

Un logging útil y mínimo suele incluir:

  • Contexto del request: método, ruta, status, duración, request id.
  • Eventos relevantes: inicio/fin de request (opcional), llamadas a servicios externos, validaciones fallidas, decisiones importantes.
  • Errores: stack trace, tipo de error, request id, status final.

console.log vs logger estructurado

console.log es rápido para depurar, pero en producción suele quedarse corto: no tiene niveles consistentes, no estructura los datos, y es difícil de filtrar/consultar (por ejemplo, en un agregador de logs).

Un logger estructurado (como pino o winston) escribe logs como objetos (normalmente JSON), con campos estables. Ventajas:

  • Niveles (info, warn, error) para filtrar.
  • Campos (requestId, route, statusCode, durationMs) para buscar y agrupar.
  • Formato consistente para herramientas de observabilidad.

Implementar un logger básico con Pino (recomendado)

Pino es ligero y rápido, ideal para APIs. La idea será:

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

  • Crear un logger base.
  • Generar un request id por request.
  • Adjuntar un logger “hijo” al request con ese request id.
  • Loggear el ciclo del request y los errores centralizados.

Paso 1: crear el logger

Crea un archivo src/logger.js:

const pino = require('pino'); const logger = pino({ level: process.env.LOG_LEVEL || 'info', base: null, timestamp: () => `,"time":"${new Date().toISOString()}"` }); module.exports = logger;

Notas:

  • level controla qué se imprime (por ejemplo, en producción podrías usar info o warn).
  • base: null evita campos automáticos que a veces no necesitas (puedes quitarlo si prefieres).

Paso 2: middleware de request id y logger por request

Crea src/middlewares/requestLogger.js:

const crypto = require('crypto'); const logger = require('../logger'); function requestLogger(req, res, next) { const requestId = crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(16).toString('hex'); req.requestId = requestId; res.setHeader('x-request-id', requestId); const log = logger.child({ requestId }); req.log = log; const start = process.hrtime.bigint(); res.on('finish', () => { const end = process.hrtime.bigint(); const durationMs = Number(end - start) / 1e6; const level = res.statusCode >= 500 ? 'error' : res.statusCode >= 400 ? 'warn' : 'info'; log[level]({ method: req.method, path: req.originalUrl, statusCode: res.statusCode, durationMs: Math.round(durationMs) }, 'request completed'); }); next(); } module.exports = requestLogger;

Qué hace este middleware:

  • Genera un requestId y lo expone en la respuesta (x-request-id) para que el cliente lo reporte si hay problemas.
  • Crea req.log como logger hijo con { requestId }.
  • Registra al finalizar el request un log con método, ruta, status y duración.
  • El nivel se ajusta automáticamente: 2xx/3xx => info, 4xx => warn, 5xx => error.

Paso 3: integrar el middleware en Express

En tu archivo principal (por ejemplo src/app.js o donde configures Express), registra el middleware lo más arriba posible (antes de rutas):

const express = require('express'); const requestLogger = require('./middlewares/requestLogger'); const app = express(); app.use(express.json()); app.use(requestLogger); // ... aquí van tus rutas module.exports = app;

Loggear eventos dentro de controladores (sin ruido)

Una vez que existe req.log, úsalo para eventos puntuales. Evita loggear “cada paso” si no aporta valor.

Ejemplo en un controlador:

async function createOrder(req, res, next) { try { req.log.info({ userId: req.user?.id }, 'creating order'); // ... lógica de creación res.status(201).json({ ok: true }); } catch (err) { next(err); } }

Recomendación: loggea identificadores (userId, orderId) y no objetos completos si pueden incluir datos sensibles.

Registrar errores en el manejador centralizado

En el manejador de errores, registra el error usando el logger del request si existe (req.log), para que el log quede correlacionado con el requestId.

Ejemplo de error handler:

function errorHandler(err, req, res, next) { const statusCode = err.statusCode || 500; const log = req.log || require('./logger'); log.error({ err: { name: err.name, message: err.message, stack: err.stack }, method: req.method, path: req.originalUrl, statusCode }, 'request failed'); res.status(statusCode).json({ error: { message: statusCode === 500 ? 'Internal Server Error' : err.message, requestId: req.requestId } }); } module.exports = errorHandler;

Detalles importantes:

  • Se loggea el stack trace en servidor, pero al cliente se le devuelve un mensaje controlado.
  • Se incluye requestId en la respuesta para soporte.

Alternativa: Winston (cuando necesitas múltiples transports)

Si necesitas enviar logs a archivo, rotación, o múltiples destinos, Winston es común. Un setup mínimo con niveles:

const winston = require('winston'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.json(), transports: [new winston.transports.Console()] }); module.exports = logger;

La correlación por request se puede hacer de forma similar creando un “child logger” (con logger.child en versiones/formatos compatibles) o adjuntando requestId manualmente en cada llamada. En APIs, Pino suele ser más directo para este patrón.

Qué NO loggear: datos sensibles y cómo prevenirlo

Un error común es loggear el body completo o headers completos. Eso puede filtrar credenciales o información personal. Reglas prácticas:

  • No loggear contraseñas, tokens, cookies, claves API, secretos, números de tarjeta, documentos de identidad.
  • No loggear el header Authorization ni Cookie.
  • Evitar loggear req.body completo; si necesitas contexto, loggea solo campos no sensibles o un resumen.
  • Redactar (masking) campos sensibles si por necesidad deben aparecer.

Ejemplo de “redacción” simple antes de loggear

Si vas a registrar parte del body, crea una función que elimine/oculte campos:

function redactBody(body) { if (!body || typeof body !== 'object') return body; const copy = { ...body }; const sensitiveKeys = ['password', 'token', 'accessToken', 'refreshToken']; for (const key of sensitiveKeys) { if (key in copy) copy[key] = '[REDACTED]'; } return copy; } // Uso: req.log.info({ body: redactBody(req.body) }, 'payload received');

Mejor aún: define una lista blanca de campos permitidos para logging (allowlist) en lugar de intentar adivinar todos los sensibles.

Checklist rápido para un logging sano en APIs

ObjetivoQué registrarQué evitar
CorrelaciónrequestId en todos los logsLogs sin contexto
RendimientodurationMs, statusCodeLoggear cada detalle interno
Erroresname, message, stack, statusEnviar stack al cliente
PrivacidadIDs y metadatosAuthorization, cookies, passwords

Ahora responde el ejercicio sobre el contenido:

¿Cuál práctica implementa mejor un logging útil y mínimo en una API Node.js con Express, manteniendo correlación y evitando exponer datos sensibles?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

La práctica recomendada es correlacionar logs con requestId, registrar datos mínimos (método, ruta, status, duración) y centralizar el log de errores con stack en servidor. Al cliente se responde con un mensaje controlado e incluye el requestId, evitando datos sensibles.

Siguiente capítulo

Conexión a base de datos en Node.js: conceptos y ejemplo con MongoDB

Arrow Right Icon
Portada de libro electrónico gratuitaNode.js para principiantes: crea un backend simple con Express
75%

Node.js para principiantes: crea un backend simple con Express

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.