# 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**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elven.works/elven-platform/elven-observability/integracao-e-instrumentacao/javascript/logs-interceptor-js.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
