npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alateamtech/logger

v2.0.1

Published

Logger centralizado para AlaTeamTech optimizado para Grafana + Loki con formato JSON estructurado

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, context y 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 log
  • project - Proyecto/aplicación
  • service - Microservicio
  • environment - Entorno (dev/prod)
  • hostname - Servidor
  • organization - 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, pwd
  • token, jwt, auth, authorization
  • key, secret, credential, credentials
  • api_key, apikey, access_token, refresh_token
  • private_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 error

2. 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 contexto

3. 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