Introducción: por qué el logging es importante en TypeScript
El logging no es solo registrar eventos en un archivo. En las aplicaciones modernas de TypeScript, un logging bien configurado es la base para la depuración, el monitoreo y el análisis de rendimiento. Sin registros de calidad, el desarrollador pierde horas reproduciendo errores, y el equipo de operaciones no puede identificar rápidamente los cuellos de botella.
TypeScript, como superconjunto de JavaScript, proporciona un tipado estricto, lo que permite crear sistemas de logging más predecibles y seguros. Sin embargo, usar un simple console.log() en producción es un antipatrón que genera ruido informativo y pérdida de datos importantes.
En este artículo, exploraremos las mejores prácticas de logging en TypeScript: desde la elección de la librería hasta la estructuración de los logs y el trabajo con contexto.
Elección de la librería de logging
En el ecosistema TypeScript/Node.js existen varias librerías populares. Consideremos tres opciones principales:
- Winston — la librería más popular con configuración flexible de transports (salida a consola, archivo, base de datos). Soporta niveles de logging y formateo.
- Pino — una librería moderna orientada al rendimiento. Ideal para sistemas de alta carga.
- Bunyan — una opción menos popular pero estable. Todos los loggers generan datos en formato JSON.
Para una aplicación típica en TypeScript, recomendamos Winston o Pino. Ejemplo de configuración de Winston con TypeScript:
import winston from 'winston';
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'error.log', level: 'error' }), ],});
logger.info('Servidor iniciado', { port: 3000 });Integración con TypeScript: tipos e interfaces
Para aprovechar al máximo el tipado, cree un wrapper para el logger utilizando interfaces:
interface LoggerOptions { level?: string; service?: string;}
interface ILogger { info(message: string, meta?: unknown): void; error(message: string, error?: Error): void; warn(message: string, meta?: unknown): void;}Esto le permitirá controlar estrictamente los tipos de datos transmitidos y evitar errores en la etapa de compilación.
Niveles de logging y su uso correcto
Los niveles de logging estándar (del más crítico al menos crítico): error, warn, info, debug, trace. Es importante mantener una semántica uniforme:
- error — solo para errores irrecuperables que requieren atención del desarrollador.
- warn — para situaciones potencialmente problemáticas que no interrumpen el funcionamiento de la aplicación.
- info — para eventos clave: inicio del servidor, procesamiento de una solicitud, autenticación exitosa.
- debug — para depuración durante el desarrollo. No active este nivel en producción.
Ejemplo de cambio dinámico del nivel según el entorno:
const logLevel = process.env.NODE_ENV === 'production' ? 'warn' : 'debug';const logger = winston.createLogger({ level: logLevel });Logs estructurados y contexto
En lugar de mensajes de texto, utilice logs estructurados en formato JSON. Esto permite analizar fácilmente los logs con el stack ELK (Elasticsearch, Logstash, Kibana) u otros sistemas.
Asegúrese de agregar información de contexto: ID de solicitud, nombre de usuario, nombre del servicio, marca de tiempo. Ejemplo usando Pino:
import pino from 'pino';
const logger = pino({ level: 'info', mixin() { return { service: 'my-api' }; },});
// Agregando c