Introdução
Todo desenvolvedor em algum momento de sua carreira já passou raiva com mudança de alguma tecnologia. Sempre que isso acontece, é necessário fazer uma série de modificações em um, ou vários, sistemas que tomamos conta e é exatamente isso que torna todo o processo de mudança doloroso. Atualmente, a empresa que estou trabalhando, decidiu por mudar a ferramenta de APM que utilizamos para centralizar o trace de todos os sistemas em um lugar só. O problema que essa decisão gera é a mudança de todos os pontos do código que contém a geração de trace utilizando a biblioteca de uma ferramenta para utilizar a biblioteca de outra ferramenta, ou seja, sempre que houver esse tipo de mudança todo esse trabalho precisará ser refeito.
Mas qual é o problema de verdade? 🤔
Nós, programadores, somos contratados para resolver problemas, porém, sempre que precisamos fazer algum tipo de “trabalho braçal”, ou seja, ficar realizando alguma tarefa repetitiva, nos sentimos angustiados e desmotivados, e é exatamente esse tipo de trabalho que é gerado quando realizamos alterações de ferramentas em algum nível.
E não tem nada que possamos fazer nessa situação a não ser fazer esse trabalho chato? 🥹
Ainda bem que a maioria dos desenvolvedores pensam igual, e com isso, criaram uma ferramenta genérica capaz de centralizar a criação e envio de métricas, traces e logs, assim, podendo ser enviado para vários backends diferentes bastando mudar algumas configurações. Essa ferramenta “milagrosa” que será abordada nesse post é o OpenTelemetry.
🚨 Observação importante: Como nem tudo são flores, o trabalho chato precisará ser feito pelo menos uma vez, mudando da biblioteca atual para o OpenTelemetry.
Explorando a Solução
Aplicação de Exemplo
Para mostrar como tudo funciona, vamos utilizar uma API simples desenvolvida utilizando o framework web FastAPI:
|
|
app.py
|
|
Para testar a aplicação, basta executar:
|
|
Instrumentando o OpenTelemetry
Existem duas formas de se instrumentar o OpenTelemetry no python, mas vou focar apenas na instrumentação automática por fins de simplicidade.
Instalação
A instalação se divide em alguns pacotes diferentes, mas, para o nosso caso, em que o foco
está na simplicidade, podemos resumir apenas no pacote opentelemetry-distro
. Ele irá
instalar todas as bibliotecas e ferramentas necessárias para fazer a instrumentação
automática.
|
|
Após fazer a instalação, vamos fazer o bootstrap
, assim, o opentelemetry identificará as
bibliotecas que utilizamos e instalará tudo que será necessário para instrumenta-las
corretamente.
|
|
É possível ver quais as bibliotecas extras que serão instaladas através do comando
opentelemetry-bootstrap -a requirements
Executando a Aplicação
Agora que temos a ferramenta instalada, o comando para iniciar a aplicação é alterado para que o opentelemetry consiga carregar dinamicamente algumas coisas e já entregar alguns traces automáticamente, ficando:
|
|
Os parâmetros também podem ser configurados através de variáveis de ambiente, o que torna mais simples o comando, ficando:
|
|
A instrumentação automática é desabilitada sempre que usamos o
--reload
no uvicorn.
Como o foco do post é no APM, apenas as configurações para habilita-lo estão sendo mostras, mas, se necessário, é preciso definir outros parâmetros para habilitar o envio de métricas e logs.
Após a execução da aplicação usando o comando novo algumas informações novas serão mostradas no console em formato json. Essas informações são alguns traces que são capturados automaticamente pela ferramenta sem precisarmos fazer nenhum tipo de definição.
Adicionando Traces Manuais
Em alguns momentos é interessante criarmos alguns traces manuais para medir o tempo gasto em alguns pontos da nossa aplicação (ex: tempo gasto para obter um dado do banco de dados ou o tempo gasto em alguma requisição externa) ou salvar alguma informação importante do momento.
Para fazer essa adição, primeiro precisamos obter um tracer
(parecido com o que fazemos
para obter um logger
):
|
|
Para criar um span
nesse tracer é necessário criar um contexto novo contendo o código
que será monitorado, exemplo:
|
|
As vezes salvar algumas informações presentes somente naquele momento do código pode ser interessante para ajudar a identificar o que estava sendo executado no momento que o trace foi gerado. Essas informações podem ser adicionas ao span de duas formas:
|
|
Código Python Final
Após as modificações descritas anteriormente, nosso código ficou da seguinte forma:
app.py
|
|
Enviando as informações coletadas para o Servidor de APM
Agora que temos nosso código escrito utilizando o OpenTelemetry precisamos apenas
configurar o backend que será utilizado para salvar essas informações. Para fazer isso
vamos definir o endpoint do coletor de dados OpenTelemetry e mudar o exporter
de trace
do console
para o otlp
(formato utilizado pelo coletor de dados OpenTelemetry).
Podemos fazer isso adicionando alguns parâmetros ao opentelemetry-instrument
ou atráves
de variáveis de ambiente, como mostrado a seguir:
|
|
Caso o
exporter otlp endpoint
não seja definido, é utilizado o valor padrãohttp://localhost:4317/
É possível enviar as informações para o coletor e o console ao mesmo tempo. Para fazer isso basta definir o
exporter
comoconsole,otlp
.
Colocando tudo para rodar
Para mostrar tudo que foi ensinado nesse post funcionando, vamos utilizar o
Jaeger como coletor dos dados e observar os dados
chegando nele. Para fazer isso vamos executa-lo utilizando o docker
:
|
|
Após a aplicação iniciar e fazermos algumas requisições, podemos ver os traces chegando no jaeger através da interface que pode ser acessada pelo link http://localhost:16686/.
Conclusão
As vezes a mudança de ferramenta é necessária, seja para reduzir custos, adicionar funcionalidades ou para padronizar em uma ferramenta só, mas, nem sempre essa mudança precisa ser sinônimo de algo ruim. Como vimos nesse post, é possível utilizar de boas ferramentas para das suporte a outras, e assim, criar um ecosistema sustentável em que não precisamos ficar recriando a roda o tempo todo.