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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@psouza.yuri/monitoria-sdk

v2.1.1

Published

SDK simplificada para observabilidade completa em aplicações Node.js/NestJS com configuração via variáveis de ambiente

Readme

🚀 Monitoria SDK

SDK simplificada para observabilidade completa em aplicações Node.js/NestJS com configuração via variáveis de ambiente.

⚡ Instalação

npm install @psouza.yuri/monitoria-sdk

🎯 Funcionalidades

  • Logs Estruturados: Logs JSON com contexto rico
  • Tracing Distribuído: Rastreamento automático de requisições
  • Métricas: Coleta automática de métricas de performance
  • Configuração via ENV: Todas as configurações via variáveis MONITORIA_*
  • Zero Config: Funciona com configurações mínimas
  • NestJS Ready: Integração automática com interceptors

🔧 Configuração Rápida

1. Variáveis de Ambiente (Recomendadas)

A SDK funciona mesmo sem variáveis de ambiente configuradas, mas funcionará em modo degradado (apenas logs no console):

# Nome do serviço (recomendado)
MONITORIA_SERVICE_NAME=meu-servico

# Endpoint do collector (recomendado para enviar logs)
MONITORIA_COLLECTOR_ENDPOINT=http://localhost:4318

Nota: Sem estas variáveis, a SDK mostrará avisos no console, mas sua aplicação continuará funcionando normalmente.

2. Configuração no main.ts

// Importar ANTES de qualquer outra coisa
import '@psouza.yuri/monitoria-sdk/telemetry';

import { NestFactory } from '@nestjs/core';
import { setupLogging } from '@psouza.yuri/monitoria-sdk';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Configurar logging (opcional - usa ENV vars)
  setupLogging(app);
  
  await app.listen(3000);
}

bootstrap();

📋 Variáveis de Ambiente Completas

Configurações Recomendadas (Para Produção)

MONITORIA_SERVICE_NAME=meu-servico
MONITORIA_COLLECTOR_ENDPOINT=http://localhost:4318

⚠️ Sem estas variáveis: A SDK funcionará em modo degradado (apenas logs no console) mas não interromperá sua aplicação.

Configurações Opcionais

# Serviço
MONITORIA_SERVICE_VERSION=1.0.0
MONITORIA_ENVIRONMENT=development

# Collector
MONITORIA_COLLECTOR_PROTOCOL=http
MONITORIA_COLLECTOR_TIMEOUT=5000

# Logging
MONITORIA_LOG_LEVEL=info
MONITORIA_ENABLE_CONSOLE=false

# Telemetria
MONITORIA_ENABLE_TRACING=true
MONITORIA_ENABLE_METRICS=true
MONITORIA_ENABLE_LOGS=true

🎨 Exemplos de Uso

Desenvolvimento

MONITORIA_SERVICE_NAME=api-dev
MONITORIA_ENVIRONMENT=development
MONITORIA_ENABLE_CONSOLE=true
MONITORIA_LOG_LEVEL=debug

Produção

MONITORIA_SERVICE_NAME=api-prod
MONITORIA_ENVIRONMENT=production
MONITORIA_ENABLE_CONSOLE=false
MONITORIA_LOG_LEVEL=info
MONITORIA_COLLECTOR_ENDPOINT=https://collector.prod.com:4318

📝 Enviando Logs em Qualquer Parte do Código

Usando o Singleton (Recomendado)

A forma mais simples e prática é usar o getLogger() que retorna uma instância singleton do logger:

// Em qualquer arquivo do seu código
import { getLogger } from '@psouza.yuri/monitoria-sdk';

// Em um service/controller/middleware
export class TodoService {
  private readonly logger = getLogger();

  async create(todo: CreateTodoDto) {
    // Log de início da operação
    this.logger.info('Criando novo todo', {
      request: {
        title: todo.title,
        category: todo.category,
      }
    });

    try {
      const result = await this.todoRepository.save(todo);
      
      // Log de sucesso
      this.logger.info('Todo criado com sucesso', {
        response: {
          id: result.id,
        }
      });

      return result;
    } catch (error) {
      // Log de erro
      this.logger.error('Erro ao criar todo', {
        error: {
          message: error.message,
          stack: error.stack,
        }
      });
      throw error;
    }
  }
}

Exemplo em Controller

import { Controller, Get, Post, Body } from '@nestjs/common';
import { getLogger } from '@psouza.yuri/monitoria-sdk';

@Controller('todos')
export class TodosController {
  private readonly logger = getLogger();

  @Post()
  async create(@Body() createTodoDto: CreateTodoDto) {
    this.logger.info('POST /todos - Recebendo requisição', {
      request: {
        body: createTodoDto,
      }
    });

    // Sua lógica aqui
    return { message: 'Todo criado' };
  }

  @Get()
  async findAll() {
    this.logger.debug('GET /todos - Buscando todos os todos');
    
    // Sua lógica aqui
    return [];
  }
}

Exemplo em Middleware/Guard

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { getLogger } from '@psouza.yuri/monitoria-sdk';

@Injectable()
export class AuthGuard implements CanActivate {
  private readonly logger = getLogger();

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    
    this.logger.debug('Verificando autenticação', {
      request: {
        path: request.url,
        method: request.method,
        userId: request.user?.id,
      }
    });

    // Sua lógica de autenticação
    return true;
  }
}

Exemplo em Service com Contexto Customizado

import { Injectable } from '@nestjs/common';
import { getLogger } from '@psouza.yuri/monitoria-sdk';
import { trace } from '@opentelemetry/api';

@Injectable()
export class PaymentService {
  private readonly logger = getLogger();

  async processPayment(paymentData: any) {
    const span = trace.getActiveSpan();
    const spanContext = span?.spanContext();

    this.logger.info('Iniciando processamento de pagamento', {
      request: {
        amount: paymentData.amount,
        currency: paymentData.currency,
      },
      // Contexto de trace para correlacionar com spans
      traceId: spanContext?.traceId,
      spanId: spanContext?.spanId,
    });

    try {
      // Processar pagamento
      const result = await this.paymentGateway.charge(paymentData);
      
      this.logger.info('Pagamento processado com sucesso', {
        response: {
          transactionId: result.id,
          status: result.status,
        }
      });

      return result;
    } catch (error) {
      this.logger.error('Erro ao processar pagamento', {
        error: {
          message: error.message,
          code: error.code,
        }
      });
      throw error;
    }
  }
}

Fazendo Override da Configuração

Se você precisar sobrescrever a configuração padrão do logger:

import { getLogger } from '@psouza.yuri/monitoria-sdk';

// Get logger com configuração customizada (apenas na primeira chamada)
const logger = getLogger({ 
  serviceName: 'custom-service' 
});

logger.info('Log com configuração customizada', {
  context: { custom: true }
});

🔍 O que é Capturado Automaticamente

Logs Estruturados

{
  "level": "info",
  "message": "GET /api/users (45.2ms)",
  "service": "meu-servico",
  "version": "1.0.0",
  "environment": "development",
  "traceId": "abc123...",
  "spanId": "def456...",
  "timestamp": "2025-01-09T15:30:00.000Z",
  "context": {
    "request": {
      "method": "GET",
      "url": "/api/users",
      "headers": {...}
    },
    "response": {
      "statusCode": 200,
      "headers": {...}
    },
    "performance": {
      "durationMs": 45.2
    }
  }
}

Traces Distribuídos

  • ✅ HTTP requests/responses
  • ✅ Express middleware
  • ✅ Controller execution
  • ✅ Database operations
  • ✅ External API calls

Métricas

  • ✅ Request count
  • ✅ Response time
  • ✅ Error rate
  • ✅ Throughput

⏱️ Entendendo Duration e Timing

📊 Conceitos de Duration

A SDK captura diferentes tipos de duration dependendo do contexto:

1. Duration do Logger (Recomendado)

  • O que mede: Tempo de processamento da API (span principal)
  • Exemplo: 8.54ms para um POST /api/v1/todos
  • Inclui: Lógica de negócio, validações, processamento
  • Não inclui: Operações de banco de dados, chamadas externas, middleware

2. Duration do Trace Completo

  • O que mede: Tempo total de todas as operações
  • Exemplo: 69ms para o mesmo POST
  • Inclui:
    • Span principal (8.54ms)
    • Operações de banco de dados (48.73ms)
    • Conexões de rede (8.18ms)
    • Middleware (0.5ms)

3. Duration do Postman/Cliente

  • O que mede: Tempo de rede (request → response)
  • Exemplo: 42ms para o mesmo POST
  • Inclui: Latência de rede + processamento total

🎯 Por que Diferentes Durations?

Cliente (Postman): 42ms
    ↓ (rede)
API recebe request
    ↓
Trace inicia: 17.08ms (middleware + handler)
    ├── Middleware: 0.5ms
    ├── Request Handler: 8.54ms ← LOGGER DURATION
    ├── POST interno (DB): 48.73ms
    └── tcp.connect: 8.18ms
    ↓
Trace total: 69ms
    ↓ (rede)
Cliente recebe response: 42ms

✅ Qual Duration Usar?

| Contexto | Duration Recomendado | Por quê? | |----------|---------------------|----------| | Logs da API | Logger (8.54ms) | Tempo real de processamento da API | | Monitoramento | Trace completo (69ms) | Visão completa de performance | | Cliente | Postman (42ms) | Experiência do usuário final |

🔧 Configuração do OpenTelemetry Collector

Para usar o Collector, você precisa configurar um arquivo collector.yaml:

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    send_batch_size: 4096
    timeout: 5s

exporters:
  # Debug (opcional)
  debug:
    verbosity: basic

  # Envio para backend de observabilidade
  otlphttp/backend:
    endpoint: https://your-backend.com/v1/logs
    headers:
      authorization: Bearer ${API_KEY}
    compression: gzip
    timeout: 30s

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/backend, debug]

Docker Compose para Collector

version: '3.8'
services:
  collector:
    image: otel/opentelemetry-collector-contrib:latest
    command: ["--config=/etc/collector.yaml"]
    volumes:
      - ./collector.yaml:/etc/collector.yaml
    ports:
      - "4317:4317"   # gRPC
      - "4318:4318"   # HTTP
    environment:
      - API_KEY=your-api-key-here

🎨 Adicionando Contexto via Decorator

É possível enriquecer os logs dinamicamente usando decorators no NestJS para adicionar atributos extras (ex.: ação, categoria, etc.).
Esses valores simples (string, número, boolean, arrays) irão automaticamente para attributes no SigNoz, ficando filtráveis.
Objetos complexos irão para o body.context, ficando navegáveis no detalhe do log.

Exemplo de uso

// add-log-context.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const AddLogContext = (ctx: Record<string, any>) =>
  SetMetadata('logContext', ctx);
// controller
@Post()
@AddLogContext({ action: 'create_todo', category: createTodoDto.category })
async create(@Body() createTodoDto: CreateTodoDto) {
  return this.todoService.create(createTodoDto);
}

No SigNoz:

  • action e category aparecerão como attributes filtráveis
  • Qualquer objeto/array complexo adicionado irá para body.context como detalhe navegável

📊 Configurando Extração de Attributes para Group By no SigNoz

O SigNoz não permite fazer group by em campos que estão dentro do body. Para permitir group by em campos específicos do body ou headers, você pode configurar quais campos devem ser extraídos para attributes.

Por que isso é necessário?

  • Attributes: Campos indexados e filtráveis, podem ser usados em group by
  • Body: Campos não indexados, apenas para navegação, não podem ser usados em group by

Configuração

// main.ts
import { setupLogging } from '@psouza.yuri/monitoria-sdk';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  setupLogging(app, {
    extractAttributes: {
      requestBody: ['category', 'status', 'priority'],
      requestHeaders: ['x-user-id', 'x-correlation-id', 'x-tenant-id'],
      responseBody: ['id', 'status'],
      responseHeaders: ['x-request-id']
    }
  });
  
  await app.listen(3000);
}

Como funciona

  1. requestBody: Campos do request.body que serão extraídos para attributes

    • Exemplo: ['category', 'status'] → attributes body.category, body.status
  2. requestHeaders: Headers do request.headers que serão extraídos

    • Exemplo: ['x-user-id'] → attribute header.x.user.id (hífens viram pontos)
    • Headers são case-insensitive
  3. responseBody: Campos do response.body que serão extraídos

    • Exemplo: ['id', 'status'] → attributes response.body.id, response.body.status
  4. responseHeaders: Headers do response.headers que serão extraídos

    • Exemplo: ['x-request-id'] → attribute response.header.x.request.id

Exemplo de Uso

Com a configuração acima, quando você fizer um request:

// POST /todos
{
  "category": "trabalho",
  "priority": "high",
  "description": "Descrição longa..."
}

No SigNoz você terá:

  • Attributes (podem ser usados em group by):
    • body.category = "trabalho"
    • body.priority = "high"
    • header.x.user.id = "12345" (se presente no header)
  • Body completo (para navegação):
    • Todos os campos incluindo description

Notas Importantes

  • ✅ Apenas valores primitivos são extraídos (string, number, boolean, arrays de primitivos)
  • ✅ Objetos complexos continuam apenas no body
  • ✅ Se um campo não existir, é ignorado silenciosamente (não quebra o fluxo)
  • ✅ Headers são case-insensitive e normalizados (hífens viram pontos)
  • ✅ A configuração é aplicada automaticamente em todos os logs do interceptor

Exemplo Completo

// main.ts
setupLogging(app, {
  extractAttributes: {
    requestBody: ['category', 'status'],
    requestHeaders: ['x-user-id']
  }
});

// Agora todos os logs terão automaticamente:
// - body.category e body.status extraídos do request.body
// - header.x.user.id extraído do header x-user-id

Depois disso, você pode fazer group by por body.category ou header.x.user.id no SigNoz! 🎉

🔍 Criando Traces Customizados

Para rastrear operações específicas do seu negócio, você pode criar spans customizados que aparecerão no trace distribuído.

1. Importar o Tracer

import { trace } from '@opentelemetry/api';

// Obter o tracer
const tracer = trace.getTracer('meu-servico', '1.0.0');

2. Criar Spans Customizados

// Exemplo: Rastrear operação de negócio
async function processarPagamento(pagamento: Pagamento) {
  // Criar span customizado
  const span = tracer.startSpan('processar-pagamento', {
    attributes: {
      'payment.id': pagamento.id,
      'payment.amount': pagamento.valor,
      'payment.method': pagamento.metodo,
      'business.operation': 'payment_processing'
    }
  });

  try {
    // Sua lógica de negócio aqui
    const resultado = await validarPagamento(pagamento);
    
    // Adicionar atributos de sucesso
    span.setAttributes({
      'payment.status': 'success',
      'payment.processed_at': new Date().toISOString()
    });

    return resultado;
  } catch (error) {
    // Marcar span como erro
    span.recordException(error);
    span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
    throw error;
  } finally {
    // Sempre finalizar o span
    span.end();
  }
}

📊 Monitoramento

SigNoz/OpenTelemetry Collector

  • Traces: Visualização completa de requisições
  • Logs: Logs estruturados com contexto
  • Métricas: Performance e health checks

Dashboards Recomendados

  • Request rate por endpoint
  • Response time percentiles
  • Error rate por serviço
  • Throughput por ambiente

🚨 Troubleshooting

A SDK mostra avisos mas a aplicação funciona

Isso é esperado! A SDK foi projetada para não quebrar sua aplicação mesmo sem variáveis de ambiente configuradas.

Você verá mensagens como:

⚠️  Monitoria SDK não inicializada - Variáveis de ambiente ausentes:
⚠️   - MONITORIA_SERVICE_NAME
⚠️   - MONITORIA_COLLECTOR_ENDPOINT
⚠️   A SDK funcionará em modo degradado (apenas logs no console)

Solução: Configure as variáveis de ambiente conforme documentado acima.

Logs não aparecem no Collector

  • Verificar MONITORIA_COLLECTOR_ENDPOINT
  • Verificar conectividade de rede
  • Verificar se o collector está rodando

Traces não aparecem

  • Verificar se import '@psouza.yuri/monitoria-sdk/telemetry' foi feito ANTES do NestJS
  • Verificar se setupLogging() foi chamado DEPOIS do NestJS
  • Verificar se requests estão sendo feitos

Erro de compilação

  • Verificar se todas as dependências estão instaladas
  • Verificar se as interfaces estão sendo importadas corretamente

❓ "Por que o duration do log é diferente do Postman?"

Resposta: São medições diferentes!

  • Logger (8.54ms): Tempo de processamento da API
  • Postman (42ms): Tempo total incluindo rede
  • Trace (69ms): Tempo de todas as operações internas

❓ "O duration do log está correto?"

Resposta: Sim! O logger mede o span principal da API, que é exatamente o que você quer para logs de aplicação.

🛠️ Desenvolvimento

Estrutura do Projeto

src/
├── telemetry.ts            # Lógica de telemetria
├── adapters/
│   └── nestjs/
│       ├── setup.ts        # Setup do NestJS
│       └── logging.interceptor.ts
├── logger/
│   └── logger.ts          # Logger principal
└── types/
    └── index.ts           # Interfaces simplificadas

Testes

npm test
npm run test:watch

Build

npm run build
npm run dev

📝 Changelog

v1.0.0 - Versão Inicial

  • ✅ Configuração via variáveis de ambiente MONITORIA_*
  • ✅ Interface LoggerConfig simplificada
  • ✅ Inicialização automática do OpenTelemetry
  • ✅ Integração completa com NestJS
  • ✅ Documentação consolidada

🤝 Contribuição

  1. Fork o projeto
  2. Crie uma branch para sua feature
  3. Commit suas mudanças
  4. Push para a branch
  5. Abra um Pull Request

📄 Licença

MIT License - veja LICENSE para detalhes.