# Instrumentação Java com Elven Observability

***

### Sumário

* Visão geral
* Pré-requisitos
* Caminho recomendado
* Quick Start — Docker + ENV (zero-code recomendado)
* Pacotes disponíveis
* Quick Start — Java Agent embedded sem Docker
* Quick Start — Java Agent oficial com extensão Elven
* Quick Start — Spring Boot Starter
* Quick Start — Inicialização manual
* Configuração por variáveis de ambiente
* Deploy com Docker — variações
* Logs e correlação com Loki
* Traces manuais, erros e atributos semânticos
* Métricas manuais
* Instrumentações automáticas
* Configuração por arquivo
* Privacidade e dados sensíveis
* Boas práticas para SaaS multi-tenant
* Validação ponta a ponta
* Troubleshooting
* FAQ

***

### Visão geral

A instrumentação Java da Elven é uma distribuição OpenTelemetry para aplicações Java 11+. Ela usa o **Java Agent oficial do OpenTelemetry** para auto-instrumentação e aplica os padrões da Elven para o stack LGTM.

O **Collector OTLP fica sempre no ambiente do cliente**. A aplicação envia traces e métricas para esse Collector local/do cliente, e o Collector encaminha os dados para Tempo e Mimir conforme a arquitetura contratada.

* **Grafana Tempo** para traces
* **Grafana Mimir** para métricas
* **Grafana Loki** para logs, via `logs-interceptor-java`
* **Grafana** para consulta, correlação, painéis e alertas

```
┌──────────────────────────────────────────────────────────────┐
│        elven-opentelemetry-instrumentation-java               │
│                                                              │
│  ┌───────────────────────┐  ┌─────────────────────────────┐  │
│  │ Java Agent             │  │ API manual                  │  │
│  │                       │  │                             │  │
│  │ - HTTP server/client  │  │ - Observability.init()      │  │
│  │ - JDBC / pools        │  │ - TracerFacade              │  │
│  │ - Messaging           │  │ - MetricFacade              │  │
│  │ - Frameworks          │  │ - LogCorrelation / MDC      │  │
│  └───────────┬───────────┘  └──────────────┬──────────────┘  │
│              │                             │                 │
│              └──────────────┬──────────────┘                 │
│                             ▼                                │
│                  OTLP HTTP/protobuf ou gRPC                  │
└─────────────────────────────┬────────────────────────────────┘
                              │
                              ▼
                    Collector OTLP no ambiente do cliente
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
                  Tempo               Mimir

Logs da aplicação ── logs-interceptor-java ──► Loki
```

| Componente                                                | O que faz                                                                                              |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `elven-opentelemetry-instrumentation-javaagent`           | Java agent embedded: agent oficial + extensão Elven no mesmo JAR. É o modo zero-code mais simples.     |
| `elven-opentelemetry-instrumentation-javaagent-extension` | Extensão Elven para usar junto com o Java agent oficial via `OTEL_JAVAAGENT_EXTENSIONS`.               |
| `elven-opentelemetry-instrumentation-core`                | API manual: `Observability`, `GlobalObservability`, `TracerFacade`, `MetricFacade` e `LogCorrelation`. |
| `elven-opentelemetry-instrumentation-spring-boot-starter` | Auto-configuração para Spring Boot quando a aplicação precisa inicializar a SDK no próprio processo.   |
| `elven-opentelemetry-instrumentation-autoconfigure`       | Defaults Elven via SPI do OpenTelemetry SDK. Usado pelo agent e por cenários avançados.                |
| `elven-opentelemetry-instrumentation-bom`                 | Alinhamento de versões Maven.                                                                          |
| `logs-interceptor-java`                                   | Biblioteca separada para envio de logs ao Loki. Esta doc mostra como correlacionar logs com traces.    |

> **Recomendação:** para aplicações web, APIs, workers com HTTP/JDBC/messaging e serviços já existentes, use o **Java Agent embedded**. Ele entrega auto-instrumentação sem alterar o código da aplicação.

***

### Pré-requisitos

* **Java 11+**
* Maven ou Gradle para aplicações que usam dependências da API manual
* Endpoint do **Collector OTLP implantado no ambiente do cliente**
* Credenciais da Elven Observability:
  * **Tenant ID**
  * **API Token**
  * **Endpoint do Collector OTLP do cliente** para traces e métricas
  * **Endpoint Loki** para logs, quando usar `logs-interceptor-java`

#### Baseline técnico

| Item                              | Versão   |
| --------------------------------- | -------- |
| Java mínimo                       | Java 11  |
| OpenTelemetry SDK BOM             | `1.61.0` |
| OpenTelemetry Java Agent          | `2.27.0` |
| OpenTelemetry Instrumentation BOM | `2.27.0` |
| Semantic Conventions              | `1.41.0` |
| Versão atual da lib Elven         | `0.1.1`  |

#### Protocolos suportados

| Protocolo          | Valor           | Uso recomendado                                             |
| ------------------ | --------------- | ----------------------------------------------------------- |
| OTLP HTTP/protobuf | `http/protobuf` | Padrão recomendado para ambientes HTTP, proxies e gateways. |
| OTLP gRPC          | `grpc`          | Útil quando o Collector do cliente aceita gRPC.             |

> Para métricas, envie OTLP para o Collector do cliente. O Collector faz a tradução para Mimir/Prometheus remote write quando necessário.

***

### Caminho recomendado

A ordem recomendada de adoção é sempre começar pelo caminho de menor atrito:

| Prioridade | Caminho                                                 | Quando usar                                                                                                      |
| ---------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| 1          | **Docker + ENV + Java Agent embedded**                  | Caminho padrão para clientes. Zero-code, previsível e fácil de aplicar em serviços existentes.                   |
| 2          | **`JAVA_TOOL_OPTIONS` em VM/ECS/serviço já empacotado** | Quando a aplicação não é rebuildada em Docker, mas permite variáveis de ambiente no runtime.                     |
| 3          | **Java Agent oficial + extensão Elven**                 | Quando a empresa já padronizou o `opentelemetry-javaagent.jar` oficial e quer só acoplar os defaults Elven.      |
| 4          | **Spring Boot Starter / API manual**                    | Quando a aplicação precisa criar spans/métricas de negócio, controlar lifecycle ou inicializar a SDK por código. |
| 5          | **Configuração por arquivo**                            | Quando o ambiente exige arquivo versionado ou montado por secret/config.                                         |

Para a maioria dos clientes, o fluxo é:

```
1. Adicionar o agent no Dockerfile
2. Configurar OTEL_* e LOGS_* no runtime
3. Subir a imagem
4. Validar traces no Tempo, métricas no Mimir e logs correlacionados no Loki
```

***

### Quick Start — Docker + ENV (zero-code recomendado)

Este é o caminho padrão para aplicações Java em container. Não exige alteração no código da aplicação.

#### 1. Adicione o Java Agent no Dockerfile

```dockerfile
FROM eclipse-temurin:21-jre

WORKDIR /app

RUN mkdir -p /opt/elven \
  && curl -fsSL \
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar \
    https://repo1.maven.org/maven2/io/github/elven-observability/elven-opentelemetry-instrumentation-javaagent/0.1.1/elven-opentelemetry-instrumentation-javaagent-0.1.1.jar

COPY target/app.jar /app/app.jar

ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar"
ENV OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
ENV OTEL_TRACES_EXPORTER="otlp"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_LOGS_EXPORTER="none"

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

#### 2. Configure as variáveis no runtime

```bash
docker run --rm -p 8080:8080 \
  -e OTEL_SERVICE_NAME=checkout-api \
  -e OTEL_SERVICE_VERSION=1.0.0 \
  -e OTEL_SERVICE_NAMESPACE=payments \
  -e OTEL_DEPLOYMENT_ENVIRONMENT=production \
  -e OTEL_RESOURCE_ATTRIBUTES=team=payments,region=sa-east-1 \
  -e OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318 \
  -e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
  -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id" \
  -e OTEL_EXPORTER_OTLP_COMPRESSION=gzip \
  -e OTEL_TRACES_EXPORTER=otlp \
  -e OTEL_METRICS_EXPORTER=otlp \
  -e OTEL_LOGS_EXPORTER=none \
  -e OTEL_TRACES_SAMPLER=parentbased_traceidratio \
  -e OTEL_TRACES_SAMPLER_ARG=1 \
  -e OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=true \
  -e OTEL_ELVEN_PRIVACY_HASH_USER_ID=true \
  -e OTEL_ELVEN_PRIVACY_REDACT_URL_CREDENTIALS=true \
  -e OTEL_ELVEN_PRIVACY_REDACT_HEADERS=true \
  -e OTEL_ELVEN_PRIVACY_REDACT_PAYLOADS=true \
  minha-api:latest
```

#### 3. Configure logs quando precisar enviar para Loki

```bash
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=seu-tenant-id
LOGS_TOKEN=seu-api-token
LOGS_APP_NAME=checkout-api
LOGS_APP_VERSION=1.0.0
LOGS_ENVIRONMENT=production
LOGS_COMPRESSION=gzip
```

> A instrumentação Java envia traces e métricas. Logs continuam com o `logs-interceptor-java`, mantendo correlação por `trace_id` e `span_id`.

#### 4. Valide o startup

No log da aplicação, confirme que o agent iniciou:

```
opentelemetry-javaagent - version: 2.27.0
```

Depois gere uma request e valide `service.name` no Tempo.

***

### Pacotes disponíveis

#### Maven com BOM

Use o BOM para travar as versões de todos os artefatos Elven:

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.github.elven-observability</groupId>
      <artifactId>elven-opentelemetry-instrumentation-bom</artifactId>
      <version>0.1.1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
```

#### API manual

```xml
<dependency>
  <groupId>io.github.elven-observability</groupId>
  <artifactId>elven-opentelemetry-instrumentation-core</artifactId>
</dependency>
```

#### Spring Boot Starter

```xml
<dependency>
  <groupId>io.github.elven-observability</groupId>
  <artifactId>elven-opentelemetry-instrumentation-spring-boot-starter</artifactId>
</dependency>
```

#### SDK autoconfigure

Use em cenários avançados nos quais a aplicação já usa `AutoConfiguredOpenTelemetrySdk`:

```xml
<dependency>
  <groupId>io.github.elven-observability</groupId>
  <artifactId>elven-opentelemetry-instrumentation-autoconfigure</artifactId>
</dependency>
```

#### Gradle

```kotlin
dependencyManagement {
    imports {
        mavenBom("io.github.elven-observability:elven-opentelemetry-instrumentation-bom:0.1.1")
    }
}

dependencies {
    implementation("io.github.elven-observability:elven-opentelemetry-instrumentation-core")
}
```

Ou sem plugin de dependency management:

```kotlin
dependencies {
    implementation(platform("io.github.elven-observability:elven-opentelemetry-instrumentation-bom:0.1.1"))
    implementation("io.github.elven-observability:elven-opentelemetry-instrumentation-core")
}
```

***

### Quick Start — Java Agent embedded sem Docker

Use este modo quando a aplicação roda em VM, ECS task customizada, systemd, serviço gerenciado ou qualquer runtime em que você consegue adicionar arquivos e variáveis de ambiente, mas não quer alterar o Dockerfile.

#### 1. Baixe o agent embedded

```bash
mkdir -p /opt/elven

curl -fsSL \
  -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar \
  https://repo1.maven.org/maven2/io/github/elven-observability/elven-opentelemetry-instrumentation-javaagent/0.1.1/elven-opentelemetry-instrumentation-javaagent-0.1.1.jar
```

#### 2. Configure as variáveis de ambiente

```bash
# === Identidade do serviço ===
export OTEL_SERVICE_NAME="checkout-api"
export OTEL_SERVICE_VERSION="1.0.0"
export OTEL_SERVICE_NAMESPACE="payments"
export OTEL_DEPLOYMENT_ENVIRONMENT="production"
export OTEL_RESOURCE_ATTRIBUTES="team=payments,region=sa-east-1"

# === Export OTLP ===
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id"
export OTEL_EXPORTER_OTLP_COMPRESSION="gzip"

# === Sinais ===
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="none"
export OTEL_PROPAGATORS="tracecontext,baggage"

# === Sampling ===
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="1"

# === Privacy Elven ===
export OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT="true"
export OTEL_ELVEN_PRIVACY_HASH_USER_ID="true"
export OTEL_ELVEN_PRIVACY_REDACT_URL_CREDENTIALS="true"
export OTEL_ELVEN_PRIVACY_REDACT_HEADERS="true"
export OTEL_ELVEN_PRIVACY_REDACT_PAYLOADS="true"
```

#### 3. Inicie a aplicação com `JAVA_TOOL_OPTIONS`

```bash
export JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar"

java -jar app.jar
```

Pronto. A aplicação passa a gerar spans automaticamente para frameworks, HTTP, banco, mensageria e bibliotecas suportadas pelo Java Agent oficial.

***

### Quick Start — Java Agent oficial com extensão Elven

Use este modo quando o ambiente já padroniza o JAR oficial `opentelemetry-javaagent.jar` e você quer carregar apenas a extensão Elven.

#### 1. Baixe o Java Agent oficial

```bash
mkdir -p /opt/otel /opt/elven

curl -fsSL \
  -o /opt/otel/opentelemetry-javaagent.jar \
  https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.27.0/opentelemetry-javaagent.jar
```

#### 2. Baixe a extensão Elven

```bash
curl -fsSL \
  -o /opt/elven/elven-opentelemetry-instrumentation-javaagent-extension.jar \
  https://repo1.maven.org/maven2/io/github/elven-observability/elven-opentelemetry-instrumentation-javaagent-extension/0.1.1/elven-opentelemetry-instrumentation-javaagent-extension-0.1.1-all.jar
```

#### 3. Configure agent + extensão

```bash
export JAVA_TOOL_OPTIONS="-javaagent:/opt/otel/opentelemetry-javaagent.jar"
export OTEL_JAVAAGENT_EXTENSIONS="/opt/elven/elven-opentelemetry-instrumentation-javaagent-extension.jar"

export OTEL_SERVICE_NAME="checkout-api"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="none"

java -jar app.jar
```

> Use **um** formato de agent por processo: embedded ou oficial + extensão. Não combine os dois no mesmo `JAVA_TOOL_OPTIONS`.

***

### Quick Start — Spring Boot Starter

Use o starter quando você quer inicializar a SDK no processo da aplicação via Spring Boot.

> Para auto-instrumentação profunda de HTTP/JDBC/frameworks, o Java Agent continua sendo o modo recomendado. O starter é útil para aplicações que preferem bootstrap explícito, custom spans, custom metrics e lifecycle integrado ao contexto Spring.

#### 1. Adicione o BOM e o starter

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.github.elven-observability</groupId>
      <artifactId>elven-opentelemetry-instrumentation-bom</artifactId>
      <version>0.1.1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.github.elven-observability</groupId>
    <artifactId>elven-opentelemetry-instrumentation-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
```

#### 2. Configure o ambiente

```bash
export OTEL_SERVICE_NAME="billing-api"
export OTEL_SERVICE_VERSION="1.0.0"
export OTEL_DEPLOYMENT_ENVIRONMENT="production"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4318"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="none"
```

#### 3. Injete `ObservabilityHandle` quando precisar de spans ou métricas manuais

```java
package com.acme.billing;

import io.github.elvenobservability.opentelemetry.instrumentation.api.ObservabilityHandle;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributeKey;
import org.springframework.stereotype.Service;

@Service
public class BillingService {
  private final ObservabilityHandle observability;

  public BillingService(ObservabilityHandle observability) {
    this.observability = observability;
  }

  public void closeInvoice(String invoiceId) {
    observability.tracer().withSpan("invoice.close", span -> {
      span.setAttribute("invoice.id", invoiceId);

      observability.metrics().increment(
          "billing_invoice_closed_total",
          1,
          Attributes.of(AttributeKey.stringKey("status"), "success"));
    });
  }
}
```

#### 4. Configuração Spring opcional

```yaml
elven:
  otel:
    enabled: true
    shutdown-on-context-close: true
```

Variáveis equivalentes:

```bash
ELVEN_OTEL_ENABLED=true
ELVEN_OTEL_SHUTDOWN_ON_CONTEXT_CLOSE=true
```

***

### Quick Start — Inicialização manual

Use este modo para jobs, aplicações console, serviços sem Spring ou cenários em que você precisa controlar a inicialização por código.

#### 1. Adicione a dependência

```xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.github.elven-observability</groupId>
      <artifactId>elven-opentelemetry-instrumentation-bom</artifactId>
      <version>0.1.1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.github.elven-observability</groupId>
    <artifactId>elven-opentelemetry-instrumentation-core</artifactId>
  </dependency>
</dependencies>
```

#### 2. Inicialize a partir das variáveis de ambiente

```java
import io.github.elvenobservability.opentelemetry.instrumentation.api.Observability;
import io.github.elvenobservability.opentelemetry.instrumentation.api.ObservabilityHandle;

public final class Main {
  public static void main(String[] args) {
    ObservabilityHandle handle = Observability.initFromEnv();

    try {
      handle.tracer().withSpan("job.run", span -> {
        span.setAttribute("job.name", "daily-close");
        handle.metrics().increment("jobs_started_total", 1);
      });

      handle.forceFlush().toCompletableFuture().join();
    } finally {
      handle.shutdown().toCompletableFuture().join();
    }
  }
}
```

#### 3. Ou inicialize com builder explícito

```java
import io.github.elvenobservability.opentelemetry.instrumentation.api.Observability;
import io.github.elvenobservability.opentelemetry.instrumentation.api.ObservabilityHandle;
import io.github.elvenobservability.opentelemetry.instrumentation.config.ExporterConfig;
import io.github.elvenobservability.opentelemetry.instrumentation.config.MetricsConfig;
import io.github.elvenobservability.opentelemetry.instrumentation.config.ObservabilityConfig;
import io.github.elvenobservability.opentelemetry.instrumentation.config.PrivacyConfig;
import io.github.elvenobservability.opentelemetry.instrumentation.config.ServiceConfig;
import io.github.elvenobservability.opentelemetry.instrumentation.config.TracingConfig;
import java.time.Duration;

ObservabilityHandle handle = Observability.init(
    ObservabilityConfig.builder()
        .service(ServiceConfig.builder()
            .serviceName("checkout-worker")
            .serviceVersion("1.0.0")
            .serviceNamespace("payments")
            .deploymentEnvironment("production")
            .attribute("team", "payments")
            .attribute("region", "sa-east-1")
            .build())
        .exporters(ExporterConfig.builder()
            .endpoint("https://otel-collector.sua-infra.com:4318")
            .protocol("http/protobuf")
            .header("Authorization", "Bearer seu-api-token")
            .header("X-Scope-OrgID", "seu-tenant-id")
            .compression("gzip")
            .timeout(Duration.ofSeconds(30))
            .build())
        .tracing(TracingConfig.builder()
            .enabled(true)
            .ratio(1.0d)
            .build())
        .metrics(MetricsConfig.builder()
            .enabled(true)
            .exportInterval(Duration.ofSeconds(60))
            .build())
        .privacy(PrivacyConfig.builder()
            .redactDbStatement(true)
            .hashUserId(true)
            .redactUrlCredentials(true)
            .redactHeaders(true)
            .redactPayloads(true)
            .build())
        .autoShutdown(true)
        .build());
```

#### Precedência de configuração

A resolução segue esta ordem:

```
Builder explícito > Java system property > variável de ambiente > arquivo opcional > default da lib
```

Exemplo com system properties:

```bash
java \
  -Dotel.service.name=checkout-api \
  -Dotel.exporter.otlp.endpoint=https://otel-collector.sua-infra.com:4318 \
  -jar app.jar
```

***

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

#### Identidade do serviço

| Variável                      | Descrição                                                | Default           |
| ----------------------------- | -------------------------------------------------------- | ----------------- |
| `OTEL_SERVICE_NAME`           | Nome do serviço. Deve ser estável e único por aplicação. | `unknown-service` |
| `OTEL_SERVICE_VERSION`        | Versão da aplicação.                                     | `0.0.0`           |
| `OTEL_SERVICE_NAMESPACE`      | Domínio, produto ou agrupador lógico.                    | —                 |
| `OTEL_DEPLOYMENT_ENVIRONMENT` | Ambiente: `production`, `staging`, `development`.        | —                 |
| `OTEL_RESOURCE_ATTRIBUTES`    | Atributos extras no formato `key=value,key2=value2`.     | —                 |

Exemplo:

```bash
OTEL_SERVICE_NAME=checkout-api
OTEL_SERVICE_VERSION=1.8.3
OTEL_SERVICE_NAMESPACE=payments
OTEL_DEPLOYMENT_ENVIRONMENT=production
OTEL_RESOURCE_ATTRIBUTES=team=payments,region=sa-east-1,product=checkout
```

#### Export OTLP

| Variável                              | Descrição                                                                        | Default                                                    |
| ------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT`         | Endpoint base OTLP. Em HTTP/protobuf, a lib deriva `/v1/traces` e `/v1/metrics`. | `http://localhost:4318`                                    |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`  | Endpoint específico de traces.                                                   | Derivado do endpoint base                                  |
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | Endpoint específico de métricas.                                                 | Derivado do endpoint base                                  |
| `OTEL_EXPORTER_OTLP_PROTOCOL`         | `http/protobuf` ou `grpc`.                                                       | `http/protobuf`                                            |
| `OTEL_EXPORTER_OTLP_HEADERS`          | Headers em `key=value,key2=value2`.                                              | —                                                          |
| `OTEL_EXPORTER_OTLP_COMPRESSION`      | `gzip` ou `none`.                                                                | `none` na API manual; `gzip` pode ser definido no ambiente |
| `OTEL_EXPORTER_OTLP_TIMEOUT`          | Timeout em milissegundos.                                                        | `30000`                                                    |
| `OTEL_EXPORTER_OTLP_INSECURE`         | Permitir transporte inseguro em gRPC quando aplicável.                           | `false`                                                    |

#### Sinais

| Variável                | Descrição                                          | Default           |
| ----------------------- | -------------------------------------------------- | ----------------- |
| `OTEL_TRACES_EXPORTER`  | Exporter de traces: `otlp`, `console` ou `none`.   | `otlp`            |
| `OTEL_METRICS_EXPORTER` | Exporter de métricas: `otlp`, `console` ou `none`. | `otlp`            |
| `OTEL_LOGS_EXPORTER`    | Exporter de logs OpenTelemetry.                    | `none`            |
| `OTEL_TRACING_ENABLED`  | Liga/desliga tracing na API manual.                | Conforme exporter |
| `OTEL_METRICS_ENABLED`  | Liga/desliga métricas na API manual.               | Conforme exporter |

> A lib Java da Elven não exporta logs via OpenTelemetry. Logs devem ser enviados pelo `logs-interceptor-java` ou pela pipeline de logs da aplicação.

#### Sampling e propagação

| Variável                      | Descrição                              | Default                    |
| ----------------------------- | -------------------------------------- | -------------------------- |
| `OTEL_PROPAGATORS`            | Propagadores de contexto.              | `tracecontext,baggage`     |
| `OTEL_TRACES_SAMPLER`         | Sampler de traces.                     | `parentbased_traceidratio` |
| `OTEL_TRACES_SAMPLER_ARG`     | Taxa de sampling entre `0.0` e `1.0`.  | `1`                        |
| `OTEL_METRIC_EXPORT_INTERVAL` | Intervalo de export de métricas em ms. | `60000`                    |
| `OTEL_METRIC_EXPORT_TIMEOUT`  | Timeout de export de métricas em ms.   | `30000`                    |

Exemplos:

```bash
# 100% dos traces
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=1

# 10% dos traces
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
```

#### Privacy Elven

| Variável                                    | Descrição                                              | Default |
| ------------------------------------------- | ------------------------------------------------------ | ------- |
| `OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT`    | Redactar `db.statement` e texto de queries por padrão. | `true`  |
| `OTEL_ELVEN_PRIVACY_HASH_USER_ID`           | Converter identidade de usuário para hash/pseudônimo.  | `true`  |
| `OTEL_ELVEN_PRIVACY_REDACT_URL_CREDENTIALS` | Remover credenciais e segredos de URLs.                | `true`  |
| `OTEL_ELVEN_PRIVACY_REDACT_HEADERS`         | Redactar headers sensíveis.                            | `true`  |
| `OTEL_ELVEN_PRIVACY_REDACT_PAYLOADS`        | Redactar atributos com payloads/mensagens sensíveis.   | `true`  |
| `OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES`   | Lista de atributos que podem sair sem redaction.       | —       |
| `OTEL_ELVEN_AUTO_SHUTDOWN`                  | Registrar shutdown hook automático.                    | `true`  |
| `OTEL_ELVEN_CONFIG_FILE`                    | Caminho de arquivo `.properties`, `.yaml` ou `.yml`.   | —       |

Exemplo para uma investigação temporária de SQL:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=false
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=db.statement,db.query.text
```

> Use allowlist de atributos crus apenas por tempo limitado e com aprovação do time responsável por segurança/dados.

#### Java Agent

| Variável                                      | Descrição                                                                        |
| --------------------------------------------- | -------------------------------------------------------------------------------- |
| `JAVA_TOOL_OPTIONS`                           | Injeta `-javaagent:/path/agent.jar` sem alterar o comando original da aplicação. |
| `OTEL_JAVAAGENT_EXTENSIONS`                   | Caminho do JAR de extensão quando usar o agent oficial.                          |
| `OTEL_JAVAAGENT_DEBUG`                        | Habilita debug do Java Agent. Use apenas em troubleshooting.                     |
| `OTEL_JAVAAGENT_LOGGING`                      | Estratégia de logs internos do agent. Padrão Elven: `simple`.                    |
| `OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED` | Liga/desliga instrumentações por padrão.                                         |
| `OTEL_INSTRUMENTATION_<NOME>_ENABLED`         | Liga/desliga uma instrumentação específica do agent.                             |

Exemplos:

```bash
OTEL_INSTRUMENTATION_JDBC_ENABLED=true
OTEL_INSTRUMENTATION_KAFKA_ENABLED=true
OTEL_INSTRUMENTATION_REDIS_ENABLED=true
OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=true
```

***

### Deploy com Docker — variações

#### Dockerfile — app com JAR pronto

```dockerfile
FROM eclipse-temurin:21-jre

WORKDIR /app

RUN mkdir -p /opt/elven \
  && curl -fsSL \
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar \
    https://repo1.maven.org/maven2/io/github/elven-observability/elven-opentelemetry-instrumentation-javaagent/0.1.1/elven-opentelemetry-instrumentation-javaagent-0.1.1.jar

COPY target/app.jar /app/app.jar

ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar"
ENV OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
ENV OTEL_TRACES_EXPORTER="otlp"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_LOGS_EXPORTER="none"

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

Credenciais e endpoints devem entrar no runtime, nunca hardcoded no Dockerfile:

```bash
docker run --rm -p 8080:8080 \
  -e OTEL_SERVICE_NAME=checkout-api \
  -e OTEL_SERVICE_VERSION=1.0.0 \
  -e OTEL_DEPLOYMENT_ENVIRONMENT=production \
  -e OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318 \
  -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id" \
  minha-api:latest
```

#### Dockerfile multi-stage com Maven

```dockerfile
FROM maven:3.9.12-eclipse-temurin-21 AS build

WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn -B -ntp package -DskipTests

FROM eclipse-temurin:21-jre

WORKDIR /app

RUN mkdir -p /opt/elven \
  && curl -fsSL \
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar \
    https://repo1.maven.org/maven2/io/github/elven-observability/elven-opentelemetry-instrumentation-javaagent/0.1.1/elven-opentelemetry-instrumentation-javaagent-0.1.1.jar

COPY --from=build /build/target/*.jar /app/app.jar

ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar"
ENV OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
ENV OTEL_TRACES_EXPORTER="otlp"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_LOGS_EXPORTER="none"

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

#### Dockerfile usando Maven para copiar o agent

Útil quando o build já usa Maven e você quer evitar URLs no Dockerfile:

```dockerfile
FROM maven:3.9.12-eclipse-temurin-21 AS agent

RUN mkdir -p /opt/elven \
  && mvn -B -ntp dependency:copy \
    -Dartifact=io.github.elven-observability:elven-opentelemetry-instrumentation-javaagent:0.1.1:jar \
    -DoutputDirectory=/opt/elven \
    -Dmdep.stripVersion=true

FROM eclipse-temurin:21-jre

WORKDIR /app
COPY --from=agent /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar
COPY target/app.jar /app/app.jar

ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar"

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

#### docker-compose.yml

```yaml
services:
  checkout-api:
    image: minha-api:latest
    ports:
      - "8080:8080"
    environment:
      # Identidade
      OTEL_SERVICE_NAME: checkout-api
      OTEL_SERVICE_VERSION: "1.0.0"
      OTEL_SERVICE_NAMESPACE: payments
      OTEL_DEPLOYMENT_ENVIRONMENT: production
      OTEL_RESOURCE_ATTRIBUTES: team=payments,region=sa-east-1,product=checkout

      # OTLP
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otel-collector.sua-infra.com:4318
      OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
      OTEL_EXPORTER_OTLP_HEADERS: Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id
      OTEL_EXPORTER_OTLP_COMPRESSION: gzip

      # Sinais
      OTEL_TRACES_EXPORTER: otlp
      OTEL_METRICS_EXPORTER: otlp
      OTEL_LOGS_EXPORTER: none
      OTEL_TRACES_SAMPLER: parentbased_traceidratio
      OTEL_TRACES_SAMPLER_ARG: "1"

      # Privacy
      OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT: "true"
      OTEL_ELVEN_PRIVACY_HASH_USER_ID: "true"
      OTEL_ELVEN_PRIVACY_REDACT_URL_CREDENTIALS: "true"
      OTEL_ELVEN_PRIVACY_REDACT_HEADERS: "true"
      OTEL_ELVEN_PRIVACY_REDACT_PAYLOADS: "true"

      # Logs via logs-interceptor-java
      LOGS_URL: https://loki.elvenobservability.com/loki/api/v1/push
      LOGS_TENANT: seu-tenant-id
      LOGS_TOKEN: seu-api-token
      LOGS_APP_NAME: checkout-api
      LOGS_APP_VERSION: "1.0.0"
      LOGS_ENVIRONMENT: production
```

***

### Logs e correlação com Loki

Esta lib **não cria pipeline de exportação de logs OpenTelemetry**. Ela fornece correlação com `trace_id`, `span_id` e `trace_flags`, para que os logs enviados ao Loki possam ser relacionados com traces no Tempo.

Use `logs-interceptor-java` para envio ao Loki.

#### Dependências de logs

Exemplo com Logback:

```xml
<properties>
  <logs-interceptor-java.version>VERSAO_PUBLICADA_DA_LIB_DE_LOGS</logs-interceptor-java.version>
</properties>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.github.elvenobservability</groupId>
      <artifactId>logs-interceptor-bom</artifactId>
      <version>${logs-interceptor-java.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>io.github.elvenobservability</groupId>
    <artifactId>logs-interceptor-core</artifactId>
  </dependency>
  <dependency>
    <groupId>io.github.elvenobservability</groupId>
    <artifactId>logs-interceptor-transport-loki</artifactId>
  </dependency>
  <dependency>
    <groupId>io.github.elvenobservability</groupId>
    <artifactId>logs-interceptor-adapter-logback</artifactId>
  </dependency>
</dependencies>
```

Substitua `VERSAO_PUBLICADA_DA_LIB_DE_LOGS` pela versão vigente do `logs-interceptor-java` disponibilizada pela Elven no gerenciador de pacotes usado pelo cliente.

#### Variáveis de logs

```bash
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
LOGS_TENANT=seu-tenant-id
LOGS_TOKEN=seu-api-token
LOGS_APP_NAME=checkout-api
LOGS_APP_VERSION=1.0.0
LOGS_ENVIRONMENT=production
LOGS_COMPRESSION=gzip
LOGS_LABEL_TEAM=payments
LOGS_LABEL_REGION=sa-east-1
```

#### Correlação manual com MDC

```java
import io.github.elvenobservability.opentelemetry.instrumentation.context.LogCorrelation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(CheckoutService.class);

handle.tracer().withSpan("checkout.process", span -> {
  try (AutoCloseable ignored = LogCorrelation.putMdc()) {
    span.setAttribute("checkout.id", "chk_123");
    logger.info("checkout processado");
  } catch (Exception ex) {
    handle.tracer().recordException(span, ex);
    throw new RuntimeException(ex);
  }
});
```

#### Pattern Logback local

Mesmo quando o envio ao Loki é feito por appender dedicado, manter `trace_id` e `span_id` no console ajuda no troubleshooting:

```xml
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{ISO8601} %-5level [%thread] trace_id=%X{trace_id} span_id=%X{span_id} %logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE"/>
  </root>
</configuration>
```

#### Como a correlação aparece

Um log correlacionado deve carregar:

```json
{
  "message": "checkout processado",
  "trace_id": "313549e4a83129bc6065bb38a24a79f0",
  "span_id": "6065bb38a24a79f0",
  "service_name": "checkout-api",
  "environment": "production"
}
```

No Grafana, use `trace_id` para navegar de Loki para Tempo.

***

### Traces manuais, erros e atributos semânticos

O Java Agent cria spans automaticamente para bibliotecas suportadas. Use a API manual para eventos de negócio e spans que só a aplicação conhece.

#### Span simples

```java
handle.tracer().withSpan("order.approve", span -> {
  span.setAttribute("order.id", "ord_123");
  span.setAttribute("order.amount_cents", 15990);
  span.setAttribute("payment.provider", "acquirer-a");
});
```

#### Registrar exceções

```java
handle.tracer().withSpan("payment.capture", span -> {
  try {
    paymentGateway.capture();
    span.setAttribute("payment.status", "captured");
  } catch (RuntimeException ex) {
    handle.tracer().recordException(span, ex);
    throw ex;
  }
});
```

O helper `recordException()` adiciona:

* evento de exception no span
* status `ERROR`
* atributo `error.type`

#### Usuário final com privacidade

Prefira `enduser.pseudo.id` em vez de `enduser.id`.

```java
handle.tracer().withSpan("user.checkout", span -> {
  handle.tracer().setPseudonymousUserId(span, user.getId());
});
```

Isso gera um identificador pseudonimizado, adequado para correlação sem expor o ID bruto do usuário.

#### Quando usar atributos crus

Evite:

```java
span.setAttribute("enduser.id", user.getId());
span.setAttribute("user.email", user.getEmail());
```

Use apenas quando houver política explícita e allowlist:

```bash
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=enduser.id
```

***

### Métricas manuais

Use métricas manuais para indicadores de negócio, filas, batches e estados internos que não aparecem automaticamente.

#### Counter

```java
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributeKey;

handle.metrics().increment(
    "checkout_requests_total",
    1,
    Attributes.of(
        AttributeKey.stringKey("status"), "success",
        AttributeKey.stringKey("payment_method"), "credit_card"));
```

#### Histogram

```java
handle.metrics().recordHistogram(
    "checkout_duration_ms",
    durationMillis,
    Attributes.of(AttributeKey.stringKey("route"), "/checkout"));
```

#### UpDownCounter

```java
handle.metrics().addUpDown(
    "queue_inflight_jobs",
    1,
    Attributes.of(AttributeKey.stringKey("queue"), "payments"));
```

#### Gauge

```java
handle.metrics().setGauge(
    "worker_pool_usage_ratio",
    0.82,
    Attributes.of(AttributeKey.stringKey("pool"), "default"));
```

#### Cuidados de cardinalidade

Evite labels com alta cardinalidade:

```java
// Evite em métricas:
AttributeKey.stringKey("user_id")
AttributeKey.stringKey("request_id")
AttributeKey.stringKey("order_id")
AttributeKey.stringKey("trace_id")
```

Prefira labels com cardinalidade controlada:

```java
status
method
route
region
team
queue
payment_provider
```

***

### Instrumentações automáticas

O Java Agent oficial instrumenta automaticamente bibliotecas e frameworks comuns. A lista exata evolui com o OpenTelemetry Java Agent, mas os grupos mais importantes são:

| Categoria        | Exemplos                                                                       |
| ---------------- | ------------------------------------------------------------------------------ |
| HTTP server      | Servlet, Spring MVC, Spring WebFlux, JAX-RS, Jetty, Tomcat, Undertow, Netty    |
| HTTP client      | Java `HttpClient`, Apache HttpClient, OkHttp, Spring `RestTemplate`, WebClient |
| Banco de dados   | JDBC, HikariCP, PostgreSQL, MySQL, MariaDB, Oracle, SQL Server                 |
| Messaging        | Kafka, RabbitMQ, JMS, AWS SQS/SNS em bibliotecas suportadas                    |
| Cache/datastores | Redis, MongoDB, Cassandra, Elasticsearch em versões suportadas                 |
| Frameworks       | Spring Boot, Micronaut, Quarkus, Dropwizard em cenários suportados             |
| Logging          | Correlação com frameworks suportados pelo agent e MDC quando aplicável         |
| GraphQL          | GraphQL Java em versões suportadas pelo agent                                  |

#### Habilitar/desabilitar instrumentações

```bash
# Todas habilitadas por padrão
OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=true

# Desabilitar uma instrumentação específica
OTEL_INSTRUMENTATION_KAFKA_ENABLED=false

# Habilitar explicitamente JDBC
OTEL_INSTRUMENTATION_JDBC_ENABLED=true
```

#### SQL statements

Por padrão, a Elven aplica redaction em statements SQL:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=true
```

Para investigar um problema específico, habilite temporariamente:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=false
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=db.statement,db.query.text
```

Volte para o padrão seguro ao terminar a análise.

***

### Configuração por arquivo

Além de variáveis de ambiente e system properties, a API manual aceita arquivo `.properties`, `.yaml` ou `.yml`.

#### Usando variável

```bash
OTEL_ELVEN_CONFIG_FILE=/etc/elven/otel.yaml
```

#### Usando builder

```java
ObservabilityHandle handle = Observability.init(
    ObservabilityConfig.builder()
        .configFile("/etc/elven/otel.yaml")
        .build());
```

#### Exemplo `.properties`

```properties
otel.service.name=checkout-api
otel.service.version=1.0.0
otel.service.namespace=payments
otel.deployment.environment=production
otel.resource.attributes=team=payments,region=sa-east-1

otel.exporter.otlp.endpoint=https://otel-collector.sua-infra.com:4318
otel.exporter.otlp.protocol=http/protobuf
otel.exporter.otlp.headers=Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id
otel.exporter.otlp.compression=gzip

otel.traces.exporter=otlp
otel.metrics.exporter=otlp
otel.logs.exporter=none
otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=1

otel.elven.privacy.redact.db.statement=true
otel.elven.privacy.hash.user.id=true
otel.elven.privacy.redact.url.credentials=true
otel.elven.privacy.redact.headers=true
otel.elven.privacy.redact.payloads=true
```

#### Exemplo `.yaml`

```yaml
otel.service.name: checkout-api
otel.service.version: 1.0.0
otel.service.namespace: payments
otel.deployment.environment: production
otel.resource.attributes: team=payments,region=sa-east-1

otel.exporter.otlp.endpoint: https://otel-collector.sua-infra.com:4318
otel.exporter.otlp.protocol: http/protobuf
otel.exporter.otlp.headers: Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id
otel.exporter.otlp.compression: gzip

otel.traces.exporter: otlp
otel.traces.sampler: parentbased_traceidratio
otel.traces.sampler.arg: 1
otel.metrics.exporter: otlp
otel.logs.exporter: none

otel.elven.privacy.redact.db.statement: true
otel.elven.privacy.hash.user.id: true
otel.elven.privacy.redact.url.credentials: true
otel.elven.privacy.redact.headers: true
otel.elven.privacy.redact.payloads: true
```

***

### Privacidade e dados sensíveis

Os defaults da Elven são **privacy-first**. A instrumentação assume que aplicações podem carregar dados pessoais, tokens, cookies, queries sensíveis e payloads de negócio.

#### Redactado por padrão

* `db.statement`
* `db.query.text`
* credenciais em URL
* query params com tokens/secrets
* headers de autenticação
* cookies
* payloads e atributos com aparência de corpo de mensagem
* `user.id` e `enduser.id` vindos de dependências legadas ou customizadas

#### Identidade de usuário

Use:

```
enduser.pseudo.id
```

Evite:

```
enduser.id
user.id
user.email
```

#### Permitir atributo cru

```bash
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=enduser.id,db.statement
```

Use somente em ambientes controlados ou janelas curtas de diagnóstico.

#### Headers

Nunca coloque tokens diretamente em `OTEL_RESOURCE_ATTRIBUTES`. Para autenticação OTLP, use:

```bash
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id"
```

E mantenha esses valores em secret manager, variável segura do runtime ou ferramenta equivalente.

***

### Boas práticas para SaaS multi-tenant

#### Identidade do serviço

Use nomes estáveis:

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

Exemplos:

```
auth-api
billing-worker
checkout-api
notifications-scheduler
```

#### Tenant routing

O roteamento de tenant deve ficar no header do transporte ou no Collector do cliente:

```bash
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=tenant-a"
```

#### Atributos de tenant

Para spans, use atributos de tenant apenas quando eles forem necessários para investigação:

```java
span.setAttribute("tenant.id", "tenant-a");
span.setAttribute("tenant.plan", "enterprise");
```

Para métricas, tenha cuidado redobrado. `tenant.id` como label pode explodir cardinalidade em SaaS com muitos clientes.

Prefira métricas agregadas por dimensões controladas:

```
plan
region
service.namespace
deployment.environment
status
```

#### Logs

No Loki, labels devem ser de baixa cardinalidade:

```bash
LOGS_LABEL_TEAM=payments
LOGS_LABEL_ENVIRONMENT=production
LOGS_LABEL_REGION=sa-east-1
```

Evite labels como:

```bash
LOGS_LABEL_USER_ID=...
LOGS_LABEL_REQUEST_ID=...
LOGS_LABEL_TRACE_ID=...
LOGS_LABEL_ORDER_ID=...
```

Esses valores devem ir no corpo do log, não como label indexado.

#### Sampling

Para serviços críticos ou baixo volume:

```bash
OTEL_TRACES_SAMPLER_ARG=1
```

Para alto volume:

```bash
OTEL_TRACES_SAMPLER_ARG=0.1
```

Em incidentes, aumente temporariamente a taxa do serviço afetado.

***

### Validação ponta a ponta

#### 1. Verifique o log de startup

Com Java Agent, procure por mensagens indicando que o agent iniciou:

```
opentelemetry-javaagent - version: 2.27.0
```

Se `OTEL_JAVAAGENT_DEBUG=true`, o agent mostra mais detalhes das instrumentações carregadas.

#### 2. Gere tráfego

```bash
curl -i http://localhost:8080/health
curl -i http://localhost:8080/api/checkout
```

#### 3. Verifique traces

No Grafana Tempo:

* filtre por `service.name="checkout-api"`
* confirme spans HTTP server/client
* confirme spans JDBC ou messaging quando a aplicação usar essas bibliotecas
* abra erros e confirme exception events

#### 4. Verifique métricas

No Grafana/Mimir:

* procure métricas runtime/processo geradas pelo agent
* procure métricas manuais criadas por `MetricFacade`
* valide labels de baixa cardinalidade

Exemplos de nomes manuais:

```
checkout_requests_total
checkout_duration_ms
queue_inflight_jobs
worker_pool_usage_ratio
```

#### 5. Verifique logs correlacionados

No Loki:

```logql
{app="checkout-api"} |= "trace_id"
```

Depois copie o `trace_id` e procure o trace correspondente no Tempo.

#### 6. Valide redaction

Confirme que dados sensíveis não aparecem crus:

* tokens em URL devem aparecer como redigidos
* headers de auth não devem aparecer em claro
* `db.statement` deve estar redigido quando `OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=true`
* IDs de usuário devem aparecer como `enduser.pseudo.id`, não `enduser.id`

***

### Troubleshooting

#### Traces não aparecem

1. Confirme que o agent está carregado:

   ```bash
   echo "$JAVA_TOOL_OPTIONS"
   ```
2. Verifique se existe `-javaagent:/caminho/agent.jar`.
3. Confirme `OTEL_SERVICE_NAME`.
4. Confirme `OTEL_TRACES_EXPORTER=otlp`.
5. Confirme `OTEL_EXPORTER_OTLP_ENDPOINT` ou `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`.
6. Verifique headers:

   ```bash
   echo "$OTEL_EXPORTER_OTLP_HEADERS"
   ```
7. Habilite debug temporariamente:

   ```bash
   OTEL_JAVAAGENT_DEBUG=true
   ```

#### Métricas não aparecem

1. Confirme `OTEL_METRICS_EXPORTER=otlp`.
2. Confirme que o endpoint OTLP aceita `/v1/metrics`.
3. Verifique `OTEL_METRIC_EXPORT_INTERVAL`; o default é `60000` ms.
4. Confirme que o Collector do cliente encaminha métricas para Mimir.
5. Evite testar e encerrar a aplicação antes do primeiro ciclo de export. Para jobs curtos, chame `forceFlush()` antes de finalizar.

#### Logs não aparecem no Loki

1. Confirme que `logs-interceptor-java` está instalado/configurado.
2. Confirme as variáveis:

   ```bash
   LOGS_URL
   LOGS_TENANT
   LOGS_TOKEN
   LOGS_APP_NAME
   ```
3. Confirme que a aplicação realmente emite logs.
4. Confirme que o appender/agent de logs está ativo.
5. Se traces aparecem mas logs não, o problema está na pipeline de logs, não na instrumentação OpenTelemetry.

#### Logs aparecem sem `trace_id`

1. Confirme que o log foi emitido dentro de um span ativo.
2. Em spans manuais, use:

   ```java
   try (AutoCloseable ignored = LogCorrelation.putMdc()) {
     logger.info("mensagem correlacionada");
   }
   ```
3. Confirme que o pattern/appender captura MDC.
4. Em chamadas assíncronas, confirme que o contexto OpenTelemetry está sendo propagado para a thread.

#### JDBC não gera spans

1. Confirme que a aplicação está rodando com Java Agent.
2. Confirme que a dependência JDBC está em versão suportada pelo agent.
3. Confirme que a conexão passa por `java.sql`/JDBC ou pool suportado.
4. Verifique se a instrumentação foi desabilitada:

   ```bash
   OTEL_INSTRUMENTATION_JDBC_ENABLED=false
   ```
5. Habilite debug temporariamente para ver decisões do agent.

#### `db.statement` não aparece

Por padrão, a Elven redacta SQL para evitar vazamento de dados.

Para diagnóstico temporário:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=false
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=db.statement,db.query.text
```

Depois volte:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=true
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=
```

#### Erro de endpoint OTLP inválido

Sintomas comuns:

```
[instrumentation-java] Invalid OTLP traces endpoint
[instrumentation-java] Invalid OTLP metrics endpoint
```

Verifique:

```bash
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318
```

O endpoint precisa ter scheme e host (`http://` ou `https://`).

#### Erro em headers OTLP

Formato correto:

```bash
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer seu-api-token,X-Scope-OrgID=seu-tenant-id"
```

Formato inválido:

```bash
OTEL_EXPORTER_OTLP_HEADERS="Authorization Bearer token"
```

Cada item precisa estar no formato `key=value`.

#### Sampling inválido

`OTEL_TRACES_SAMPLER_ARG` precisa estar entre `0.0` e `1.0`:

```bash
OTEL_TRACES_SAMPLER_ARG=0.25
```

Valores como `25`, `100` ou `-1` são inválidos.

#### Aplicação não inicia com agent

1. Confirme que o JAR existe:

   ```bash
   ls -lh /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar
   ```
2. Confirme permissão de leitura.
3. Confirme que a imagem usa Java 11+.
4. Confirme que há apenas um `-javaagent` OpenTelemetry no processo.
5. Rode localmente com debug:

   ```bash
   JAVA_TOOL_OPTIONS="-javaagent:/opt/elven/elven-opentelemetry-instrumentation-javaagent.jar" \
   OTEL_JAVAAGENT_DEBUG=true \
   java -jar app.jar
   ```

#### Duplicate SDK setup

Se a aplicação já registra `GlobalOpenTelemetry`, a API manual reutiliza a instância global quando possível. Evite inicializar múltiplas SDKs no mesmo processo.

Boas práticas:

* com Java Agent, não chame outro bootstrap OpenTelemetry concorrente
* com Spring Boot Starter, deixe apenas um bean `ObservabilityHandle`
* em testes, isole processos ou use reset apenas em contexto de teste

***

### FAQ

#### Preciso alterar código para usar a lib?

Não no modo Java Agent embedded. Basta adicionar o JAR e configurar `JAVA_TOOL_OPTIONS` + variáveis de ambiente.

#### A lib envia logs para Loki?

Não diretamente. Ela configura traces, métricas e correlação de logs. Para envio de logs ao Loki, use `logs-interceptor-java`.

#### Posso usar só a API manual sem Java Agent?

Sim. Nesse modo você consegue criar spans e métricas manuais, configurar SDK e exportar OTLP. Para auto-instrumentação completa de HTTP, JDBC e frameworks, use o Java Agent.

#### Posso usar Java Agent e Spring Boot Starter juntos?

Pode, mas normalmente não é necessário. Em aplicações Spring Boot, prefira:

* Java Agent para auto-instrumentação zero-code
* Starter/API manual apenas quando precisar de `ObservabilityHandle`, spans manuais ou métricas manuais

Evite criar duas SDKs OpenTelemetry concorrentes.

#### Qual endpoint devo usar?

Na maioria dos ambientes, use o endpoint do Collector OTLP implantado no cliente:

```bash
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

Se usar endpoints separados:

```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://otel-collector.sua-infra.com:4318/v1/traces
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://otel-collector.sua-infra.com:4318/v1/metrics
```

#### Como vejo SQL statements?

Por padrão eles são redigidos. Para diagnóstico temporário:

```bash
OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=false
OTEL_ELVEN_PRIVACY_ALLOW_RAW_ATTRIBUTES=db.statement,db.query.text
```

Use com cuidado e volte ao padrão seguro depois.

#### Como correlaciono Loki e Tempo?

Garanta que logs tenham `trace_id` e `span_id`.

Com spans manuais, use:

```java
try (AutoCloseable ignored = LogCorrelation.putMdc()) {
  logger.info("evento correlacionado");
}
```

Com `logs-interceptor-java`, o MDC é enviado junto no corpo do log.

#### Qual versão devo usar?

Use:

```
0.1.1
```

com groupId:

```
io.github.elven-observability
```

e artifact principal:

```
elven-opentelemetry-instrumentation-java
```

***

### Checklist de deploy

* [ ] Aplicação roda em Java 11+
* [ ] `JAVA_TOOL_OPTIONS` aponta para o Java Agent embedded ou para o agent oficial
* [ ] `OTEL_JAVAAGENT_EXTENSIONS` configurado quando usar agent oficial + extensão
* [ ] `OTEL_SERVICE_NAME` definido
* [ ] `OTEL_SERVICE_VERSION` definido
* [ ] `OTEL_DEPLOYMENT_ENVIRONMENT` definido
* [ ] `OTEL_EXPORTER_OTLP_ENDPOINT` ou endpoints por sinal configurados
* [ ] `OTEL_EXPORTER_OTLP_HEADERS` contém autenticação e tenant corretos
* [ ] `OTEL_TRACES_EXPORTER=otlp`
* [ ] `OTEL_METRICS_EXPORTER=otlp`
* [ ] `OTEL_LOGS_EXPORTER=none`
* [ ] Privacy Elven mantida ligada em produção
* [ ] Sampling adequado ao volume do serviço
* [ ] `logs-interceptor-java` configurado quando logs precisam ir ao Loki
* [ ] Logs carregam `trace_id` e `span_id`
* [ ] Traces aparecem no Tempo com `service.name` correto
* [ ] Métricas aparecem no Mimir/Grafana
* [ ] Nenhum token, cookie, header sensível ou payload aparece cru nos spans


---

# 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/java/instrumentacao-java-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.
