Logs Interceptor JS

Elven Observability Official Logger

Um coletor de logs, eventos e exceções de alta performance para apps Node.js que envia tudo ao Grafana Loki. Oferece interceptação automática de console, suporte a labels dinâmicas, envio assíncrono com buffer otimizado e integração instantânea com o Elven Stack.

✨ Features

Alta performance com buffer assíncrono, envio em lote e compressão gzip

🔗 Captura automática de:

  • console.log, console.error, console.warn, etc
  • Exceções não tratadas e promises rejeitadas
  • Eventos personalizados via trackEvent

🏷️ Suporte a labels fixas e dinâmicas (com trace/span id via OpenTelemetry)

📦 Buffer inteligente com envio em lote configurável

Resiliente a falhas de rede com retry automático

🧵 Compatível com apps simples ou complexos (web servers, workers, CLI)

🔁 Garantia de envio no encerramento do processo

🗜️ Compressão gzip para reduzir uso de banda

📊 Métricas em tempo real e health check integrado

📦 Instalação

npm install logs-interceptor

🚀 Uso Básico

import { init, logger } from "logs-interceptor";

init({
  transport: {
    url: "<https://loki.elvenobservability.com/loki/api/v1/push>",
    tenantId: "seu-tenant", // Seu Tenant ID
    authToken: "seu-token", // Seu JWT
    compression: true, // Gzip habilitado
  },
  appName: "my-js-app",
  environment: "production",
  interceptConsole: true,
  labels: { env: "production" },
  dynamicLabels: {
    hostname: () => require('os').hostname(),
    memory: () => Math.round(process.memoryUsage().heapUsed / 1024 / 1024) + 'MB'
  },
  buffer: {
    maxSize: 100,
    flushInterval: 5000,
  }
});

console.log("Isso será enviado ao Loki!");
logger.trackEvent("usuario_cadastrado", { user_id: 123 });
throw new Error("Erro de teste!"); // Capturado automaticamente

⚙️ Opções de Configuração

Transport

ParâmetroTipoDescriçãoPadrão
urlstringEndpoint do Loki (Push API)
tenantIdstringUsado no header X-Scope-OrgID
authTokenstringToken JWT para autenticaçãoundefined
timeoutnumberTimeout HTTP em ms5000
maxRetriesnumberTentativas em caso de falha3
compressionbooleanHabilita compressão gziptrue

Aplicação

ParâmetroTipoDescriçãoPadrão
appNamestringNome da aplicação (label fixa)
versionstringVersão da aplicação“1.0.0”
environmentstringAmbiente (prod, dev, staging)“production”
labelsobjectLabels fixas por log{}
dynamicLabelsobjectLabels geradas dinamicamente{}

Buffer & Performance

ParâmetroTipoDescriçãoPadrão
buffer.maxSizenumberLogs no buffer antes de forçar envio100
buffer.flushIntervalnumberTempo máximo antes do envio (ms)5000
buffer.autoFlushbooleanFlush automático habilitadotrue
interceptConsolebooleanCaptura console.* automaticamentefalse
enableMetricsbooleanColeta métricas de performancetrue

Filtering & Sampling

ParâmetroTipoDescriçãoPadrão
filter.levelsarrayNíveis de log permitidos[‘debug’,’info’,’warn’,’error’,’fatal’]
filter.samplingRatenumberTaxa de amostragem (0.0-1.0)1.0
filter.maxMessageLengthnumberTamanho máximo da mensagem8192

🧪 Exemplo com Express.js

import express from "express";
import { init, logger } from "logs-interceptor";

init({
  transport: {
    url: "<https://loki.elvenobservability.com/loki/api/v1/push>",
    tenantId: "seu-tenantId",
    authToken: "seu-token",
    compression: true,
  },
  appName: "express-app",
  environment: "production",
  interceptConsole: true,
  labels: {
    service: "api",
    env: "production"
  },
  dynamicLabels: {
    hostname: () => require('os').hostname(),
    uptime: () => Math.round(process.uptime()) + 's'
  },
});

const app = express();

app.get("/ping", (req, res) => {
  console.log("ping chamado"); // Interceptado automaticamente
  logger.trackEvent("api_ping", { ip: req.ip });
  res.send({ message: "pong" });
});

app.get("/erro", () => {
  throw new Error("Erro proposital"); // Capturado automaticamente
});

app.listen(3000, () => {
  console.log("Servidor iniciado na porta 3000");
});

🚀 Auto-inicialização com NODE_OPTIONS

Para apps existentes sem modificação de código:

NODE_OPTIONS="--require logs-interceptor/preload" \\
LOGS_INTERCEPTOR_URL="<https://loki.elvenobservability.com/loki/api/v1/push>" \\
LOGS_INTERCEPTOR_TENANT_ID="seu-tenant" \\
LOGS_INTERCEPTOR_APP_NAME="minha-api" \\
LOGS_INTERCEPTOR_AUTH_TOKEN="seu-token" \\
LOGS_INTERCEPTOR_ENVIRONMENT="production" \\
node app.js

📊 Métricas & Monitoramento

import { logger } from "logs-interceptor";

// Métricas em tempo real
const metrics = logger.getMetrics();
console.log({
  logsProcessed: metrics.logsProcessed,
  logsDropped: metrics.logsDropped,
  flushCount: metrics.flushCount,
  avgFlushTime: metrics.avgFlushTime,
  errorCount: metrics.errorCount
});

// Health check
const health = logger.getHealth();
console.log({
  healthy: health.healthy,
  uptime: health.uptime,
  bufferUtilization: health.bufferUtilization
});

💡 Boas Práticas

SituaçãoRecomendações
Scripts curtos (CLI, cron)Use await logger.flush() antes do exit
Apps web ou workersConfiguração padrão já garante envio em background
Alta concorrênciaUse maxSize: 500+ e flushInterval: 10000
Ambientes com OpenTelemetryLabels trace_id e span_id são capturados automaticamente
ProduçãoHabilite compression: true e samplingRate < 1.0 se necessário

🧯 Troubleshooting

ProblemaCausa comumSolução
Logs não aparecemtenantId, authToken ou URL erradosVerifique configuração e use debug: true
Logs incompletosProcesso encerrando rápidoUse await logger.flush() antes do exit
HTTP 400Payload malformadoVerifique se Loki está rodando na URL
Performance ruimBuffer muito pequenoAumente maxSize e flushInterval

🔄 Flush Manual

import { logger } from "logs-interceptor";

// Forçar envio imediato
await logger.flush();

// Exemplo com graceful shutdown
process.on('SIGTERM', async () => {
  console.log('Shutting down gracefully...');
  await logger.flush();
  process.exit(0);
});

📤 Eventos Customizados

import { logger } from "logs-interceptor";

logger.trackEvent("usuario_ativo", {
  user_id: "abc123",
  source: "mobile",
  action: "login"
});

logger.trackEvent("compra_realizada", {
  order_id: "12345",
  value: 99.90,
  payment_method: "credit_card"
});

No Loki aparecerá como:

[EVENT] usuario_ativo {"user_id": "abc123", "source": "mobile", "action": "login"}

📁 Formato dos Logs no Loki

Cada log enviado inclui:

  • Timestamp com nanossegundos
  • Labels fixas e dinâmicas (ex: app, env, trace_id, span_id)
  • Prefixo do tipo de log ([INFO], [ERROR], [EVENT], etc)
  • Conteúdo serializado automaticamente para objetos e erros

Exemplo de payload enviado:

{
  "streams": [
    {
      "stream": {
        "app": "minha-api",
        "environment": "production",
        "level": "info",
        "hostname": "api-01",
        "trace_id": "abc123def456"
      },
      "values": [
        ["1640995200000000000", "[INFO] Usuario logado com sucesso {\\"userId\\": 123}"]
      ]
    }
  ]
}

🧠 Integração com OpenTelemetry

Este logger se integra automaticamente com o OpenTelemetry, capturando os IDs de trace e span ativos no contexto atual.

Isso permite correlação perfeita entre logs e traces no Grafana Tempo, permitindo investigações completas ponta a ponta.

🔗 O que já está incluso:

  • trace_id e span_id são adicionados automaticamente como labels dinâmicas
  • Nenhuma configuração extra é necessária, desde que o app já use o OpenTelemetry
  • Compatível com qualquer instrumentação OpenTelemetry existente

🧪 Exemplo com traces

import { context, trace } from "@opentelemetry/api";
import { init, logger } from "logs-interceptor";

// Inicialize o OpenTelemetry antes:
const tracer = trace.getTracer("minha-lib");

init({
  transport: {
    url: "<https://loki.elvenobservability.com/loki/api/v1/push>",
    tenantId: "elven",
    authToken: "seu-token"
  },
  appName: "meu-app",
  labels: { env: "prod" },
});

tracer.startActiveSpan("processarPedido", (span) => {
  console.log("Processando pedido");
  logger.info("Pedido iniciado", { orderId: 12345 });
  span.end();
});

📈 No Loki:

Cada log conterá automaticamente:

{
  "trace_id": "abcdef123456789",
  "span_id": "12345678",
  "app": "meu-app",
  "level": "info"
}

Você poderá fazer queries no Loki filtrando por trace_id e, com isso, visualizar os logs do mesmo trace mostrado no Grafana Tempo (ou trace view da Elven Platform).

🐳 Docker & Kubernetes

Docker Compose

version: '3.8'
services:
  app:
    build: .
    environment:
      - NODE_OPTIONS=--require logs-interceptor/preload
      - LOGS_INTERCEPTOR_URL=https://loki.elvenobservability.com/loki/api/v1/push
      - LOGS_INTERCEPTOR_TENANT_ID=meu-tenant
      - LOGS_INTERCEPTOR_AUTH_TOKEN=meu-token
      - LOGS_INTERCEPTOR_APP_NAME=minha-api
      - LOGS_INTERCEPTOR_ENVIRONMENT=production

Kubernetes

env:
- name: NODE_OPTIONS
  value: "--require logs-interceptor/preload"
- name: LOGS_INTERCEPTOR_URL
  value: "<https://loki.elvenobservability.com/loki/api/v1/push>"
- name: LOGS_INTERCEPTOR_TENANT_ID
  valueFrom:
    secretKeyRef:
      name: elven-credentials
      key: tenant-id
- name: LOGS_INTERCEPTOR_AUTH_TOKEN
  valueFrom:
    secretKeyRef:
      name: elven-credentials
      key: auth-token

📄 Licença

Este projeto é licenciado sob a MIT License.

🙌 Contribuindo

  1. Fork este repositório
  2. Crie uma branch com sua melhoria
  3. Envie um Pull Request

Toda ajuda é bem-vinda para tornar o logger ainda mais poderoso!


Feito com ❤️ pela equipe Elven Observability

Nesta página
Rolar para cima