# Instrumentação Node.js com Elven Observability

***

### Sumário

* Visão geral
* Pré-requisitos
* Instalação
* Quick Start — Zero-Code (recomendado)
* Quick Start — Inicialização programática
* Configuração por variáveis de ambiente
* Guia por framework
* Usando logger, tracer e metrics
* Instrumentações automáticas
* Configuração avançada
* Integrações com bibliotecas de logging
* Boas práticas
* Deploy com Docker
* Troubleshooting
* FAQ

***

### Visão geral

A instrumentação Node.js da Elven é composta por **um pacote unificado** que coordena dois componentes internos:

```
┌──────────────────────────────────────────────────────────┐
│            elven-unified-observability-js                 │
│                                                          │
│  ┌────────────────────────┐  ┌────────────────────────┐  │
│  │  Logs Interceptor      │  │  OTel Instrumentation  │  │
│  │                        │  │                        │  │
│  │  - Captura logs        │  │  - Traces distribuídos │  │
│  │  - Intercepta console  │  │  - Métricas            │  │
│  │  - Batching + retry    │  │  - Auto-instrumentação │  │
│  │  - Envia para Loki     │  │  - Exporta via OTLP    │  │
│  └───────────┬────────────┘  └───────────┬────────────┘  │
│              │                           │               │
└──────────────┼───────────────────────────┼───────────────┘
               │                           │
               ▼                           ▼
    Elven Loki Gateway            Elven OTLP Collector
         (logs)                  (traces & métricas)
```

| Componente                                   | O que faz                                                                      |
| -------------------------------------------- | ------------------------------------------------------------------------------ |
| **`elven-unified-observability-js`**         | Pacote principal — instale apenas este. Coordena bootstrap, config e lifecycle |
| **`elven-logs-interceptor`**                 | Pipeline de logs: captura, filtra, faz batching e envia para Loki              |
| **`elven-opentelemetry-instrumentation-js`** | Distro OTel: configura traces, métricas, auto-instrumentação e privacy         |

> **Você só precisa instalar o pacote unificado.** As dependências são resolvidas automaticamente.

***

### Pré-requisitos

* **Node.js 18+**
* **npm**, **yarn** ou **pnpm**
* **Coletor OpenTelemetry (OTLP)** implantado na sua infraestrutura (a Elven realiza o deploy do collector no seu ambiente — não é um endpoint compartilhado)
* Credenciais da Elven Observability:
  * **Tenant ID**
  * **API Token**
  * **Endpoint do seu Collector OTLP** (para traces/métricas)
  * **Endpoint Loki** (para logs)

***

### Instalação

```bash
npm install elven-unified-observability-js
```

Verificar instalação:

```bash
node -e "require('elven-unified-observability-js'); console.log('OK')"
```

***

### Quick Start — Zero-Code (recomendado)

O modo **zero-code** instrumenta sua aplicação sem nenhuma alteração no código fonte. A lib usa o mecanismo `--require` do Node.js para inicializar tudo antes da sua app rodar.

#### 1. Crie um arquivo `.env` na raiz do projeto

```bash
# === Identidade do serviço ===
LOGS_APP_NAME=meu-servico-api
LOGS_APP_VERSION=1.0.0
NODE_ENV=production

# === Telemetria (traces + métricas) ===
OTEL_SERVICE_NAME=meu-servico-api
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp

# === Logs ===
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=seu-tenant-id
LOGS_TOKEN=seu-api-token
LOGS_COMPRESSION=gzip

# === Controle ===
ENABLE_LOGGING=true
ENABLE_METRICS=true
ENABLE_TRACING=true
```

#### 2. Rode sua aplicação com `--require`

```bash
node --require elven-unified-observability-js/register app.js
```

Ou via `NODE_OPTIONS` (funciona com qualquer runner):

```bash
NODE_OPTIONS="--require elven-unified-observability-js/register" node app.js
NODE_OPTIONS="--require elven-unified-observability-js/register" npx ts-node app.ts
NODE_OPTIONS="--require elven-unified-observability-js/register" npx nest start
NODE_OPTIONS="--require elven-unified-observability-js/register" npx next start
```

**Pronto!** Traces, métricas e logs já estão sendo coletados.

***

### Quick Start — Inicialização programática

Use esta abordagem quando precisar de controle sobre a configuração, ou quando não puder usar `--require`.

> **Importante:** A chamada `init()` deve ser a **primeira coisa** a rodar na aplicação, antes de importar frameworks (Express, Fastify, etc.), para que o OpenTelemetry consiga instrumentar as bibliotecas corretamente.

#### Exemplo mínimo

```typescript
import { init, logger, tracer, metrics } from "elven-unified-observability-js";

async function bootstrap() {
  await init({
    serviceName: "meu-servico-api",
    version: "1.0.0",
    environment: "production",

    logging: {
      transport: {
        url: "https://loki.elvenobservability.com/loki/api/v1/push",
        tenantId: "seu-tenant-id",
        authToken: "seu-api-token",
        compression: "gzip",
      },
    },

    telemetry: {
      tracing: { enabled: true },
      metrics: { enabled: true },
    },

    enableLogging: true,
    enableMetrics: true,
    enableTracing: true,
  });

  // Usar os singletons globais
  logger.info("aplicação iniciada", { version: "1.0.0" });
  metrics.getMeter("app").createCounter("startups_total").add(1);

  // Importar e iniciar sua app DEPOIS do init
  const { app } = await import("./app");
  app.listen(3000);
}

bootstrap();
```

#### Exemplo com JavaScript (CommonJS)

```javascript
const { init, logger, tracer, metrics } = require("elven-unified-observability-js");

async function bootstrap() {
  await init({
    serviceName: "meu-servico",
    version: "1.0.0",
    environment: process.env.NODE_ENV || "production",

    logging: {
      transport: {
        url: process.env.LOGS_URL,
        tenantId: process.env.LOGS_TENANT,
        authToken: process.env.LOGS_TOKEN,
      },
    },

    enableLogging: true,
    enableTracing: true,
    enableMetrics: true,
  });

  logger.info("serviço pronto");

  // Iniciar app
  require("./app");
}

bootstrap();
```

***

### Configuração por variáveis de ambiente

#### Identidade do serviço

| Atributo | 1a opção                                | 2a opção               | Default           |
| -------- | --------------------------------------- | ---------------------- | ----------------- |
| Nome     | `LOGS_APP_NAME` / `LOKI_APP_NAME`       | `OTEL_SERVICE_NAME`    | `unknown-service` |
| Versão   | `LOGS_APP_VERSION` / `LOKI_APP_VERSION` | `OTEL_SERVICE_VERSION` | `1.0.0`           |
| Ambiente | `LOGS_ENVIRONMENT` / `LOKI_ENVIRONMENT` | `NODE_ENV`             | `development`     |

#### Controle geral

| Variável         | Descrição                  | Default |
| ---------------- | -------------------------- | ------- |
| `ENABLE_LOGGING` | Habilitar pipeline de logs | `true`  |
| `ENABLE_METRICS` | Habilitar métricas OTel    | `true`  |
| `ENABLE_TRACING` | Habilitar traces OTel      | `true`  |

#### Telemetria — Traces e Métricas

| Variável                         | Descrição                                    | Default         |
| -------------------------------- | -------------------------------------------- | --------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT`    | URL do coletor OTLP                          | —               |
| `OTEL_EXPORTER_OTLP_PROTOCOL`    | Protocolo: `http/protobuf` ou `grpc`         | `http/protobuf` |
| `OTEL_EXPORTER_OTLP_COMPRESSION` | Compressão: `gzip` ou `none`                 | `none`          |
| `OTEL_TRACES_EXPORTER`           | Exporter: `otlp`, `console` ou `none`        | `otlp`          |
| `OTEL_METRICS_EXPORTER`          | Exporter: `otlp` ou `none`                   | `otlp`          |
| `OTEL_TRACING_ENABLED`           | Override explícito para tracing              | `true`          |
| `OTEL_METRICS_ENABLED`           | Override explícito para métricas             | `true`          |
| `OTEL_METRIC_EXPORT_INTERVAL`    | Intervalo de export de métricas (ms)         | `60000`         |
| `OTEL_AUTO_SHUTDOWN`             | Registrar handlers SIGTERM/SIGINT para flush | `true`          |
| `OTEL_ZERO_CODE`                 | Se `false`, o preload não inicializa         | `true`          |

#### Telemetria — Privacy

| Variável                           | Descrição                         | Default             |
| ---------------------------------- | --------------------------------- | ------------------- |
| `OTEL_PRIVACY_REDACT_DB_STATEMENT` | Redactar statements SQL nos spans | `true`              |
| `OTEL_PRIVACY_HASH_USER_ID`        | Fazer hash de user IDs nos spans  | `true` (via config) |

#### Logs — Transporte

Todas as variáveis `LOGS_*` aceitam o prefixo `LOKI_*` como fallback.

| Variável                     | Descrição                              | Default |
| ---------------------------- | -------------------------------------- | ------- |
| `LOGS_URL`                   | URL completa do Loki push endpoint     | —       |
| `LOGS_TENANT`                | Tenant ID (header `X-Scope-OrgID`)     | —       |
| `LOGS_TOKEN`                 | Token de autenticação (Bearer)         | —       |
| `LOGS_COMPRESSION`           | `gzip`, `brotli`, `snappy` ou `none`   | `gzip`  |
| `LOGS_COMPRESSION_LEVEL`     | Nível de compressão (0-11)             | `6`     |
| `LOGS_COMPRESSION_THRESHOLD` | Tamanho mínimo para comprimir (bytes)  | —       |
| `LOGS_USE_WORKERS`           | Usar Worker Threads para processamento | `false` |
| `LOGS_MAX_WORKERS`           | Número máximo de workers (1-64)        | `2`     |
| `LOGS_CONNECTION_POOLING`    | Pool de conexões HTTP (undici)         | `false` |
| `LOGS_MAX_SOCKETS`           | Conexões simultâneas no pool           | —       |
| `LOGS_TIMEOUT`               | Timeout HTTP (ms)                      | `10000` |
| `LOGS_MAX_RETRIES`           | Retentativas em caso de falha          | `3`     |
| `LOGS_RETRY_DELAY`           | Delay base entre retentativas (ms)     | `1000`  |

#### Logs — Buffer e Filtro

| Variável                         | Descrição                             | Default                       |
| -------------------------------- | ------------------------------------- | ----------------------------- |
| `LOGS_BUFFER_MAX_SIZE`           | Tamanho máximo do buffer              | —                             |
| `LOGS_BUFFER_FLUSH_INTERVAL`     | Intervalo de flush automático (ms)    | —                             |
| `LOGS_BUFFER_MAX_MEMORY_MB`      | Limite de memória do buffer (MB)      | —                             |
| `LOGS_BUFFER_MAX_AGE`            | Idade máxima de um log no buffer (ms) | —                             |
| `LOGS_BUFFER_AUTO_FLUSH`         | Habilitar auto-flush                  | `true`                        |
| `LOGS_FILTER_LEVELS`             | Níveis permitidos (vírgula)           | `debug,info,warn,error,fatal` |
| `LOGS_FILTER_SAMPLING_RATE`      | Taxa de amostragem (0.0 a 1.0)        | `1.0`                         |
| `LOGS_FILTER_SANITIZE`           | Sanitizar dados sensíveis             | `false`                       |
| `LOGS_FILTER_MAX_MESSAGE_LENGTH` | Tamanho máximo da mensagem            | —                             |

#### Logs — Resiliência

| Variável                                  | Descrição                      | Default       |
| ----------------------------------------- | ------------------------------ | ------------- |
| `LOGS_CIRCUIT_BREAKER_ENABLED`            | Habilitar circuit breaker      | `true`        |
| `LOGS_CIRCUIT_BREAKER_FAILURE_THRESHOLD`  | Falhas para abrir o circuito   | `5`           |
| `LOGS_CIRCUIT_BREAKER_RESET_TIMEOUT`      | Tempo para tentar fechar (ms)  | `30000`       |
| `LOGS_CIRCUIT_BREAKER_HALF_OPEN_REQUESTS` | Requests de teste em half-open | `3`           |
| `LOGS_DLQ_ENABLED`                        | Habilitar Dead Letter Queue    | `false`       |
| `LOGS_DLQ_TYPE`                           | Tipo: `memory` ou `file`       | `memory`      |
| `LOGS_DLQ_MAX_SIZE`                       | Tamanho máximo da DLQ          | `1000`        |
| `LOGS_DLQ_MAX_RETRIES`                    | Retentativas da DLQ            | `3`           |
| `LOGS_DLQ_BASE_PATH`                      | Diretório para DLQ em arquivo  | `./.logs-dlq` |

#### Logs — Interceptação e Debug

| Variável                         | Descrição                              | Default |
| -------------------------------- | -------------------------------------- | ------- |
| `LOGS_INTERCEPT_CONSOLE`         | Capturar `console.log/info/warn/error` | `false` |
| `LOGS_PRESERVE_ORIGINAL_CONSOLE` | Manter output original ao interceptar  | `true`  |
| `LOGS_ENABLE_METRICS`            | Métricas internas do logger            | `false` |
| `LOGS_ENABLE_HEALTH_CHECK`       | Health check do logger                 | `false` |
| `LOGS_DEBUG`                     | Logs de debug interno da lib           | `false` |
| `LOGS_SILENT_ERRORS`             | Suprimir erros internos                | `false` |

#### Labels customizados

Qualquer variável com prefixo `LOGS_LABEL_` vira um label no Loki:

```bash
LOGS_LABEL_SERVICE=minha-api
LOGS_LABEL_ENVIRONMENT=production
LOGS_LABEL_REGION=us-east-1
LOGS_LABEL_CLUSTER=prod
```

***

### Guia por framework

#### Express

```typescript
import { init, logger } from "elven-unified-observability-js";

async function bootstrap() {
  await init({
    serviceName: "minha-api-express",
    logging: {
      transport: {
        url: process.env.LOGS_URL,
        tenantId: process.env.LOGS_TENANT,
        authToken: process.env.LOGS_TOKEN,
      },
    },
    enableLogging: true,
    enableTracing: true,
    enableMetrics: true,
  });

  const express = (await import("express")).default;
  const app = express();

  app.use((req, res, next) => {
    const start = Date.now();
    res.on("finish", () => {
      logger.info("request", {
        method: req.method,
        path: req.path,
        status: res.statusCode,
        duration_ms: Date.now() - start,
      });
    });
    next();
  });

  app.get("/health", (req, res) => res.json({ status: "ok" }));

  app.listen(3000, () => logger.info("server started", { port: 3000 }));
}

bootstrap();
```

Ou via zero-code (sem alterar o código):

```bash
NODE_OPTIONS="--require elven-unified-observability-js/register" node app.js
```

#### Fastify

```bash
OTEL_INSTR_FASTIFY=true
NODE_OPTIONS="--require elven-unified-observability-js/register" node app.js
```

#### NestJS

```bash
OTEL_INSTR_NESTJS=true
OTEL_INSTR_EXPRESS=true
NODE_OPTIONS="--require elven-unified-observability-js/register" npx nest start
```

#### Next.js

```bash
NODE_OPTIONS="--require elven-unified-observability-js/register" npx next start
```

#### PM2

No `ecosystem.config.js`:

```javascript
module.exports = {
  apps: [{
    name: "minha-api",
    script: "app.js",
    node_args: "--require elven-unified-observability-js/register",
    env: {
      LOGS_APP_NAME: "minha-api",
      LOGS_URL: "https://loki.elvenobservability.com/loki/api/v1/push",
      LOGS_TENANT: "seu-tenant-id",
      LOGS_TOKEN: "seu-api-token",
      OTEL_SERVICE_NAME: "minha-api",
      OTEL_EXPORTER_OTLP_ENDPOINT: "https://otel-collector.sua-infra.com:4318",
    },
  }],
};
```

***

### Usando logger, tracer e metrics

Após a inicialização, os singletons ficam disponíveis em qualquer arquivo:

```typescript
import { logger, tracer, metrics } from "elven-unified-observability-js";
```

#### Logger

```typescript
// Níveis de log
logger.debug("detalhes técnicos", { key: "value" });
logger.info("evento importante", { userId: "123", action: "login" });
logger.warn("algo inesperado", { retryCount: 3 });
logger.error("falha no processamento", { error: "timeout", service: "payments" });
logger.fatal("erro crítico irrecuperável");

// Log genérico com nível
logger.log("info", "mensagem customizada", { custom: "data" });

// Tracking de eventos
logger.trackEvent("user.signup", { plan: "pro", source: "landing" });

// Context — adiciona metadata a todos os logs no bloco
logger.withContext({ requestId: "abc-123", userId: "456" }, () => {
  logger.info("processando request");  // inclui requestId e userId
  logger.info("request finalizado");
});

// Context async
await logger.withContextAsync({ requestId: "abc-123" }, async () => {
  logger.info("processando async");
  await someAsyncWork();
  logger.info("async finalizado");
});

// Flush manual
await logger.flush();

// Health e métricas do logger
const health = logger.getHealth();
const loggerMetrics = logger.getMetrics();
```

#### Tracer

```typescript
// Span com callback (gerencia lifecycle automaticamente)
await tracer.startActiveSpan("process-order", async (span) => {
  span.setAttribute("order.id", "ORD-789");
  span.setAttribute("order.total", 150.00);

  // ... lógica ...

  span.end();
});

// Span com try/catch
await tracer.startActiveSpan("fetch-data", async (span) => {
  try {
    const data = await fetch("https://api.example.com/data");
    span.setAttribute("response.status", data.status);
    return data.json();
  } catch (err) {
    span.recordException(err as Error);
    throw err;
  } finally {
    span.end();
  }
});
```

#### Metrics

```typescript
const meter = metrics.getMeter("minha-api");

// Contador
const requestCounter = meter.createCounter("requests_total");
requestCounter.add(1, { method: "GET", route: "/api/users" });

// Histograma
const latencyHistogram = meter.createHistogram("request_duration_ms");
latencyHistogram.record(145.2, { route: "/api/users" });

// UpDownCounter (para valores que sobem e descem)
const activeConnections = meter.createUpDownCounter("active_connections");
activeConnections.add(1);   // nova conexão
activeConnections.add(-1);  // conexão fechada
```

***

### Instrumentações automáticas

A lib instrumenta automaticamente as bibliotecas instaladas no seu projeto. Controle quais ficam ativas:

#### Toggles por variável de ambiente

| Variável                  | Biblioteca        | Default |
| ------------------------- | ----------------- | ------- |
| `OTEL_INSTR_HTTP`         | http/https nativo | `true`  |
| `OTEL_INSTR_EXPRESS`      | Express           | `true`  |
| `OTEL_INSTR_FASTIFY`      | Fastify           | `true`  |
| `OTEL_INSTR_GRAPHQL`      | GraphQL           | `true`  |
| `OTEL_INSTR_PG`           | PostgreSQL (pg)   | `true`  |
| `OTEL_INSTR_MYSQL`        | MySQL (mysql2)    | `true`  |
| `OTEL_INSTR_MONGODB`      | MongoDB           | `true`  |
| `OTEL_INSTR_MONGOOSE`     | Mongoose          | `true`  |
| `OTEL_INSTR_REDIS`        | Redis + ioredis   | `true`  |
| `OTEL_INSTR_KNEX`         | Knex.js           | `true`  |
| `OTEL_INSTR_GENERIC_POOL` | generic-pool      | `true`  |
| `OTEL_INSTR_NESTJS`       | NestJS            | `true`  |
| `OTEL_INSTR_KOA`          | Koa               | `true`  |
| `OTEL_INSTR_PINO`         | Pino              | `true`  |
| `OTEL_INSTR_WINSTON`      | Winston           | `true`  |
| `OTEL_INSTR_DNS`          | DNS               | `false` |
| `OTEL_INSTR_NET`          | Net (TCP)         | `false` |

Valores aceitos: `true` / `1` / `yes` / `on` para habilitar, `false` / `0` / `no` / `off` para desabilitar.

#### Exemplo: habilitar apenas o necessário

```bash
# API Express com PostgreSQL e Redis
OTEL_INSTR_EXPRESS=true
OTEL_INSTR_PG=true
OTEL_INSTR_REDIS=true
OTEL_INSTR_HTTP=true
```

#### Via config programático

```typescript
await init({
  // ...
  telemetry: {
    instrumentations: {
      express: true,
      pg: true,
      redis: true,
      mongodb: false,
      graphql: false,
    },
  },
});
```

***

### Configuração avançada

#### Config completo programático

```typescript
import { init } from "elven-unified-observability-js";

await init({
  serviceName: "payments-service",
  version: "2.1.0",
  environment: "production",

  logging: {
    transport: {
      url: "https://loki.elvenobservability.com/loki/api/v1/push",
      tenantId: "meu-tenant",
      authToken: "meu-token",
      compression: "gzip",
      compressionLevel: 6,
      useWorkers: true,
      maxWorkers: 4,
      enableConnectionPooling: true,
      maxSockets: 50,
      timeout: 10000,
      maxRetries: 3,
      retryDelay: 1000,
    },
    labels: {
      team: "payments",
      cluster: "prod-us",
    },
    buffer: {
      maxSize: 500,
      flushInterval: 3000,
      maxMemoryMB: 256,
    },
    filter: {
      levels: ["info", "warn", "error", "fatal"],
      samplingRate: 1.0,
      sanitize: true,
      maxMessageLength: 8192,
    },
    circuitBreaker: {
      enabled: true,
      failureThreshold: 10,
      resetTimeout: 30000,
    },
    deadLetterQueue: {
      enabled: true,
      type: "file",
      maxSize: 5000,
      basePath: "./.logs-dlq",
    },
    interceptConsole: true,
    preserveOriginalConsole: true,
  },

  telemetry: {
    exporters: {
      protocol: "http/protobuf",
      compression: "gzip",
    },
    tracing: {
      enabled: true,
      sampling: { ratio: 1.0 },
    },
    metrics: {
      enabled: true,
      exportIntervalMillis: 15000,
    },
    privacy: {
      redactDbStatement: true,
      hashUserId: true,
    },
    instrumentations: {
      express: true,
      pg: true,
      redis: true,
      mongodb: true,
      graphql: false,
    },
  },

  enableLogging: true,
  enableMetrics: true,
  enableTracing: true,
});
```

***

### Integrações com bibliotecas de logging

#### Winston

```typescript
import winston from "winston";
import { WinstonTransport } from "elven-logs-interceptor";

const winstonLogger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new WinstonTransport(),  // envia para Loki via pipeline Elven
  ],
});

winstonLogger.info("log via winston", { custom: "metadata" });
```

#### Morgan (HTTP access logs)

```typescript
import express from "express";
import morgan from "morgan";
import { MorganAdapter } from "elven-logs-interceptor";
import { logger } from "elven-unified-observability-js";

const app = express();

app.use(morgan("combined", {
  stream: MorganAdapter.createStream(logger),
}));
```

***

### Boas práticas

#### Naming convention para serviços

Use nomes descritivos e consistentes:

```
{dominio}-{componente}
```

Exemplos: `payments-api`, `orders-worker`, `notifications-scheduler`, `auth-service`.

#### Labels no Loki

Use labels para dimensões de baixa cardinalidade. **Não** use para valores de alta cardinalidade:

```bash
# Bom: baixa cardinalidade
LOGS_LABEL_SERVICE=payments-api
LOGS_LABEL_ENVIRONMENT=production
LOGS_LABEL_CLUSTER=prod-us

# Ruim: alta cardinalidade — use como metadata no log
# LOGS_LABEL_USER_ID=...
# LOGS_LABEL_REQUEST_ID=...
```

#### Sampling em produção

Para serviços com alto throughput:

```typescript
await init({
  telemetry: {
    tracing: {
      sampling: { ratio: 0.1 },  // 10% dos traces
    },
  },
  // ...
});
```

#### Inicialização antes dos imports

Para que o OTel consiga instrumentar corretamente, a inicialização deve acontecer **antes** de importar os frameworks. O modo zero-code (`--require`) garante isso automaticamente. No modo programático:

```typescript
// ✅ Correto: init antes do import
await init({ ... });
const express = (await import("express")).default;

// ❌ Errado: import antes do init
import express from "express";  // OTel não consegue instrumentar
await init({ ... });
```

#### Worker Threads para alta carga

Para serviços com alto volume de logs, habilite workers para não bloquear a event loop:

```bash
LOGS_USE_WORKERS=true
LOGS_MAX_WORKERS=4
```

#### Connection Pooling

Para envio eficiente ao Loki:

```bash
LOGS_CONNECTION_POOLING=true
LOGS_MAX_SOCKETS=50
```

***

### Deploy com Docker

#### Dockerfile

Se a lib já está no `package.json` da aplicação:

```dockerfile
FROM node:20-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

CMD ["node", "--require", "elven-unified-observability-js/register", "app.js"]
```

Se você quer **adicionar a instrumentação direto no Dockerfile** sem alterar o `package.json` da aplicação (útil para instrumentar apps existentes sem mexer no código):

```dockerfile
FROM node:20-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

# Instalar a lib de observabilidade separadamente
RUN npm install elven-unified-observability-js

COPY . .

CMD ["node", "--require", "elven-unified-observability-js/register", "app.js"]
```

#### docker run

```bash
docker build -t minha-api .

docker run -p 3000:3000 \
  -e LOGS_APP_NAME=minha-api \
  -e LOGS_APP_VERSION=1.0.0 \
  -e NODE_ENV=production \
  -e OTEL_SERVICE_NAME=minha-api \
  -e OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318 \
  -e OTEL_TRACES_EXPORTER=otlp \
  -e OTEL_METRICS_EXPORTER=otlp \
  -e LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push \
  -e LOGS_TENANT=seu-tenant-id \
  -e LOGS_TOKEN=seu-api-token \
  -e LOGS_COMPRESSION=gzip \
  -e ENABLE_LOGGING=true \
  -e ENABLE_TRACING=true \
  -e ENABLE_METRICS=true \
  minha-api
```

#### docker-compose.yml

```yaml
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      # Identidade
      LOGS_APP_NAME: minha-api
      LOGS_APP_VERSION: "1.0.0"
      NODE_ENV: production
      OTEL_SERVICE_NAME: minha-api

      # Telemetria
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otel-collector.sua-infra.com:4318
      OTEL_TRACES_EXPORTER: otlp
      OTEL_METRICS_EXPORTER: otlp
      OTEL_METRIC_EXPORT_INTERVAL: "15000"
      OTEL_INSTR_EXPRESS: "true"
      OTEL_PRIVACY_REDACT_DB_STATEMENT: "true"

      # Logs
      LOGS_URL: https://loki.elvenobservability.com/loki/api/v1/push
      LOGS_TENANT: seu-tenant-id
      LOGS_TOKEN: seu-api-token
      LOGS_COMPRESSION: gzip
      LOGS_INTERCEPT_CONSOLE: "true"

      # Controle
      ENABLE_LOGGING: "true"
      ENABLE_TRACING: "true"
      ENABLE_METRICS: "true"
```

#### Dockerfile multi-stage (produção)

```dockerfile
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
# Se a lib não está no package.json, adicione aqui:
RUN npm install elven-unified-observability-js
COPY --from=builder /app/dist ./dist

RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

EXPOSE 3000
CMD ["node", "--require", "elven-unified-observability-js/register", "dist/app.js"]
```

#### Kubernetes — Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minha-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-api
  template:
    metadata:
      labels:
        app: minha-api
    spec:
      containers:
        - name: api
          image: minha-api:1.0.0
          ports:
            - containerPort: 3000
          env:
            - name: LOGS_APP_NAME
              value: minha-api
            - name: NODE_ENV
              value: production
            - name: OTEL_SERVICE_NAME
              value: minha-api
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: https://otel-collector.sua-infra.com:4318
            - name: OTEL_TRACES_EXPORTER
              value: otlp
            - name: OTEL_METRICS_EXPORTER
              value: otlp
            - name: LOGS_URL
              value: https://loki.elvenobservability.com/loki/api/v1/push
            - name: LOGS_TENANT
              valueFrom:
                secretKeyRef:
                  name: elven-credentials
                  key: tenant-id
            - name: LOGS_TOKEN
              valueFrom:
                secretKeyRef:
                  name: elven-credentials
                  key: api-token
            - name: LOGS_COMPRESSION
              value: gzip
            - name: ENABLE_LOGGING
              value: "true"
            - name: ENABLE_TRACING
              value: "true"
            - name: ENABLE_METRICS
              value: "true"
            - name: LOGS_LABEL_CLUSTER
              value: prod-us
```

***

### Troubleshooting

#### A aplicação não inicia / crash no bootstrap

**Sintoma:** Erro no require ou na inicialização.

**Verificações:**

1. Confirme a versão do Node.js (`node --version`): requer **18+**
2. Confirme que o pacote está instalado: `npm ls elven-unified-observability-js`
3. Se usar init programático, verifique que `init()` é chamado com `await`

```bash
node -e "require('elven-unified-observability-js'); console.log('OK')"
```

#### Logs não aparecem no Loki

**Sintoma:** A aplicação roda normalmente, mas nenhum log aparece.

**Verificações:**

1. Confirme que `LOGS_URL` está correto e inclui o path completo: `https://loki.elvenobservability.com/loki/api/v1/push`
2. Confirme que `LOGS_TENANT` e `LOGS_TOKEN` estão preenchidos
3. Confirme que `ENABLE_LOGGING=true`
4. Habilite debug: `LOGS_DEBUG=true` e verifique o stderr

```bash
LOGS_DEBUG=true node --require elven-unified-observability-js/register app.js
```

#### Traces não aparecem

**Sintoma:** Logs funcionam, mas traces não aparecem.

**Verificações:**

1. Confirme que `OTEL_EXPORTER_OTLP_ENDPOINT` está definido
2. Confirme que `OTEL_TRACES_EXPORTER=otlp`
3. Confirme que `ENABLE_TRACING=true`
4. No modo programático, verifique que `init()` roda **antes** de importar o framework

#### Auto-instrumentação não funciona

**Sintoma:** Requests HTTP não geram spans automaticamente.

**Verificações:**

1. A auto-instrumentação requer que o OTel seja carregado **antes** dos módulos. Use `--require` ou faça `init()` antes de qualquer `import`/`require`
2. Confirme que a instrumentação está ativa: `OTEL_INSTR_EXPRESS=true`
3. Confirme que a biblioteca está instalada: `npm ls express`

#### Erro de autenticação (401/403) no Loki

**Sintoma:** `LOGS_DEBUG=true` mostra erro de autenticação.

**Verificações:**

1. Confirme `LOGS_TOKEN` no painel da Elven
2. Confirme `LOGS_TENANT`
3. Verifique se o token não expirou

#### Console interceptado não mostra output

**Sintoma:** `console.log` não aparece no terminal.

**Causa:** `LOGS_INTERCEPT_CONSOLE=true` com `LOGS_PRESERVE_ORIGINAL_CONSOLE=false`.

**Solução:** Defina `LOGS_PRESERVE_ORIGINAL_CONSOLE=true` para manter o output no terminal além de enviar para o Loki.

#### Memory leak / alto consumo de memória

**Sintoma:** Uso de memória cresce continuamente.

**Verificações:**

1. Limite o buffer: `LOGS_BUFFER_MAX_SIZE=500` e `LOGS_BUFFER_MAX_MEMORY_MB=256`
2. Habilite circuit breaker: `LOGS_CIRCUIT_BREAKER_ENABLED=true`
3. Se o Loki estiver inacessível, logs acumulam. A DLQ em arquivo evita crescimento em memória:

```bash
LOGS_DLQ_ENABLED=true
LOGS_DLQ_TYPE=file
LOGS_DLQ_BASE_PATH=./.logs-dlq
```

***

### FAQ

**Preciso instalar os 3 pacotes separadamente?** Não. Instale apenas `elven-unified-observability-js` — ele puxa as duas libs automaticamente.

**Funciona com TypeScript?** Sim. O pacote inclui type definitions (`.d.ts`). Funciona com `ts-node`, `tsx`, `tsc`, etc.

**Funciona com ESM e CommonJS?** Sim. O pacote exporta CommonJS (`dist/index.js`) e é compatível com ESM via dynamic import. O `--require` funciona em ambos os modos.

**Posso usar apenas logs sem traces?** Sim. Defina `ENABLE_TRACING=false` e `ENABLE_METRICS=false`.

**Posso usar apenas traces sem logs?** Sim. Defina `ENABLE_LOGGING=false`.

**Qual a diferença entre `LOGS_URL` no JS e `LOKI_URL` na Lambda layer?** Na lib JS, `LOGS_URL` deve ser a URL **completa** incluindo o path (`/loki/api/v1/push`). Na Lambda layer (Go), `LOKI_URL` é apenas a URL **base**, pois a extension appenda o path automaticamente.

**Posso usar com Winston / Morgan?** Sim. A lib fornece `WinstonTransport` e `MorganAdapter` que redirecionam logs para a pipeline do Loki. Veja a seção Integrações com bibliotecas de logging.

**Qual versão mínima do Node.js?** Node.js **18** ou superior.

**O `init()` é idempotente?** Sim. Chamar `init()` mais de uma vez retorna os mesmos singletons sem reinicializar.

**Como funciona o circuit breaker?** Se o Loki retornar erros consecutivos, o circuito "abre" e logs são descartados (ou vão para a DLQ). Após o timeout de reset, tenta fechar gradualmente com requests de teste (half-open).

**Posso usar com PM2?** Sim. Use `node_args: "--require elven-unified-observability-js/register"` no `ecosystem.config.js`. Veja a seção PM2.

**E com serverless (Lambda)?** Para Lambda, use as layers dedicadas em vez desta lib. As layers são otimizadas para o lifecycle da Lambda.
