Flaskでトレース情報を取得!OpenTelemetryでアプリの動きを丸裸にする方法
生徒
「先生、Flaskで作ったアプリの処理がどこで時間がかかっているのか、もっと詳しく知る方法はありますか?」
先生
「それなら『トレース情報』を取得するのが一番ですよ。OpenTelemetry(オープンテレメトリ)という道具を使えば、リクエストがアプリの中をどう旅したかが全てわかります。」
生徒
「旅、ですか?なんだか難しそうですが、初心者でも設定できますか?」
先生
「大丈夫です!専門用語を噛み砕きながら、実際のコードと一緒に見ていきましょう。」
1. トレース情報とOpenTelemetryとは?
ウェブアプリを運用していると、一つのボタンを押したあとに、裏側で「データベースを見に行く」「画像を処理する」「メールを送る」といった複数の作業が連鎖して動きます。この一連の流れを追いかける記録のことを「トレース情報」と呼びます。
そして、このトレース情報を世界共通のルールで取得するための道具がOpenTelemetry(オープンテレメトリ)です。以前はツールごとに使い方がバラバラでしたが、OpenTelemetryという共通規格ができたおかげで、一度設定してしまえば、いろいろな監視ツールにデータを送れるようになりました。プログラミング未経験の方なら、アプリの健康状態を測るための「超精密なカルテ」を作る仕組みだと思ってください。
2. 分散トレーシングで処理の「迷子」を防ぐ
最近のシステムは、一つの大きなプログラムではなく、複数の小さなプログラムが協力して動くことが増えています。これを「分散システム」と呼びます。一つの処理が遅いとき、どのプログラムが原因なのかを突き止めるのは非常に困難です。
そこで役立つのが「分散トレーシング」です。リクエストに目印(ID)を付けて、複数のプログラムをまたいでも「これはあの時のリクエストだ!」と追いかけられるようにします。OpenTelemetryはこの目印の管理が得意です。これを使えば、複雑な迷路のようなシステムの中でも、処理がどこで足踏みしているのかを一目で見つけることができます。まさに可観測性(オブザーバビリティ)の核心と言える機能です。
3. OpenTelemetryをFlaskに導入する準備
Flaskでトレース情報を集めるには、Python専用のライブラリ(便利な追加機能)をインストールする必要があります。パソコンの「ターミナル」や「コマンドプロンプト」という黒い画面を開いて、以下のコマンドを入力してみましょう。
pip install opentelemetry-api opentelemetry-sdk opentelemetry-instrumentation-flask opentelemetry-exporter-otlp
難しい名前が並んでいますが、これらは「計測のルールを決めるもの」「実際に計測する機能」「Flaskと繋げるための接着剤」「データを送信するトラック」といった役割を分担しています。これらを一括で入れることで、FlaskがOpenTelemetryに対応できるようになります。
4. Flaskアプリでトレースを自動取得する最小コード
準備ができたら、実際にFlaskアプリに組み込んでみましょう。OpenTelemetryには「自動計装(じどうけいそう)」という素晴らしい機能があり、数行書くだけでFlaskの動きを勝手に記録してくれます。
from flask import Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# トレースを管理する大元(プロバイダー)を準備します
trace.set_tracer_provider(TracerProvider())
app = Flask(__name__)
# FlaskInstrumentorを使って、Flaskに「自動記録機能」を取り付けます
FlaskInstrumentor().instrument_app(app)
@app.route('/')
def hello():
return "自動トレースが有効になっています!"
if __name__ == '__main__':
app.run(port=5000)
このコードを実行すると、Flaskがリクエストを受け取った時間や、どんな結果を返したかが自動的に記録されるようになります。プログラミング初心者の方でも、おまじないのように数行追加するだけで本格的な監視が始められるのです。
5. 特定の処理を細かく記録する「スパン」の作成
自動記録だけでも便利ですが、時には「この計算処理にどれくらいかかったか」をもっと詳しく知りたい場合があります。そのときに使う単位が「スパン」です。スパンとは、一連の流れの中の「一区切りの作業」を指します。
以下のコードでは、重い計算を「計算中」という名前のスパンで囲んで記録する方法を紹介します。
import time
from flask import Flask
from opentelemetry import trace
app = Flask(__name__)
tracer = trace.get_tracer(__name__)
@app.route('/calculate')
def calculate():
# 「重い計算」という名前で特別な記録(スパン)を開始します
with tracer.start_as_current_span("heavy_calculation"):
time.sleep(2) # 2秒かかる計算のつもり
print("計算が終わりました")
return "計算結果を保存しました"
if __name__ == '__main__':
app.run()
このように with tracer.start_as_current_span という書き方を使うことで、特定の処理にだけスポットライトを当てて、その開始と終了の時間を精密に計ることができます。
6. コンソール(画面)にトレースを出力して確認する
データを外部に送る前に、まずは自分のパソコンの画面にトレース情報が出るか確認してみましょう。デバッグ(間違い探し)の際にも非常に役立つ手法です。記録した情報を画面に表示する「コンソール・エクスポート」という設定を追加します。
from flask import Flask
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
# 設定:記録した情報を画面(コンソール)に出すようにします
provider = TracerProvider()
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)
@app.route('/debug')
def debug():
return "コンソールにトレースが出力されています"
if __name__ == '__main__':
app.run()
これを実行してページにアクセスすると、プログラムを動かしている黒い画面に、たくさんの情報が表示されます。そこには「どこから来たアクセスか」「何ミリ秒かかったか」といった詳細が記されています。
7. 取得したデータを可視化ツールへ送信する
画面に出すだけでなく、グラフなどで綺麗に見たいですよね。OpenTelemetryで集めたデータは、Jaeger(イェーガー)やZipkin(ジップキン)といった専用の可視化ツールに送るのが一般的です。
送信には「OTLP(OpenTelemetry Line Protocol)」という通信ルールを使います。これにより、自分のFlaskアプリはそのままに、送信先だけを自由に変えることができます。可視化ツールを使えば、一本の横棒グラフ(ガントチャート)のように、どの処理が何番目に動いてどれくらい時間がかかったかが一目で分かるようになります。これが、プロの現場で行われている「パフォーマンス改善」の第一歩です。
8. エラー時のトレース情報で原因を即座に特定
トレース情報は、アプリが正常に動いているときだけでなく、エラーが発生したときこそ真価を発揮します。OpenTelemetryは、エラーが起きた場所のスパンに「赤いフラグ」を立てて、その時のエラーメッセージを一緒に保存してくれます。
from flask import Flask
from opentelemetry import trace
app = Flask(__name__)
tracer = trace.get_tracer(__name__)
@app.route('/risk')
def risk():
with tracer.start_as_current_span("dangerous_operation") as span:
try:
# 存在しないファイルを開こうとするエラー
with open("non_existent_file.txt") as f:
content = f.read()
except Exception as e:
# エラー内容をスパンに記録します
span.record_exception(e)
span.set_status(trace.Status(trace.StatusCode.ERROR))
return "エラーを記録しました", 500
return "成功しました"
この機能があれば、後から「あの時、何が原因で失敗したのか?」を、まるでタイムマシンのように振り返って確認することができます。パソコン操作に不慣れな初心者の方も、この「自動記録」の安心感を知れば、もっと自由に開発を楽しめるようになるはずです。
9. パフォーマンスへの影響と導入の注意点
最後に、注意点もお話ししておきます。全ての処理を細かく記録しようとすると、ほんの少しだけアプリの動きが重くなったり、通信量が増えたりすることがあります。これを「オーバーヘッド」と呼びます。
そのため、本番の大きなサービスでは「100回のうち1回だけ記録する」といった「サンプリング」という手法を使うのが一般的です。OpenTelemetryにはこのサンプリング機能も備わっています。最初は難しく考えず、まずは自分の手元の環境で全ての動きを観察することから始めてみましょう。自分の書いたコードがどのようにコンピュータの中を駆け抜けているのかを知ることは、エンジニアとしての成長に大きく繋がります。
10. 可観測性の三本柱としてのトレース
可観測性には「ログ(日記)」「メトリクス(数値)」「トレース(足跡)」という三本の柱があると言われています。今回学んだトレースは、その中でも最も詳細な「足跡」を辿る技術です。Flaskの基本を学んだあとのステップとして、このOpenTelemetryを使いこなせるようになると、アプリの不具合を直すスピードが劇的に上がります。複雑なパズルを解くような楽しさが、トレース監視にはあります。ぜひ、今回紹介したコードを実際に動かして、その強力さを体感してみてください。