Introducción: ¿Por qué es necesario el logging en TypeScript?
El logging no es solo registrar mensajes en la consola. En las aplicaciones TypeScript modernas (del lado del servidor con Node.js, del lado del cliente con React/Angular o proyectos full-stack), el logging se convierte en una herramienta crítica para la depuración, el monitoreo y el análisis del rendimiento. Sin un logging de calidad, los desarrolladores pierden horas reproduciendo errores, y los ingenieros DevOps no pueden determinar rápidamente la causa de una falla en producción.
TypeScript, como superconjunto de JavaScript, agrega tipado estricto, lo que permite crear sistemas de logging más robustos y predecibles. En este artículo, analizaremos las mejores prácticas que te ayudarán a organizar un logging eficiente en proyectos TypeScript.
Principios básicos del logging
Niveles de logging
El uso correcto de los niveles de logging es la base de cualquier sistema de monitoreo. En TypeScript, se acostumbra usar los siguientes niveles (en orden ascendente de importancia):
debug— información de depuración detallada (solo para desarrollo);info— mensajes informativos estándar (inicio del servidor, solicitudes);warn— advertencias (problemas no críticos);error— errores que requieren atención;fatal— errores críticos que provocan la detención de la aplicación.
Ejemplo de configuración de niveles con la librería Winston:
import winston from 'winston';
const logger = winston.createLogger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', format: winston.format.json(), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'error.log', level: 'error' }), ],});
logger.info('Servidor iniciado en el puerto 3000');logger.warn('La memoria está cerca del límite');logger.error('Error de conexión a la BD');Logs estructurados
Evita los mensajes de cadena simples. Utiliza logs estructurados con información de contexto (formato JSON). Esto simplifica la búsqueda y el análisis en sistemas como ELK o Grafana.
// Malologger.info(`Usuario ${userId} realizó la acción ${action}`);
// Buenologger.info('Acción del usuario', { userId: userId, action: action, timestamp: new Date().toISOString(), ip: request.ip,});Este enfoque permite filtrar logs por campos y construir gráficos.
Mejores prácticas para TypeScript
Uso de TypeScript para tipificar los logs
TypeScript permite definir interfaces para el contexto de los logs, lo que previene errores accidentales en los nombres de los campos:
interface LogContext { userId: string; action: string; durationMs: number; success: boolean;}
function logUserAction(context: LogContext): void { logger.info('User action', context);}
// El compilador verificará que todos los campos se hayan pasado correctamentelogUserAction({ userId: '123', action: 'login', durationMs: 150, success: true,});Evita registrar datos sensibles
Nunca registres contraseñas, tokens, números de tarjetas de crédito o datos personales (GDPR). Utiliza utilidades para enmascarar:
function sanitizeLogData(data: Record<string, unknown>): Record<string, unknown> { const sensitiveFields = ['password', 'token', 'ssn']; const sanitized = { ...data }; for (const field of sensitiveFields) { if (sanitized[field]) { sanitized[field] = '***'; } } return sanitized;}
logger.info('User registration', sanitizeLogData(userData));Logging asíncrono y rendimiento
En aplicaciones de alta carga, el logging síncrono puede ralentizar el trabajo. Utiliza transportes asíncronos (por ejemplo, winston soporta buffering).