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âmetro
Tipo
Descrição
Padrão

url

string

Endpoint do Loki (Push API)

tenantId

string

Usado no header X-Scope-OrgID

authToken

string

Token JWT para autenticação

undefined

timeout

number

Timeout HTTP em ms

5000

maxRetries

number

Tentativas em caso de falha

3

compression

boolean

Habilita compressão gzip

true

Aplicação

Parâmetro
Tipo
Descrição
Padrão

appName

string

Nome da aplicação (label fixa)

version

string

Versão da aplicação

“1.0.0”

environment

string

Ambiente (prod, dev, staging)

“production”

labels

object

Labels fixas por log

{}

dynamicLabels

object

Labels geradas dinamicamente

{}

Buffer & Performance

Parâmetro
Tipo
Descrição
Padrão

buffer.maxSize

number

Logs no buffer antes de forçar envio

100

buffer.flushInterval

number

Tempo máximo antes do envio (ms)

5000

buffer.autoFlush

boolean

Flush automático habilitado

true

interceptConsole

boolean

Captura console.* automaticamente

false

enableMetrics

boolean

Coleta métricas de performance

true

Filtering & Sampling

Parâmetro
Tipo
Descrição
Padrão

filter.levels

array

Níveis de log permitidos

[‘debug’,’info’,’warn’,’error’,’fatal’]

filter.samplingRate

number

Taxa de amostragem (0.0-1.0)

1.0

filter.maxMessageLength

number

Tamanho máximo da mensagem

8192

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ção
Recomendações

Scripts curtos (CLI, cron)

Use await logger.flush() antes do exit.

Apps web ou workers

Configuração padrão já garante envio em background.

Alta concorrência

Use maxSize: 500+ e flushInterval: 10000.

Ambientes com OpenTelemetry

Labels trace_id e span_id são capturados automaticamente.

Produção

Habilite compression: true e samplingRate < 1.0 se necessário.

Troubleshooting

Problema
Causa comum
Solução

Logs não aparecem

tenantId, authToken ou URL errados

Verifique configuração e use debug: true

Logs incompletos

Processo encerrando rápido

Use await logger.flush() antes do exit

HTTP 400

Payload malformado

Verifique se Loki está rodando na URL

Performance ruim

Buffer muito pequeno

Aumente 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

Last updated

Was this helpful?