Observabilidade de Aplicações (APM)
- Introdução à Observabilidade de Aplicações
- Sinais e Métricas das Aplicações
- Instrumentação com Bibliotecas OpenTelemetry
- Ingestão de Dados e Backend de Armazenamento
- Visualização de Dados de Aplicações
Introdução à Observabilidade de Aplicações
O que é a Observabilidade de Aplicações?
Observabilidade de Aplicações permite entender um sistema externamente, possibilitando fazer perguntas sobre ele sem precisar conhecer seu funcionamento interno. Além disso, facilita a solução de problemas desconhecidos e imprevistos . A observabilidade ajuda a responder à pergunta: “Por que isso está acontecendo?”
Para fazer perguntas sobre um sistema, é necessário que sua aplicação esteja devidamente instrumentada. Isso significa que o código da aplicação ou algum componente intimamente ligado à aplicação como o Application Server, máquina virtual da linguagem da aplicação ou intepretador de comandos da linguagem devem emitir sinais, como rastreamentos (traces), métricas e logs. Uma aplicação está bem instrumentada quando os desenvolvedores não precisam adicionar mais instrumentação para investigar problemas, pois já possuem todas as informações necessárias.
O Priax, além de possuir mecanismos de instrumentação próprios pode é compatível com mecanismos consagrados do mercado como OpenTelemetry, Jaeger ou Opensearch APM. Todos esses mecanismos são usados para instrumentar o código da aplicação e tornar um sistema observável.
Telemetria, métricas e confiabilidade de Aplicações
Telemetria se refere aos dados emitidos por um sistema e seu comportamento. Esses dados podem ser apresentados na forma de rastreamentos, métricas e logs.
A confiabilidade responde à pergunta: “O serviço está fazendo o que os usuários esperam que ele faça?”. Por exemplo, um sistema pode estar disponível 100% do tempo, mas, se ao clicar em "Adicionar ao Carrinho" para incluir um par de sapatos pretos, o sistema não adicionar sempre os sapatos pretos, ele é considerado não confiável.
Métricas são agregações ao longo do tempo de dados numéricos sobre sua infraestrutura ou aplicação. Exemplos incluem:
- Taxa de erro do sistema
- Uso da CPU
- Taxa de solicitações de um serviço
SLI (Service Level Indicator) é uma medida do comportamento de um serviço. Um bom SLI mede o serviço do ponto de vista dos usuários. Um exemplo de SLI é a velocidade de carregamento de uma página web.
SLO (Service Level Objective) é a forma como a confiabilidade é comunicada dentro da organização ou para outras equipes, vinculando um ou mais SLIs ao valor de negócio.
Entendendo o Rastreamento Distribuído
O rastreamento distribuído permite observar solicitações conforme elas se propagam por sistemas distribuídos e complexos. Ele melhora a visibilidade sobre a saúde de uma aplicação ou sistema e ajuda a depurar comportamentos difíceis de reproduzir localmente.
O rastreamento distribuído é essencial para sistemas distribuídos, que frequentemente apresentam problemas não determinísticos ou complexos demais para serem reproduzidos em um ambiente local.
Para entender o rastreamento distribuído, é importante conhecer seus principais componentes: logs, spans e traces.
Logs
Um log é uma mensagem com carimbo de tempo emitida por serviços ou componentes. Diferente dos rastreamentos, os logs não estão necessariamente associados a uma solicitação ou transação específica. Eles estão presentes em praticamente todo software e foram amplamente utilizados por desenvolvedores e operadores para entender o comportamento dos sistemas.
Exemplo de log:
I, [2021-02-23T13:26:23.505892 #22473] INFO -- : [6459ffe1-ea53-4044-aaa3-bf902868f730] Started GET "/" for ::1 at 2021-02-23 13:26:23 -0800
No entanto, os logs não são suficientes para rastrear a execução do código, pois geralmente carecem de informações contextuais, como o local de origem da chamada.
Os logs se tornam muito mais úteis quando são incluídos como parte de um span ou quando estão correlacionados a um trace e um span.
Spans
Um span representa uma unidade de trabalho ou uma operação. Ele rastreia operações específicas de uma solicitação, fornecendo uma visão detalhada do que ocorreu durante sua execução.
Um span inclui:
- Nome
- Dados temporais
- Mensagens de log estruturadas
- Metadados (Atributos) que fornecem mais informações sobre a operação rastreada.
Atributos de Span
Os atributos são metadados associados a um span.
| Chave | Valor |
| http.request.method | GET |
| network.protocol.version | 1.1 |
| url.path | /webshop/articles/4 |
| url.query | ?s=1 |
| server.address | example.com |
| server.port | 8080 |
| url.scheme | https |
| http.route | /webshop/articles/:article_id |
| http.response.status_code | 200 |
| client.address | 192.0.2.4 |
| client.socket.address | 192.0.2.5 (o cliente passa por um proxy) |
| user_agent.original | Mozilla/5.0 (Windows NT 10.0; Win64; ...) |
Rastreamentos Distribuídos
Um rastreamento distribuído (trace) registra os caminhos percorridos por solicitações (feitas por uma aplicação ou usuário final) enquanto atravessam arquiteturas multi-serviço, como aplicações baseadas em microsserviços ou serverless.
Um trace é composto por um ou mais spans:
- O span raiz representa o início e o fim de uma solicitação.
- Os spans filhos fornecem um contexto detalhado sobre o que ocorre durante a solicitação.
Sem rastreamento, identificar a causa raiz de problemas de desempenho em sistemas distribuídos pode ser desafiador. O rastreamento simplifica a depuração e facilita o entendimento de sistemas complexos, detalhando o que acontece com uma solicitação enquanto ela se propaga pelo sistema.
Muitos backends de observabilidade visualizam os traces como diagramas de cascata (waterfall diagrams), que demonstram a relação entre spans pai e filhos, representando relações hierárquicas e aninhadas.
Propagação de Contexto
Entenda o conceito que viabiliza o Rastreamento Distribuído.
Com a propagação de contexto, os sinais podem ser correlacionados entre si, independentemente de onde são gerados. Embora não se limite apenas ao rastreamento, a propagação de contexto permite que os rastreamentos construam informações causais sobre um sistema que está distribuído de forma arbitrária entre processos e limites de rede.
Para entender a propagação de contexto, é necessário conhecer dois conceitos principais: contexto e propagação.
Contexto
O contexto é um objeto que contém as informações necessárias para que os serviços emissores e receptores, ou unidades de execução, possam correlacionar um sinal com outro.
Por exemplo: se o serviço A chama o serviço B, um span do serviço A, cujo ID está presente no contexto, será utilizado como span pai para o próximo span criado no serviço B. O ID do rastreamento (trace ID) também será incluído no contexto e utilizado para o próximo span criado no serviço B. Isso significa que esse novo span fará parte do mesmo rastreamento que o span do serviço A.
Propagação
A propagação é o mecanismo responsável por mover o contexto entre serviços e processos. Ela serializa ou desserializa o objeto de contexto e fornece as informações relevantes para que o contexto seja transferido de um serviço para outro.
Geralmente, a propagação é gerenciada automaticamente pelas bibliotecas de instrumentação e é transparente para o usuário. No entanto, caso seja necessário realizar a propagação de contexto manualmente, você pode utilizar a API de Propagadores (Propagators API) de cada biblioteca de instrumentação.
Sinais e Métricas das Aplicações
Tipos de Sinais
Conheça as categorias de telemetria suportadas pelo Priax e bibliotecas de instrumentação compatíveis:
O objetivo de uma biblioteca de instrumentação é coletar, processar e exportar sinais. Sinais são saídas do sistema que descrevem a atividade subjacente do sistema operacional e das aplicações em execução em uma plataforma. Um sinal pode representar algo que você deseja medir em um ponto específico no tempo, como a temperatura ou o uso de memória, ou um evento que percorre os componentes de um sistema distribuído e que você gostaria de rastrear.
Com o Priax você pode agrupar diferentes sinais para observar o funcionamento interno de uma mesma tecnologia sob diferentes perspectivas.
Atualmente, o Priax é capaz de exibir, detalhar, quantificar e detalhar as seguintes categorias de sinais:
- Rastreamentos (traces)
- Métricas
- Logs
- Baggage
Rastreamentos (Traces)
O caminho de uma solicitação através da sua aplicação.
Os rastreamentos nos dão uma visão geral do que acontece quando uma solicitação é feita a uma aplicação. Seja sua aplicação um monólito com um único banco de dados ou uma complexa malha de serviços, os rastreamentos são essenciais para entender o “caminho” completo que uma solicitação percorre na sua aplicação.
Vamos explorar isso com três unidades de trabalho representadas como Spans (faixas de execução):
Observação
Os exemplos de JSON a seguir não representam um formato específico, especialmente o OTLP/JSON, que é mais detalhado.
1. Span Hello:
{
"name": "hello",
"context": {
"trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
"span_id": "051581bf3cb55c13"
},
"parent_id": null,
"start_time": "2022-04-29T18:52:58.114201Z",
"end_time": "2022-04-29T18:52:58.114687Z",
"attributes": {
"http.route": "some_route1"
},
"events": [
{
"name": "Guten Tag!",
"timestamp": "2022-04-29T18:52:58.114561Z",
"attributes": { "event_attributes": 1 }
}
]
}
Este é o span raiz, indicando o início e o fim da operação inteira. Observe que ele tem um campo trace_id, mas não tem parent_id, o que o caracteriza como a raiz do rastreamento.
2. Span hello-greetings:
{
"name": "hello-greetings",
"context": {
"trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
"span_id": "5fb397be34d26b51"
},
"parent_id": "051581bf3cb55c13",
"start_time": "2022-04-29T18:52:58.114304Z",
"end_time": "2022-04-29T22:52:58.114561Z",
"attributes": {
"http.route": "some_route2"
},
"events": [
{
"name": "hey there!",
"timestamp": "2022-04-29T18:52:58.114561Z",
"attributes": {
"event_attributes": 1
}
},
{
"name": "bye now!",
"timestamp": "2022-04-29T18:52:58.114585Z",
"attributes": {
"event_attributes": 1
}
}
]
}
Este span encapsula tarefas específicas, como exibir saudações, e seu parent_id é o ID do span raiz hello. Ele compartilha o mesmo trace_id, indicando que faz parte do mesmo rastreamento.
3. Span hello-salutations:
{
"name": "hello-salutations",
"context": {
"trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
"span_id": "93564f51e1abe1c2"
},
"parent_id": "051581bf3cb55c13",
"start_time": "2022-04-29T18:52:58.114492Z",
"end_time": "2022-04-29T18:52:58.114631Z",
"attributes": {
"http.route": "some_route3"
},
"events": [
{
"name": "hey there!",
"timestamp": "2022-04-29T18:52:58.114561Z"
}
]
}
Este span representa a terceira operação neste rastreamento e, assim como o anterior, é um filho do span raiz (hello). Isso também o torna um irmão do span hello-greetings
Esses três blocos de JSON compartilham o mesmo trace_id e utilizam o parent_id para representar a hierarquia. Isso cria o rastreamento completo!
Uma observação importante é que cada Span se assemelha a um log estruturado, com contexto, correlação e hierarquia embutidos. Contudo, esses “logs estruturados” podem vir de diferentes processos, serviços, VMs ou datacenters, permitindo que o rastreamento represente uma visão ponta a ponta de qualquer sistema.
Detalhamento de uma span
Uma span representa uma unidade de trabalho ou operação e inclui:
- Nome
- ID do Span pai (vazio para spans raiz)
- Timestamps de início e fim
- Contexto do Span
- Atributos
- Eventos
- Links
- Status do Span
Exemplo de span:
{
"name": "/v1/sys/health",
"context": {
"trace_id": "7bba9f33312b3dbb8b2c2c62bb7abe2d",
"span_id": "086e83747d0e381e"
},
"parent_id": "",
"start_time": "2021-10-22 16:04:01.209458162 +0000 UTC",
"end_time": "2021-10-22 16:04:01.209514132 +0000 UTC",
"status_code": "STATUS_CODE_OK",
"status_message": "",
"attributes": {
"net.transport": "IP.TCP",
"net.peer.ip": "172.17.0.1",
"net.peer.port": "51820",
"net.host.ip": "10.177.2.152",
"net.host.port": "26040",
"http.method": "GET",
"http.target": "/v1/sys/health",
"http.server_name": "mortar-gateway",
"http.route": "/v1/sys/health",
"http.user_agent": "Consul Health Check",
"http.scheme": "http",
"http.host": "10.177.2.152:26040",
"http.flavor": "1.1"
},
"events": [
{
"name": "",
"message": "OK",
"timestamp": "2021-10-22 16:04:01.209512872 +0000 UTC"
}
]
}
Contexto do Span
Os spans podem ser aninhados, como é indicado pela presença de um ID de span pai: os spans filhos representam suboperações. Isso permite que os spans capturem com mais precisão o trabalho realizado em uma aplicação. O span pai terá um trace_Id idêntico ao seu filho indicando portanto que atende a uma mesma requisição ao sistema.
O contexto do span é um objeto imutável presente em todos os spans que contém as seguintes informações:
- Trace ID: identifica o trace ao qual o span pertence.
- Span ID: identifica o próprio span.
- Trace Flags: uma codificação binária com informações sobre o trace.
- Trace State: uma lista de pares chave-valor que podem conter informações específicas de fornecedores.
Atributos
Os atributos são pares chave-valor usados para adicionar metadados a um span, permitindo capturar informações sobre a operação monitorada.
Por exemplo, se um span acompanha a operação de adicionar um item ao carrinho de compras em um sistema de e-commerce, você pode registrar o ID do usuário, o ID do item e o ID do carrinho.
- Você pode adicionar atributos durante ou após a criação de um span.
- É recomendável adicionar os atributos no momento da criação para que possam ser aproveitados pelo SDK na amostragem. Caso precise adicionar valores posteriormente, atualize o span.
Regras para atributos:
- As chaves devem ser strings não nulas.
- Os valores devem ser uma string, booleano, número (ponto flutuante ou inteiro) ou um array desses tipos.
Além disso, existem Atributos Semânticos, que são convenções padronizadas para nomes de atributos comuns. Utilizar nomes semânticos facilita a padronização de metadados entre sistemas.
Eventos do Span
Um evento do span pode ser visto como uma mensagem de log estruturada (ou anotação) associada a um span. Geralmente é usado para marcar um ponto singular e significativo no tempo durante a duração de um span.
Exemplo prático em um navegador web:
- Carregamento da página → O span é mais apropriado, pois acompanha uma operação com início e fim.
- Página se torna interativa → Um evento do span é mais adequado, pois representa um ponto específico no tempo.
Quando usar eventos do span versus atributos?
Se o timestamp específico for relevante, use um evento do span. Caso contrário, use atributos.
Links do Span
Os links permitem associar um span a um ou mais spans, indicando uma relação causal.
Por exemplo, em um sistema distribuído, algumas operações são rastreadas por um trace. Em resposta a essas operações, uma nova tarefa pode ser enfileirada para execução assíncrona. Para associar o trace da operação subsequente ao trace original, criamos um link de span.
Links são opcionais, mas são uma maneira eficaz de relacionar spans de diferentes traces.
Status do Span
Cada span possui um status com três valores possíveis:
- Unset: (padrão) indica que a operação foi concluída sem erros.
- Error: indica que ocorreu algum erro, como um HTTP 500 em um servidor.
- Ok: explicitamente definido pelo desenvolvedor para marcar o span como bem-sucedido.
Nota: Quando não houver um status Ok para spans que concluíram sem erros, considera-se um evento bem sucedido, pois o valor padrão Unset já cobre esse caso. Ok é usado para deixar explícito que um span é considerado bem-sucedido, sem ambiguidades.
Tipo do Span (Span Kind)
Ao criar um span, ele pode ser classificado como Client, Server, Internal, Producer ou Consumer. O tipo de span informa ao backend de rastreamento como o trace deve ser montado.
- Client: Representa uma chamada remota síncrona de saída, como uma solicitação HTTP ou consulta ao banco de dados.
- Server: Representa uma chamada remota síncrona recebida, como uma solicitação HTTP ou RPC.
- Internal: Representa operações que não cruzam limites de processo (ex: chamadas de funções locais ou middlewares).
- Producer: Representa a criação de uma tarefa a ser processada de forma assíncrona (ex: inserção em uma fila).
- Consumer: Representa o processamento de uma tarefa criada por um producer. Pode ser iniciado muito tempo após o término do span do producer.
Se o tipo não for especificado, ele é considerado Internal.
Métricas
Uma métrica é uma medida de um serviço capturada em tempo de execução. O momento em que a medição é registrada é conhecido como um evento de métrica, que inclui não apenas o valor medido, mas também o horário da captura e os metadados associados.
Métricas de aplicação e requisição são indicadores importantes de disponibilidade e desempenho. Métricas personalizadas podem oferecer insights sobre como os indicadores de disponibilidade impactam a experiência do usuário ou o negócio. Os dados coletados podem ser utilizados para alertar sobre interrupções ou para acionar decisões de escalonamento automático durante períodos de alta demanda.
Para compreender como as métricas funcionam quando se utiliza as bibliotecas de instrumentação compatíveis com o Priax, vamos analisar os componentes principais que ajudam na instrumentação do código.
Provedor de Meter (Meter Provider)
O Meter Provider é uma fábrica de Meters. Em aplicações, geralmente é inicializado uma única vez e seu ciclo de vida corresponde ao ciclo de vida da aplicação. A inicialização de um Meter Provider também envolve a configuração de Recursos e Exportadores. Esse é, normalmente, o primeiro passo ao configurar métricas no Priax. Em alguns SDKs compatíveis, um Meter Provider global já é inicializado automaticamente.
Meter
Um Meter é responsável por criar instrumentos de métrica que capturam medições sobre um serviço em tempo de execução. Os Meters são criados a partir de Meter Providers.
Exportador de Métricas (Metric Exporter)
Os Exportadores de Métricas enviam dados de métricas para um consumidor. Esse consumidor pode ser uma saída padrão (para depuração durante o desenvolvimento), o Priax utiliza o Opentelemetry Collector, ou qualquer backend de código aberto ou proprietário de sua escolha.
Instrumentos de Métrica (Metric Instruments)
No Priax, as medições são capturadas por instrumentos de métrica, definidos pelos seguintes atributos:
- Nome
- Tipo
- Unidade (opcional)
- Descrição (opcional)
O nome, a unidade e a descrição são definidos pelo desenvolvedor ou por convenções semânticas para métricas comuns, como requisições e processos.
Os tipos de instrumentos compatíveis incluem:
- Counter: Valor acumulado ao longo do tempo (ex.: um odômetro, que só aumenta).
- Counter Assíncrono: Similar ao Counter, mas coletado uma vez por exportação, útil quando você tem acesso apenas ao valor agregado.
- UpDownCounter: Valor acumulado que pode aumentar ou diminuir (ex.: o tamanho de uma fila).
- UpDownCounter Assíncrono: Similar ao UpDownCounter, mas coletado uma vez por exportação.
- Gauge: Mede um valor atual no momento da leitura (ex.: marcador de combustível de um carro). É síncrono.
- Gauge Assíncrono: Similar ao Gauge, mas coletado uma vez por exportação.
- Histogram: Agrega valores no lado do cliente, como latências de requisições. Útil para estatísticas de valores, como quantas requisições levaram menos de 1s.
Agregação
Além dos instrumentos de métrica, a agregação é um conceito importante. A agregação combina uma grande quantidade de medições em estatísticas exatas ou estimadas sobre eventos de métrica em uma janela de tempo. O protocolo OTLP transporta essas métricas agregadas.
A API do Priax fornece uma agregação padrão para cada instrumento, que pode ser personalizada usando Views. O objetivo do projeto Priax é fornecer agregações padrão que sejam compatíveis com ferramentas de visualização e backends de telemetria.
Diferentemente do rastreamento de requisições, que captura o ciclo de vida de uma requisição, as métricas fornecem informações estatísticas em agregados. Exemplos de uso incluem:
- Total de bytes lidos por um serviço, por tipo de protocolo.
- Total de bytes lidos e bytes por requisição.
- Duração de chamadas de sistema.
- Tamanhos de requisição para identificar tendências.
- Uso de CPU ou memória de um processo.
- Valores médios de saldo em contas.
- Quantidade atual de requisições ativas.
Views
As Views oferecem flexibilidade para personalizar a saída de métricas pelo SDK. É possível:
- Escolher quais instrumentos de métrica serão processados ou ignorados.
- Configurar a agregação e os atributos a serem reportados nas métricas.
Suporte por Linguagem
O suporte às implementações específicas da API e do SDK de Métricas, por linguagem, está conforme a tabela abaixo:
| Linguagem | Status |
| C++ | Estável |
| C#/.NET | Estável |
| Erlang/Elixir | Em desenvolvimento |
| Go | Estável |
| Java | Estável |
| JavaScript | Estável |
| PHP | Estável |
| Python | Estável |
| Ruby | Em desenvolvimento |
| Rust | Beta |
| Swift | Em desenvolvimento |
Logs
Um log é um registro textual com marcação de tempo, podendo ser estruturado (recomendado) ou não estruturado, acompanhado de metadados opcionais. Entre todos os sinais de telemetria, os logs possuem o maior legado, já que a maioria das linguagens de programação inclui capacidades de logging nativas ou bibliotecas amplamente utilizadas.
Logs de Aplicação no Priax
O Priax não define uma API ou SDK específicos para criar logs. Em vez disso, os logs no Priax são aqueles já existentes, oriundos de frameworks de logging ou componentes de infraestrutura. Os SDKs e bibliotecas de instrumentação compatíveis com o Priax e a autoinstrumentação utilizam diversos componentes para correlacionar automaticamente logs com traces.
O suporte da biblioteca de instrumentação Priax para logs é projetado para ser totalmente compatível com sistemas existentes, oferecendo ferramentas para adicionar contexto adicional aos logs e manipulá-los em um formato comum, independentemente da origem.
Nas aplicações, os logs são criados usando qualquer biblioteca ou funcionalidade nativa de logging. Ao adicionar autoinstrumentação ou ativar um SDK de instrumentação compatível com o Priax, os logs são correlacionados automaticamente existentes com quaisquer traces e spans ativos, encapsulando o corpo do log com seus respectivos IDs.
Suporte por Linguagem
As bibliotecas de instrumentação atualmente compatíveis com o Priax suportam o envio de logs para as seguintes linguagens de programação:
| Linguagem | Status |
|---|---|
| C++ | Estável |
| C#/.NET | Estável |
| Erlang/Elixir | Em desenvolvimento |
| Go | Beta |
| Java | Estável |
| JavaScript | Em desenvolvimento |
| PHP | Estável |
| Python | Em desenvolvimento |
| Ruby | Em desenvolvimento |
| Rust | Beta |
| Swift | Em desenvolvimento |
Logs Estruturados, Não Estruturados e Semi-Estruturados
Logs Estruturados
Logs estruturados seguem um formato consistente e legível por máquina, como JSON.
Exemplo em aplicação:
{
"timestamp": "2024-08-04T12:34:56.789Z",
"level": "INFO",
"service": "autenticacao-usuario",
"mensagem": "Login do usuário bem-sucedido",
"contexto": {
"userId": "12345",
"ipAddress": "192.168.1.1"
}
}
Logs Não Estruturados
Logs não estruturados não seguem um formato consistente. Apesar de serem mais legíveis para humanos, são difíceis de processar em larga escala.
Exemplo:[ERRO] 2024-08-04 12:45:23 - Falha ao conectar ao banco de dados. Timeout.
Logs Semi-Estruturados
Logs semi-estruturados utilizam padrões consistentes, mas com formatações variadas entre sistemas.
Exemplo:2024-08-04T12:45:23Z level=ERROR service=autenticacao-usuario action=login mensagem="Senha inválida"
Baggage
Informações contextuais propagadas entre sinais.
O Baggage representa informações contextuais que são mantidas junto ao contexto. O Baggage é uma estrutura de chave-valor que permite a propagação de dados arbitrários ao lado do contexto.
Com o Baggage, é possível transmitir dados entre serviços e processos, tornando essas informações acessíveis para serem adicionadas a traces, métricas ou logs nos serviços subsequentes.
Para que serve o Baggage
O Baggage é ideal para incluir informações que estão disponíveis no início de uma requisição, mas que precisam ser acessadas em estágios posteriores. Exemplos incluem:
- Identificação de Contas
- IDs de Usuários
- IDs de Produtos
- IPs de origem
Propagar essas informações usando o Baggage possibilita análises mais detalhadas na telemetria. Por exemplo, incluir um ID de Usuário em um span que monitora uma chamada ao banco de dados facilita responder perguntas como: “Quais usuários estão enfrentando as chamadas mais lentas ao banco de dados?”. Também é possível registrar informações de uma operação downstream e incluir o mesmo ID de Usuário nos dados de log.
Considerações de segurança do Baggage
Itens sensíveis no Baggage podem ser compartilhados com recursos não intencionais, como APIs de terceiros. Isso ocorre porque instrumentações automáticas incluem o Baggage na maioria das requisições de rede do seu serviço.
Especificamente, o Baggage e outras partes do contexto de trace são enviados em cabeçalhos HTTP, ficando visíveis para quem inspecionar o tráfego da rede. Se o tráfego estiver restrito à sua rede interna, esse risco pode não se aplicar, mas é importante lembrar que serviços downstream podem propagar o Baggage para fora da sua rede.
Além disso, não há verificações de integridade embutidas para garantir que os itens do Baggage sejam de sua origem. Por isso, tome cuidado ao acessá-los.
O Baggage não é o mesmo que atributos
Uma diferença importante é que o Baggage é um armazenamento separado de chave-valor, e não está associado diretamente aos atributos de spans, métricas ou logs, a menos que seja adicionado explicitamente.
Para adicionar entradas do Baggage como atributos, é necessário ler os dados do Baggage e incluí-los manualmente como atributos em spans, métricas ou logs.
Como um dos usos comuns do Baggage é adicionar dados como atributos de spans em um trace inteiro, várias linguagens oferecem Baggage Span Processors que automaticamente adicionam dados do Baggage como atributos na criação de spans.
Instrumentação com Bibliotecas OpenTelemetry
O envio de sinais para o Priax pode ser realizado através de instrumentação própria, alterando o código da aplicação a ser observada sem inserir componentes de terceiros à sua infraestrutura. Este processo, no entanto pode ser custoso e levar algum tempo. Para que se possa acelerar esse processo, recomendamos, apesar de não ser a única opção, as bibliotecas e SDKs open-source do OpenTelemetry. Tais bibliotecas seguem os padrões OpenTracing e se tornaram a mais difundida solução de instrumentação para o mercado de Observabilidade.
Como o OpenTelemetry facilita a instrumentação
Para tornar um sistema observável, é necessário instrumentá-lo: ou seja, o código dos componentes do sistema deve emitir traces, métricas e logs.
Com o OpenTelemetry, você pode instrumentar seu código de duas maneiras principais:
-
Soluções baseadas em código
Utilizando APIs e SDKs oficiais disponíveis para a maioria das linguagens de programação. -
Soluções sem código
Ideais para casos em que você não pode ou não deseja modificar o aplicativo que precisa de telemetria.
Soluções baseadas em código
Essas soluções permitem uma telemetria mais profunda e detalhada, gerada diretamente pela sua aplicação. Elas utilizam a API do OpenTelemetry para produzir telemetria personalizada, complementando as informações coletadas por soluções sem código.
Soluções sem código
Perfeitas para começar rapidamente ou quando o aplicativo não pode ser modificado. Essas soluções oferecem uma telemetria rica baseada em bibliotecas utilizadas ou no ambiente em que a aplicação está sendo executada. Elas fornecem dados sobre o que está acontecendo nas "bordas" da aplicação, como interações externas e dependências.
Nota: É possível usar ambas as abordagens ao mesmo tempo para maximizar a observabilidade.
Benefícios adicionais do OpenTelemetry
O OpenTelemetry vai além de oferecer soluções com e sem código. Ele também inclui os seguintes recursos:
- Bibliotecas compatíveis: Bibliotecas podem usar a API do OpenTelemetry como dependência, sem impactar aplicações que não importem o SDK.
- Sinais flexíveis: Para cada tipo de sinal (traces, métricas, logs), existem várias formas de criá-los, processá-los e exportá-los.
- Correlação de sinais: Com a propagação de contexto integrada, é possível correlacionar sinais, independentemente de onde foram gerados.
- Recursos e Escopos de Instrumentação: Permitem agrupar sinais por diferentes entidades, como o host, sistema operacional ou cluster Kubernetes.
- Padrões e especificações: Cada implementação de linguagem do OpenTelemetry segue os requisitos e expectativas definidos pela especificação oficial.
- Convenções semânticas: Fornecem um esquema de nomenclatura comum para padronizar métricas, logs e traces entre diferentes bases de código e plataformas.
Formas de Instrumentação
Instrumentação sem código
A instrumentação sem código adiciona as capacidades da API e SDK do OpenTelemetry à sua aplicação, geralmente por meio de uma instalação de agente ou algo semelhante a um agente. Os mecanismos específicos variam conforme a linguagem, incluindo manipulação de bytecode, monkey patching ou eBPF, para injetar chamadas à API e ao SDK do OpenTelemetry diretamente na aplicação.
Como funciona
Normalmente, a instrumentação sem código adiciona suporte para bibliotecas que sua aplicação utiliza. Isso significa que requisições e respostas, chamadas a bancos de dados, chamadas de filas de mensagens, entre outros, serão instrumentados automaticamente. No entanto, o código da sua aplicação geralmente não é instrumentado. Para isso, é necessário utilizar instrumentação baseada em código.
Além disso, a instrumentação sem código permite configurar as bibliotecas de instrumentação e os exportadores que serão carregados.
Configuração
Você pode configurar a instrumentação sem código por meio de variáveis de ambiente e outros mecanismos específicos da linguagem, como propriedades do sistema ou argumentos passados para métodos de inicialização. Para começar, é necessário apenas configurar o nome do serviço para identificá-lo no backend de observabilidade escolhido.
Outras opções de configuração incluem:
- Configuração específica para fontes de dados.
- Configuração de exportadores.
- Configuração de propagadores.
- Configuração de recursos.
Suporte de linguagens para instrumentação automática
A instrumentação automática está disponível para as seguintes linguagens:
- .NET
- Go
- Java
- JavaScript
- PHP
- Python
Para realizar a instrumentação de sua aplicação, utilizando o método Zero Code, siga as instruções da página do OpenTelemetry.
Instrumentação Baseada em Código
A instrumentação baseada em código permite criar telemetria personalizada diretamente no código da sua aplicação. Abaixo estão os passos essenciais para configurar essa abordagem:
1. Importar a API e o SDK do OpenTelemetry
- Serviços: Dependem da API e do SDK do OpenTelemetry.
- Bibliotecas: Apenas dependem da API.
Para mais detalhes sobre a API e o SDK, consulte a especificação do OpenTelemetry.
2. Configurar a API do OpenTelemetry
-
Fornecedores de Tracer e Meter:
- Crie um TracerProvider para gerar traços.
- Crie um MeterProvider para gerar métricas.
-
Nomeação:
- Use um nome que identifique o componente sendo instrumentado.
- Exemplo: para uma biblioteca, use algo como
com.example.myLibrary. - Inclua uma versão no formato semver (ex.:
semver:1.0.0).
3. Configurar o SDK do OpenTelemetry
- Exportação de dados: Configure o SDK para exportar telemetria a um backend de análise.
- Opções específicas da linguagem: Verifique as opções de ajuste disponíveis para a sua linguagem.
A configuração pode ser feita programaticamente, por meio de arquivos de configuração ou outros mecanismos.
4. Criar Dados de Telemetria
- Traços e Métricas:
- Gere traços e eventos de métricas com os objetos
TracereMeter.
- Gere traços e eventos de métricas com os objetos
- Bibliotecas de Instrumentação:
- Use bibliotecas de instrumentação disponíveis para suas dependências.
- Consulte o repositório ou registro da sua linguagem para obter mais informações.
5. Exportar Dados de Telemetria
- Métodos de Exportação:
-
Exportação no processo:
- Importe e use exportadores para traduzir os objetos de telemetria do OpenTelemetry em formatos apropriados para ferramentas de análise (ex.: Jaeger ou Prometheus).
-
Exportação via OTLP e Collector:
- Use o protocolo OTLP para enviar dados ao OpenTelemetry Collector, que pode atuar como um proxy, sidecar ou processo separado.
- O Collector encaminha os dados para ferramentas de análise.
-
Para realizar a instrumentação de sua aplicação, utilizando o método baseado em código siga as instruções da página do OpenTelemetry.
Instrumentação de Bibliotecas
A instrumentação nativa de bibliotecas com OpenTelemetry oferece uma experiência aprimorada tanto para desenvolvedores quanto para usuários finais, eliminando a necessidade de expor e documentar hooks personalizados. Aqui está como funciona e os benefícios associados:
Como Adicionar Instrumentação Nativa
- Instrumentação via Hooks ou dynamic runtime patching:
OpenTelemetry fornece bibliotecas de instrumentação para várias linguagens, que tipicamente utilizam hooks de bibliotecas ou dynamic runtime patching do código.
Vantagens da Instrumentação Nativa
-
Melhoria na Observabilidade e Experiência do Usuário:
- Remove a necessidade de hooks personalizados, facilitando a integração.
- APIs do OpenTelemetry são fáceis de usar e consistentes para os usuários finais.
-
Telemetria Consistente e Correlação Aprimorada:
- Traços, logs e métricas provenientes do código da biblioteca e da aplicação são correlacionados, criando uma visão coesa dos eventos.
- Convenções comuns garantem uniformidade entre tecnologias, bibliotecas e linguagens.
-
Extensibilidade e Ajustes Fáceis:
- Sinais de telemetria podem ser ajustados (filtrados, processados, agregados) para atender a diferentes cenários de consumo.
- OpenTelemetry oferece extensibilidade bem documentada para personalização.
Para realizar a instrumentação de sua aplicação, utilizando as bibliotecas nativas OpenTelemetry, siga as instruções da página do OpenTelemetry.
Instrumentação com Kubernetes Operator
A instrumentação de aplicações que rodam em workloads Kubernetes é facilitada pelo Opentelemetry Kubernetes Operator que é uma implementação de um Operador Kubernetes que gerencia coletores e a auto-instrumentação das cargas de trabalho usando bibliotecas de instrumentação do OpenTelemetry.
Introdução
O Operador OpenTelemetry é uma implementação de um Operador Kubernetes.
O operador gerencia:
- Coletor OpenTelemetry
- Auto-instrumentação das cargas de trabalho (pods) usando bibliotecas de instrumentação do OpenTelemetry
Início Rápido
Para instalar o operador em um cluster existente, certifique-se de ter o cert-manager instalado e execute:
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
Assim que a implantação do opentelemetry-operator estiver pronta, crie uma instância do Coletor OpenTelemetry (otelcol) como o exemplo abaixo:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: simplest
spec:
config: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
exporters:
# NOTA: Antes da versão v0.86.0, use `logging` ao invés de `debug`.
debug:
service:
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [debug]
EOF
Para mais opções de configuração e para configurar a injeção de auto-instrumentação das cargas de trabalho usando as bibliotecas de instrumentação do OpenTelemetry, continue lendo aqui.
Injeção de Auto-instrumentação
Configurar Instrumentação Automática
Para gerenciar a instrumentação automática, o operador precisa ser configurado para identificar quais pods instrumentar e qual instrumentação automática usar nesses pods. Isso é feito por meio do CRD de Instrumentação.
A criação correta do recurso de Instrumentação é fundamental para que a instrumentação automática funcione. Certifique-se de que todos os endpoints e variáveis de ambiente estejam configurados corretamente.
.NET
O seguinte comando criará um recurso básico de Instrumentação configurado especificamente para serviços .NET:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: demo-instrumentation
spec:
exporter:
endpoint: http://demo-collector:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
EOF
Por padrão, o recurso de Instrumentação para serviços .NET usa OTLP com o protocolo HTTP/Protobuf. O endpoint configurado deve ser capaz de receber OTLP via HTTP/Protobuf, como no exemplo: http://demo-collector:4318.
Excluindo bibliotecas de instrumentação
Se você não quiser usar determinadas bibliotecas, configure as variáveis de ambiente OTEL_DOTNET_AUTO_[SIGNAL]_[NAME]_INSTRUMENTATION_ENABLED=false.
Exemplo:
dotnet:
env:
- name: OTEL_DOTNET_AUTO_TRACES_GRPCNETCLIENT_INSTRUMENTATION_ENABLED
value: false
- name: OTEL_DOTNET_AUTO_METRICS_PROCESS_INSTRUMENTATION_ENABLED
value: false
Java
O seguinte comando criará um recurso básico de Instrumentação para serviços Java:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: demo-instrumentation
spec:
exporter:
endpoint: http://demo-collector:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
EOF
Por padrão, a instrumentação Java usa OTLP com HTTP/Protobuf.
Excluindo bibliotecas de instrumentação
Para desativar bibliotecas específicas:
- Use
OTEL_INSTRUMENTATION_[NAME]_ENABLED=false. - Para desativar todas por padrão e ativar somente algumas:
OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=false
OTEL_INSTRUMENTATION_[NAME]_ENABLED=true
Configurar Instrumentação Automática
Para gerenciar a instrumentação automática, o operador precisa ser configurado para identificar quais pods instrumentar e qual instrumentação automática usar nesses pods. Isso é feito por meio do CRD de Instrumentação.
A criação correta do recurso de Instrumentação é fundamental para que a instrumentação automática funcione. Certifique-se de que todos os endpoints e variáveis de ambiente estejam configurados corretamente.
.NET
O seguinte comando criará um recurso básico de Instrumentação configurado especificamente para serviços .NET:
Por padrão, o recurso de Instrumentação para serviços .NET usa OTLP com o protocolo HTTP/Protobuf. O endpoint configurado deve ser capaz de receber OTLP via HTTP/Protobuf, como no exemplo: http://demo-collector:4318.
Excluindo bibliotecas de instrumentação
Se você não quiser usar determinadas bibliotecas, configure as variáveis de ambiente OTEL_DOTNET_AUTO_[SIGNAL]_[NAME]_INSTRUMENTATION_ENABLED=false.
Exemplo:
Java
O seguinte comando criará um recurso básico de Instrumentação para serviços Java:
Por padrão, a instrumentação Java usa OTLP com HTTP/Protobuf.
Excluindo bibliotecas de instrumentação
Para desativar bibliotecas específicas:
Node.js
O seguinte comando criará um recurso básico de Instrumentação para serviços Node.js:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: demo-instrumentation
spec:
exporter:
endpoint: http://demo-collector:4317
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
EOF
Por padrão, a instrumentação Node.js usa OTLP com gRPC.
Gerenciar bibliotecas de instrumentação
- Para ativar bibliotecas específicas:
nodejs:
env:
- name: OTEL_NODE_ENABLED_INSTRUMENTATIONS
value: http,nestjs-core
- Para desativar específicas:
nodejs:
env:
- name: OTEL_NODE_DISABLED_INSTRUMENTATIONS
value: fs,grpc
Python
O seguinte comando criará um recurso básico de Instrumentação para serviços Python:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: python-instrumentation
spec:
exporter:
endpoint: http://demo-collector:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
EOF
Por padrão, Python usa OTLP com HTTP/Protobuf.
Logs em Python
Para ativar a instrumentação automática de logs:
python:
env:
- name: OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED
value: 'true'
Excluindo bibliotecas de instrumentação
Defina OTEL_PYTHON_DISABLED_INSTRUMENTATIONS com os pacotes a serem excluídos.
Go
Para serviços Go, crie o recurso básico de Instrumentação:
kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: demo-instrumentation
spec:
exporter:
endpoint: http://demo-collector:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
EOF
A instrumentação Go usa um agente eBPF em um sidecar, que requer permissões elevadas.
Adicione anotações para ativar a instrumentação automática:
instrumentation.opentelemetry.io/inject-go: 'true'
instrumentation.opentelemetry.io/otel-go-auto-target-exe: '/path/to/executable'
Habilitar Instrumentação Automática
Adicione anotações ao seu deployment para ativar a instrumentação automática:
- .NET:
instrumentation.opentelemetry.io/inject-dotnet: "true" - Java:
instrumentation.opentelemetry.io/inject-java: "true" - Node.js:
instrumentation.opentelemetry.io/inject-nodejs: "true" - Python:
instrumentation.opentelemetry.io/inject-python: "true" - Go:
instrumentation.opentelemetry.io/inject-go: "true"
Ingestão de Dados e Backend de Armazenamento
O Priax suporta diferentes backends de armazenamento para Traces, Logs e Métricas coletados e podendo trabalhar com Opensearch, ou Elasticsearch. Para que os dados sejam injetados nessas bases de dados, utilizamos o OpenTelemetry Collector, e pode ser combinado ou não com outras tecnologias de transporte e transformação de dados como o Data Prepper e o Kafka.
Apresentaremos aqui os conceitos básicos do Collector e cenários práticos de implantação para utilização destas tecnologias com o Priax.
Data Collector
O Collector oferece uma implementação independente de fornecedor para receber, processar e exportar dados de telemetria. Ele elimina a necessidade de operar e manter múltiplos agentes/coletores. O Collector funciona com escalabilidade aprimorada e suporta formatos de dados de observabilidade de código aberto (por exemplo, Jaeger, Prometheus, Fluent Bit, etc.), enviando para um ou mais backends de código aberto ou comerciais. O agente Collector local é o destino padrão para o qual as bibliotecas de instrumentação exportam seus dados de telemetria.
Objetivos
- Usabilidade: Configuração padrão prática, suporta protocolos populares, funciona e coleta dados imediatamente.
- Desempenho: Altamente estável e eficiente sob diferentes cargas e configurações.
- Observabilidade: Um exemplo de serviço observável.
- Extensibilidade: Personalizável sem necessidade de alterar o código principal.
- Unificação: Base de código única, implantável como agente ou coletor, com suporte para rastros, métricas e logs.
Em abbientes de desenvolvimento ou testes e para ter resultados rápido com a observabilidade de maneira pontual é possível enviar seus dados diretamente de sua aplicação para um backend. Em ambientes de produção, no entanto, recomendamos usar um coletor junto com seus serviços, pois ele permite que o serviço descarregue dados rapidamente, realize cache quando necessário e, além disso, o coletor pode lidar com tarefas adicionais, como tentativas de reenvio, agrupamento, criptografia ou até mesmo filtragem de dados sensíveis.
O Collector facilita a unificação de dados, visto que suporta diversas formas de ingestão (Receivers) e diversas formas de exportação de dados (Exporters). Abaixo podemos ver como o Collector trabalha e como ele pode ser utilizado para enviar dados para os Backends de Armazenamento compatíveis com o Priax.
Como se pode observar o Collector pode receber dados diretamente das aplicações instrumentadas, de outros sistemas de observabilidade como o Prometheus ou o Jaeger mas também pode receber dados de outros Collectors ou buscar dados em sistemas Kafka, que podem ter sido alimentados por outros Collectors. Dessa forma pode-se criar um layout de implantação flexível onde os Collectors podem atuar como gateways concentradores ou como agentes de simples transmissão de dados para os BackEnds de Armazenamento. Para enviar dados para o Backend de armazenamento compatível com o Priax (Opensearch) o Collector pode ou não utilizar o Data Prepper (ver abaixo as informações sobre o DataPrepper).
Para compreender mais sobre os layouts de implantação do Data Collector, consulte a página do OpenTelemetry que trata sobre o assunto.
Instalação e Configuração dos Collectors
A instalação dos Collectors é relativamente simples e pode ser realizada diretamente sobre os sistemas operacionais Windows ou Linux ou ainda utilizando Docker ou Kubernetes. As instruções para instalação podem ser consultadas na página de instalação do Collector do OpenTelemetry.
A Configuração completa do Collector pode ser consultada na extensa documentação fornecida pelo OpenTelemetry porém aqui apresentamos alguns cenários tipicamente utilizados.
Configuração do Collector para uso com e sem Data Prepper
receivers:
otlp:
protocols:
http:
#include_metadata: true
cors:
allowed_origins: ["http://*"]
allowed_headers: ["*"]
#max_age: 7200
#endpoint: 127.0.0.1:4318
kafka:
protocol_version: 3.3.0
group_id: priaxapm1
encoding: otlp_proto
brokers:
- priax-teste-01.servicebus.windows.net:9093
topic: priax
auth:
sasl:
username: $$ConnectionString
password: Endpoint=sb://kafkaAzure.servicebus.windows.net/;SharedAccessKeyName=teste-priax;SharedAccessKey=z5kREHICeAw0EEMySt7cP2kgh4XGb/zh4+AEhMe7r0g=;EntityPath=priax
mechanism: PLAIN
tls:
insecure: true
processors:
batch: {}
tail_sampling/priax:
decision_wait: 40s
policies:
- name: sample-ottl
type: ottl_condition
ottl_condition:
span:
- "status.code == 2 and attributes[\"lime.envelope.type\"] == nil"
- "status.code == 2 and attributes[\"lime.envelope.type\"] != nil and attributes[\"lime.envelope.type\"] != \"Notification\""
- "end_time - start_time > Duration(\"29s\")"
- "attributes[\"sample.force\"] == true"
- name: probabilistic-policy
type: probabilistic
probabilistic:
hash_salt: "custom-salt"
sampling_percentage: 0.1
k8sattributes:
extract:
metadata:
- k8s.namespace.name
- k8s.deployment.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.cronjob.name
- k8s.job.name
- k8s.node.name
- k8s.pod.name
- k8s.pod.uid
- k8s.pod.start_time
passthrough: false
pod_association:
- sources:
- from: resource_attribute
name: k8s.pod.ip
- sources:
- from: resource_attribute
name: k8s.pod.uid
- sources:
- from: connection
memory_limiter:
check_interval: 5s
limit_percentage: 80
spike_limit_percentage: 25
resource:
attributes:
- action: insert
from_attribute: k8s.pod.uid
key: service.instance.id
exporters:
debug:
verbosity: detailed
otlp/data-prepper:
endpoint: data-prepper:21890
tls:
insecure: true
opensearch:
http:
endpoint: https://opensearchserver:9200
auth:
authenticator: basicauth/client
extensions:
health_check:
pprof:
zpages:
endpoint: ":55679"
basicauth/client:
client_auth:
username: ingester
password: ingesterpass
service:
extensions: [health_check, pprof, zpages]
pipelines:
traces:
receivers: [otlp]
processors: [k8sattributes,memory_limiter,resource,batch,tail_sampling/priax]
exporters: [debug,opensearch]
metrics:
receivers: [otlp]
processors: [k8sattributes,memory_limiter,resource,batch]
exporters: [debug,opensearch]
logs:
receivers: [otlp]
processors: [k8sattributes,memory_limiter,resource,batch]
exporters: [debug,opensearch]
traceswithKafkaandDataprepper:
receivers: [kafka]
processors: [k8sattributes,memory_limiter,resource,batch]
exporters: [debug,otlp/dataprepper]
metricswithKafkaandDataprepper:
receivers: [kafka]
processors: [k8sattributes,memory_limiter,resource,batch]
exporters: [debug,otlp/dataprepper]
logswithKafkaandDataprepper:
receivers: [kafka]
processors: [k8sattributes,memory_limiter,resource,batch]
exporters: [debug,otlp/dataprepper]
Amostragem de Traces e Spans
É importante perceber que a configuração acima define que serão enviadas ao Backend de Armazenamento o 0,1% das spans coletadas pela instrumentação. Isso se dá nos trechos destacados abaixo:
[...]
processors:
[...]
tail_sampling/priax:
decision_wait: 40s
policies:
- name: sample-ottl
type: ottl_condition
ottl_condition:
span:
- "end_time - start_time > Duration(\"29s\")"
- "attributes[\"sample.force\"] == true"
- name: probabilistic-policy
type: probabilistic
probabilistic:
hash_salt: "custom-salt"
sampling_percentage: 0.1
[...]
pipelines:
traces:
receivers: [otlp]
processors: [k8sattributes,memory_limiter,resource,batch,tail_sampling/priax]
# Para não utilizar o sampling neste pipeline basta rever o processador tail_sampling/priax desta seção.
exporters: [debug,opensearch]
traces:
receivers:
- kafka
processors:
- batch
- tail_sampling/priax
exporters:
- otlp/data-prepper
[...]
Para que sejam enviados 100% dos traces para o Backend de Armazenamento, remova a configuração de sampling do pipeline desejado conforme comentado no exemplo acima.
Data Prepper
Data Prepper
O Data Prepper é um componente da infraestrutura do Opensearch. É um coletor de dados do lado do servidor, capaz de filtrar, enriquecer, transformar, normalizar e agregar dados para análise e visualização posteriores. É a ferramenta de ingestão de dados preferida para o OpenSearch, recomendada para a maioria dos casos de uso de ingestão de dados no OpenSearch, especialmente para o processamento de conjuntos de dados grandes e complexos.
Com o Data Prepper, você pode criar pipelines personalizados para melhorar a visão operacional de suas aplicações. Dois casos de uso comuns do Data Prepper são a análise de rastros (trace analytics) e a análise de logs (log analytics). A análise de rastros ajuda a visualizar fluxos de eventos e identificar problemas de desempenho. Já a análise de logs oferece ferramentas para aprimorar as capacidades de busca, realizar análises detalhadas e obter insights sobre o desempenho e o comportamento das suas aplicações.
Conceitos-chave e fundamentos
O Data Prepper processa dados por meio de pipelines personalizáveis. Esses pipelines são compostos por componentes modulares que podem ser ajustados para atender às suas necessidades, inclusive permitindo a integração de implementações próprias. Um pipeline do Data Prepper consiste nos seguintes componentes:
- Uma fonte (source)
- Um ou mais destinos (sinks)
- (Opcional) Um buffer
- (Opcional) Um ou mais processadores (processors)
Cada pipeline contém dois componentes obrigatórios: a fonte e o destino. Se um buffer, um processador, ou ambos estiverem ausentes do pipeline, o Data Prepper usará o buffer padrão bounded_blocking e um processador no-op. É importante observar que uma única instância do Data Prepper pode conter um ou mais pipelines.
O uso do Data Prepper com o Priax, facilita a adoção de padrões do Opensearch e é altamente recomendável.
Instalação do Data Prepper
A instalação do Data Prepper é relativamente simples e sua documentação pode ser consultada diretamente na página do OpenSearch.
Configurações básicas de pipeline
A configuração básica do Data Prepper para uso com o Priax pode ser vista abaixo:
entry-pipeline:
delay: "100"
source:
otel_trace_source:
ssl: false
buffer:
buffer_size: 10240
batch_size: 160
sink:
- pipeline:
name: "raw-trace-pipeline"
- pipeline:
name: "service-map-pipeline"
raw-trace-pipeline:
source:
pipeline:
name: "entry-pipeline"
buffer:
bounded_blocking:
buffer_size: 10240
batch_size: 160
processor:
- otel_trace_raw:
sink:
- stdout: null
- opensearch:
hosts: ["http://opensearchserver:9200"]
username: ingester
password: passwordingester
max_retries: 20
bulk_size: 4
insecure: true
index_type: trace-analytics-raw
service-map-pipeline:
delay: "100"
source:
pipeline:
name: "entry-pipeline"
buffer:
bounded_blocking:
buffer_size: 10240
batch_size: 160
processor:
- service_map_stateful:
sink:
- stdout: null
- opensearch:
hosts: ["http://opensearchserver:9200"]
username: ingester
password: passwordingester
max_retries: 20
bulk_size: 4
insecure: true
index_type: trace-analytics-service-map
Visualização de Dados de Aplicações
Conceitos Essenciais
O Priax oferece os recursos visuais para analisar o desempenho, a estrutura, as dependências de aplicações. Para que se possa compreender essas telas é importante entender alguns conceitos iniciais.
| Conceito | Descrição |
| Serviço | Os serviços são os blocos de construção das arquiteturas modernas de microserviços. Em termos gerais, um serviço agrupa endpoints, consultas ou tarefas com o objetivo de construir o aplicativo. Em aplicações mais antigas, um serviço representa uma porção lógica da aplicação que desempenha uma porção de trabalho delimitável da aplicação. |
| Transação | Representam um trecho de código delimitado sendo executado dentro de um serviço. Tal trecho de código pode ser o atendimento de usuários remotos através de um socket de rede (por exemplo um servidor Web atendendo a um usuário), a execução de uma lógica ou função interna para montar a resposta para esse usuário ou a conexão à serviços de rede externos para coletar dados ou informações necessárias. |
| Indicadores/Métricas | Métricas APM funcionam como métricas regulares, mas com controles específicos para o APM. Use essas métricas para receber alertas no nível do serviço sobre acessos, erros e diversas medidas de latência. |
| Trace | Um trace é usado para rastrear o tempo gasto por um aplicativo processando uma solicitação e o status dessa solicitação. Cada trace consiste de um ou mais spans. |
| Instrumentação | Instrumentação é o processo de adicionar código ao seu aplicativo para capturar e relatar dados de observabilidade. |
Explorando Serviços, Traces e Transações
Depois de instrumentar serviços e ativar o discovery de transações no Priax, os serviços serão populados com as transações que estão sendo executadas pelas aplicações e suas interações com usuários e demais sistemas com os quais trocam informações. No Priax, um serviço pode ser representado por um Container (oriundos de sistemas Docker ou similares), um Deplyment, Statefulset, Daemonset (esses três últimos componentes de um cluster Kubernetes) ou ainda por aplicações hospedadas em servidores de aplicação ou interpretadores de linguagens (java, .net, php, ruby, perl, pyton, etc).
Abaixo podemos ver um exemplo de Deployment de um cluster Kubernetes com as transações executadas dentro de seus Pods. Neste caso o Deployment representa o Serviço da aplicação instrumentada.
Para ver o mapa de um serviço instrumentado, basta selecioná-lo e no canto superior direito do painel da esquerda acessar suas dependências no botão .
.
Nesta tela é exibido o mapa de dependências do Serviço. Podemos visualizar tanto os componentes internos das aplicações, representados por transações quanto os componentes da infraestrutura que suportam essas transações, como bases de dados e a estrutura do Kubernetes. Ao selecionar uma Transação, são exibidas à direita os gráficos de Time Series dos indicadores e métricas que são gerados ao longo do tempo como a média de tempo de execução, taxa de erros e taxa de transferência de dados.
Ao selecionar uma operação na árvore, abaixo são exibidas os detalhes das transações e clicando na aba Transactions você pode filtrar o tempo de exibição que deseja para visualizar as ocorrências quando aquela operação foi executada pela aplicação.
Desta fora são exibidas todas execuções da operação ocorridas naquele período de tempo.
No ícone , podemos personalizar a janela de tempo que será exibida. Podemos também dar zoom-in e zoom-out ao longo do tempo para poder ter mais detalhes das execuções da transação. As barras que representam cada execução fornecem informações importantes sobre o tempo que levou cada execução. Barras mais longas foram as execuções que mais demoraram, e o tempo de execução pode ser visto passando o mouse em cima de cada execução, o que nos fornece outras informações como ID da Span e do Trace.
Ao clicar em uma das execuções podemos explorar todo o Trace da qual a execução da Transação faz parte e assim podemos compreender exatamente como transcorreu aquela execução, como foi chamada, por qual transação e o que ocorreu com aquela chamada de sistema como um todo.
O gráfico de Destacado em vermelho, acima, mostra a linha de tempo e a relação pai-filho entre as spans (execuções da transação) e o gráfico em verde, mostra como o tempo foi percentualmente distribuído entre as spans pai e filho de cada trace.
Clicando em cada span em qualquer um dos gráficos, é possível ver os detalhes de cada execução, incluindo logs e parâmetros de execução.
As métricas para cada transação tal como: tempo médio Latency (ou Avg Transaction Time), Número de execuções por período (Calls), taxa de erros por período (Error Tax), são exibidas no painel da direita, além de diversos dashboards com será demonstrado nesse documento.
Dentre as diversas tipos de transações que o Priax consegue detectar e analisar, exibir estatísticas, gráfcos e realizar drill down , além de produzir eventos e alertas estão:
- Transações de Servidor HTTP, gRPC, RPC,
- Transações internas (trechos de código que são executados internamente na aplicação, sem interação externa)
- Chamadas de Web Services para outros sistemas, estejam eles instrumentados ou não;
- Consultas à bancos de dados relacionais e não relacionais;
- Produção e consumo em sistemas de mensagens e filas como Kafka e RabbitMQ;
- Dados produzidos pelo navegador, coletado diretamente o comportamento do usuário.
Os indicadores tradicionalmente coletadas para cada Transação são:
- Média de Tempo de Execução ao longo do tempo para 5, 10 e 15 minutos.
- Média de Taxa de erros de execução ao longo do tempo para 5, 10 e 15 minutos.
- Média de taxa de transferência de dados para 5, 10 e 15 minutos.
No entanto as métricas podem ser customizadas para cada caso.
Detectando problemas em Transações
Ao detectar um problema, que pode ser pela detecção de limites auto-calculados o Priax realiza, com a utilização de técnicas de análise de dados e machine learning, uma análise profunda de causa raiz, diferenciando os indicadores que representam um sintoma dos indicadores que de fato estão causando o problema, e unificando isso em um único Evento.
A partir do momento que um novo evento é detectado e sua causa raiz isolada, ele pode ser direcionado para a equipe e indivíduo correto, podendo ele realizar análise das correlações dos indicadores e itens de configuração envolvidos no Evento.
Ao receber um evento, você pode realizar a análise de impactos e de dependências da Transação causa-raiz do evento, o que nos permite entender quais outras Transações, Serviços e Aplicações estão sendo afetadas pelo problema. Ao analisar as dependências, também podemos detectar indicadores de infraestrutura que podem estar causando indiretamente o Evento.
Sempre que um Evento é criado são realizadas as análises que evitam a duplicação de eventos, criação de falsos positivos e a criação de eventos isolados quando esses possuem a mesma causa raíz. Para tal o Priax realiza:
- Identificação de Indicadores correlacionados na mesma árvore de Causa-Efeito: Este procedimento garante que apenas um evento seja criado quando múltiplas transações dependentes entre sí, ou ainda recursos que sustentam essas transações estejam reportando comportamento anormal. Neste caso o Item de Configuração ou transação de mais baixo nível na árvore de causa-efeito será a eleito como causa raíz, e todos os demais itens impactados serão reportados como impactos neste mesmo evento.
- Análise flapping: Utilizada para indicadores com alternância constante entre estados, evitando a criação de eventos múltiplos para o mesmo indicador causa-raiz.
As trigger para disparo de Eventos podem ser configuradas manualmente ou ainda podem ser calculadas baseadas nos dados históricos com técnicas de Análise de Dados e Machine Learning.
Na tela acima, de edição do Indicador é possível habilitar o aprendizado de Triggers, definir se os limites deverão ser abaixo, acima ou abaixo e acima da normalidade, definir se haverá um perfil diferente de trigger por dia da semana e por horário, além de definir qual será o menor nível de alerta que será gerado pelas trigger automaticamente aprendidas.
Para saber mais sobre o Gerenciamento de Eventos, consulte o capítulo específico sobre Gerenciamento de Operações e Eventos neste manual.
APM Dashboards
Após instrumentar uma aplicação ou serviço e coletar métricas, traces e logs é possível no Priax criar painéis e relatórios a respeito de Transações ocorridas dentro de cada Serviço da Aplicação. Ao longo do tempo, com a execução continuada das transações, são produzidos dados Time Series com os indicadores relevantes a respeito de cada transação, como por exemplo:
- Média de Tempo de Execução ao longo do tempo para 5, 10 e 15 minutos.
- Média de Taxa de erros de execução ao longo do tempo para 5, 10 e 15 minutos.
- Média de taxa de transferência (throughput) de dados para 5, 10 e 15 minutos.
Tais dados podem ser visualizados em Dashboards customizados agregados por:
- Aplicações: Conjunto de Serviços que são agrupados logicamente formando a Aplicação.
- Serviços; Componente instrumentado e identificado com o mesmo nome de Serviço.
- EndPoints: Todo o destino de conexão identificado pelo conjunto de transações executadas em determinado período de tempo.
- Transação: Cada operação identificada por um nome na instrumentação realizada.
Abaixo pode ser visualizado um exemplo de Dashboard que apresenta dados sobre as Transações executadas ao londo de determinado período. Os Dashboards podem ser acessados através do ícone destacado na barra de ícones da esquerda na imagem abaixo.
Na tela acima são exibidos de acordo com o Número destacado:
- Tela de Filtros: Os filtros podem ser aplicados para definir quais Transações serão considerados no Dashboard. Neste dashboard de exemplo, foram adicionados componentes para que se possa filtrar por Aplicação, Serviço, Transação e Host que executou a transação. É possível, no entanto, filtrar por qualquer Atributo presente nas Spans, como por exemplo, por qualquer campo que represente um endpoint ou pelo tipo de operação realizada.
- Gráfico que exibe a taxa de execução e de erros ocorrida nas transações filtradas em cada período de tempo.
- Gráfico que exibe latência média e nos percentis 75 e 99 para cada período de tempo.
Todas os dashboards podem ser filtrados ao nível de Transação específica, podendo exibir dados dos seguintes tipos de transações entre outros:
- Transações de Servidor HTTP, gRPC, RPC: Contendo os endereços, métodos e detalhes da requisição realizada.
- Transações internas (trechos de código que são executados internamente na aplicação, sem interação externa): Contendo informações sobre o código executado, linguagem, versões, etc.
- Chamadas de Web Services para outros sistemas, estejam eles instrumentados ou não: Contendo os detalhes sobre a requisição http realizada.
- Consultas à bancos de dados relacionais e não relacionais: Contendo as consultar realizadas (Statement SQL) anonimizadas, a base de dados consultada, o endereço dos servidores consultados, etc.
- Produção e consumo em sistemas de mensagens e filas como Kafka e RabbitMQ: Contendo o nome dos tópicos os métodos de consumo e demais informações sobre o consumo ou produção nesses sistemas.
- Dados produzidos pelo navegador, coletado diretamente o comportamento do usuário: Contendo o detalhes sobre o User Agent (navegador) utilizado, ip, nome do usuário quando aplicável.
Os dashboards são completamente customizáveis, podendo receber novos componentes, filtros, gráficos e layout de exibição. Novos dashboards podem ser criados e publicados, criando novas abas de exibição no topo da tela de dashboards exibida na tela acima.
