Registrar telemetria com a API

A API é um conjunto de classes e interfaces para registar telemetria através dos sinais chaves de observabilidade. O SDK é uma referência integrada da implementação da API, configuração para processar e exportar telemetria. Essa página é uma visão geral de conceitos da API, incluindo descrições, links para documentações relevantes (Javadocs), coordenadas de artefato, e exemplos de uso da API.

A API consiste nos seguintes componentes principais:

  • Contexto: Um padrão de API para propagação de contexto através de uma aplicação e entre limites de aplicações, incluindo contexto de rastros e bagagem.
  • TracerProvider: O ponto de entrada para a API de traços.
  • MeterProvider: O ponto de entrada para a API de métricas.
  • LoggerProvider: O ponto de entrada para a API de logs.
  • OpenTelemetry: Uma estrutura para componentes principais da API (ou seja, TracerProvider, MeterProvider, LoggerProvider, ContextPropagators) que é conveniente passar para a instrumentação.

A API é desenvolvida para suportar múltiplos ambientes. Duas implementações são providenciadas pelo OpenTelemetry:

  • SDK é uma referência integrada de implementação da API. É a escolha correta para a maioria dos usuários.
  • Implementação Noop. Uma implementação minimalista, sem dependências para instrumentações usarem por padrão quando o usuário não instala uma instância.

A API é desenvolvida ser adotado como dependência direta por bibliotecas, frameworks, e responsáveis pela aplicação. Isto inicializa com fortes garantias de compatibilidade retroativa, sem dependências transitivas, e suporta Java 8+. Bibliotecas e frameworks devem depender somente da API e somente chamar métodos da API, e orientar aplicações / usuários finais para adicionar uma dependência do SDK e instalarem uma instância configurada.

Componentes da API

A seção a seguir descreve a API do OpenTelemetry. Cada seção de componente inclue:

  • Uma breve descrição, incluindo um link para a referência do tipo na Javadoc.
  • Links para recursos relevantes para compreender os métodos e argumentos da API.
  • Uma exploração simples do uso da API.

Contexto da API

O artefato io.opentelemetry:opentelemetry-api-context:1.51.0 contém um padrão de APIs (ou seja, distribuído separadamente da API do OpenTelemetry) para propagação de contexto através de uma aplicação e entre limites de aplicações.

Isso consiste em:

  • Contexto: é um conjunto imutável de pares chave-valor, com utilitários para propagação implícita ou explícita através da aplicação.
  • Armazenamento de Contexto: Um mecanismo para armazenar e receber o contexto atual, assumindo por padrão o contexto local da thread.
  • ContextPropagators: Uma estrutura com os propagadores registrados para propagar o Contexto entre os limites da aplicação.

A extensão io.opentelemetry:opentelemetry-extension-kotlint:1.51.0 é um conjunto de ferramentas para propagação de contexto em programações assíncronas e concorrentes (coroutines).

Contexto

Contexto é um conjunto imutável de pares chave-valor, com utilitários para propagação implícita dentro de uma aplicação e entre limites da aplicação (threads). Propagação implícita significa que o contexto pode ser acessado sem precisar passá-lo explicitamente como um argumento. Contexto é um conceito recorrente na API do OpenTelemetry:

  • Os trechos ativos atualmente são armazenados no contexto, e por padrão um trecho pai é atribuído para qualquer trecho ativo no contexto atual.
  • As medições registradas em instrumentos de métricas aceitam um argumento de contexto, usado para vincular as medições a trechos por meio de exemplos e por padrão utilizam o trecho que estiver ativo no contexto atual.
  • LogRecords aceitam um argumento de contexto, usado para vincular registros de log a trechos, e por padrão, utilizam qualquer trecho ativo no contexto atual.

O trecho de código a seguir explora o uso da API Contexto:

package otel;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.Scope;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ContextUsage {
  public static void contextUsage() throws Exception {
    // Define an example context key
    ContextKey<String> exampleContextKey = ContextKey.named("example-context-key");

    // Context doesn't contain the key until we add it
    // Context.current() accesses the current context
    // output => current context value: null
    System.out.println("current context value: " + Context.current().get(exampleContextKey));

    // Add entry to context
    Context context = Context.current().with(exampleContextKey, "value");

    // The local context var contains the added value
    // output => context value: value
    System.out.println("context value: " + context.get(exampleContextKey));
    // The current context still doesn't contain the value
    // output => current context value: null
    System.out.println("current context value: " + Context.current().get(exampleContextKey));

    // Calling context.makeCurrent() sets Context.current() to the context until the scope is
    // closed, upon which Context.current() is restored to the state prior to when
    // context.makeCurrent() was called. The resulting Scope implements AutoCloseable and is
    // normally used in a try-with-resources block. Failure to call Scope.close() is an error and
    // may cause memory leaks or other issues.
    try (Scope scope = context.makeCurrent()) {
      // The current context now contains the added value
      // output => context value: value
      System.out.println("context value: " + Context.current().get(exampleContextKey));
    }

    // The local context var still contains the added value
    // output => context value: value
    System.out.println("context value: " + context.get(exampleContextKey));
    // The current context no longer contains the value
    // output => current context value: null
    System.out.println("current context value: " + Context.current().get(exampleContextKey));

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    // Context instances can be explicitly passed around application code, but it's more convenient
    // to use implicit context, calling Context.makeCurrent() and accessing via Context.current().
    // Context provides a number of utilities for implicit context propagation. These utilities wrap
    // utility classes like Scheduler, ExecutorService, ScheduledExecutorService, Runnable,
    // Callable, Consumer, Supplier, Function, etc and modify their behavior to call
    // Context.makeCurrent() before running.
    context.wrap(ContextUsage::callable).call();
    context.wrap(ContextUsage::runnable).run();
    context.wrap(executorService).submit(ContextUsage::runnable);
    context.wrap(scheduledExecutorService).schedule(ContextUsage::runnable, 1, TimeUnit.SECONDS);
    context.wrapConsumer(ContextUsage::consumer).accept(new Object());
    context.wrapConsumer(ContextUsage::biConsumer).accept(new Object(), new Object());
    context.wrapFunction(ContextUsage::function).apply(new Object());
    context.wrapSupplier(ContextUsage::supplier).get();
  }

  /** Exemplo {@link java.util.concurrent.Callable}. */
  private static Object callable() {
    return new Object();
  }

  /** Exemplo {@link Runnable}. */
  private static void runnable() {}

  /** Exemplo {@link java.util.function.Consumer}. */
  private static void consumer(Object object) {}

  /** Exemplo {@link java.util.function.BiConsumer}. */
  private static void biConsumer(Object object1, Object object2) {}

  /** Exemplo {@link java.util.function.Function}. */
  private static Object function(Object object) {
    return object;
  }

  /** Exemplo {@link java.util.function.Supplier}. */
  private static Object supplier() {
    return new Object();
  }
}

Armazenamento de contexto

ContextStorage é um mecanismo para armazenar and retrieving o Contexto atual.

A implementação padrão ContextStorage armazena Contexto em uma thread local.

Propagação de Contexto

Propagação de contexto é uma estrutura de propagadores registrados para propagar o Contexto através dos limites da aplicação. O contexto é injetado em um portador carrier ao sair da aplicação (ou seja, em uma requisição HTTP de saída), e extraído de um portador carrier ao entrar em uma aplicação (ou seja, ao atender uma requisição HTTP).

Veja SDK TextMapPropagators para implementação de propagadores.

O trecho de código a seguir explora a injeção da API ContextPropagators:

package otel;

import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class InjectContextUsage {
  private static final TextMapSetter<HttpRequest.Builder> TEXT_MAP_SETTER = new HttpRequestSetter();

  public static void injectContextUsage() throws Exception {
    // Create a ContextPropagators instance which propagates w3c trace context and w3c baggage
    ContextPropagators propagators =
        ContextPropagators.create(
            TextMapPropagator.composite(
                W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance()));

    // Create an HttpRequest builder
    HttpClient httpClient = HttpClient.newBuilder().build();
    HttpRequest.Builder requestBuilder =
        HttpRequest.newBuilder().uri(new URI("http://127.0.0.1:8080/resource")).GET();

    // Given a ContextPropagators instance, inject the current context into the HTTP request carrier
    propagators.getTextMapPropagator().inject(Context.current(), requestBuilder, TEXT_MAP_SETTER);

    // Send the request with the injected context
    httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.discarding());
  }

  /** {@link TextMapSetter} with a {@link HttpRequest.Builder} carrier. */
  private static class HttpRequestSetter implements TextMapSetter<HttpRequest.Builder> {
    @Override
    public void set(HttpRequest.Builder carrier, String key, String value) {
      if (carrier == null) {
        return;
      }
      carrier.setHeader(key, value);
    }
  }
}

O trecho de código a seguir explora o uso da extração da API ContextPropagators:

package otel;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.context.propagation.TextMapSetter;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class ExtractContextUsage {
  private static final TextMapGetter<HttpExchange> TEXT_MAP_GETTER = new HttpRequestGetter();

  public static void extractContextUsage() throws Exception {
    // Create a ContextPropagators instance which propagates w3c trace context and w3c baggage
    ContextPropagators propagators =
        ContextPropagators.create(
            TextMapPropagator.composite(
                W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance()));

    // Create a server, which uses the propagators to extract context from requests
    HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
    server.createContext("/path", new Handler(propagators));
    server.setExecutor(null);
    server.start();
  }

  private static class Handler implements HttpHandler {
    private final ContextPropagators contextPropagators;

    private Handler(ContextPropagators contextPropagators) {
      this.contextPropagators = contextPropagators;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
      // Extract the context from the request and make the context current
      Context extractedContext =
          contextPropagators
              .getTextMapPropagator()
              .extract(Context.current(), exchange, TEXT_MAP_GETTER);
      try (Scope scope = extractedContext.makeCurrent()) {
        // Do work with the extracted context
      } finally {
        String response = "success";
        exchange.sendResponseHeaders(200, response.length());
        OutputStream os = exchange.getResponseBody();
        os.write(response.getBytes(StandardCharsets.UTF_8));
        os.close();
      }
    }
  }

  /** {@link TextMapSetter} with a {@link HttpExchange} carrier. */
  private static class HttpRequestGetter implements TextMapGetter<HttpExchange> {
    @Override
    public Iterable<String> keys(HttpExchange carrier) {
      return carrier.getRequestHeaders().keySet();
    }

    @Override
    public String get(HttpExchange carrier, String key) {
      if (carrier == null) {
        return null;
      }
      List<String> headers = carrier.getRequestHeaders().get(key);
      if (headers == null || headers.isEmpty()) {
        return null;
      }
      return headers.get(0);
    }
  }
}

OpenTelemetry API

O artefato io.opentelemetry:opentelemetry-api:1.51.0 contém a API do OpenTelemetry, incluindo traços, métricas, registros, implementação noop, bagagem, implementação da chave TextMapPropagator, e uma dependência do Contexto da API.

Provedores e Escopos

Provedores e escopos são conceitos recorrentes na API do OpenTelemetry. Um escopo é uma unidade lógica dentro da aplicação que contém telemetrias associadas. Um provedor provê componentes para gravação de telemetria relativo a um escopo particular:

Um escopo é identificado pelo triplo (nome, versão, schemaUrl). Deve-se ter cuidado para garantir que a identidade do escopo é único. Uma abordagem típica é definir o nome do escopo como o nome do pacote ou o nome completo da classe, e definir a versão do escopo como a versão da biblioteca. Se estiver emitindo telemetria para múltiplos sinais (ou seja, métricas e traços), o mesmo escopo deve ser usado. Veja escopo de instrumentação para detalhes.

O trecho de código a seguir explora o uso da API provider and scope:

package otel;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerProvider;

public class ProvidersAndScopes {

  private static final String SCOPE_NAME = "fully.qualified.name";
  private static final String SCOPE_VERSION = "1.0.0";
  private static final String SCOPE_SCHEMA_URL = "https://example";

  public static void providersUsage(OpenTelemetry openTelemetry) {
    // Access providers from an OpenTelemetry instance
    TracerProvider tracerProvider = openTelemetry.getTracerProvider();
    MeterProvider meterProvider = openTelemetry.getMeterProvider();
    // NOTE: LoggerProvider is a special case and should only be used to bridge logs from other
    // logging APIs / frameworks into OpenTelemetry.
    LoggerProvider loggerProvider = openTelemetry.getLogsBridge();

    // Access tracer, meter, logger from providers to record telemetry for a particular scope
    Tracer tracer =
        tracerProvider
            .tracerBuilder(SCOPE_NAME)
            .setInstrumentaçãoVersion(SCOPE_VERSION)
            .setSchemaUrl(SCOPE_SCHEMA_URL)
            .build();
    Meter meter =
        meterProvider
            .meterBuilder(SCOPE_NAME)
            .setInstrumentaçãoVersion(SCOPE_VERSION)
            .setSchemaUrl(SCOPE_SCHEMA_URL)
            .build();
    Logger logger =
        loggerProvider
            .loggerBuilder(SCOPE_NAME)
            .setInstrumentaçãoVersion(SCOPE_VERSION)
            .setSchemaUrl(SCOPE_SCHEMA_URL)
            .build();

    // ...optionally, shorthand versions are available if scope version and schemaUrl aren't
    // available
    tracer = tracerProvider.get(SCOPE_NAME);
    meter = meterProvider.get(SCOPE_NAME);
    logger = loggerProvider.get(SCOPE_NAME);
  }
}

Atributos

Atributos é um conjunto de pares chave-valor que representa os padrões de definição dos atributos. Attributes é um conceito recorrente na API do OpenTelemetry:

Veja atributos de semântica para constantes de atributos geradas pela convenção semântica.

Veja nomenclatura de atributos para orientações sobre nomenclatura de atributos.

O trecho de código a seguir explora o uso da API Attributes:

package otel;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import java.util.Map;

public class AttributesUsage {
  // Establish static constant for attribute keys and reuse to avoid allocations
  private static final AttributeKey<String> SHOP_ID = AttributeKey.stringKey("com.acme.shop.id");
  private static final AttributeKey<String> SHOP_NAME =
      AttributeKey.stringKey("com.acme.shop.name");
  private static final AttributeKey<Long> CUSTOMER_ID =
      AttributeKey.longKey("com.acme.customer.id");
  private static final AttributeKey<String> CUSTOMER_NAME =
      AttributeKey.stringKey("com.acme.customer.name");

  public static void attributesUsage() {
    // Use a varargs initializer and pre-allocated attribute keys. This is the most efficient way to
    // create attributes.
    Attributes attributes =
        Attributes.of(
            SHOP_ID,
            "abc123",
            SHOP_NAME,
            "opentelemetry-demo",
            CUSTOMER_ID,
            123L,
            CUSTOMER_NAME,
            "Jack");

    // ...or use a builder.
    attributes =
        Attributes.builder()
            .put(SHOP_ID, "abc123")
            .put(SHOP_NAME, "opentelemetry-demo")
            .put(CUSTOMER_ID, 123)
            .put(CUSTOMER_NAME, "Jack")
            // Optionally initialize attribute keys on the fly
            .put(AttributeKey.stringKey("com.acme.string-key"), "value")
            .put(AttributeKey.booleanKey("com.acme.bool-key"), true)
            .put(AttributeKey.longKey("com.acme.long-key"), 1L)
            .put(AttributeKey.doubleKey("com.acme.double-key"), 1.1)
            .put(AttributeKey.stringArrayKey("com.acme.string-array-key"), "value1", "value2")
            .put(AttributeKey.booleanArrayKey("come.acme.bool-array-key"), true, false)
            .put(AttributeKey.longArrayKey("come.acme.long-array-key"), 1L, 2L)
            .put(AttributeKey.doubleArrayKey("come.acme.double-array-key"), 1.1, 2.2)
            // Optionally omit initializing AttributeKey
            .put("com.acme.string-key", "value")
            .put("com.acme.bool-key", true)
            .put("come.acme.long-key", 1L)
            .put("come.acme.double-key", 1.1)
            .put("come.acme.string-array-key", "value1", "value2")
            .put("come.acme.bool-array-key", true, false)
            .put("come.acme.long-array-key", 1L, 2L)
            .put("come.acme.double-array-key", 1.1, 2.2)
            .build();

    // Attributes has a variety of methods for manipulating and reading data.
    // Read an attribute key:
    String shopIdValue = attributes.get(SHOP_ID);
    // Inspect size:
    int size = attributes.size();
    boolean isEmpty = attributes.isEmpty();
    // Convert to a map representation:
    Map<AttributeKey<?>, Object> map = attributes.asMap();
    // Iterate through entries, printing each to the template: <key> (<type>): <value>\n
    attributes.forEach(
        (attributeKey, value) ->
            System.out.printf(
                "%s (%s): %s%n", attributeKey.getKey(), attributeKey.getType(), value));
    // Convert to a builder, remove the com.acme.customer.id and any entry whose key starts with
    // com.acme.shop, and build a new instance:
    AttributesBuilder builder = attributes.toBuilder();
    builder.remove(CUSTOMER_ID);
    builder.removeIf(attributeKey -> attributeKey.getKey().startsWith("com.acme.shop"));
    Attributes trimmedAttributes = builder.build();
  }
}

OpenTelemetry

OpenTelemetry é um recipiente para os principais componentes da API, sendo conveniente para ser passado para a instrumentação.

OpenTelemetry é composto por:

O trecho de código a seguir explora o uso da API do OpenTelemetry:

package otel;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators;

public class OpenTelemetryUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void openTelemetryUsage(OpenTelemetry openTelemetry) {
    // Access TracerProvider, MeterProvider, LoggerProvider, ContextPropagators
    TracerProvider tracerProvider = openTelemetry.getTracerProvider();
    MeterProvider meterProvider = openTelemetry.getMeterProvider();
    LoggerProvider loggerProvider = openTelemetry.getLogsBridge();
    ContextPropagators propagators = openTelemetry.getPropagators();
  }
}

GlobalOpenTelemetry

GlobalOpenTelemetry mantém uma instância singleton de OpenTelemetry.

A instrumentação deve evitar usar GlobalOpenTelemetry. Em vez disso, aceita OpenTelemetry como argumento de inicialização e utilizar a implementação Noop como padrão caso nenhuma instância seja fornecida. Há uma exceção a essa regra: a instância do OpenTelemetry instalado pelo Java agent está disponível por meio do GlobalOpenTelemetry. Usuários com instrumentações manuais adicionais são incentivados a acessá-las por GlobalOpenTelemetry.get().

GlobalOpenTelemetry.get() é garantido para sempre retornar o mesmo resultado. Se GlobalOpenTelemetry.get() for chamado antes de GlobalOpenTelemetry.set(..), GlobalOpenTelemetry será configurado com a implementação noop e chamadas futuras para GlobalOpenTelemetry.set(..) vão lançar uma exceção. Por isso, é crítico chamar GlobalOpenTelemetry.set(..) o mais cedo possível no ciclo de vida da aplicação, e antes de qualquer instrumentação chamar GlobalOpenTelemetry.get(). Isso ajuda a identificar problemas de ordem de inicialização: chamar GlobalOpenTelemetry.set() tarde demais (ou seja, depois que a instrumentação GlobalOpenTelemetry.get() ser chamada) gera uma exceção ao invés de falhar silenciosamente.

Se autoinstrumentação sem código estiver presente, GlobalOpenTelemetry pode ser inicializado automaticamente definindo -Dotel.java.global-autoconfigure.enabled=true (ou via variável de ambiente export OTEL_JAVA_GLOBAL_AUTOCONFIGURE_ENABLED=true). Quando ativado, a primeira chamada para GlobalOpenTelemetry.get() aciona a autoconfiguração e chama GlobalOpenTelemetry.set(..) com o resultado da instância OpenTelemetry.

O trecho de código a seguir explora o uso da propagação da API GlobalOpenTelemetry:

package otel;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;

public class GlobalOpenTelemetryUsage {

  public static void openTelemetryUsage(OpenTelemetry openTelemetry) {
    // Set the GlobalOpenTelemetry instance as early in the application lifecycle as possible
    // Set must only be called once. Calling multiple times raises an exception.
    GlobalOpenTelemetry.set(openTelemetry);

    // Get the GlobalOpenTelemetry instance.
    openTelemetry = GlobalOpenTelemetry.get();
  }
}

TracerProvider

TracerProvider é um ponto de entrada da API para traços e provedores de Traços. Veja provedores e escopos para informação sobre provedores e escopos.

Traços

Traços é usado para registros de trechos dentro de um escopo de instrumentação. See provedores e escopos para informação sobre provedores e escopos.

Trechos

Construtores de trechos e Trechos são usados para construir e gravar dados para trechos.

SpanBuilder é usado para adicionar dados para um trecho antes de inicializar uma chamada Span startSpan(). Dados podem ser adicionados / atualizados depois de inicializar uma chamada através de vários métodos de atualização de Trecho. Os dados providos por SpanBuilder antes da inicialização, fornecido como uma entrada para Amostradores.

O trecho de código a seguir explora o uso da API SpanBuilder:

package otel;

import static io.opentelemetry.context.Context.current;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import java.util.Arrays;

public class SpanUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void spanUsage(Tracer tracer) {
    // Get a span builder by providing the span name
    Span span =
        tracer
            .spanBuilder("span name")
            // Set span kind
            .setSpanKind(SpanKind.INTERNAL)
            // Set attributes
            .setAttribute(AttributeKey.stringKey("com.acme.string-key"), "value")
            .setAttribute(AttributeKey.booleanKey("com.acme.bool-key"), true)
            .setAttribute(AttributeKey.longKey("com.acme.long-key"), 1L)
            .setAttribute(AttributeKey.doubleKey("com.acme.double-key"), 1.1)
            .setAttribute(
                AttributeKey.stringArrayKey("com.acme.string-array-key"),
                Arrays.asList("value1", "value2"))
            .setAttribute(
                AttributeKey.booleanArrayKey("come.acme.bool-array-key"),
                Arrays.asList(true, false))
            .setAttribute(
                AttributeKey.longArrayKey("come.acme.long-array-key"), Arrays.asList(1L, 2L))
            .setAttribute(
                AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2))
            // Optionally omit initializing AttributeKey
            .setAttribute("com.acme.string-key", "value")
            .setAttribute("com.acme.bool-key", true)
            .setAttribute("come.acme.long-key", 1L)
            .setAttribute("come.acme.double-key", 1.1)
            .setAllAttributes(WIDGET_RED_CIRCLE)
            // Uncomment to optionally explicitly set the parent span context. If omitted, the
            // span's parent will be set using Context.current().
            // .setParent(parentContext)
            // Uncomment to optionally add links.
            // .addLink(linkContext, linkAttributes)
            // Start the span
            .startSpan();

    // Check if span is recording before computing additional data
    if (span.isRecording()) {
      // Update the span name with information not available when starting
      span.updateName("new span name");

      // Add additional attributes not available when starting
      span.setAttribute("com.acme.string-key2", "value");

      // Add additional span links not available when starting
      span.addLink(exampleLinkContext());
      // optionally include attributes on the link
      span.addLink(exampleLinkContext(), WIDGET_RED_CIRCLE);

      // Add span events
      span.addEvent("my-event");
      // optionally include attributes on the event
      span.addEvent("my-event", WIDGET_RED_CIRCLE);

      // Record exception, syntactic sugar for a span event with a specific shape
      span.recordException(new RuntimeException("error"));

      // Set the span status
      span.setStatus(StatusCode.OK, "status description");
    }

    // Finally, end the span
    span.end();
  }

  /** Return a dummy link context. */
  private static SpanContext exampleLinkContext() {
    return Span.fromContext(current()).getSpanContext();
  }
}

Trechos pai são um importante aspecto dos traços. Cada trecho tem um pai opcional. Ao coletar todos os trechos de um traço e seguir o pai de cada um, nós conseguimos construir uma hierarquia. As APIs de trechos são construídas sobre o contexto, que permite que o contexto de um trecho seja passado de forma implícita por toda a aplicação e através das threads. Quando um trecho é criado, o trecho pai é definido com o trecho presente em Context.current() a menos que não tenha um trecho ou o contexto seja sobrescrito explicitamente.

A maior parte das boas práticas de uso do contexto da API se aplica aos trechos. O contexto de um trecho é propagado através dos limites da aplicação com o W3CTraceContextPropagator e outros TextMapPropagators.

O trecho de código a seguir explora o uso da propagação da API Span:

package otel;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;

public class SpanAndContextUsage {
  private final Tracer tracer;

  SpanAndContextUsage(Tracer tracer) {
    this.tracer = tracer;
  }

  public void nestedSpanUsage() {
    // Start a span. Since we don't call makeCurrent(), we must explicitly call setParent on
    // children. Wrap code in try / finally to ensure we end the span.
    Span span = tracer.spanBuilder("span").startSpan();
    try {
      // Start a child span, explicitly setting the parent.
      Span childSpan =
          tracer
              .spanBuilder("span child")
              // Explicitly set parent.
              .setParent(span.storeInContext(Context.current()))
              .startSpan();
      // Call makeCurrent(), adding childSpan to Context.current(). Spans created inside the scope
      // will have their parent set to childSpan.
      try (Scope childSpanScope = childSpan.makeCurrent()) {
        // Call another method which creates a span. The span's parent will be childSpan since it is
        // started in the childSpan scope.
        doWork();
      } finally {
        childSpan.end();
      }
    } finally {
      span.end();
    }
  }

  private int doWork() {
    Span doWorkSpan = tracer.spanBuilder("doWork").startSpan();
    try (Scope scope = doWorkSpan.makeCurrent()) {
      int result = 0;
      for (int i = 0; i < 10; i++) {
        result += i;
      }
      return result;
    } finally {
      doWorkSpan.end();
    }
  }
}

MeterProvider

MeterProvider é um ponto de entrada da API para métricas e provedores Medidores. Veja provedores e escopos para informação de provedores e escopos.

Medidor

Medidor é usado para obter instrumentação de um escopo de instrumentação particular. Veja provedores e escopos para informação de provedores e escopos. Existem uma variedade de instrumentações, cada uma com uma semântica diferente e com comportamentos padrões no SDK. É importante escolher a instrumentação correta para cada particular caso de Uso:

InstrumentoSync or AsyncDescriçãoExemploAgregação padrão do SDK
ContadorsyncRecord monotonic (positive) valores.Registra logins de usuáriosum (monotonic=true)
Contador AssíncronoasyncObserve monotonic sums.Observa o número de classes carregadas no JVMsum (monotonic=true)
UpDownCountersyncRegistra non-monotonic (positive and negative) valores.Registra quando itens são adicionados e removidos da filasum (monotonic=false)
Async UpDownCounterasyncObserva non-monotonic (positive and negative) sums.Observa o uso de memória do pool JVMsum (monotonic=false)
HistogramasyncRegistra monotonic (positive) valores where the distribution is important.Registra duração de requisições HTTP processadas pelo servidorExplicitBucketHistogram
MedidorsyncRegistra the latest value where spatial re-aggregation does not make sense [1].Registra temperaturaLastValue
Medidor AssíncronoasyncObserva the latest value where spatial re-aggregation does not make sense [1].Observa utilização de CPULastValue

[1]: Reagregação espacial é o processo de mesclar fluxos de atributos descartando atributos que não são necessários. Por exemplo, dadas as séries com atributos {"color": "red", "shape": "square"}, {"color": "blue", "shape": "square"}, você pode realizar a reagregação espacial descartando o atributo color, e mesclando as séries cujos atributos restantes sejam iguais depois de descartar o atributo color. A maioria das agregações possuem uma função de mesclagem útil para reagregação espacial (ou seja, somas são somadas juntos), mas os indicadores agregam pelo LastValue são uma exceção. Por exemplo, suponhas que as séries mencionadas anteriormente estejam acompanhando a temperatura de certos objetos. Como você combina as séries depois de remover o atributo color? Não há uma boa resposta, exceto jogar uma moeda e escolher um valor aleatório.

As APIs de instrumentação possuem uma variedade de recursos:

  • Criação baseada em padrões de construtores.
  • Nome da instrumentação obrigatório.
  • Unidade e descrição opcional.
  • Registro de valores que podem ser long ou double, o que é configurado via construtor.

Veja as diretrizes de métricas para detalhes sobre nomenclatura de métricas e unidades.

Veja diretrizes para autores de bibliotecas de instrumentação para orientações adicionais sobre a escolha de instrumentos.

Contador

LongCounter e DoubleCounter são usados para registrar valores monótonos (positivos).

O trecho de código a seguir explora o uso da API counter:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;
import static otel.Util.customContext;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;

public class CounterUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void counterUsage(Meter meter) {
    // Construct a counter to record measurements that are always positive (monotonically
    // increasing).
    LongCounter counter =
        meter
            .counterBuilder("fully.qualified.counter")
            .setDescrição("A count of produced widgets")
            .setUnit("{widget}")
            // optionally change the type to double
            // .ofDoubles()
            .build();

    // Record a measurement with no attributes or context.
    // Attributes defaults to Attributes.empty(), context to Context.current().
    counter.add(1L);

    // Record a measurement with attributes, using pre-allocated attributes whenever possible.
    counter.add(1L, WIDGET_RED_CIRCLE);
    // Sometimes, attributes must be computed using application context.
    counter.add(
        1L, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));

    // Record a measurement with attributes, and context.
    // Most users will opt to omit the context argument, preferring the default Context.current().
    counter.add(1L, WIDGET_RED_CIRCLE, customContext());
  }
}

Contador assíncrono

ObservableLongCounter e ObservableDoubleCounter são usados para observar somas monótonas (positivas).

O trecho de código a seguir explora o uso da API async counter:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongCounter;
import java.util.concurrent.atomic.AtomicLong;

public class AsyncCounterUsage {
  // Pre-allocate attributes whenever possible
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void asyncCounterUsage(Meter meter) {
    AtomicLong widgetCount = new AtomicLong();

    // Construct an async counter to observe an existing counter in a callback
    ObservableLongCounter asyncCounter =
        meter
            .counterBuilder("fully.qualified.counter")
            .setDescrição("A count of produced widgets")
            .setUnit("{widget}")
            // Uncomment to optionally change the type to double
            // .ofDoubles()
            .buildWithCallback(
                // the callback is invoked when a MetricReader reads metrics
                observableMeasurement -> {
                  long currentWidgetCount = widgetCount.get();

                  // Record a measurement with no attributes.
                  // Attributes defaults to Attributes.empty().
                  observableMeasurement.record(currentWidgetCount);

                  // Record a measurement with attributes, using pre-allocated attributes whenever
                  // possible.
                  observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE);
                  // Sometimes, attributes must be computed using application context.
                  observableMeasurement.record(
                      currentWidgetCount,
                      Attributes.of(
                          WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));
                });

    // Optionally close the counter to unregister the callback when required
    asyncCounter.close();
  }
}

UpDownCounter

LongUpDownCounter e DoubleUpDownCounter são usados para registrar valores não-monótono (positivos e negativos).

O trecho de código a seguir explora o uso da API updowncounter:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;
import static otel.Util.customContext;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;

public class UpDownCounterUsage {

  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void usage(Meter meter) {
    // Construct an updowncounter to record measurements that go up and down.
    LongUpDownCounter upDownCounter =
        meter
            .upDownCounterBuilder("fully.qualified.updowncounter")
            .setDescrição("Current length of widget processing queue")
            .setUnit("{widget}")
            // Uncomment to optionally change the type to double
            // .ofDoubles()
            .build();

    // Record a measurement with no attributes or context.
    // Attributes defaults to Attributes.empty(), context to Context.current().
    upDownCounter.add(1L);

    // Record a measurement with attributes, using pre-allocated attributes whenever possible.
    upDownCounter.add(-1L, WIDGET_RED_CIRCLE);
    // Sometimes, attributes must be computed using application context.
    upDownCounter.add(
        -1L, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));

    // Record a measurement with attributes, and context.
    // Most users will opt to omit the context argument, preferring the default Context.current().
    upDownCounter.add(1L, WIDGET_RED_CIRCLE, customContext());
  }
}

Async UpDownCounter

ObservableLongUpDownCounter e ObservableDoubleUpDownCounter são usados para observe non-monotonic (positive and negative) sums.

O trecho de código a seguir explora o uso da API async updowncounter:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableLongUpDownCounter;
import java.util.concurrent.atomic.AtomicLong;

public class AsyncUpDownCounterUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void asyncUpDownCounterUsage(Meter meter) {
    AtomicLong queueLength = new AtomicLong();

    // Construct an async updowncounter to observe an existing up down counter in a callback
    ObservableLongUpDownCounter asyncUpDownCounter =
        meter
            .upDownCounterBuilder("fully.qualified.updowncounter")
            .setDescrição("Current length of widget processing queue")
            .setUnit("{widget}")
            // Uncomment to optionally change the type to double
            // .ofDoubles()
            .buildWithCallback(
                // the callback is invoked when a MetricReader reads metrics
                observableMeasurement -> {
                  long currentWidgetCount = queueLength.get();

                  // Record a measurement with no attributes.
                  // Attributes defaults to Attributes.empty().
                  observableMeasurement.record(currentWidgetCount);

                  // Record a measurement with attributes, using pre-allocated attributes whenever
                  // possible.
                  observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE);
                  // Sometimes, attributes must be computed using application context.
                  observableMeasurement.record(
                      currentWidgetCount,
                      Attributes.of(
                          WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));
                });

    // Optionally close the counter to unregister the callback when required
    asyncUpDownCounter.close();
  }
}

Histograma

DoubleHistogram e LongHistogram são usados para registra valores monótonos (positivos) onde a distribuição é importante.

O trecho de código a seguir explora o uso da API histogram:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;
import static otel.Util.customContext;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;

public class HistogramUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void histogramUsage(Meter meter) {
    // Construct a histogram to record measurements where the distribution is important.
    DoubleHistogram histogram =
        meter
            .histogramBuilder("fully.qualified.histogram")
            .setDescrição("Length of time to process a widget")
            .setUnit("s")
            // Uncomment to optionally provide advice on useful default explicit bucket boundaries
            // .setExplicitBucketBoundariesAdvice(Arrays.asList(1.0, 2.0, 3.0))
            // Uncomment to optionally change the type to long
            // .ofLongs()
            .build();

    // Record a measurement with no attributes or context.
    // Attributes defaults to Attributes.empty(), context to Context.current().
    histogram.record(1.1);

    // Record a measurement with attributes, using pre-allocated attributes whenever possible.
    histogram.record(2.2, WIDGET_RED_CIRCLE);
    // Sometimes, attributes must be computed using application context.
    histogram.record(
        3.2, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));

    // Record a measurement with attributes, and context.
    // Most users will opt to omit the context argument, preferring the default Context.current().
    histogram.record(4.4, WIDGET_RED_CIRCLE, customContext());
  }
}

Gauge

DoubleGauge and LongGauge são usados para registram o último valor onde a reagregação espacial não faz sentido.

O trecho de código a seguir explora o uso da API gauge:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;
import static otel.Util.customContext;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.api.metrics.Meter;

public class GaugeUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void gaugeUsage(Meter meter) {
    // Construct a gauge to record measurements as they occur, which cannot be spatially
    // re-aggregated.
    DoubleGauge gauge =
        meter
            .gaugeBuilder("fully.qualified.gauge")
            .setDescrição("The current temperature of the widget processing line")
            .setUnit("K")
            // Uncomment to optionally change the type to long
            // .ofLongs()
            .build();

    // Record a measurement with no attributes or context.
    // Attributes defaults to Attributes.empty(), context to Context.current().
    gauge.set(273.0);

    // Record a measurement with attributes, using pre-allocated attributes whenever possible.
    gauge.set(273.0, WIDGET_RED_CIRCLE);
    // Sometimes, attributes must be computed using application context.
    gauge.set(
        273.0,
        Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));

    // Record a measurement with attributes, and context.
    // Most users will opt to omit the context argument, preferring the default Context.current().
    gauge.set(1L, WIDGET_RED_CIRCLE, customContext());
  }
}

Async Gauge

ObservableDoubleGauge e ObservableLongGauge são usados para observar o último valor quando reagregação especial não faz sentido.

O trecho de código a seguir explora o uso da API async gauge:

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_SHAPE;
import static otel.Util.computeWidgetColor;
import static otel.Util.computeWidgetShape;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
import java.util.concurrent.atomic.AtomicReference;

public class AsyncGaugeUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void asyncGaugeUsage(Meter meter) {
    AtomicReference<Double> processingLineTemp = new AtomicReference<>(273.0);

    // Construct an async counter to observe an existing counter in a callback
    ObservableDoubleGauge asyncGauge =
        meter
            .gaugeBuilder("fully.qualified.gauge")
            .setDescrição("The current temperature of the widget processing line")
            .setUnit("K")
            // Uncomment to optionally change the type to long
            // .ofLongs()
            .buildWithCallback(
                // the callback is invoked when a MetricReader reads metrics
                observableMeasurement -> {
                  double currentWidgetCount = processingLineTemp.get();

                  // Record a measurement with no attributes.
                  // Attributes defaults to Attributes.empty().
                  observableMeasurement.record(currentWidgetCount);

                  // Record a measurement with attributes, using pre-allocated attributes whenever
                  // possible.
                  observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE);
                  // Sometimes, attributes must be computed using application context.
                  observableMeasurement.record(
                      currentWidgetCount,
                      Attributes.of(
                          WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));
                });

    // Optionally close the gauge to unregister the callback when required
    asyncGauge.close();
  }
}

LoggerProvider

LoggerProvider é o ponto de entrada da API para logs e provê Loggers. Veja provedores e escopos para informação sobre provedores e escopos.

Logger

Logger é usado para emitir registros de logs para um escopo de instrumentação. Veja provedores e escopos para informação sobre provedores e escopos.

LogRecordBuilder

LogRecordBuilder é usado para construir e emitir registros de logs.

O trecho de código a seguir explora o uso da API LogRecordBuilder:

package otel;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.Value;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.Severity;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class LogRecordUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void logRecordUsage(Logger logger) {
    logger
        .logRecordBuilder()
        // Set body. Note, setBody(..) is called multiple times for demonstration purposes but only
        // the last call is used.
        // Set the body to a string, syntactic sugar for setBody(Value.of("log message"))
        .setBody("log message")
        // Optionally set the body to a Value to record arbitrarily complex structured data
        .setBody(Value.of("log message"))
        .setBody(Value.of(1L))
        .setBody(Value.of(1.1))
        .setBody(Value.of(true))
        .setBody(Value.of(new byte[] {'a', 'b', 'c'}))
        .setBody(Value.of(Value.of("entry1"), Value.of("entry2")))
        .setBody(
            Value.of(
                Map.of(
                    "stringKey",
                    Value.of("entry1"),
                    "mapKey",
                    Value.of(Map.of("stringKey", Value.of("entry2"))))))
        // Set severity
        .setSeverity(Severity.DEBUG)
        .setSeverityText("debug")
        // Set timestamp
        .setTimestamp(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
        // Optionally set the timestamp when the log was observed
        .setObservedTimestamp(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
        // Set attributes
        .setAttribute(AttributeKey.stringKey("com.acme.string-key"), "value")
        .setAttribute(AttributeKey.booleanKey("com.acme.bool-key"), true)
        .setAttribute(AttributeKey.longKey("com.acme.long-key"), 1L)
        .setAttribute(AttributeKey.doubleKey("com.acme.double-key"), 1.1)
        .setAttribute(
            AttributeKey.stringArrayKey("com.acme.string-array-key"),
            Arrays.asList("value1", "value2"))
        .setAttribute(
            AttributeKey.booleanArrayKey("come.acme.bool-array-key"), Arrays.asList(true, false))
        .setAttribute(AttributeKey.longArrayKey("come.acme.long-array-key"), Arrays.asList(1L, 2L))
        .setAttribute(
            AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2))
        .setAllAttributes(WIDGET_RED_CIRCLE)
        // Uncomment to optionally explicitly set the context used to correlate with spans. If
        // omitted, Context.current() is used.
        // .setContext(context)
        // Emit the log record
        .emit();
  }
}

Implementação Noop

O método OpenTelemetry#noop() fornece acesso à implementação Noop do OpenTelemetry e de todos os componentes da API. Como o nome sugere, a implementação noop não faz nada e foi projetada para não ter nenhum impacto de performance. A Instrumentação pode ver um impacto na performance mesmo quando noop é usada se ela estiver calculando / alocando valores de atributos e outros dados necessários para registrar a telemetria. O noop é uma instância padrão útil do OpenTelemetry quando o usuário não configurou ou instalou uma implementação concreta como o SDK.

O trecho de código a seguir explora o uso da API OpenTelemetry#noop():

package otel;

import static otel.Util.WIDGET_COLOR;
import static otel.Util.WIDGET_RED_CIRCLE;
import static otel.Util.WIDGET_SHAPE;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;

public class NoopUsage {
  private static final String SCOPE_NAME = "fully.qualified.name";

  public static void noopUsage() {
    // Access the noop OpenTelemetry instance
    OpenTelemetry noopOpenTelemetry = OpenTelemetry.noop();

    // Noop tracing
    Tracer noopTracer = OpenTelemetry.noop().getTracer(SCOPE_NAME);
    noopTracer
        .spanBuilder("span name")
        .startSpan()
        .setAttribute(WIDGET_SHAPE, "square")
        .setStatus(StatusCode.OK)
        .addEvent("event-name", Attributes.builder().put(WIDGET_COLOR, "red").build())
        .end();

    // Noop metrics
    Attributes attributes = WIDGET_RED_CIRCLE;
    Meter noopMeter = OpenTelemetry.noop().getMeter(SCOPE_NAME);
    DoubleHistogram histogram = noopMeter.histogramBuilder("fully.qualified.histogram").build();
    histogram.record(1.0, attributes);
    // counter
    LongCounter counter = noopMeter.counterBuilder("fully.qualified.counter").build();
    counter.add(1, attributes);
    // async counter
    noopMeter
        .counterBuilder("fully.qualified.counter")
        .buildWithCallback(observable -> observable.record(10, attributes));
    // updowncounter
    LongUpDownCounter upDownCounter =
        noopMeter.upDownCounterBuilder("fully.qualified.updowncounter").build();
    // async updowncounter
    noopMeter
        .upDownCounterBuilder("fully.qualified.updowncounter")
        .buildWithCallback(observable -> observable.record(10, attributes));
    upDownCounter.add(-1, attributes);
    // gauge
    DoubleGauge gauge = noopMeter.gaugeBuilder("fully.qualified.gauge").build();
    gauge.set(1.1, attributes);
    // async gauge
    noopMeter
        .gaugeBuilder("fully.qualified.gauge")
        .buildWithCallback(observable -> observable.record(10, attributes));

    // Noop logs
    Logger noopLogger = OpenTelemetry.noop().getLogsBridge().get(SCOPE_NAME);
    noopLogger
        .logRecordBuilder()
        .setBody("log message")
        .setAttribute(WIDGET_SHAPE, "square")
        .setSeverity(Severity.INFO)
        .emit();
  }
}

Atributos de Semântica

A convenção semântica descreve como coletar telemetria em uma forma padronizada de operações comuns. Isso inclui um registro de atributos, com definições enumeradas para todos os atributos referenciados nas convenções, organizado pelo domínio. O projeto de convenção semântica do Java gera constantes de convenção semântica, onde podem ser utilizadas para ajudar a instrumentação conforme:

DescriçãoArtefato
Código gerado para convenções semânticas estáveisio.opentelemetry.semconv:opentelemetry-semconv:1.34.0-alpha
Código gerado para convenções semânticas incubadasio.opentelemetry.semconv:opentelemetry-semconv-incubating:1.34.0-alpha

Os atributos constantes gerados pela convenção semântica são instâncias de AttributeKey<T>, e podem ser utilizados em qualquer lugar da API do OpenTelemetry que aceite atributos.

O trecho de código a seguir explora o uso da API de atributos de convenção semântica:

package otel;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.semconv.HttpAttributes;
import io.opentelemetry.semconv.ServerAttributes;
import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes;

public class SemanticAttributesUsage {
  public static void semanticAttributesUsage() {
    // Semantic attributes are organized by top-level domain and whether they are stable or
    // incubating.
    // For example:
    // - stable attributes starting with http.* are in the HttpAttributes class.
    // - stable attributes starting with server.* are in the ServerAttributes class.
    // - incubating attributes starting with http.* are in the HttpIncubatingAttributes class.
    // Attribute keys which define an enumeration of values are accessible in an inner
    // {AttributeKey}Values class.
    // For example, the enumeration of http.request.method values is available in the
    // HttpAttributes.HttpRequestMethodValues class.
    Attributes attributes =
        Attributes.builder()
            .put(HttpAttributes.HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.GET)
            .put(HttpAttributes.HTTP_ROUTE, "/users/:id")
            .put(ServerAttributes.SERVER_ADDRESS, "example")
            .put(ServerAttributes.SERVER_PORT, 8080L)
            .put(HttpIncubatingAttributes.HTTP_RESPONSE_BODY_SIZE, 1024)
            .build();
  }
}

Bagagem

Bagagem é um conjunto de pares chave-valor definidos pela aplicação, associadas a uma requisição distribuída ou a execução de um fluxo de trabalho. As chaves e valores da bagagem são strings, e os valores possuem metadados opcionais. A telemetria pode ser enriquecida com dados da bagagem ao configurar o SDK para adicionar essas entradas como atributos dos trechos, métricas, e registros de logs. A API de bagagem é construída sobre o contexto, que permite que o contexto do trecho seja passado implicitamente dentro da aplicação e através dos limites das threads. A maioria das diretrizes de uso da API de contexto também se aplicam a bagagem.

A bagagem é propagada através dos limites da aplicação com o W3CBaggagePropagator (veja TextMapPropagator para detalhes).

O trecho de código a seguir explora o uso da API Baggage:

package otel;

import static io.opentelemetry.context.Context.current;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.baggage.BaggageEntry;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Scope;
import java.util.Map;
import java.util.stream.Collectors;

public class BaggageUsage {
  private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE;

  public static void baggageUsage() {
    // Access current baggage with Baggage.current()
    // output => context baggage: {}
    Baggage currentBaggage = Baggage.current();
    System.out.println("current baggage: " + asString(currentBaggage));
    // ...or from a Context
    currentBaggage = Baggage.fromContext(current());

    // Baggage has a variety of methods for manipulating and reading data.
    // Convert to builder and add entries:
    Baggage newBaggage =
        Baggage.current().toBuilder()
            .put("shopId", "abc123")
            .put("shopName", "opentelemetry-demo", BaggageEntryMetadata.create("metadata"))
            .build();
    // ...or uncomment to start from empty
    // newBaggage = Baggage.empty().toBuilder().put("shopId", "abc123").build();
    // output => new baggage: {shopId=abc123(), shopName=opentelemetry-demo(metadata)}
    System.out.println("new baggage: " + asString(newBaggage));
    // Read an entry:
    String shopIdValue = newBaggage.getEntryValue("shopId");
    // Inspect size:
    int size = newBaggage.size();
    boolean isEmpty = newBaggage.isEmpty();
    // Convert to map representation:
    Map<String, BaggageEntry> map = newBaggage.asMap();
    // Iterate through entries:
    newBaggage.forEach((s, baggageEntry) -> {});

    // The current baggage still doesn't contain the new entries
    // output => context baggage: {}
    System.out.println("current baggage: " + asString(Baggage.current()));

    // Calling Baggage.makeCurrent() sets Baggage.current() to the baggage until the scope is
    // closed, upon which Baggage.current() is restored to the state prior to when
    // Baggage.makeCurrent() was called.
    try (Scope scope = newBaggage.makeCurrent()) {
      // The current baggage now contains the added value
      // output => context baggage: {shopId=abc123(), shopName=opentelemetry-demo(metadata)}
      System.out.println("current baggage: " + asString(Baggage.current()));
    }

    // The current baggage no longer contains the new entries:
    // output => context baggage: {}
    System.out.println("current baggage: " + asString(Baggage.current()));
  }

  private static String asString(Baggage baggage) {
    return baggage.asMap().entrySet().stream()
        .map(
            entry ->
                String.format(
                    "%s=%s(%s)",
                    entry.getKey(),
                    entry.getValue().getValue(),
                    entry.getValue().getMetadata().getValue()))
        .collect(Collectors.joining(", ", "{", "}"));
  }
}

API em incubação

O Artefato io.opentelemetry:opentelemetry-api-incubator:1.51.0-alpha contém traços, métricas, registros e contextos de APIs experimentais. APIs em incubação estão sujeitos a mudanças incompatíveis em versões menores. Frequentemente, essas APIs representam funcionalidades experimentais de especificação ou designs de API que queremos avaliar com o feedback dos usuários antes de nos comprometer com elas. Encorajamos os usuários a testarem essas APIs e abrir problemas issues com qualquer feedback (positivo ou negativo). Bibliotecas não devem depender dessas APIs em incubação, pois os usuários podem estar sujeitos a erros em tempo de execução quando ocorrem conflitos de versão transitivos.

Veja incubator README para APIs disponíveis e exemplos de uso.


Last modified June 15, 2025: npm run fix:dict (39a41a63)