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" \<br>
LOGS_INTERCEPTOR_URL="<https://loki.elvenobservability.com/loki/api/v1/push>" \<br>
LOGS_INTERCEPTOR_TENANT_ID="seu-tenant" \<br>
LOGS_INTERCEPTOR_APP_NAME="minha-api" \<br>
LOGS_INTERCEPTOR_AUTH_TOKEN="seu-token" \<br>
LOGS_INTERCEPTOR_ENVIRONMENT="production" \<br>
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_idespan_idsã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
- Fork este repositório
- Crie uma branch com sua melhoria
- Envie um Pull Request
Toda ajuda é bem-vinda para tornar o logger ainda mais poderoso!
Feito com ❤️ pela equipe Elven Observability