Gerenciar Telemetria com SDK

O SDK é uma referência integrada da implementação da API, processando e exportando a telemetria produzida pela instrumentação das chamadas de API. Esta página é uma visão geral dos conceitos do SDK, incluindo descrições, links relevantes para Javadocs, coordenadas dos artefatos, configurações programadas de amostras e mais. Veja Configure o SDK para detalhes na configuração do SDK, incluindo Autoconfiguração do SDK sem código.

O SDK consiste nos seguintes componentes de alto nível:

  • SdkTracerProvider: A implementação do SDK TracerProvider, incluindo ferramentas para amostragem, processamento, e exportação de trechos.
  • SdkMeterProvider: A implementação do SDK MeterProvider, incluindo ferramentas para configuração de fluxo de métricas e lendo / exportando métricas.
  • SdkLoggerProvider: A implementação do SDK LoggerProvider, incluindo ferramentas para processamento e exportação de logs.
  • TextMapPropagator: Propaga o contexto através dos limites dos processos.

Essa combinação no OpenTelemetrySdk, transporta um objeto que facilita a passagem de Componentes SDK totalmente configurados para instrumentação.

O SDK é empacotado com uma variedade de componentes integrados que são suficientes para muitos casos de uso, e suporta plugins de interfaces para extensibilidade.

SDK interfaces de extensões para plugins

Quando os componentes integrados são suficientes, o SDK pode ser extendido por uma implementação para várias interfaces de extensões para plugins:

Componentes do SDK

O artefato io.opentelemetry:opentelemetry-sdk:1.48.0 contém o OpenTelemetry SDK.

A seção a seguir descreve o núcleo (core) dos componentes do SDK voltados ao usuário. Cada seção do componente incluí:

OpenTelemetrySdk

OpenTelemetrySdk é uma implementação do SDK do OpenTelemetry. Ele armazena os componentes de alto nível do SDK, o que facilita a passagem de componentes do SDK totalmente configurados para a instrumentação.

OpenTelemetrySdk é configurado pelo responsável da aplicação, e consiste em:

O trecho de código a seguir demonstra a configuração programática do OpenTelemetrySdk:

package otel;

import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;

public class OpenTelemetrySdkConfig {
  public static OpenTelemetrySdk create() {
    Resource resource = ResourceConfig.create();
    return OpenTelemetrySdk.builder()
        .setTracerProvider(SdkTracerProviderConfig.create(resource))
        .setMeterProvider(SdkMeterProviderConfig.create(resource))
        .setLoggerProvider(SdkLoggerProviderConfig.create(resource))
        .setPropagators(ContextPropagatorsConfig.create())
        .build();
  }
}

Recursos

Recursos é uma lista de atributos definindo a origem da telemetria. Uma aplicação deve associar o mesmo recurso com o SdkTracerProvider, SdkMeterProvider, SdkLoggerProvider.

O trecho de código a seguir demonstra a configuração programática do Resource:

package otel;

import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ServiceAttributes;

public class ResourceConfig {
  public static Resource create() {
    return Resource.getDefault().toBuilder()
        .put(ServiceAttributes.SERVICE_NAME, "my-service")
        .build();
  }
}

SdkTracerProvider

SdkTracerProvider é uma implementação do SDK do TracerProvider, e é responsável por lidar com a telemetria de traços produzidos pela API.

SdkTracerProvider é configurado pelo responsável da aplicação, e consiste em:

  • Recursos: Os trechos associados com o traço.
  • Amostrador: Configura quais trechos são gravados e amostrados.
  • SpanProcessors: Processa trechos quando eles iniciam e quando eles finalizam.
  • SpanExporters: Exporta trechos fora dos processos (junção dos associados com SpanProcessors).
  • SpanLimits: Controla o limite de dados associados com os trechos.

O trecho de código a seguir demonstra a configuração programática do SdkTracerProvider:

package otel;

import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;

public class SdkTracerProviderConfig {
  public static SdkTracerProvider create(Resource resource) {
    return SdkTracerProvider.builder()
        .setResource(resource)
        .addSpanProcessor(
            SpanProcessorConfig.batchSpanProcessor(
                SpanExporterConfig.otlpHttpSpanExporter("http://localhost:4318/v1/spans")))
        .setSampler(SamplerConfig.parentBasedSampler(SamplerConfig.traceIdRatioBased(.25)))
        .setSpanLimits(SpanLimitsConfig::spanLimits)
        .build();
  }
}

Amostrador

Uma Amostra é uma interfaces de extensão para plugins responsável por determinar quais trechos são gravados e amostrados.

Amostras integradas ao SDK e mantidas pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
ParentBasedio.opentelemetry:opentelemetry-sdk:1.48.0Amostra trechos com base no status de amostragem do trecho pai.
AlwaysOnio.opentelemetry:opentelemetry-sdk:1.48.0Amostra todos os trechos.
AlwaysOffio.opentelemetry:opentelemetry-sdk:1.48.0Excluí todos os trechos.
TraceIdRatioBasedio.opentelemetry:opentelemetry-sdk:1.48.0Amostra trechos baseados em a razão configurável.
JaegerRemoteSamplerio.opentelemetry:opentelemetry-sdk-extension-jaeger-remote-sampler:1.48.0Amostra trechos baseados em uma configuração de um servidor remoto.
LinksBasedSamplerio.opentelemetry.contrib:opentelemetry-samplers:1.45.0-alphaAmostra trechos baseados no status de amostragem dos links do trecho.
RuleBasedRoutingSamplerio.opentelemetry.contrib:opentelemetry-samplers:1.45.0-alphaAmostra trechos baseados em regras configuráveis.
ConsistentSamplersio.opentelemetry.contrib:opentelemetry-consistent-sampling:1.45.0-alphaVárias implementações consistentes de amostrador, conforme definido por amostras probabilísticas.

O trecho de código a seguir demonstra a configuração programática do Sampler:

package otel;

import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.time.Duration;

public class SamplerConfig {
  public static Sampler parentBasedSampler(Sampler root) {
    return Sampler.parentBasedBuilder(root)
        .setLocalParentNotSampled(Sampler.alwaysOff())
        .setLocalParentSampled(Sampler.alwaysOn())
        .setRemoteParentNotSampled(Sampler.alwaysOff())
        .setRemoteParentSampled(Sampler.alwaysOn())
        .build();
  }

  public static Sampler alwaysOn() {
    return Sampler.alwaysOn();
  }

  public static Sampler alwaysOff() {
    return Sampler.alwaysOff();
  }

  public static Sampler traceIdRatioBased(double ratio) {
    return Sampler.traceIdRatioBased(ratio);
  }

  public static Sampler jaegerRemoteSampler() {
    return JaegerRemoteSampler.builder()
        .setInitialSampler(Sampler.alwaysOn())
        .setEndpoint("http://endpoint")
        .setPollingInterval(Duration.ofSeconds(60))
        .setServiceName("my-service-name")
        .build();
  }
}

Implementando a interface da Amostra para fornecer sua própria lógica de amostragem personalizada. Por exemplo:

package otel;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
import java.util.List;

public class CustomSampler implements Sampler {
  @Override
  public SamplingResult shouldSample(
      Context parentContext,
      String traceId,
      String name,
      SpanKind spanKind,
      Attributes attributes,
      List<LinkData> parentLinks) {
    // Callback invoked when span is started, before any SpanProcessor is called.
    // If the SamplingDecision is:
    // - DROP: the span is dropped. A valid span context is created and SpanProcessor#onStart is
    // still called, but no data is recorded and SpanProcessor#onEnd is not called.
    // - RECORD_ONLY: the span is recorded but not sampled. Data is recorded to the span,
    // SpanProcessor#onStart and SpanProcessor#onEnd are called, but the span's sampled status
    // indicates it should not be exported out of process.
    // - RECORD_AND_SAMPLE: the span is recorded and sampled. Data is recorded to the span,
    // SpanProcessor#onStart and SpanProcessor#onEnd are called, and the span's sampled status
    // indicates it should be exported out of process.
    return SpanKind.SERVER == spanKind ? SamplingResult.recordAndSample() : SamplingResult.drop();
  }

  @Override
  public String getDescription() {
    // Return a description of the sampler.
    return this.getClass().getSimpleName();
  }
}

SpanProcessor

Um SpanProcessor é uma interface de extensão para plugins com funções de retorno invocadas quando um trecho é inicializado e finalizado. Elas são frequentemente combinadas com SpanExporters para exportar trechos ao limites dos processos, mas também possui outras aplicações, como enriquecimento de dados.

Span processors integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
BatchSpanProcessorio.opentelemetry:opentelemetry-sdk:1.48.0Agrupa trechos amostrados e os exporta por meio de um SpanExporter configurável.
SimpleSpanProcessorio.opentelemetry:opentelemetry-sdk:1.48.0Exporta cada amostra de trecho por meio de um SpanExporter configurável.
BaggageSpanProcessorio.opentelemetry.contrib:opentelemetry-baggage-processor:1.45.0-alphaEnriquece os trechos com bagagem.
JfrSpanProcessorio.opentelemetry.contrib:opentelemetry-jfr-events:1.45.0-alphaCria eventos JFR a partir de trechos.
StackTraceSpanProcessorio.opentelemetry.contrib:opentelemetry-span-stacktrace:1.45.0-alphaEnriquece trechos selecionados com dados de stack trace.
InferredSpansProcessorio.opentelemetry.contrib:opentelemetry-inferred-spans:1.45.0-alphaGera trechos a partir do profiler assíncrono em vez de instrumentação.

O trecho de código a seguir demonstra a configuração programática do SpanProcessor

package otel;

import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.time.Duration;

public class SpanProcessorConfig {
  public static SpanProcessor batchSpanProcessor(SpanExporter spanExporter) {
    return BatchSpanProcessor.builder(spanExporter)
        .setMaxQueueSize(2048)
        .setExporterTimeout(Duration.ofSeconds(30))
        .setScheduleDelay(Duration.ofSeconds(5))
        .build();
  }

  public static SpanProcessor simpleSpanProcessor(SpanExporter spanExporter) {
    return SimpleSpanProcessor.builder(spanExporter).build();
  }
}

Implementando a interface do SpanProcessor para fornecer seu próprio processamento lógico de trecho. Por exemplo:

package otel;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;

public class CustomSpanProcessor implements SpanProcessor {

  @Override
  public void onStart(Context parentContext, ReadWriteSpan span) {
    // Callback invoked when span is started.
    // Enrich the record with a custom attribute.
    span.setAttribute("my.custom.attribute", "hello world");
  }

  @Override
  public boolean isStartRequired() {
    // Indicate if onStart should be called.
    return true;
  }

  @Override
  public void onEnd(ReadableSpan span) {
    // Callback invoked when span is ended.
  }

  @Override
  public boolean isEndRequired() {
    // Indicate if onEnd should be called.
    return false;
  }

  @Override
  public CompletableResultCode shutdown() {
    // Optionally shutdown the processor and cleanup any resources.
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode forceFlush() {
    // Optionally process any records which have been queued up but not yet processed.
    return CompletableResultCode.ofSuccess();
  }
}

SpanExporter

Um SpanExporter é uma interface de extensão para plugins responsável por exportar trechos foras dos processos. Ao invés de registrar diretamente com SdkTracerProvider, eles são associados com SpanProcessors (normalmente BatchSpanProcessor).

Exportadores de trechos integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
OtlpHttpSpanExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta trechos via OTLP http/protobuf.
OtlpGrpcSpanExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta trechos via OTLP grpc.
LoggingSpanExporterio.opentelemetry:opentelemetry-exporter-logging:1.48.0Registra trechos para JUL em um formato de debugging.
OtlpJsonLoggingSpanExporterio.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Registra trechos para JUL em um JSON OTLP codificado.
OtlpStdoutSpanExporterio.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Registra trechos para o System.out em formato OTLP Arquivo JSON Codificado (experimental).
ZipkinSpanExporterio.opentelemetry:opentelemetry-exporter-zipkin:1.48.0Exporta trechos para o Zipkin.
InterceptableSpanExporterio.opentelemetry.contrib:opentelemetry-processors:1.45.0-alphaPasses trechos para um interceptador flexível antes da exportação.
KafkaSpanExporterio.opentelemetry.contrib:opentelemetry-kafka-exporter:1.45.0-alphaExporta trechos escrevendo para um tópico do Kafka.

[1]: Veja OTLP exporters para detalhes de implementação.

O trecho de código a seguir demonstra configuração programática do SpanExporter:

package otel;

import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.time.Duration;

public class SpanExporterConfig {
  public static SpanExporter otlpHttpSpanExporter(String endpoint) {
    return OtlpHttpSpanExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static SpanExporter otlpGrpcSpanExporter(String endpoint) {
    return OtlpGrpcSpanExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static SpanExporter logginSpanExporter() {
    return LoggingSpanExporter.create();
  }

  public static SpanExporter otlpJsonLoggingSpanExporter() {
    return OtlpJsonLoggingSpanExporter.create();
  }
}

Implementando a interface do SpanExporter para fornecer sua própria lógica de exportação de trechos. Por exemplo:

package otel;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CustomSpanExporter implements SpanExporter {

  private static final Logger logger = Logger.getLogger(CustomSpanExporter.class.getName());

  @Override
  public CompletableResultCode export(Collection<SpanData> spans) {
    // Export the records. Typically, records are sent out of process via some network protocol, but
    // we simply log for illustrative purposes.
    logger.log(Level.INFO, "Exporting spans");
    spans.forEach(span -> logger.log(Level.INFO, "Span: " + span));
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode flush() {
    // Export any records which have been queued up but not yet exported.
    logger.log(Level.INFO, "flushing");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode shutdown() {
    // Shutdown the exporter and cleanup any resources.
    logger.log(Level.INFO, "shutting down");
    return CompletableResultCode.ofSuccess();
  }
}

SpanLimits

SpanLimits define restrições para os dados capturados pelos trechos, incluindo o comprimento máximo dos atributos, máximo numero de atributos, entre outros.

O trecho de código a seguir demonstra a configuração programática do SpanLimits:

package otel;

import io.opentelemetry.sdk.trace.SpanLimits;

public class SpanLimitsConfig {
  public static SpanLimits spanLimits() {
    return SpanLimits.builder()
        .setMaxNumberOfAttributes(128)
        .setMaxAttributeValueLength(1024)
        .setMaxNumberOfLinks(128)
        .setMaxNumberOfAttributesPerLink(128)
        .setMaxNumberOfEvents(128)
        .setMaxNumberOfAttributesPerEvent(128)
        .build();
  }
}

SdkMeterProvider

SdkMeterProvider é a implementação do SDK MeterProvider, e é responsável por gerenciar a telemetria de métricas produzidas pela API.

SdkMeterProvider é configurado pelo responsável pela aplicação, e consiste em:

  • Resource: O recurso ao qual as métricas estão associadas.
  • MetricReader: Lê o estado agregado das métricas.
    • Opcionalmente, com o CardinalityLimitSelector para substituir o limite de cardinalidade por tipo de instrumento. Se não configurado, cada é limitado a 2000 combinações únicas de atributos por ciclo de coleta. Limites de cardinalidade também são configuráveis para instrumentos individuais via views. Consulte limites de cardinalidade para mais detalhes.
  • MetricExporter: Exporta métricas para fora do processo (em conjunto com o MetricReader associado).
  • Views: Configura fluxos de métricas, incluindo a exclusão de métricas não utilizadas.

O trecho de código a seguir demonstra a configuração programática SdkMeterProvider:

package otel;

import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.resources.Resource;
import java.util.List;
import java.util.Set;

public class SdkMeterProviderConfig {
  public static SdkMeterProvider create(Resource resource) {
    SdkMeterProviderBuilder builder =
        SdkMeterProvider.builder()
            .setResource(resource)
            .registerMetricReader(
                MetricReaderConfig.periodicMetricReader(
                    MetricExporterConfig.otlpHttpMetricExporter(
                        "http://localhost:4318/v1/metrics")));
    // Descomente para opcionalmente registrar um leitor de métrica com limites de cardinalidade.
    // builder.registerMetricReader(
    //     MetricReaderConfig.periodicMetricReader(
    //         MetricExporterConfig.otlpHttpMetricExporter("http://localhost:4318/v1/metrics")),
    //     instrumentType -> 100);

    ViewConfig.dropMetricView(builder, "some.custom.metric");
    ViewConfig.histogramBucketBoundariesView(
        builder, "http.server.request.duration", List.of(1.0, 5.0, 10.0));
    ViewConfig.attributeFilterView(
        builder, "http.client.request.duration", Set.of("http.request.method"));
    ViewConfig.cardinalityLimitsView(builder, "http.server.active_requests", 100);
    return builder.build();
  }
}

MetricReader

O MetricReader é uma interface de extensão para plugins que é responsável por ler as métricas agregadas. Eles são muitas vezes associadas com MetricExporters para exportar métricas fora do processo, mas pode também ser usada para servir métricas para scrapers externos em protocolos baseados em pull.

Metric readers integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
PeriodicMetricReaderio.opentelemetry:opentelemetry-sdk:1.48.0Lê as métricas em uma base periódica e os exporta por meio de uma configuração do MetricExporter.
PrometheusHttpServerio.opentelemetry:opentelemetry-exporter-prometheus:1.48.0-alphaDisponibiliza métricas em um servidor HTTP em vários formatos Prometheus.

O trecho de código a seguir demonstra a configuração programática do MetricReader:

package otel;

import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import java.time.Duration;

public class MetricReaderConfig {
  public static MetricReader periodicMetricReader(MetricExporter metricExporter) {
    return PeriodicMetricReader.builder(metricExporter).setInterval(Duration.ofSeconds(60)).build();
  }

  public static MetricReader prometheusMetricReader() {
    return PrometheusHttpServer.builder().setHost("localhost").setPort(9464).build();
  }
}

Implementa a interface do MetricReader para fornecer sua própria lógica de leitura de métricas. Por exemplo:

package otel;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CustomMetricReader implements MetricReader {

  private static final Logger logger = Logger.getLogger(CustomMetricExporter.class.getName());

  private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
  private final AtomicReference<CollectionRegistration> collectionRef =
      new AtomicReference<>(CollectionRegistration.noop());

  @Override
  public void register(CollectionRegistration collectionRegistration) {
    // Callback invoked when SdkMeterProvider is initialized, providing a handle to collect metrics.
    collectionRef.set(collectionRegistration);
    executorService.scheduleWithFixedDelay(this::collectMetrics, 0, 60, TimeUnit.SECONDS);
  }

  private void collectMetrics() {
    // Collect metrics. Typically, records are sent out of process via some network protocol, but we
    // simply log for illustrative purposes.
    logger.log(Level.INFO, "Collecting metrics");
    collectionRef
        .get()
        .collectAllMetrics()
        .forEach(metric -> logger.log(Level.INFO, "Metric: " + metric));
  }

  @Override
  public CompletableResultCode forceFlush() {
    // Export any records which have been queued up but not yet exported.
    logger.log(Level.INFO, "flushing");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode shutdown() {
    // Shutdown the exporter and cleanup any resources.
    logger.log(Level.INFO, "shutting down");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) {
    // Specify the required aggregation temporality as a function of instrument type
    return AggregationTemporalitySelector.deltaPreferred()
        .getAggregationTemporality(instrumentType);
  }

  @Override
  public MemoryMode getMemoryMode() {
    // Optionally specify the memory mode, indicating whether metric records can be reused or must
    // be immutable
    return MemoryMode.REUSABLE_DATA;
  }

  @Override
  public Aggregation getDefaultAggregation(InstrumentType instrumentType) {
    // Optionally specify the default aggregation as a function of instrument kind
    return Aggregation.defaultAggregation();
  }
}

MetricExporter

A MetricExporter é uma interface de extensão para plugins responsável por exportar métricas fora do processo. Ao invés de registrar diretamente com SdkMeterProvider, eles são associados com o PeriodicMetricReader.

Metric exporters integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
OtlpHttpMetricExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta métricas via OTLP http/protobuf.
OtlpGrpcMetricExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta métricas via OTLP grpc.
LoggingMetricExporterio.opentelemetry:opentelemetry-exporter-logging:1.48.0Registra métricas para JUL em um formato debugging.
OtlpJsonLoggingMetricExporterio.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Registra métricas para JUL em um JSON OTLP codificado.
OtlpStdoutMetricExporterio.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Registra métricas para System.out em formato OTLP Arquivo JSON Codificado (experimental).
InterceptableMetricExporterio.opentelemetry.contrib:opentelemetry-processors:1.45.0-alphaPasses métricas para um interceptador flexível antes da exportação.

[1]: Veja OTLP exporters para detalhes de implementação.

O trecho de código a seguir demonstra a configuração programática do MetricExporter:

package otel;

import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.time.Duration;

public class MetricExporterConfig {
  public static MetricExporter otlpHttpMetricExporter(String endpoint) {
    return OtlpHttpMetricExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static MetricExporter otlpGrpcMetricExporter(String endpoint) {
    return OtlpGrpcMetricExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static MetricExporter logginMetricExporter() {
    return LoggingMetricExporter.create();
  }

  public static MetricExporter otlpJsonLoggingMetricExporter() {
    return OtlpJsonLoggingMetricExporter.create();
  }
}

Implementa a interface do MetricExporter para fornecer sua própria lógica de exportação de métricas. Por exemplo:

package otel;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.MemoryMode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CustomMetricExporter implements MetricExporter {

  private static final Logger logger = Logger.getLogger(CustomMetricExporter.class.getName());

  @Override
  public CompletableResultCode export(Collection<MetricData> metrics) {
    // Export the records. Typically, records are sent out of process via some network protocol, but
    // we simply log for illustrative purposes.
    logger.log(Level.INFO, "Exporting metrics");
    metrics.forEach(metric -> logger.log(Level.INFO, "Metric: " + metric));
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode flush() {
    // Export any records which have been queued up but not yet exported.
    logger.log(Level.INFO, "flushing");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode shutdown() {
    // Shutdown the exporter and cleanup any resources.
    logger.log(Level.INFO, "shutting down");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) {
    // Specify the required aggregation temporality as a function of instrument type
    return AggregationTemporalitySelector.deltaPreferred()
        .getAggregationTemporality(instrumentType);
  }

  @Override
  public MemoryMode getMemoryMode() {
    // Optionally specify the memory mode, indicating whether metric records can be reused or must
    // be immutable
    return MemoryMode.REUSABLE_DATA;
  }

  @Override
  public Aggregation getDefaultAggregation(InstrumentType instrumentType) {
    // Optionally specify the default aggregation as a function of instrument kind
    return Aggregation.defaultAggregation();
  }
}

Views

Views permitem que os fluxos de métricas sejam personalizados, incluindo a alteração de nomes de métricas, descrições de métricas, agregações de métricas (ou seja, limites de intervalos de histograma), o conjunto de chaves de atributos a serem mantidos, limite de cardinalidade, etc.

O trecho de código a seguir demonstra a configuração programática do View:

package otel;

import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.View;
import java.util.List;
import java.util.Set;

public class ViewConfig {
  public static SdkMeterProviderBuilder dropMetricView(
      SdkMeterProviderBuilder builder, String metricName) {
    return builder.registerView(
        InstrumentSelector.builder().setName(metricName).build(),
        View.builder().setAggregation(Aggregation.drop()).build());
  }

  public static SdkMeterProviderBuilder histogramBucketBoundariesView(
      SdkMeterProviderBuilder builder, String metricName, List<Double> bucketBoundaries) {
    return builder.registerView(
        InstrumentSelector.builder().setName(metricName).build(),
        View.builder()
            .setAggregation(Aggregation.explicitBucketHistogram(bucketBoundaries))
            .build());
  }

  public static SdkMeterProviderBuilder attributeFilterView(
      SdkMeterProviderBuilder builder, String metricName, Set<String> keysToRetain) {
    return builder.registerView(
        InstrumentSelector.builder().setName(metricName).build(),
        View.builder().setAttributeFilter(keysToRetain).build());
  }

  public static SdkMeterProviderBuilder cardinalityLimitsView(
      SdkMeterProviderBuilder builder, String metricName, int cardinalityLimit) {
    return builder.registerView(
        InstrumentSelector.builder().setName(metricName).build(),
        View.builder().setCardinalityLimit(cardinalityLimit).build());
  }
}

SdkLoggerProvider

SdkLoggerProvider é a implementação do SDK do LoggerProvider, e é responsável por gerenciar a telemetria dos logs produzidos pela API.

SdkLoggerProvider é configurado pelo responsável da aplicação, e consiste em:

  • Resource: O recurso ao qual os logs estão associados.
  • LogRecordProcessor: Processa os logs quando eles são emitidos.
  • LogRecordExporter: Exporta logs fora do processo (em conjunto com os LogRecordProcessor associados).
  • LogLimits: Controla os limites dos dados associados com os logs.

O trecho de código a seguir demonstra a configuração programática do SdkLoggerProvider:

package otel;

import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.resources.Resource;

public class SdkLoggerProviderConfig {
  public static SdkLoggerProvider create(Resource resource) {
    return SdkLoggerProvider.builder()
        .setResource(resource)
        .addLogRecordProcessor(
            LogRecordProcessorConfig.batchLogRecordProcessor(
                LogRecordExporterConfig.otlpHttpLogRecordExporter("http://localhost:4318/v1/logs")))
        .setLogLimits(LogLimitsConfig::logLimits)
        .build();
  }
}

LogRecordProcessor

A LogRecordProcessor é uma interface de extensão para plugins com um callback invocado quando um log é emitido. Eles são frequentemente associados com LogRecordExporters para exportar logs fora do processo, mas tem outras aplicações, como enriquecimento de dados.

Os processadores de registros de log integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
BatchLogRecordProcessorio.opentelemetry:opentelemetry-sdk:1.48.0Agrupa os registros de log e os exporta por meio de um LogRecordExporter configurável.
SimpleLogRecordProcessorio.opentelemetry:opentelemetry-sdk:1.48.0Exporta cada registro de log a via de um LogRecordExporter configurável.
EventToSpanEventBridgeio.opentelemetry.contrib:opentelemetry-processors:1.45.0-alphaRegistra os eventos de log como eventos de trecho no trecho atual.

O trecho de código a seguir demonstra a configuração programática do LogRecordProcessor:

package otel;

import io.opentelemetry.sdk.logs.LogRecordProcessor;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import java.time.Duration;

public class LogRecordProcessorConfig {
  public static LogRecordProcessor batchLogRecordProcessor(LogRecordExporter logRecordExporter) {
    return BatchLogRecordProcessor.builder(logRecordExporter)
        .setMaxQueueSize(2048)
        .setExporterTimeout(Duration.ofSeconds(30))
        .setScheduleDelay(Duration.ofSeconds(1))
        .build();
  }

  public static LogRecordProcessor simpleLogRecordProcessor(LogRecordExporter logRecordExporter) {
    return SimpleLogRecordProcessor.create(logRecordExporter);
  }
}

Implementando a interface do LogRecordProcessor para fornecer sua própria lógica personalizada de processamento de logs. Por exemplo:

package otel;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.LogRecordProcessor;
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;

public class CustomLogRecordProcessor implements LogRecordProcessor {

  @Override
  public void onEmit(Context context, ReadWriteLogRecord logRecord) {
    // Callback invoked when log record is emitted.
    // Enrich the record with a custom attribute.
    logRecord.setAttribute(AttributeKey.stringKey("my.custom.attribute"), "hello world");
  }

  @Override
  public CompletableResultCode shutdown() {
    // Optionally shutdown the processor and cleanup any resources.
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode forceFlush() {
    // Optionally process any records which have been queued up but not yet processed.
    return CompletableResultCode.ofSuccess();
  }
}

LogRecordExporter

Um LogRecordExporter é uma interface de extensão para plugins responsável por exportar registros de logs fora do processo. Ao invés de registrar diretamente com SdkLoggerProvider, eles podem ser associados ao LogRecordProcessors (tipicamente BatchLogRecordProcessor).

Span exporters integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
OtlpHttpLogRecordExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta registros de logs via OTLP http/protobuf.
OtlpGrpcLogRecordExporter [1]io.opentelemetry:opentelemetry-exporter-otlp:1.48.0Exporta registros de logs via OTLP grpc.
SystemOutLogRecordExporterio.opentelemetry:opentelemetry-exporter-logging:1.48.0Grava registros de logs para system out em um formato de debugging.
OtlpJsonLoggingLogRecordExporter [2]io.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Grava registros de logs para JUL em um JSON OTLP codificado.
OtlpStdoutLogRecordExporterio.opentelemetry:opentelemetry-exporter-logging-otlp:1.48.0Grava registros de logs para System.out em OTLP Arquivo JSON Codificado (experimental).
InterceptableLogRecordExporterio.opentelemetry.contrib:opentelemetry-processors:1.45.0-alphaPassa registros de logs para um interceptador flexível antes de exportar.

[1]: Veja Exportadores OTLP para detalhes de implementação.

[2]: OtlpJsonLoggingLogRecordExporter registra no JUL e pode causar loops infinitos (ex. JUL -> SLF4J -> Logback -> OpenTelemetry Appender -> OpenTelemetry Log SDK -> JUL) se não for configurado com cuidado.

O trecho de código a seguir demonstra a configuração programática do LogRecordProcessor:

package otel;

import io.opentelemetry.exporter.logging.SystemOutLogRecordExporter;
import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import java.time.Duration;

public class LogRecordExporterConfig {
  public static LogRecordExporter otlpHttpLogRecordExporter(String endpoint) {
    return OtlpHttpLogRecordExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static LogRecordExporter otlpGrpcLogRecordExporter(String endpoint) {
    return OtlpGrpcLogRecordExporter.builder()
        .setEndpoint(endpoint)
        .addHeader("api-key", "value")
        .setTimeout(Duration.ofSeconds(10))
        .build();
  }

  public static LogRecordExporter systemOutLogRecordExporter() {
    return SystemOutLogRecordExporter.create();
  }

  public static LogRecordExporter otlpJsonLoggingLogRecordExporter() {
    return OtlpJsonLoggingLogRecordExporter.create();
  }
}

Implementando a interface do LogRecordExporter para fornecer sua própria lógica personalizada de exportação de registros. Por exemplo:

package otel;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CustomLogRecordExporter implements LogRecordExporter {

  private static final Logger logger = Logger.getLogger(CustomLogRecordExporter.class.getName());

  @Override
  public CompletableResultCode export(Collection<LogRecordData> logs) {
    // Export the records. Typically, records are sent out of process via some network protocol, but
    // we simply log for illustrative purposes.
    System.out.println("Exporting logs");
    logs.forEach(log -> System.out.println("log record: " + log));
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode flush() {
    // Export any records which have been queued up but not yet exported.
    logger.log(Level.INFO, "flushing");
    return CompletableResultCode.ofSuccess();
  }

  @Override
  public CompletableResultCode shutdown() {
    // Shutdown the exporter and cleanup any resources.
    logger.log(Level.INFO, "shutting down");
    return CompletableResultCode.ofSuccess();
  }
}

LogLimits

LogLimits define restrições para os dados capturados pelos registros de logs, incluindo o comprimento máximo dos atributos, e número máximo de atributos.

O trecho de código a seguir demonstra a configuração programática do LogRecordProcessor:

package otel;

import io.opentelemetry.sdk.logs.LogLimits;

public class LogLimitsConfig {
  public static LogLimits logLimits() {
    return LogLimits.builder()
        .setMaxNumberOfAttributes(128)
        .setMaxAttributeValueLength(1024)
        .build();
  }
}

TextMapPropagator

TextMapPropagator é uma interface de extensão para plugins responsável por propagar o contexto através dos processos conectados em um formato de texto.

TextMapPropagators integrados ao SDK e mantidos pela comunidade em opentelemetry-java-contrib:

ClasseArtefatoDescrição
W3CTraceContextPropagatorio.opentelemetry:opentelemetry-api:1.48.0Propaga contextos de rastros usando W3C contextos de rastros protocolo de propagação.
W3CBaggagePropagatorio.opentelemetry:opentelemetry-api:1.48.0Propaga baggage usando W3C baggage protocolo de propagação.
MultiTextMapPropagatorio.opentelemetry:opentelemetry-context:1.48.0Compor múltiplos propagadores.
JaegerPropagatorio.opentelemetry:opentelemetry-extension-trace-propagators:1.48.0Propagador de contextos de rastros usando o protocolo de propagação do Jaeger.
B3Propagatorio.opentelemetry:opentelemetry-extension-trace-propagators:1.48.0Propagador de contextos de rastros usando o protocolo de propagação B3.
OtTracePropagatorio.opentelemetry:opentelemetry-extension-trace-propagators:1.48.0Propagador de contextos de rastros usando o protocolo de propagação do OpenTracing.
PassThroughPropagatorio.opentelemetry:opentelemetry-api-incubator:1.48.0-alphaPropaga um conjunto configurável de campos sem participar da telemetria.
AwsXrayPropagatorio.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.45.0-alphaPropaga contextos de rastros usando o protocolo de propagação do AWS X-Ray.
AwsXrayLambdaPropagatorio.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.45.0-alphaPropaga contextos de rastros usando variáveis de ambiente e o protocolo de propagação do AWS X-Ray.

O trecho de código a seguir demonstra a configuração programática do TextMapPropagator:

package otel;

import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;

public class ContextPropagatorsConfig {
  public static ContextPropagators create() {
    return ContextPropagators.create(
        TextMapPropagator.composite(
            W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance()));
  }
}

Implementando a interface do TextMapPropagator para fornecer sua própria lógica de propagação. Por exemplo:

package otel;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;
import java.util.Collection;
import java.util.Collections;

public class CustomTextMapPropagator implements TextMapPropagator {

  @Override
  public Collection<String> fields() {
    // Return fields used for propagation. See W3CTraceContextPropagator for reference
    // implementation.
    return Collections.emptyList();
  }

  @Override
  public <C> void inject(Context context, C carrier, TextMapSetter<C> setter) {
    // Inject context. See W3CTraceContextPropagator for reference implementation.
  }

  @Override
  public <C> Context extract(Context context, C carrier, TextMapGetter<C> getter) {
    // Extract context. See W3CTraceContextPropagator for reference implementation.
    return context;
  }
}

Apêndice

Registro interno

Os componentes do SDK registram uma variedade de informações no java.util.logging, em diferentes níveis de log e usando nomes de loggers baseados no nome totalmente qualificado da classe Fully Qualified Class Name - FQDN do respectivo componente.

Por padrão, as mensagens de log são gerenciados pelo manipulador raíz na sua aplicação. Se você não tem um manipulador raíz personalizado para sua aplicação, logs de nível INFO ou superior serão enviados ao console por padrão.

Você pode querer mudar o comportamento do logger para OpenTelemetry. Por exemplo, você pode reduzir o nível de logging para fornecer informações adicionais quando estiver em modo debugging, aumentar o nível para uma classe particular para ignorar erros originados de uma classe, ou instalar um comportamento personalizado ou filtrar para executar um código personalizado sempre que o OpenTelemetry registrar uma mensagem específica. Não há uma lista detalhada de nomes de loggers e informações de log mantida. No entanto, todas as APIs, SDK, contribuições e componentes de instrumentação compartilham o mesmo prefixo do pacote io.opentelemetry.*. Isso pode ser útil para ativar logs mais detalhados para todos os io.opentelemetry.*, inspecionar a saída, e restringir aos pacotes ou FQDNs de interesse.

Por exemplo:

## Desligar todos os loggins do OpenTelemetry
io.opentelemetry.level = OFF
## Desligar logging somente para o BatchSpanProcessor
io.opentelemetry.sdk.trace.export.BatchSpanProcessor.level = OFF
## Registrar mensagens "FINE" para ajudar no *debugging*
io.opentelemetry.level = FINE

## Sets o padrão ConsoleHandler's nível de loggers
## Observe o impacto do logging fora do OpenTelemetry também
java.util.logging.ConsoleHandler.level = FINE

Para um controle mais detalhado e tratamento de casos especiais, manipuladores e filtros personalizados podem ser especificados com código.

// Custom filter which does not log errors which come from the export
public class IgnoreExportErrorsFilter implements java.util.logging.Filter {

 public boolean isLoggable(LogRecord record) {
    return !record.getMessage().contains("Exception thrown by the export");
 }
}
## Registrando o filtro personalizado no BatchSpanProcessor
io.opentelemetry.sdk.trace.export.BatchSpanProcessor = io.opentelemetry.extension.logging.IgnoreExportErrorsFilter

Exportadores OTLP

As seções span exporter, metric exporter, e log exporter descrevem os exportadores OTLP do tipo:

  • OtlpHttp{Signal}Exporter, que exporta dados via OTLP http/protobuf
  • OtlpGrpc{Signal}Exporter, que exporta dados via OTLP grpc

Os exportadores para todos os sinais estão disponíveis via io.opentelemetry:opentelemetry-exporter-otlp:1.48.0, e possuem uma sobreposição significativa entre as versões grpc and http/protobuf do protocolo OTLP, e entre os sinais. As seções a seguir detalham esses conceitos chave:

  • Emissores: Uma abstração para diferentes bibliotecas do lado do cliente HTTP / gRPC.
  • Autenticação opções para exportadores OTLP.

Emissores

Um exportador OTLP depende de várias bibliotecas do lado do cliente para executar requisições HTTP e gRPC. Não existe uma única biblioteca de cliente HTTP / gRPC que atenda a todos os casos de uso no ecossistema Java:

  • Java 11+ traz o java.net.http.HttpClient integrado, mas o opentelemetry-java precisa oferecer suporte a usuários com o Java 8+, e esse cliente não pode ser usado para exportação via gRPC devido a ausência de suporte para cabeçalhos de trailer.
  • OkHttp fornece um cliente HTTP poderoso com suporte para cabeçalhos de trailer, mas depende da biblioteca padrão do Kotlin.
  • grpc-java oferece sua própria abstração ManagedChannel com várias implementações de transporte, mas não é adequado para o protocolo http/protobuf.

Para atender diversos casos de uso, opentelemetry-exporter-otlp usa uma abstração interna de envio (sender), com uma variedade de implementações para refletir as restrições da aplicação. Para usar outra implementação, exclua a dependência padrão io.opentelemetry:opentelemetry-exporter-sender-okhttp, e adicione a dependência alternativa.

ArtefatoDescriçãoProtocolos OTLPPadrão
io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.48.0Implementação baseada em OkHttp.grpc, http/protobufYes
io.opentelemetry:opentelemetry-exporter-sender-jdk:1.48.0Implementação baseada em java.net.http.HttpClient do Java 11+.http/protobufNo
io.opentelemetry:opentelemetry-exporter-sender-grpc-managed-channel:1.48.0 [1]Implementação baseada em ManagedChannel do grpc-javagrpcNo

[1]: Para usar opentelemetry-exporter-sender-grpc-managed-channel, você precisa adicionar uma dependência em Implementação de Transporte gRPC.

Autenticação

O exportador OTLP provê mecanismos para autenticação baseada em cabeçalho estático e dinâmico, e para mTLS.

Se você usa Autoconfiguração sem código do com variáveis de ambientes e propriedades do sistema, veja propriedades relevantes do sistema:

  • otel.exporter.otlp.headers autenticação baseada em cabeçalho estático.
  • otel.exporter.otlp.client.key, otel.exporter.otlp.client.certificate para autenticação mTLS.

O trecho de código a seguir demonstra a a configuração programática da autenticação baseada em cabeçalho estático e dinâmico:

package otel;

import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.function.Supplier;

public class OtlpAuthenticationConfig {
  public static void staticAuthenticationHeader(String endpoint) {
    // If the OTLP destination accepts a static, long-lived authentication header like an API key,
    // set it as a header.
    // This reads the API key from the OTLP_API_KEY env var to avoid hard coding the secret in
    // source code.
    String apiKeyHeaderName = "api-key";
    String apiKeyHeaderValue = System.getenv("OTLP_API_KEY");

    // Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
    OtlpHttpSpanExporter spanExporter =
        OtlpHttpSpanExporter.builder()
            .setEndpoint(endpoint)
            .addHeader(apiKeyHeaderName, apiKeyHeaderValue)
            .build();
    OtlpHttpMetricExporter metricExporter =
        OtlpHttpMetricExporter.builder()
            .setEndpoint(endpoint)
            .addHeader(apiKeyHeaderName, apiKeyHeaderValue)
            .build();
    OtlpHttpLogRecordExporter logRecordExporter =
        OtlpHttpLogRecordExporter.builder()
            .setEndpoint(endpoint)
            .addHeader(apiKeyHeaderName, apiKeyHeaderValue)
            .build();
  }

  public static void dynamicAuthenticationHeader(String endpoint) {
    // If the OTLP destination requires a dynamic authentication header, such as a JWT which needs
    // to be periodically refreshed, use a header supplier.
    // Here we implement a simple supplier which adds a header of the form "Authorization: Bearer
    // <token>", where <token> is fetched from refreshBearerToken every 10 minutes.
    String username = System.getenv("OTLP_USERNAME");
    String password = System.getenv("OTLP_PASSWORD");
    Supplier<Map<String, String>> supplier =
        new AuthHeaderSupplier(() -> refreshToken(username, password), Duration.ofMinutes(10));

    // Initialize OTLP Span, Metric, and LogRecord exporters using a similar pattern
    OtlpHttpSpanExporter spanExporter =
        OtlpHttpSpanExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
    OtlpHttpMetricExporter metricExporter =
        OtlpHttpMetricExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
    OtlpHttpLogRecordExporter logRecordExporter =
        OtlpHttpLogRecordExporter.builder().setEndpoint(endpoint).setHeaders(supplier).build();
  }

  private static class AuthHeaderSupplier implements Supplier<Map<String, String>> {
    private final Supplier<String> tokenRefresher;
    private final Duration tokenRefreshInterval;
    private Instant refreshedAt = Instant.ofEpochMilli(0);
    private String currentTokenValue;

    private AuthHeaderSupplier(Supplier<String> tokenRefresher, Duration tokenRefreshInterval) {
      this.tokenRefresher = tokenRefresher;
      this.tokenRefreshInterval = tokenRefreshInterval;
    }

    @Override
    public Map<String, String> get() {
      return Collections.singletonMap("Authorization", "Bearer " + getToken());
    }

    private synchronized String getToken() {
      Instant now = Instant.now();
      if (currentTokenValue == null || now.isAfter(refreshedAt.plus(tokenRefreshInterval))) {
        currentTokenValue = tokenRefresher.get();
        refreshedAt = now;
      }
      return currentTokenValue;
    }
  }

  private static String refreshToken(String username, String password) {
    // For a production scenario, this would be replaced with an out-of-band request to exchange
    // username / password for bearer token.
    return "abc123";
  }
}

Testando


Last modified February 25, 2025: running npm run fix:format (cd485f7a)