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

@scouts/logger-node

v0.1.1

Published

Biblioteca de logging modular e desacoplada para Node.js, construída com padrão de composição via Proxy.

Readme

logger-node

Biblioteca de logging modular e desacoplada para Node.js, construída com padrão de composição via Proxy.

Características

  • Modular: Funcionalidades podem ser compostas independentemente
  • Desacoplado: Sem dependências diretas entre módulos
  • Type-safe: Totalmente tipado com TypeScript
  • Flexível: Suporte a diferentes implementações de sink e redactor
  • Performático: Uso de Proxy para interceptação eficiente

Composição de Funcionalidades

O logger-node usa um padrão de composição via Proxy que permite adicionar funcionalidades de forma modular e desacoplada.

Uso Básico

import { 
  createLogger, 
  attachMetrics, 
  attachRedactor, 
  attachSink 
} from '@scouts/logger-node';

// Criar logger base
let logger = createLogger({ level: 'info' });

// Adicionar métricas
logger = attachMetrics(logger);

// Adicionar redação de dados sensíveis
const redactor = createRedactor({ keys: ['password', 'token'] });
logger = attachRedactor(logger, redactor);

// Adicionar sink para destino de logs
const sink = createConsoleSink();
logger = attachSink(logger, sink, {
  service: 'my-service',
  environment: 'production',
  version: '1.0.0'
});

// Usar logger com todas as funcionalidades
await logger.info('User logged in', { user: 'john', password: 'secret' });
// Output: User logged in { user: 'john', password: '[REDACTED]' }

Ordem de Composição

A ordem de composição determina o fluxo de processamento:

  1. metrics → redactor → sink: Métricas contam antes de redação
  2. redactor → sink → metrics: Redação antes de enviar ao sink
  3. sink → redactor → metrics: Sink recebe dados não redatados

Recomendação: metrics → redactor → sink

Exemplos de Composição

// Logger simples com sink
let logger = createLogger();
logger = attachSink(logger, consoleSink);

// Logger com redação de senhas
let logger = createLogger();
const redactor = createRedactor({ keys: ['password', 'token'] });
logger = attachRedactor(logger, redactor);
logger = attachSink(logger, fileSink);

// Logger completo com métricas
let logger = createLogger();
logger = attachMetrics(logger);
logger = attachRedactor(logger, redactor);
logger = attachSink(logger, sink);

Context Module - Correlation ID Tracking

O módulo de contexto fornece rastreamento de correlation IDs usando AsyncLocalStorage para logging distribuído.

Uso Básico

import { getCid, runWithCid, runWithCidAsync, ensureCid } from '@scouts/logger-node';

// Executar função com CID específico
const result = runWithCid(() => {
  const cid = getCid(); // Retorna o CID do contexto atual
  return { cid, data: 'processed' };
}, 'req-123');

// Executar função async com CID
const asyncResult = await runWithCidAsync(async () => {
  const cid = getCid();
  await someAsyncOperation();
  return { cid, result: 'done' };
}, 'req-456');

// Garantir CID válido (normalização segura)
const cid = ensureCid('  valid-cid  '); // Retorna 'valid-cid' (trimmed)
const newCid = ensureCid('invalid@#$'); // Gera novo UUID

Framework Integration Patterns

Express.js Middleware

import { Request, Response, NextFunction } from 'express';
import { runWithCidAsync } from '@scouts/logger-node';

export function correlationIdMiddleware(req: Request, res: Response, next: NextFunction) {
  const cid = req.headers['x-correlation-id'] as string || 
              req.headers['x-request-id'] as string;
  
  runWithCidAsync(async () => {
    next();
  }, cid);
}

Fastify Hook

import { FastifyInstance } from 'fastify';
import { runWithCidAsync } from '@scouts/logger-node';

export async function registerCorrelationId(fastify: FastifyInstance) {
  fastify.addHook('preHandler', async (request, reply) => {
    const cid = request.headers['x-correlation-id'] as string;
    await runWithCidAsync(async () => {
      // Request handlers will have access to getCid()
    }, cid);
  });
}

NestJS Middleware

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { runWithCidAsync } from '@scouts/logger-node';

@Injectable()
export class CorrelationIdMiddleware implements NestMiddleware {
  async use(req: Request, res: Response, next: NextFunction) {
    const cid = req.headers['x-correlation-id'] as string;
    
    await runWithCidAsync(async () => {
      next();
    }, cid);
  }
}

// app.module.ts
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CorrelationIdMiddleware)
      .forRoutes('*');
  }
}

NestJS Interceptor para Logger Bindings

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { getCid } from '@scouts/logger-node';

@Injectable()
export class CorrelationIdInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const cid = getCid();
    
    // Bind CID to logger context (example with custom logger)
    if (cid) {
      // logger.setContext({ cid });
    }
    
    return next.handle();
  }
}

OpenTelemetry Bridge (Preparado)

import { trace } from '@opentelemetry/api';
import { getCid } from '@scouts/logger-node';

// Função para vincular CID ao span ativo
export function setCidInSpan(): void {
  const cid = getCid();
  const span = trace.getActiveSpan();
  
  if (cid && span) {
    span.setAttribute('correlation.id', cid);
  }
}

// Usar em middleware/interceptor
export function correlationMiddleware(req: Request, res: Response, next: NextFunction) {
  runWithCidAsync(async () => {
    setCidInSpan(); // Vincula CID ao span OTel
    next();
  }, req.headers['x-correlation-id'] as string);
}

Segurança e Normalização

O ensureCid aplica normalização segura por padrão:

  • Trim: Remove espaços nas bordas
  • Whitelist: Apenas [A-Za-z0-9._:-] permitidos
  • Limite: Máximo 128 caracteres
  • Rejeição: Caracteres de controle e especiais geram novo UUID
// Exemplos de normalização
ensureCid('  valid-cid  ');     // → 'valid-cid'
ensureCid('invalid@#$');       // → 'uuid-gerado'
ensureCid('a'.repeat(200));    // → 'a'.repeat(128)
ensureCid('cid\nwith\r\n');    // → 'uuid-gerado'

Configuração Avançada

// Charset customizado
ensureCid('VALID123', { charset: /^[A-Z0-9]+$/ });

// Tamanho customizado
ensureCid('long-cid', { maxLen: 50 });

// Gerador ULID (requer peer dependency 'ulid')
ensureCid('test', { generator: 'ulid' });

Notas sobre Decorators e Metadados

⚠️ Importante: Ao criar decorators que envolvem métodos, preserve os metadados do NestJS:

// ❌ Evitar: Proxy que quebra metadados
function badDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
  return new Proxy(descriptor.value, {
    apply: (fn, thisArg, args) => {
      return runWithCid(fn.bind(thisArg), getCid());
    }
  });
}

// ✅ Correto: Preservar metadados
function goodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  
  descriptor.value = function(...args: any[]) {
    return runWithCid(() => original.apply(this, args), getCid());
  };
  
  // Preservar metadados do NestJS
  const metadataKeys = Reflect.getMetadataKeys(target, key);
  metadataKeys.forEach(metaKey => {
    const value = Reflect.getMetadata(metaKey, target, key);
    Reflect.defineMetadata(metaKey, value, descriptor.value, key);
  });
  
  return descriptor;
}

API Reference

Context Functions

  • getCid(): string | undefined - Obtém CID do contexto atual
  • runWithCid<T>(fn: () => T, cid?: string): T - Executa função com CID
  • runWithCidAsync<T>(fn: () => Promise<T>, cid?: string): Promise<T> - Executa função async com CID
  • ensureCid(input?: string | string[], opts?: EnsureOpts): string - Garante CID válido com normalização

attachMetrics(logger, collector?, options?)

Adiciona coleta de métricas ao logger.

attachRedactor(logger, redactor)

Adiciona redação de dados sensíveis ao logger.

attachSink(logger, sink, options?)

Adiciona destino de logs ao logger.

Building

Run nx build logger-node to build the library.

Running unit tests

Run nx test logger-node to execute the unit tests via Jest.