Primeiros Passos
Esta página mostrará como começar a usar o OpenTelemetry em Python.
Você aprenderá como instrumentar automaticamente uma aplicação simples, de forma que rastros, métricas, e logs sejam emitidos para o console.
Pré-requisitos
Certifique-se de ter o seguinte instalado localmente:
Aplicação de Exemplo
O exemplo a seguir usa uma aplicação básica em Flask. Se você não estiver usando Flask, tudo bem — você pode usar OpenTelemetry Python com outros frameworks web também, como Django e FastAPI. Para uma lista completa de bibliotecas para frameworks suportados, consulte o registro.
Para exemplos mais elaborados, consulte exemplos.
Instalação
Para começar, configure um ambiente em um novo diretório:
mkdir otel-getting-started
cd otel-getting-started
python3 -m venv venv
source ./venv/bin/activate
Agora instale Flask:
pip install flask
Crie e inicie um Servidor HTTP
Crie um arquivo app.py
e adicione o seguinte código a ele:
from random import randint
from flask import Flask, request
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route("/rolldice")
def roll_dice():
player = request.args.get('player', default=None, type=str)
result = str(roll())
if player:
logger.warning("%s esta jogando os dados: %s", player, result)
else:
logger.warning("Jogador anonimo esta jogando os dados: %s", result)
return result
def roll():
return randint(1, 6)
Execute a aplicação utilizando o comando abaixo e acesse http://localhost:8080/rolldice no seu navegador para garantir que está funcionando.
flask run -p 8080
Instrumentação
A instrumentação sem código gerará dados de telemetria em seu nome. Existem
várias opções que você pode seguir, abordadas em mais detalhes em
Instrumentação sem código. Aqui usaremos o agente
opentelemetry-instrument
.
Instale o pacote opentelemetry-distro
, que contém a API e SDK do
OpenTelemetry, além das ferramentas opentelemetry-bootstrap
e
opentelemetry-instrument
que serão utilizadas a seguir.
pip install opentelemetry-distro
Execute o comando opentelemetry-bootstrap
:
opentelemetry-bootstrap -a install
Isso instalará a instrumentação do Flask.
Execute a aplicação instrumentada
Agora, você poderá executar a sua aplicação instrumentada com
opentelemetry-instrument
e fazer com que os dados sejam emitidos no console:
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument \
--traces_exporter console \
--metrics_exporter console \
--logs_exporter console \
--service_name dice-server \
flask run -p 8080
Acesse http://localhost:8080/rolldice no seu navegador e recarregue a página algumas vezes. Depois de um tempo, você deverá ver os trechos exibidos no console, como o seguinte:
Ver exemplo de saída
{
"name": "/rolldice",
"context": {
"trace_id": "0xdb1fc322141e64eb84f5bd8a8b1c6d1f",
"span_id": "0x5c2b0f851030d17d",
"trace_state": "[]"
},
"kind": "SpanKind.SERVER",
"parent_id": null,
"start_time": "2023-10-10T08:14:32.630332Z",
"end_time": "2023-10-10T08:14:32.631523Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"http.method": "GET",
"http.server_name": "127.0.0.1",
"http.scheme": "http",
"net.host.port": 8080,
"http.host": "localhost:8080",
"http.target": "/rolldice?rolls=12",
"net.peer.ip": "127.0.0.1",
"http.user_agent": "curl/8.1.2",
"net.peer.port": 58419,
"http.flavor": "1.1",
"http.route": "/rolldice",
"http.status_code": 200
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.17.0",
"service.name": "dice-server",
"telemetry.auto.version": "0.38b0"
},
"schema_url": ""
}
}
{
"body": "Jogador anonimo esta jogando os dados: 3",
"severity_number": "<SeverityNumber.WARN: 13>",
"severity_text": "WARNING",
"attributes": {
"otelSpanID": "5c2b0f851030d17d",
"otelTraceID": "db1fc322141e64eb84f5bd8a8b1c6d1f",
"otelServiceName": "dice-server"
},
"timestamp": "2023-10-10T08:14:32.631195Z",
"trace_id": "0xdb1fc322141e64eb84f5bd8a8b1c6d1f",
"span_id": "0x5c2b0f851030d17d",
"trace_flags": 1,
"resource": "BoundedAttributes({'telemetry.sdk.language': 'python', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': '1.17.0', 'service.name': 'dice-server', 'telemetry.auto.version': '0.38b0'}, maxlen=None)"
}
O trecho gerado rastreia o tempo de vida de uma requisição para a rota
/rolldice
.
A linha de log emitida durante a solicitação contém o mesmo ID de rastro e ID de trecho e é exportada para o console via o exportador de logs.
Envie mais algumas solicitações para esta rota e em seguida, espere um pouco ou pare a execução da aplicação e você verá métricas na saída do console, como o seguinte:
Ver exemplo de saída
{
"resource_metrics": [
{
"resource": {
"attributes": {
"service.name": "unknown_service",
"telemetry.auto.version": "0.34b0",
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.13.0"
},
"schema_url": ""
},
"schema_url": "",
"scope_metrics": [
{
"metrics": [
{
"data": {
"aggregation_temporality": 2,
"data_points": [
{
"attributes": {
"http.flavor": "1.1",
"http.host": "localhost:5000",
"http.method": "GET",
"http.scheme": "http",
"http.server_name": "127.0.0.1"
},
"start_time_unix_nano": 1666077040061693305,
"time_unix_nano": 1666077098181107419,
"value": 0
}
],
"is_monotonic": false
},
"description": "mede o número de requisições HTTP simultâneas que estão atualmente em andamento",
"name": "http.server.active_requests",
"unit": "requests"
},
{
"data": {
"aggregation_temporality": 2,
"data_points": [
{
"attributes": {
"http.flavor": "1.1",
"http.host": "localhost:5000",
"http.method": "GET",
"http.scheme": "http",
"http.server_name": "127.0.0.1",
"http.status_code": 200,
"net.host.port": 5000
},
"bucket_counts": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"count": 1,
"explicit_bounds": [
0, 5, 10, 25, 50, 75, 100, 250, 500, 1000
],
"max": 1,
"min": 1,
"start_time_unix_nano": 1666077040063027610,
"sum": 1,
"time_unix_nano": 1666077098181107419
}
]
},
"description": "mede a duração da requisição HTTP recebida",
"name": "http.server.duration",
"unit": "ms"
}
],
"schema_url": "",
"scope": {
"name": "opentelemetry.instrumentation.flask",
"schema_url": "",
"version": "0.34b0"
}
}
]
}
]
}
Adicione instrumentação manual à instrumentação automática
A instrumentação automática captura telemetria nas bordas dos seus sistemas, como requisições HTTP de entrada e saída, mas não captura o que está acontecendo na sua aplicação. Para isso, você precisará escrever alguma instrumentação manual. Aqui está como você pode facilmente vincular a instrumentação manual com a instrumentação automática.
Rastros
Primeiro, modifique app.py
para incluir código que inicializa um rastreador e
o utiliza para criar um rastro que é filho do que é gerado automaticamente:
from random import randint
from flask import Flask
from opentelemetry import trace
# Adquira um rastreador
tracer = trace.get_tracer("diceroller.tracer")
app = Flask(__name__)
@app.route("/rolldice")
def roll_dice():
return str(roll())
def roll():
# Isso cria um novo trecho que é filho do atual
with tracer.start_as_current_span("roll") as rollspan:
res = randint(1, 6)
rollspan.set_attribute("roll.value", res)
return res
Agora, execute a aplicação novamente:
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument \
--traces_exporter console \
--metrics_exporter console \
--logs_exporter console \
--service_name dice-server \
flask run -p 8080
Quando você enviar uma requisição ao servidor, verá dois trechos no rastro
emitido para o console, chamado roll
, que registra o seu pai como o criado
automaticamente:
Ver exemplo de saída
{
"name": "roll",
"context": {
"trace_id": "0x6f781c83394ed2f33120370a11fced47",
"span_id": "0x623321c35b8fa837",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": "0x09abe52faf1d80d5",
"start_time": "2023-10-10T08:18:28.679261Z",
"end_time": "2023-10-10T08:18:28.679560Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"roll.value": "6"
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.17.0",
"service.name": "dice-server",
"telemetry.auto.version": "0.38b0"
},
"schema_url": ""
}
}
{
"name": "/rolldice",
"context": {
"trace_id": "0x6f781c83394ed2f33120370a11fced47",
"span_id": "0x09abe52faf1d80d5",
"trace_state": "[]"
},
"kind": "SpanKind.SERVER",
"parent_id": null,
"start_time": "2023-10-10T08:18:28.678348Z",
"end_time": "2023-10-10T08:18:28.679677Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"http.method": "GET",
"http.server_name": "127.0.0.1",
"http.scheme": "http",
"net.host.port": 8080,
"http.host": "localhost:8080",
"http.target": "/rolldice?rolls=12",
"net.peer.ip": "127.0.0.1",
"http.user_agent": "curl/8.1.2",
"net.peer.port": 58485,
"http.flavor": "1.1",
"http.route": "/rolldice",
"http.status_code": 200
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.17.0",
"service.name": "dice-server",
"telemetry.auto.version": "0.38b0"
},
"schema_url": ""
}
}
O parent_id
de roll
é o mesmo que o span_id
para /rolldice
, indicando
uma relação pai-filho!
Métricas
Agora modifique app.py
para incluir código que inicializa um medidor e o usa
para criar um instrumento contador que conta o número de jogadas para cada valor
de jogada possível:
# Estas são as declarações de _imports_ necessários
from opentelemetry import trace
from opentelemetry import metrics
from random import randint
from flask import Flask, request
import logging
# Adquira um rastreador
tracer = trace.get_tracer("diceroller.tracer")
# Adquira um medidor.
meter = metrics.get_meter("diceroller.meter")
# Agora crie um instrumento contador para fazer medições
roll_counter = meter.create_counter(
"dice.rolls",
description="O número de jogadas por valor de jogada",
)
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route("/rolldice")
def roll_dice():
# Isso cria um novo trecho que é filho do atual
with tracer.start_as_current_span("roll") as roll_span:
player = request.args.get('player', default = None, type = str)
result = str(roll())
roll_span.set_attribute("roll.value", result)
# Isso adiciona 1 ao contador para o valor de jogada dado
roll_counter.add(1, {"roll.value": result})
if player:
logger.warn("{} esta jogando os dados: {}", player, result)
else:
logger.warn("Jogador anonimo esta jogando os dados: %s", result)
return result
def roll():
return randint(1, 6)
Agora, execute a aplicação novamente:
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument \
--traces_exporter console \
--metrics_exporter console \
--logs_exporter console \
--service_name dice-server \
flask run -p 8080
Quando você enviar uma requisição para o servidor, verá a métrica do contador de jogadas emitida para o console, com contagens de valor separadas para cada jogada:
Ver exemplo de saída
{
"resource_metrics": [
{
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.17.0",
"service.name": "dice-server",
"telemetry.auto.version": "0.38b0"
},
"schema_url": ""
},
"scope_metrics": [
{
"scope": {
"name": "opentelemetry.instrumentation.flask",
"version": "0.38b0",
"schema_url": ""
},
"metrics": [
{
"name": "http.server.active_requests",
"description": "mede o número de requisições HTTP simultâneas que estão atualmente em andamento",
"unit": "requests",
"data": {
"data_points": [
{
"attributes": {
"http.method": "GET",
"http.host": "localhost:8080",
"http.scheme": "http",
"http.flavor": "1.1",
"http.server_name": "127.0.0.1"
},
"start_time_unix_nano": 1696926005694857000,
"time_unix_nano": 1696926063549782000,
"value": 0
}
],
"aggregation_temporality": 2,
"is_monotonic": false
}
},
{
"name": "http.server.duration",
"description": "mede a duração da requisição HTTP recebida",
"unit": "ms",
"data": {
"data_points": [
{
"attributes": {
"http.method": "GET",
"http.host": "localhost:8080",
"http.scheme": "http",
"http.flavor": "1.1",
"http.server_name": "127.0.0.1",
"net.host.port": 8080,
"http.status_code": 200
},
"start_time_unix_nano": 1696926005695798000,
"time_unix_nano": 1696926063549782000,
"count": 7,
"sum": 6,
"bucket_counts": [
1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"explicit_bounds": [
0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0,
750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0
],
"min": 0,
"max": 1
}
],
"aggregation_temporality": 2
}
}
],
"schema_url": ""
},
{
"scope": {
"name": "diceroller.meter",
"version": "",
"schema_url": ""
},
"metrics": [
{
"name": "dice.rolls",
"description": "O número de jogadas por valor de jogada",
"unit": "",
"data": {
"data_points": [
{
"attributes": {
"roll.value": "5"
},
"start_time_unix_nano": 1696926005695491000,
"time_unix_nano": 1696926063549782000,
"value": 3
},
{
"attributes": {
"roll.value": "6"
},
"start_time_unix_nano": 1696926005695491000,
"time_unix_nano": 1696926063549782000,
"value": 1
},
{
"attributes": {
"roll.value": "1"
},
"start_time_unix_nano": 1696926005695491000,
"time_unix_nano": 1696926063549782000,
"value": 1
},
{
"attributes": {
"roll.value": "3"
},
"start_time_unix_nano": 1696926005695491000,
"time_unix_nano": 1696926063549782000,
"value": 1
},
{
"attributes": {
"roll.value": "4"
},
"start_time_unix_nano": 1696926005695491000,
"time_unix_nano": 1696926063549782000,
"value": 1
}
],
"aggregation_temporality": 2,
"is_monotonic": true
}
}
],
"schema_url": ""
}
],
"schema_url": ""
}
]
}
Envie telemetria para o OpenTelemetry Collector
O OpenTelemetry Collector é um componente crítico da maioria das implantações em produção. Alguns exemplos de quando é benéfico utilizar um Collector:
- Um único coletor de telemetria compartilhado por vários serviços, para reduzir a sobrecarga de troca de exportadores
- Agregando rastros entre vários serviços, executados em várias instâncias
- Um local central para processar rastros antes de exportá-los para um backend
A menos que você tenha apenas um único serviço ou esteja experimentando, você desejará usar um Collector em implantações de produção.
Configure e execute um Collector local
Primeiro, salve o seguinte código de configuração do Collector em um arquivo no
diretório /tmp/
:
# /tmp/otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
# NOTA: Antes da v0.86.0 use `logging` em vez de `debug`.
debug:
verbosity: detailed
processors:
batch:
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
processors: [batch]
metrics:
receivers: [otlp]
exporters: [debug]
processors: [batch]
logs:
receivers: [otlp]
exporters: [debug]
processors: [batch]
Em seguida, execute o comando docker para adquirir e executar o Collector com base nesta configuração:
docker run -p 4317:4317 \
-v /tmp/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector:latest \
--config=/etc/otel-collector-config.yaml
Agora, você terá uma instância do Collector em execução localmente, ouvindo na porta 4317.
Modifique o comando para exportar trechos e métricas via OTLP
O próximo passo é modificar o comando para enviar trechos e métricas para o coletor via OTLP em vez do console.
Para fazer isso, instale o pacote de exportador OTLP:
pip install opentelemetry-exporter-otlp
O agente opentelemetry-instrument
detectará o pacote que você acabou de
instalar e usará a exportação OTLP na próxima vez que for executado.
Execute a aplicação
Execute a aplicação como antes, mas não exporte para o console:
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument --logs_exporter otlp flask run -p 8080
Por padrão, opentelemetry-instrument
exporta rastros e métricas via OTLP/gRPC
e os enviará para localhost:4317
, endereço onde o Collector está ouvindo.
Quando você acessar a rota /rolldice
agora, verá a saída no processo do
Collector em vez do processo do Flask, que deve ser algo assim:
Ver exemplo de saída
2022-06-09T20:43:39.915Z DEBUG debugexporter/debug_exporter.go:51 ResourceSpans #0
Resource labels:
-> telemetry.sdk.language: STRING(python)
-> telemetry.sdk.name: STRING(opentelemetry)
-> telemetry.sdk.version: STRING(1.12.0rc1)
-> telemetry.auto.version: STRING(0.31b0)
-> service.name: STRING(unknown_service)
InstrumentationLibrarySpans #0
InstrumentationLibrary app
Span #0
Trace ID : 7d4047189ac3d5f96d590f974bbec20a
Parent ID : 0b21630539446c31
ID : 4d18cee9463a79ba
Name : roll
Kind : SPAN_KIND_INTERNAL
Start time : 2022-06-09 20:43:37.390134089 +0000 UTC
End time : 2022-06-09 20:43:37.390327687 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Attributes:
-> roll.value: INT(5)
InstrumentationLibrarySpans #1
InstrumentationLibrary opentelemetry.instrumentation.flask 0.31b0
Span #0
Trace ID : 7d4047189ac3d5f96d590f974bbec20a
Parent ID :
ID : 0b21630539446c31
Name : /rolldice
Kind : SPAN_KIND_SERVER
Start time : 2022-06-09 20:43:37.388733595 +0000 UTC
End time : 2022-06-09 20:43:37.390723792 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Attributes:
-> http.method: STRING(GET)
-> http.server_name: STRING(127.0.0.1)
-> http.scheme: STRING(http)
-> net.host.port: INT(5000)
-> http.host: STRING(localhost:5000)
-> http.target: STRING(/rolldice)
-> net.peer.ip: STRING(127.0.0.1)
-> http.user_agent: STRING(curl/7.82.0)
-> net.peer.port: INT(53878)
-> http.flavor: STRING(1.1)
-> http.route: STRING(/rolldice)
-> http.status_code: INT(200)
2022-06-09T20:43:40.025Z INFO debugexporter/debug_exporter.go:56 MetricsExporter {"#metrics": 1}
2022-06-09T20:43:40.025Z DEBUG debugexporter/debug_exporter.go:66 ResourceMetrics #0
Resource labels:
-> telemetry.sdk.language: STRING(python)
-> telemetry.sdk.name: STRING(opentelemetry)
-> telemetry.sdk.version: STRING(1.12.0rc1)
-> telemetry.auto.version: STRING(0.31b0)
-> service.name: STRING(unknown_service)
InstrumentationLibraryMetrics #0
InstrumentationLibrary app
Metric #0
Descriptor:
-> Name: roll_counter
-> Description: O número de jogadas por valor de jogada
-> Unit:
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: AGGREGATION_TEMPORALITY_CUMULATIVE
NumberDataPoints #0
Data point attributes:
-> roll.value: INT(5)
StartTimestamp: 2022-06-09 20:43:37.390226915 +0000 UTC
Timestamp: 2022-06-09 20:43:39.848587966 +0000 UTC
Value: 1
Próximos passos
Existem várias opções disponíveis para instrumentação automática em Python. Veja Instrumentação sem código para aprender sobre e como configurá-las.
Há muito mais na instrumentação manual do que apenas criar um trecho filho. Para aprender detalhes sobre a inicialização da instrumentação manual e muitas outras partes da API do OpenTelemetry que você pode usar, veja Instrumentação Manual.
Existem várias opções para exportar seus dados de telemetria com OpenTelemetry. Para aprender como exportar seus dados para um backend preferido, veja Exporters.
Se você gostaria de explorar um exemplo mais complexo, dê uma olhada na demonstração do OpenTelemetry, que inclui o Serviço de Recomendação baseado em Python e o Gerador de Carga.
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!