Bibliotecas
O OpenTelemetry fornece bibliotecas de instrumentação para várias bibliotecas, geralmente feitas por meio de hooks de biblioteca ou monkey-patching do código da biblioteca.
A instrumentação nativa de bibliotecas com OpenTelemetry oferece melhor observabilidade e experiência para desenvolvedores, eliminando a necessidade das bibliotecas exporem e documentarem hooks. Outros benefícios fornecidos pela instrumentação nativa incluem:
- Hooks personalizados de logging podem ser substituídos por APIs OpenTelemetry comuns e fáceis de usar, os usuários utilizarão somente o OpenTelemetry
- Rastros, logs e métricas do código da biblioteca e da aplicação são correlacionados e coerentes
- Convenções comuns permitem que os usuários obtenham uma telemetria semelhante e consistente com a mesma tecnologia e entre diferentes bibliotecas e linguagens
- Sinais de telemetria podem ser ajustados (filtrados, processados, agregados) para diversos cenários de consumo usando uma grande variedade de pontos de extensibilidade bem documentados do OpenTelemetry.
Convenção semântica
Convenções semânticas são a principal fonte de verdade e indicam quais informações devem ser incluídas nos trechos produzidos por frameworks web, clientes RPC, bancos de dados, clientes de mensagens, infraestrutura e mais. As convenções tornam a instrumentação consistente: usuários que trabalham com telemetria não precisam aprender as especificidades de cada biblioteca, e fornecedores de observabilidade podem criar experiências para uma ampla variedade de tecnologias, por exemplo, bancos de dados ou sistemas de mensagens. Quando as bibliotecas seguem as convenções, muitos cenários podem ser habilitados automaticamente, sem necessidade de intervenção ou configuração por parte do usuário.
As convenções semânticas estão em constante evolução, e novas são adicionadas
regularmente. Se ainda não existirem convenções para a sua biblioteca,
considere adicioná-las.
Preste atenção especial aos nomes dos trechos: procure usar nomes significativos
e considere a cardinalidade ao defini-los. Também defina o atributo
schema_url
, que pode ser utilizado
para registrar a versão das convenções semânticas em uso
Se tiver algum feedback ou quiser adicionar uma nova convenção, contribua juntando-se ao Instrumentation Slack, ou através de uma nova discussão ou pull request no repositório de Especificações _(Specification) .
Definindo trechos
Pense na sua biblioteca do ponto de vista de um usuário e no que ele poderia querer saber sobre o comportamento e a atividade da biblioteca. Como mantenedor da biblioteca, você conhece os detalhes internos, mas o usuário provavelmente estará mais interessado na funcionalidade da aplicação do que no funcionamento interno da biblioteca. Considere quais informações podem ser úteis para analisar o uso da sua biblioteca e pense em uma maneira apropriada de modelar esses dados. Alguns aspectos a serem considerados incluem:
- Trechos e hierarquias de trecho
- Atributos numéricos em trechos, como uma alternativa a métricas agregadas
- Eventos em trechos
- Métricas agregadas
Por exemplo, se sua biblioteca está fazendo requisições a um banco de dados, crie trechos apenas para a requisição lógica ao banco de dados. As requisições físicas pela rede devem ser instrumentadas nas bibliotecas que implementam essa funcionalidade. Além disso, é preferível capturar outras atividades, como a serialização de objetos/dados como eventos em trechos, ao invés de trechos adicionais.
Siga as convenções semânticas ao definir atributos dos trechos.
Quando não instrumentar
Algumas bibliotecas atuam como camadas finas que encapsulam chamadas de rede. Há uma grande chance de que o OpenTelemetry já tenha uma biblioteca de instrumentação para o cliente RPC subjacente. Confira o registro para encontrar as bibliotecas existentes. Caso uma biblioteca já exista, pode não ser necessário instrumentar a biblioteca que encapsula essas chamadas.
Como regra geral, instrumente sua biblioteca apenas em seu próprio nível. Não a instrumente caso todos os casos a seguir se apliquem:
- Sua biblioteca é um proxy simples em cima de APIs documentadas ou autoexplicativas.
- O OpenTelemetry já possui instrumentação para as chamadas de rede subjacentes.
- Não existem convenções que sua biblioteca deva seguir para enriquecer a telemetria.
Em caso de dúvida, não instrumente. Se optar por não instrumentar, ainda pode ser útil fornecer uma maneira de configurar manipuladores do OpenTelemetry para a instância interna do cliente RPC. Isso é essencial em linguagens que não suportam instrumentação totalmente automática e ainda é útil em outras.
O restante deste documento fornece orientações sobre o que e como instrumentar, caso decida fazê-lo.
OpenTelemetry API
O primeiro passo é adicionar a dependência do pacote OpenTelemetry API.
O OpenTelemetry possui dois módulos principais: API e SDK. A API do OpenTelemetry é um conjunto de abstrações e implementações não operacionais. A menos que sua aplicação importe o SDK do OpenTelemetry, sua instrumentação não faz nada e não impacta o desempenho da aplicação.
Bibliotecas devem usar apenas a API do OpenTelemetry
Caso você esteja com receio de adicionar novas dependências, então aqui estão algumas considerações para ajudar a minimizar problemas de conflitos de dependências:
- A API de rastros do OpenTelemetry alcançou estabilidade no início de 2021. Ela segue o Versionamento Semântico 2.0.
- Utilize a versão mais antiga estável da API do OpenTelemetry (1.0.*) e evite atualizá-la, a menos que precise usar novas funcionalidades.
- Enquanto sua instrumentação se estabiliza, considere lançá-la como um pacote separado, para que isso não cause problemas para usuários que não a utilizam. Você pode mantê-la em seu repositório ou adicioná-la ao OpenTelemetry, para que seja distribuída junto com outras bibliotecas de instrumentação.
- As convenções semânticas são estáveis, mas sujeitas à evolução: embora isso não cause problemas funcionais, pode ser necessário atualizar sua instrumentação de tempos em tempos. Ter a instrumentação em um pacote experimental ou no repositório contrib do OpenTelemetry pode ajudar a manter as convenções atualizadas sem causar mudanças disruptivas para seus usuários.
Obtendo um rastreador
Toda a configuração da aplicação é ocultada da sua biblioteca por meio da API de
Rastreamento. As bibliotecas podem permitir que as aplicações passem instâncias
de TracerProvider
para facilitar testes e injeção de dependências, ou podem
obtê-las a partir do
TracerProvider global. As
implementações do OpenTelemetry em diferentes linguagens podem ter preferências
distintas para passar instâncias ou acessar o global, dependendo do que é mais
comum na linguagem.
Ao obter o rastreador, forneça o nome e a versão da sua biblioteca (ou do pacote de rastreamento) - essas informações aparecerão na telemetria e ajudarão os usuários a processar e filtrar a telemetria, além de entender sua origem e depurar/relatar quaisquer problemas de instrumentação.
O que instrumentar
APIs Públicas
APIs públicas são bons candidatos para rastreamento: trechos criados para chamadas de APIs públicas permitem que os usuários mapeiem a telemetria para o código da aplicação, entendam a duração e o resultado das chamadas da biblioteca. Quais chamadas devem ser rastreadas:
- Métodos públicos que fazem chamadas de rede internamente ou operações locais que levam tempo significativo e podem falhar (ex.: operações de Entrada/Saída)
- Handlers que processam requisições ou mensagens
Exemplo de instrumentação
O exemplo a seguir mostra como instrumentar uma aplicação Java:
private static Tracer tracer = getTracer(TracerProvider.noop());
public static void setTracerProvider(TracerProvider tracerProvider) {
tracer = getTracer(tracerProvider);
}
private static Tracer getTracer(TracerProvider tracerProvider) {
return tracerProvider.getTracer("demo-db-client", "0.1.0-beta1");
}
private Response selectWithTracing(Query query) {
// consulte as convenções para obter orientações sobre nomes de trechos e atributos
Span span = tracer.spanBuilder(String.format("SELECT %s.%s", dbName, collectionName))
.setSpanKind(SpanKind.CLIENT)
.setAttribute("db.name", dbName)
...
.startSpan();
// torna o trecho ativo e permite correlacionar logs e trechos aninhados
try (Scope unused = span.makeCurrent()) {
Response response = query.runWithRetries();
if (response.isSuccessful()) {
span.setStatus(StatusCode.OK);
}
if (span.isRecording()) {
// preencha atributos de resposta para códigos de resposta e outras informações
}
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getClass().getSimpleName());
throw e;
} finally {
span.end();
}
}
Siga as convenções para preencher atributos. Se nenhuma delas se aplicar, consulte as convenções gerais.
Trechos de rede aninhados e outros trechos
Chamadas de rede são geralmente rastreadas com autoinstrumentações do OpenTelemetry através da implementação correspondente do cliente.
Se o OpenTelemetry não suportar o rastreamento do seu cliente de rede, aqui estão algumas considerações para ajudar a decidir o melhor curso de ação:
- Rastrear chamadas de rede melhoraria a observabilidade para os usuários ou sua capacidade de apoiá-los?
- Sua biblioteca é um encapsulador de uma API RPC pública e documentada? Os
usuários precisariam obter suporte do serviço subjacente em caso de problemas?
- instrumente a biblioteca e certifique-se de rastrear tentativas individuais de rede
- Rastrear essas chamadas com trechos seria muito verboso? Ou impactaria
notavelmente o desempenho?
- Use logs com verbosidade ou eventos de trecho: logs podem ser correlacionados ao trecho raiz (chamadas de API pública), enquanto eventos de rastro devem ser definidos no trecho da API pública.
- Se eles precisarem ser trechos (para carregar e propagar contexto de um único rastro), coloque-os atrás de uma opção de configuração e desative-os por padrão.
Se o OpenTelemetry já suportar o rastreamento de suas chamadas de rede, você provavelmente não quer duplicá-lo. Pode haver algumas exceções:
- Para suportar usuários sem auto-instrumentação, que pode não funcionar em certos ambientes ou quando os usuários podem ter preocupações com monkey-patching.
- Para habilitar protocolos personalizados ou legados de correlação e propagação de contexto com o serviço subjacente.
- Enriquecer trechos de RPC com informações absolutamente essenciais específicas da biblioteca/serviço não cobertas pela auto-instrumentação
Uma solução genérica para evitar duplicação está em construção.
Eventos
Rastros são um tipo de sinal que seus aplicativos podem emitir. Eventos (ou logs) e rastros se complementam, não se duplicam. Sempre que você tiver algo que deva ter um certo nível de verbosidade, logs são uma escolha melhor do que rastros.
Caso a sua aplicação já utilize log ou algum módulo semelhante, é possível que o módulo de logs já tenha integração com o OpenTelemetry. Para descobrir, veja o registro. As integrações geralmente adicionam o contexto de rastros ativo em todos os logs, para que os usuários possam correlacioná-los.
Se sua linguagem e ecossistema não tiverem suporte comum para logs, use span events para compartilhar detalhes adicionais do aplicativo. Eventos podem ser mais convenientes se você quiser adicionar atributos também.
Como regra geral, use eventos ou logs para dados verbosos em vez de rastros. Sempre anexe eventos à instância do trecho que sua instrumentação criou. Evite usar o trecho ativo se puder, pois você não controla a que ele se refere.
Propagação de contexto
Extraindo contexto
Se você trabalha em uma biblioteca ou serviço que recebe chamadas upstream,
como um framework web ou um consumidor de mensagens, você deve extrair o
contexto da requisição ou mensagem recebida. O OpenTelemetry fornece a API
Propagator
, que oculta padrões específicos de propagação e lê o Context
de
rastreamento do cabeçalho. No caso de uma única resposta, há apenas um contexto
no cabeçalho, que se torna o pai dos novos trechos criado pela biblioteca.
Após criar um trecho, você deve passar o novo contexto de rastreamento para o código da aplicação (callback ou handler), tornando o rastro ativo; se possível, você deve fazer isso explicitamente.
// extrair o contexto
Context extractedContext = propagator.extract(Context.current(), httpExchange, getter);
Span span = tracer.spanBuilder("receive")
.setSpanKind(SpanKind.SERVER)
.setParent(extractedContext)
.startSpan();
// tornar o trecho ativo para que qualquer telemetria aninhada seja correlacionada
try (Scope unused = span.makeCurrent()) {
userCode();
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end();
}
No caso de um sistema de mensagens, você pode receber mais de uma mensagem de uma vez. As mensagens recebidas se tornam links no trecho que você cria. Consulte as convenções de mensagens para mais detalhes.
Injetando contexto
Quando você faz uma chamada de saída, geralmente vai querer propagar o contexto
para o serviço downstream. Nesse caso, você deve criar um novo trecho para
rastrear a chamada de saída e usar a API Propagator
para injetar o contexto na
mensagem. Podem haver outros casos em que você queira injetar o contexto, por
exemplo, ao criar mensagens para processamento assíncrono. O exemplo a seguir em
Java mostra como propagar o contexto. Consulte
Injeção de contexto em Java
para mais exemplos.
Span span = tracer.spanBuilder("send")
.setSpanKind(SpanKind.CLIENT)
.startSpan();
// tornar o trecho ativo para que qualquer telemetria aninhada seja correlacionada
// até mesmo chamadas de rede podem ter camadas aninhadas de trechos, logs ou eventos
try (Scope unused = span.makeCurrent()) {
// injetar o contexto
propagator.inject(Context.current(), transportLayer, setter);
send();
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end();
}
Podem haver algumas exceções onde não é necessário propagar o contexto:
- O serviço downstream não suporta metadados ou proíbe campos desconhecidos.
- O serviço downstream não define protocolos de correlação. Considere adicionar suporte para a propagação de contexto em uma versão futura.
- O serviço downstream suporta um protocolo de correlação personalizado.
- Melhor esforço com propagador personalizado: use o contexto de rastreamento do OpenTelemetry, se for compatível, ou gere e insira IDs de correlação personalizados no trecho.
Em processo
- Torne seus trechos ativos ou atuais, pois isso permite correlacionar trechos com logs e qualquer autoinstrumentação aninhada.
- Se a biblioteca tiver uma noção de contexto, ofereça suporte opcional para a
propagação explícita de contexto de rastreamento, além dos trechos ativos.
- Coloque rastros (contexto de rastreamento) criados pela biblioteca no contexto explicitamente, documente como acessá-los.
- Permita que os usuários passem o contexto de rastreamento em seu contexto.
- Dentro da biblioteca, propague o contexto de rastreamento explicitamente.
Trechos ativos podem mudar durante callbacks!
- Capture o contexto ativo dos usuários na superfície da API pública assim que possível e use-o como contexto pai para seus trechos.
- Passe o contexto adiante e aplique atributos, exceções, eventos nas instâncias propagadas explicitamente.
- Isso é essencial caso você inicie processos explicitamente, realize processamento em segundo plano ou outras operações que podem falhar devido a limitações no fluxo de contexto assíncrono de sua linguagem.
Considerações adicionais
Registro de Instrumentação
Adicione sua biblioteca de instrumentação ao registro do OpenTelemetry, para que os usuários possam encontrá-la.
Performance
A API do OpenTelemetry não executa operações quando não há SDK configurado na aplicação. Quando o SDK do OpenTelemetry é configurado, ele consome recursos limitados.
Aplicações reais, especialmente em grande escala, frequentemente têm amostragem baseada em cabeçalho configurada. Techos não amostrados são baratos e você pode verificar se o trecho está gravando, para evitar alocações extras e cálculos potencialmente caros, enquanto preenche atributos. O exemplo a seguir em Java mostra como fornecer atributos para amostragem e verificar a gravação do trecho.
// alguns atributos são importantes para a amostragem e devem ser fornecidos no momento da criação
Span span = tracer.spanBuilder(String.format("SELECT %s.%s", dbName, collectionName))
.setSpanKind(SpanKind.CLIENT)
.setAttribute("db.name", dbName)
...
.startSpan();
// outros atributos, especialmente aqueles caros de calcular
// devem ser adicionados se o trecho estiver gravando
if (span.isRecording()) {
span.setAttribute("db.statement", sanitize(query.statement()))
}
Tratamento de Erros
A API do OpenTelemetry não falha em argumentos inválidos, nunca lança exceções e suprime erros, o que significa que é tolerante em tempo de execução. Dessa forma, problemas de instrumentação não afetam a lógica da aplicação. Teste a instrumentação para identificar problemas que o OpenTelemetry oculta em tempo de execução.
Testes
Como o OpenTelemetry oferece uma variedade de autoinstrumentações, experimente como a sua instrumentação interage com outras telemetrias: requisições de entrada, requisições de saída, logs, entre outros. Utilize uma aplicação típica, com frameworks e bibliotecas populares e com todo o rastreamento habilitado ao testar sua instrumentação. Verifique como bibliotecas semelhantes à sua são exibidas.
Para testes unitários, você geralmente pode simular ou criar versões fictícias
de SpanProcessor
e SpanExporter
, como mostra o exemplo Java a seguir:
@Test
public void checkInstrumentation() {
SpanExporter exporter = new TestExporter();
Tracer tracer = OpenTelemetrySdk.builder()
.setTracerProvider(SdkTracerProvider.builder()
.addSpanProcessor(SimpleSpanProcessor.create(exporter)).build()).build()
.getTracer("test");
// executa teste ...
validateSpans(exporter.exportedSpans);
}
class TestExporter implements SpanExporter {
public final List<SpanData> exportedSpans = Collections.synchronizedList(new ArrayList<>());
@Override
public CompletableResultCode export(Collection<SpanData> spans) {
exportedSpans.addAll(spans);
return CompletableResultCode.ofSuccess();
}
...
}
Feedback
Was this page helpful?
Thank you. Your feedback is appreciated!
Please let us know how we can improve this page. Your feedback is appreciated!