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


---

# 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/instrumentacao-node.js-com-elven-observability.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.
