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

@mks2508/better-logger

v0.18.2-alpha.1

Published

Logger avanzado para consola con estilos CSS, temas adaptativos, badges automáticos, SVG, animaciones, CLI integrado y soporte dual browser/terminal con colores ANSI

Readme

🚀 Better Logger

Logger avanzado para consola con estilos CSS, log context tipo MDC, transports (File / HTTP / OTLP→SigNoz), hooks, serializers y CLI integrado. Soporte dual browser/terminal.

NPM Version TypeScript License

✨ Features

Core

  • 🎨 Estilos CSS en consola — gradientes, sombras, %c formatting, presets listos
  • 🏷️ Badges — etiquetado flexible con badges() / badge()
  • 🌗 Temas adaptativos — detección automática light/dark
  • 🎯 TypeScript first — tipado completo, cero any en la superficie pública

Niveles y verbosidad

  • 📊 6 niveles jerárquicostrace < debug < info < warn < error < critical (+ silent)
  • 🔭 trace alineado con OpenTelemetry — severity number OTel (1-24) listo para SigNoz
  • 🔇 Verbosidad filtrablesetVerbosity('warn') filtra todo por debajo

Log context (MDC)

  • 🧩 Contexto estructuradowithContext({ requestId, userId }) se mergea en cada TransportRecord
  • 🧬 Loggers hijueloschild({ ... }) hereda contexto sin mutar al padre
  • 🏷️ Resource OTelsetResource({ 'service.name': ... }) por logger

Transports

  • 📁 File — async (fs.promises), bounded buffer, sanitización de path, fallback a localStorage en browser
  • 🌐 HTTP — batching, retry con backoff, bounded buffer (sin OOM), status check
  • 🔭 OTLP → SigNoz — payload OTLP/HTTP JSON spec-compliant, ingestion key desde env var (nunca hardcodeada)
  • 🧩 Custom — implementa ITransport y registra con addTransport()

Hooks, serializers y más

  • 🪝 Hookson('beforeLog', ...) (awaited, soporta redacción PII) / on('afterLog', ...)
  • 🔄 Middleware — pipeline use((entry, next) => ...)
  • 🧬 Serializers — transforma tipos antes de loggear (addSerializer(Error, fn))
  • 🖥️ CLI integrado — spinners, boxes, tablas, steps, headers

📦 Instalación

npm install @mks2508/better-logger
# o
bun add @mks2508/better-logger

🚀 Quick Start

import logger from '@mks2508/better-logger';

logger.info('Application started');
logger.success('Database connected');
logger.warn('High memory usage');
logger.error('Connection failed');
logger.critical('Disk full');
logger.trace('verbose internals');  // filtrado por defecto (verbosity=debug)

Importar métodos sueltos

import { info, error, success, trace } from '@mks2508/better-logger';
info('Proceso iniciado');
success('✓ Completado');

📊 Niveles de log

trace(-1) < debug(0) < info(1) < warn(2) < error(3) < critical(4)

trace es el nivel más bajo, alineado con la banda TRACE de OpenTelemetry (severity 1-4). Cada TransportRecord incluye severityNumber y severityText OTel automáticamente.

import { LOG_LEVELS } from '@mks2508/better-logger';

logger.setVerbosity('debug');   // muestra trace + debug + ...
logger.setVerbosity('warn');    // solo warn, error, critical
logger.setVerbosity('silent');  // nada

success() emite a INFO severity (con styling de success y tag: 'success' en el record, para que transports distingan success de info genérico).

🧩 Log Context (MDC)

Contexto estructurado que se adjunta a cada log emitido y se mergea en TransportRecord.attributes:

// Mutar el logger (chaining)
logger.withContext({ requestId: 'req_123', userId: 'u_42' });
logger.info('processing');  // → attributes: { requestId, userId }

// Limpiar
logger.clearContext();

// Snapshot read-only
const ctx = logger.getContext();

Loggers hijuelos (inmutables)

child() devuelve un logger nuevo con contexto merged, sin tocar al padre — ideal para request-scoped logging:

const requestLogger = logger.child({ requestId: getRequestId() });
requestLogger.info('auth ok');     // lleva requestId
logger.info('unrelated');          // NO lleva requestId (padre intacto)

Resource OTel

logger.setResource({
  'service.name': 'my-app',
  'service.version': '1.2.3',
  'deployment.environment': 'production'
});

📡 Transports

Los transports reciben cada TransportRecord y lo mandan a un destino. Se registran con addTransport():

import logger, { FileTransport, HttpTransport, OtlpTransport } from '@mks2508/better-logger';

File (Node.js)

logger.addTransport({
  target: new FileTransport({
    destination: '/var/log/app.log',
    batchSize: 100,
    flushInterval: 5000,
    maxBufferSize: 10_000  // hard cap, drop oldest on overflow
  }),
  level: 'info'
});
  • Escritura async con fs.promises (no bloquea el event loop)
  • Bounded buffer — si se llena, dropea el record más viejo (avisa vía onError)
  • Sanitización de destination (sin path traversal)
  • En browser: fallback silencioso a localStorage

HTTP

logger.addTransport({
  target: new HttpTransport({
    url: 'https://logs.example.com/ingest',
    batchSize: 50,
    flushInterval: 5000,
    maxBufferSize: 10_000,
    maxRetries: 3,
    headers: { 'Authorization': `Bearer ${process.env.LOG_TOKEN}` }
  }),
  level: 'warn'
});
  • Batching + flush periódico
  • Retry con backoff exponencial (bounded)
  • Status check — solo re-bufferea en errores recuperables (5xx, red), descarta en 4xx
  • Bounded buffer (sin OOM en outages largos)

OTLP → SigNoz (o cualquier backend OTLP/HTTP)

logger.addTransport({
  target: new OtlpTransport({
    endpoint: 'https://otelcollector.example.com:4318',
    serviceName: 'my-app',           // requerido (service.name)
    serviceVersion: '1.2.3',
    environment: 'production',
    ingestKeyEnvVar: 'SIGNOZ_KEY',   // lee process.env.SIGNOZ_KEY — NUNCA hardcodear
    batchSize: 50,
    flushInterval: 5000
  })
});

logger.info('Hola desde better-logger → SigNoz');
// → POST <endpoint>/v1/logs con payload OTLP/HTTP JSON
// → visible en SigNoz Logs UI

El transport POSTea a <endpoint>/v1/logs con el body LogsData spec-compliant (resourceLogsscopeLogslogRecords). La ingestion key se lee de la env var indicada en ingestKeyEnvVar al construir el transport — no se escribe en código ni en el record.

🔐 Seguridad: la key vive en tu gestor de secrets (Bitwarden / Coolify env) y se inyecta vía process.env. El transport no la loguea ni la serializa.

Registry de strings

Para los 4 built-ins puedes usar el nombre en vez de la instancia:

logger.addTransport({ target: 'file', options: { destination: '/var/log/app.log' } });
logger.addTransport({ target: 'console' });

Para Otlp conviene la instancia directa (opciones tipadas: endpoint, serviceName, ingestKeyEnvVar).

Custom transport

import type { ITransport, TransportRecord } from '@mks2508/better-logger';

const elastic: ITransport = {
  name: 'elasticsearch',
  async write(record: TransportRecord) {
    await fetch('https://es.example.com/logs/_doc', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(record)
    });
  },
  async flush() { /* ... */ },
  async close() { /* ... */ }
};

logger.addTransport({ target: elastic });

Flush y shutdown

await logger.flushTransports();   // fuerza el envío del buffer
await logger.closeTransports();   // cierra todos (drain)

En shutdown limpio: await logger.cleanup() hace drain de transports + reset de estado.

🪝 Hooks & Middleware

// beforeLog es AWAITED — las mutaciones se reflejan en el mensaje emitido
logger.on('beforeLog', (entry) => {
  entry.message = entry.message.replace(/password=\S+/g, 'password=***');
  return entry;
});

// afterLog es fire-and-forget (no cambia el mensaje ya en pantalla)
logger.on('afterLog', (entry) => {
  metrics.increment(`logs.${entry.level}`);
});

// Correlation ID en cada log
logger.use((entry, next) => {
  entry.correlationId = getCorrelationId();
  next();
});

on() / once() / off() / use() devuelven una función de unsub.

🧬 Serializers

Transforma tipos antes de serializarlos al log:

logger.addSerializer(Error, (err) => ({
  name: err.name,
  message: err.message,
  stack: err.stack?.split('\n').slice(0, 5)
}));

logger.addSerializer(User, (user) => ({
  id: user.id,
  email: user.email   // password omitido
}));

logger.error('Failed:', new Error('timeout'));  // Error serializado custom

🏷️ Scoped Loggers

// Scope simple
const auth = logger.scope('Auth');
auth.info('validating token');

// Component (auto-badge COMPONENT)
const db = logger.component('Database');
db.lifecycle('connect', 'pool ready');

// API (auto-badge API, +métodos slow/rateLimit/auth/deprecated)
const api = logger.api('GraphQL');
api.slow('query timeout', 1200);
api.deprecated('use v2 endpoint');

Context logger (bloques anidados)

const request = logger.scope('Request');

await request.context('auth').runAsync(async () => {
  // prefix efectivo: "Request:auth"
  request.info('checking credentials');
  await request.context('db').runAsync(async () => {
    // prefix: "Request:auth:db"
    request.info('querying user');
  });
});

🎨 Styling

// Presets
logger.preset('cyberpunk');     // neón, glow
logger.preset('glassmorphism'); // blur moderno
logger.preset('minimal');
logger.preset('debug');

// Toggles
logger.hideTimestamp().showLocation().hideBadges();

// Tema
logger.setTheme('dark');   // 'default' | 'dark' | 'light' | 'neon' | 'minimal' | 'cyberpunk'

Estilos custom (StyleBuilder)

import { createStyle } from '@mks2508/better-logger';

const style = createStyle()
  .bg('linear-gradient(45deg, #667eea, #764ba2)')
  .color('white')
  .padding('12px 24px')
  .rounded('8px')
  .shadow('0 4px 15px rgba(102, 126, 234, 0.4)')
  .build();

console.log('%c🚀 Hello', style);

🖥️ CLI Primitives

Spinners, boxes, tablas, steps y headers para CLIs Node.js:

logger.header('Deploy', 'production');
logger.step(2, 5, 'building');
logger.spinner('uploading...');

logger.box('Build complete', { border: 'round' });
logger.cliTable([
  { service: 'api', status: 'ok' },
  { service: 'db', status: 'ok' }
]);

🔧 Configuración avanzada

import { Logger } from '@mks2508/better-logger';

const logger = new Logger({
  prefix: 'APP',
  verbosity: 'debug',
  enableStackTrace: true,
  theme: 'dark',
  timestampFormat: 'iso'
});

logger.updateConfig({ verbosity: 'warn' });
logger.resetConfig();

🌐 Compatibilidad

  • Browsers modernos — soporte completo (CSS, DevTools, localStorage fallback)
  • Node.js — core + transports (File async, HTTP, OTLP)
  • TypeScript — definiciones completas
  • ESM & CommonJS — ambos exports

🔗 Recursos

📄 Licencia

MIT — ver LICENSE.