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

FROM eclipse-temurin:21-jre

WORKDIR /app

RUN mkdir -p /opt/elven <br>
  && curl -fsSL <br>
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar <br>
    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

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

3. Configure logs quando precisar enviar para Loki

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:

<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

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

Spring Boot Starter

<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:

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

Gradle

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:

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

mkdir -p /opt/elven

curl -fsSL <br>
  -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar <br>
  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

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

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

mkdir -p /opt/otel /opt/elven

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

2. Baixe a extensão Elven

curl -fsSL <br>
  -o /opt/elven/elven-opentelemetry-instrumentation-javaagent-extension.jar <br>
  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

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

<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

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

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

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

Variáveis equivalentes:

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

<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

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

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:

java <br>
  -Dotel.service.name=checkout-api <br>
  -Dotel.exporter.otlp.endpoint=https://otel-collector.sua-infra.com:4318 <br>
  -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:

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:

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

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:

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

FROM eclipse-temurin:21-jre

WORKDIR /app

RUN mkdir -p /opt/elven <br>
  && curl -fsSL <br>
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar <br>
    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:

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

Dockerfile multi-stage com Maven

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 <br>
  && curl -fsSL <br>
    -o /opt/elven/elven-opentelemetry-instrumentation-javaagent.jar <br>
    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:

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

RUN mkdir -p /opt/elven <br>
  && mvn -B -ntp dependency:copy <br>
    -Dartifact=io.github.elven-observability:elven-opentelemetry-instrumentation-javaagent:0.1.1:jar <br>
    -DoutputDirectory=/opt/elven <br>
    -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

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:

<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

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

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:

<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:

{
  "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

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

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.

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:

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

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

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

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

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

UpDownCounter

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

Gauge

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

Cuidados de cardinalidade

Evite labels com alta cardinalidade:

// 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:

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

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

OTEL_ELVEN_PRIVACY_REDACT_DB_STATEMENT=true

Para investigar um problema específico, habilite temporariamente:

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

OTEL_ELVEN_CONFIG_FILE=/etc/elven/otel.yaml

Usando builder

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

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

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

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:

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:

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:

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:

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

Evite labels como:

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:

OTEL_TRACES_SAMPLER_ARG=1

Para alto volume:

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

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:

{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:

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

Depois volte:

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:

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:

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

Formato inválido:

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:

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" <br> OTEL_JAVAAGENT_DEBUG=true <br> 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:

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

Se usar endpoints separados:

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:

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:

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