# 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

&#x20;**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**
