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
espan_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
- 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