<aside> 📝 参考資料
<aside> 💡 ここで扱うのはトレースだけ https://opentelemetry.io/ja/docs/concepts/signals/traces/
トレースは、リクエストがアプリケーションに投げられたときに何が起こるかの全体像を教えてくれます。
</aside>
計装していない状態
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "fastapi",
# "uvicorn",
# ]
# ///
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def hello(param: str | None = None):
return {"message": "Hello World", "param": param}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
uv run app.pyで起動します。
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# ]
# ///
import httpx
async def get(url: str, param: str | None = None):
async with httpx.AsyncClient() as client:
response = await client.get(url, params={"param": param})
response.raise_for_status()
return response
if __name__ == "__main__":
import asyncio
url = "<http://localhost:8000/>"
response = asyncio.run(get(url, "awesome"))
assert response.json() == {"message": "Hello World", "param": "awesome"}
サーバを起動した状態で、別のターミナルでuv run client.py
<aside>
✨ sys.executable(Python処理系のパス)をprintして「Select Interpreter」(VS CodeのPython拡張)
https://docs.python.org/ja/3/library/sys.html#sys.executable
シンタックスハイライトやジャンプが使えるようになります
</aside>
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "fastapi",
# "opentelemetry-sdk",
# "opentelemetry-instrumentation-fastapi",
# "uvicorn",
# ]
# ///
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
ConsoleSpanExporter,
)
from opentelemetry.trace import get_tracer_provider, set_tracer_provider
set_tracer_provider(TracerProvider())
get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
app = FastAPI()
@app.get("/")
async def hello(param: str | None = None):
return {"message": "Hello World", "param": param}
FastAPIInstrumentor.instrument_app(app)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
<aside> 📝 計装コードのリファレンス トレーサープロバイダー https://opentelemetry.io/ja/docs/concepts/signals/traces/#tracer-provider
Tracerのファクトリー
set_tracer_providerでグローバルに設定
get_tracer_providerで取得
トレーサープロバイダーの設定
BatchSpanProcessor
span_exporterとしてConsoleSpanExporter
クライアントのプログラムを実行すると、サーバを起動したターミナルに出力
{
"name": "GET / http send",
"context": {
"trace_id": "0x98849e894c54dca1ccc09a55eeff99f5",
"span_id": "0xc314092788163d3c",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": "0x1b4bd118b2a1c3e9",
"start_time": "2025-11-07T13:15:46.077006Z",
"end_time": "2025-11-07T13:15:46.077028Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"asgi.event.type": "http.response.start",
"http.status_code": 200
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
}
}
{
"name": "GET / http send",
"context": {
"trace_id": "0x98849e894c54dca1ccc09a55eeff99f5",
"span_id": "0xcdacc254a2829d2b",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": "0x1b4bd118b2a1c3e9",
"start_time": "2025-11-07T13:15:46.077281Z",
"end_time": "2025-11-07T13:15:46.077288Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"asgi.event.type": "http.response.body"
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
}
}
{
"name": "GET /",
"context": {
"trace_id": "0x98849e894c54dca1ccc09a55eeff99f5",
"span_id": "0x1b4bd118b2a1c3e9",
"trace_state": "[]"
},
"kind": "SpanKind.SERVER",
"parent_id": null,
"start_time": "2025-11-07T13:15:46.076566Z",
"end_time": "2025-11-07T13:15:46.077347Z",
"status": {
"status_code": "UNSET"
},
"attributes": {
"http.scheme": "http",
"http.host": "127.0.0.1:8000",
"net.host.port": 8000,
"http.flavor": "1.1",
"http.target": "/",
"http.url": "<http://localhost:8000/?param=awesome>",
"http.method": "GET",
"http.server_name": "localhost:8000",
"http.user_agent": "python-httpx/0.28.1",
"net.peer.ip": "127.0.0.1",
"net.peer.port": 54388,
"http.route": "/",
"http.status_code": 200
},
"events": [],
"links": [],
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.38.0",
"service.name": "unknown_service"
},
"schema_url": ""
}
}
スパン https://opentelemetry.io/ja/docs/concepts/signals/traces/#spans
スパン は、作業や操作の単位を表します。 スパンはトレースの構成要素です。
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "httpx",
# "opentelemetry-sdk",
# "opentelemetry-instrumentation-httpx",
# ]
# ///
import httpx
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
ConsoleSpanExporter,
)
from opentelemetry.trace import get_tracer_provider, set_tracer_provider
set_tracer_provider(TracerProvider())
get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
HTTPXClientInstrumentor().instrument()
async def get(url: str, param: str | None = None):
async with httpx.AsyncClient() as client:
response = await client.get(url, params={"param": param})
response.raise_for_status()
return response
if __name__ == "__main__":
import asyncio
url = "<http://localhost:8000/>"
response = asyncio.run(get(url, "awesome"))
assert response.json() == {"message": "Hello World", "param": "awesome"}