@alateamtech/logger
v2.0.1
Published
Logger centralizado para AlaTeamTech optimizado para Grafana + Loki con formato JSON estructurado
Maintainers
Readme
@alateamtech/logger
Logger centralizado para AlaTeamTech optimizado para Grafana + Loki con formato JSON estructurado. Diseñado para aplicaciones modernas que requieren observabilidad avanzada y análisis de logs.
✨ Características Principales
- 📊 Optimizado para Loki: Formato JSON estructurado perfecto para Grafana + Loki
- 📝 Niveles de Log:
debug,info,warn,error - 🖥️ Desarrollo Local: Formato pretty para consola en desarrollo
- 📊 Metadata Estructurada:
project,service,data,contexty más - 🔄 Detección de Entorno: Configura automáticamente según el entorno
- 🛡️ Sanitización Automática: Elimina automáticamente passwords, tokens, etc.
- 🎯 TypeScript: Completamente tipado
- 🧪 Testeado: 33 tests pasando
- ⚡ Ligero: Solo Winston como dependencia
📦 Instalación
npm install @alateamtech/logger⚡ Inicio Rápido
Uso Básico
import { AlaTeamLogger } from '@alateamtech/logger';
const logger = new AlaTeamLogger({
defaultProject: 'mi-app',
defaultService: 'api',
});
logger.info('Aplicación iniciada');
// Output: {"project":"mi-app","service":"api","level":"info","message":"Aplicación iniciada","timestamp":"2025-09-21T19:00:00.000Z",...}Con Metadata Estructurada
logger.info('Usuario creado', {
data: {
userId: '123',
email: '[email protected]',
},
requestId: 'req-456',
});Logging Avanzado
// Performance tracking
logger.timing('database_query', 150);
// Audit logging
logger.audit('user_login', 'user123', {
data: { ip: '192.168.1.1', success: true },
});
// Context logging
logger.logWithContext('info', 'Proceso completado', {
step: 'validation',
duration: 250,
});🎯 Configuración
Configuración Básica
const logger = new AlaTeamLogger({
level: 'info', // Nivel mínimo de logs
defaultProject: 'mi-proyecto', // Proyecto por defecto
defaultService: 'mi-servicio', // Servicio por defecto
format: 'json', // json | simple | pretty
version: '1.0.0', // Versión de la aplicación
organization: 'MiOrganización', // Organización
});Configuración por Entorno
// Desarrollo - formato pretty automático
process.env.NODE_ENV = 'development';
const devLogger = new AlaTeamLogger();
// Producción - formato JSON automático
process.env.NODE_ENV = 'production';
const prodLogger = new AlaTeamLogger();Variables de Entorno
NODE_ENV=production # Entorno (development/production)
APP_VERSION=1.2.3 # Versión de la aplicación
HOSTNAME=web-server-01 # Hostname del servidor📊 Formato de Logs para Loki
Estructura JSON
Todos los logs siguen esta estructura optimizada para Loki:
{
"level": "info",
"message": "Usuario creado exitosamente",
"timestamp": "2025-09-21T19:00:00.000Z",
"project": "ecommerce",
"service": "user-service",
"environment": "production",
"hostname": "web-01",
"pid": 1234,
"organization": "AlaTeamTech",
"version": "1.2.3",
"data": {
"userId": "user123",
"email": "[email protected]"
},
"requestId": "req-456",
"context": {
"action": "create_user",
"duration": 150
}
}Labels para Loki
Los siguientes campos son ideales como labels en Loki:
level- Nivel del logproject- Proyecto/aplicaciónservice- Microservicioenvironment- Entorno (dev/prod)hostname- Servidororganization- Organización
🔍 Integración con Grafana + Loki
Configuración de Loki
# promtail.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: app-logs
static_configs:
- targets:
- localhost
labels:
job: app-logs
__path__: /var/log/app/*.log
pipeline_stages:
- json:
expressions:
level: level
project: project
service: service
environment: environment
hostname: hostname
- labels:
level:
project:
service:
environment:
hostname:Queries de Ejemplo en Grafana
# Todos los logs de error
{level="error"}
# Logs de un servicio específico
{project="ecommerce", service="user-service"}
# Logs con filtro de texto
{project="ecommerce"} |= "Usuario creado"
# Métricas de rate por nivel
rate({project="ecommerce"}[5m]) by (level)
# Logs de performance
{project="ecommerce"} | json | performance="true"🎨 Formatos de Salida
JSON (Producción)
{
"level": "info",
"message": "Usuario creado",
"timestamp": "2025-09-21T19:00:00.000Z",
"project": "app",
"service": "api"
}Pretty (Desarrollo)
[2025-09-21 19:00:00] INFO: Usuario creado
{
"project": "app",
"service": "api",
"data": {
"userId": "123"
}
}Simple
2025-09-21T19:00:00.000Z [INFO] app.api: Usuario creado🔒 Sanitización de Datos Sensibles
El logger automáticamente sanitiza campos sensibles:
logger.info('Login exitoso', {
data: {
email: '[email protected]',
password: 'secret123', // → '[REDACTED]'
token: 'jwt-token', // → '[REDACTED]'
api_key: 'key-123', // → '[REDACTED]'
},
});Campos sanitizados automáticamente:
password,passwd,pwdtoken,jwt,auth,authorizationkey,secret,credential,credentialsapi_key,apikey,access_token,refresh_tokenprivate_key,public_key,cert,certificate
👶 Logger Hijo
Crea loggers con metadata heredada:
const userLogger = logger.child({
userId: 'user123',
sessionId: 'session456',
});
userLogger.info('Acción realizada');
// Incluye automáticamente userId y sessionId📈 Logging Avanzado
Performance Tracking
const start = Date.now();
// ... operación ...
const duration = Date.now() - start;
logger.timing('database_query', duration, {
data: { query: 'SELECT * FROM users', rows: 150 },
});Audit Logging
logger.audit('user_created', 'admin123', {
data: {
targetUserId: 'user456',
permissions: ['read', 'write'],
},
});Context Logging
logger.logWithContext(
'info',
'Proceso completado',
{
step: 'validation',
duration: 250,
success: true,
},
{
requestId: 'req-789',
}
);🔧 Gestión de Niveles
// Cambiar nivel dinámicamente
logger.setLevel('debug');
// Obtener nivel actual
const currentLevel = logger.getLevel(); // 'debug'
// Verificar si un nivel está habilitado
if (logger.getLevel() === 'debug') {
logger.debug('Información de debug detallada');
}🏗️ Factory Pattern
import { createLogger } from '@alateamtech/logger';
// Crear múltiples loggers
const apiLogger = createLogger({
defaultProject: 'ecommerce',
defaultService: 'api',
});
const dbLogger = createLogger({
defaultProject: 'ecommerce',
defaultService: 'database',
});📚 Ejemplos de Uso
API REST
import { createLogger } from '@alateamtech/logger';
const logger = createLogger({
defaultProject: 'ecommerce',
defaultService: 'api',
});
app.use((req, res, next) => {
req.logger = logger.child({
requestId: req.headers['x-request-id'],
userId: req.user?.id,
});
next();
});
app.post('/users', (req, res) => {
req.logger.info('Creando usuario', {
data: { email: req.body.email },
});
try {
const user = createUser(req.body);
req.logger.info('Usuario creado exitosamente', {
data: { userId: user.id },
});
res.json(user);
} catch (error) {
req.logger.error('Error creando usuario', error);
res.status(500).json({ error: 'Internal server error' });
}
});Microservicio
import { createLogger } from '@alateamtech/logger';
const logger = createLogger({
defaultProject: 'payment-system',
defaultService: 'payment-processor',
version: process.env.APP_VERSION,
});
class PaymentProcessor {
async processPayment(paymentData) {
const paymentLogger = logger.child({
paymentId: paymentData.id,
userId: paymentData.userId,
});
paymentLogger.info('Iniciando procesamiento de pago');
const start = Date.now();
try {
await this.validatePayment(paymentData);
paymentLogger.info('Pago validado');
const result = await this.chargePayment(paymentData);
const duration = Date.now() - start;
paymentLogger.timing('payment_processing', duration, {
data: { amount: paymentData.amount, success: true },
});
paymentLogger.audit('payment_processed', paymentData.userId, {
data: {
paymentId: paymentData.id,
amount: paymentData.amount,
},
});
return result;
} catch (error) {
paymentLogger.error('Error procesando pago', error, {
data: { amount: paymentData.amount },
});
throw error;
}
}
}Worker/Job Processor
import { createLogger } from '@alateamtech/logger';
const logger = createLogger({
defaultProject: 'notification-system',
defaultService: 'email-worker',
});
class EmailWorker {
async processEmailJob(job) {
const jobLogger = logger.child({
jobId: job.id,
jobType: job.type,
});
jobLogger.info('Procesando job de email', {
data: {
recipient: job.data.email,
template: job.data.template,
},
});
const start = Date.now();
try {
await this.sendEmail(job.data);
const duration = Date.now() - start;
jobLogger.timing('email_send', duration);
jobLogger.info('Email enviado exitosamente');
} catch (error) {
jobLogger.error('Error enviando email', error, {
data: {
recipient: job.data.email,
template: job.data.template,
},
});
// Re-queue job for retry
throw error;
}
}
}🚀 Mejores Prácticas
1. Usa Niveles Apropiados
// ✅ Correcto
logger.debug('Detalles internos de procesamiento');
logger.info('Usuario logueado exitosamente');
logger.warn('Límite de rate alcanzado');
logger.error('Error conectando a base de datos');
// ❌ Incorrecto
logger.error('Usuario logueado'); // No es un error
logger.debug('Error crítico'); // Debería ser error2. Incluye Contexto Relevante
// ✅ Correcto
logger.info('Pedido creado', {
data: { orderId: '123', userId: '456', amount: 99.99 },
requestId: 'req-789',
});
// ❌ Incorrecto
logger.info('Pedido creado'); // Sin contexto3. Usa Loggers Hijo para Contexto
// ✅ Correcto
const requestLogger = logger.child({
requestId: req.id,
userId: req.user.id,
});
requestLogger.info('Iniciando procesamiento');
requestLogger.info('Validación completada');
requestLogger.info('Procesamiento finalizado');
// ❌ Incorrecto - repetir contexto
logger.info('Iniciando procesamiento', { requestId: req.id, userId: req.user.id });
logger.info('Validación completada', { requestId: req.id, userId: req.user.id });4. Maneja Errores Apropiadamente
// ✅ Correcto
try {
await processOrder(orderId);
logger.info('Pedido procesado exitosamente', { data: { orderId } });
} catch (error) {
logger.error('Error procesando pedido', error, {
data: { orderId, step: 'payment' },
});
throw error;
}
// ❌ Incorrecto
try {
await processOrder(orderId);
} catch (error) {
logger.error('Error'); // Sin contexto ni objeto error
}📄 Licencia
MIT © AlaTeamTech
Construido con ❤️ por el equipo de AlaTeamTech
