チュートリアル - Datadog Agent と同じホスト上の Python アプリケーションのトレースを有効にする

概要

このチュートリアルでは、ホスト上にインストールされたサンプル Python アプリケーションでトレースを有効にするための手順を説明します。このシナリオでは、アプリケーションと同じホスト上に Datadog Agent をインストールします。

このチュートリアルのインストールシナリオを示す図

コンテナ内のアプリケーション、コンテナ内の Agent、異なる言語で書かれたアプリケーションなど、その他のシナリオについては、その他のトレース有効化のチュートリアルを参照してください。

Python の一般的なトレース設定ドキュメントについては、Python アプリケーションのトレースを参照してください。

前提条件

Agent のインストール

Datadog Agent をマシンにインストールしていない場合は、Integrations > Agent にアクセスし、お使いの OS を選択してください。例えば、ほとんどの Linux プラットフォームでは、<YOUR_API_KEY>Datadog API キーに置き換えて、以下のスクリプトを実行することで Agent をインストールすることができます。

DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=<YOUR_API_KEY> DD_SITE="datadoghq.com" bash -c "$(curl -L https://install.datadoghq.com/scripts/install_script.sh)"

datadoghq.com 以外の Datadog サイトにデータを送信するには、DD_SITE 環境変数を Datadog サイトに置き換えてください。

もしホストに既に Agent がインストールされている場合は、少なくともバージョン 7.28 であることを確認してください。Python アプリケーションをトレースするために ddtrace を使用するために必要な Datadog Agent の最小バージョンは、トレーシングライブラリ開発者向けドキュメントに記載されています。

Events > Explorer を開き、オプションで Datadog ソースファセットでフィルタリングし、ホストへの Agent インストールを確認するイベントを探して、Agent が実行されており、Datadog にデータを送信していることを確認します。

Agent がホストにインストールされたことを示す Datadog からのメッセージを表示するイベントエクスプローラー。
数分後、Datadog にホストが表示されない場合 (Infrastructure > Host map)、Organization Settings > API Keys にある組織の正しい API キーを使用したことを確認してください。

サンプル Python アプリケーションのインストールと実行

次に、トレースするためのサンプルアプリケーションをインストールします。このチュートリアルのコードサンプルは github.com/Datadog/apm-tutorial-python で見ることができます。以下を実行することで git リポジトリの複製を行います。

git clone https://github.com/DataDog/apm-tutorial-python.git

Poetry または pip のいずれかを使用して、サンプルに必要な Python の依存関係を設定し、構成し、インストールします。以下のいずれかを実行します。

poetry install
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

以下を実行することでアプリケーションを起動します。

python -m notes_app.app

サンプルの notes_app アプリケーションは、インメモリデータベースにデータを保存する基本的な REST API です。別のターミナルを開き、curl を使っていくつかの API リクエストを送信します。

curl -X GET 'localhost:8080/notes'
まだデータベースに何もないので {} を返します
curl -X POST 'localhost:8080/notes?desc=hello'
ノートに hello という説明と 1 という ID 値を追加します。( 1, hello) を返します。
curl -X GET 'localhost:8080/notes?id=1'
id の値が 1 であるノートを返します: ( 1, hello)
curl -X POST 'localhost:8080/notes?desc=otherNote'
otherNote という説明と 2 という ID 値を持つノートを追加します。( 2, otherNote) を返します
curl -X GET 'localhost:8080/notes'
データベースの内容を返します: { "1": "hello", "2": "otherNote" }
curl -X PUT 'localhost:8080/notes?id=1&desc=UpdatedNote'
最初のノートの説明の値を UpdatedNote に更新します。
curl -X DELETE 'localhost:8080/notes?id=1'
データベースから最初のノートを削除します。

さらに API コールを実行し、アプリケーションのアクションを確認します。終了したら、Ctrl+C でアプリケーションを停止します。

Datadog トレーシングのインストール

次に、トレーシングライブラリを Poetry または pip (最小バージョン 18) を使ってインストールします。apm-tutorial-python ディレクトリから、以下を実行します。

poetry add ddtrace
poetry install
pip install ddtrace

自動インスツルメンテーションによる Python アプリケーションの起動

トレースの生成と収集を開始するには、前回とは少し異なる方法でサンプルアプリケーションを再起動します。以下を実行します。

DD_SERVICE=notes DD_ENV=dev DD_VERSION=0.1.0 \
 ddtrace-run python -m notes_app.app

このコマンドは、DD_SERVICEDD_VERSIONDD_ENV 環境変数を設定して統合サービスタグ付けを有効にし、Datadog 全体のデータ相関を可能にするものです。

再びアプリケーションにリクエストを送るには、curl を使用します。

curl -X GET 'localhost:8080/notes'
{}
curl -X POST 'localhost:8080/notes?desc=hello'
( 1, hello)
curl -X GET 'localhost:8080/notes?id=1'
( 1, hello)
curl -X POST 'localhost:8080/notes?desc=newNote'
( 2, newNote)
curl -X GET 'localhost:8080/notes'
{ "1": "hello", "2": "newNote" }

しばらく待って、Datadog の UI を見てみてください。APM > Traces に移動します。Traces リストには、次のように表示されます。

Traces ビューには、ホストから入ってくるトレースデータが表示されます。

もし、トレースが表示されない場合は、Traces Search フィールドのフィルターをクリアしてください (使用していない ENV などの環境変数にフィルターをかけている場合があります)。

トレースの検証

Traces ページで、POST /notes トレースをクリックすると、各スパンにかかった時間や、あるスパンが完了する前に他のスパンが発生したことを示すフレームグラフが表示されます。グラフの上部にあるバーは、前の画面で選択したスパンです (この場合、ノートアプリケーションへの最初のエントリポイントです)。

バーの幅は、それが完了するまでにかかった時間を示します。低い深さのバーは、高い深さのバーの寿命の間に完了するスパンを表します。

POST トレースのフレームグラフは次のようになります。

POST トレースのフレームグラフ。

GET /notes トレースは次のようになります。

GET トレースのフレームグラフ。

Python アプリケーションにカスタムインスツルメンテーションを追加する

自動インスツルメンテーションは便利ですが、より細かいスパンが欲しい場合もあります。Datadog の Python DD Trace API では、アノテーションやコードを使用してコード内のスパンを指定することができます。

次のステップでは、コードにアノテーションを追加して、いくつかのサンプルメソッドをトレースする方法を説明します。

  1. notes_app/notes_helper.py を開きます。

  2. 以下のインポートを追加します。

    from ddtrace import tracer

  3. NotesHelper クラスの中に、notes_helper というトレーサーラッパーを追加して、notes_helper.long_running_process メソッドがどのように動作するかを確認できるようにします。

    class NotesHelper:
    
        @tracer.wrap(service="notes_helper")
        def long_running_process(self):
            time.sleep(.3)
            logging.info("Hello from the long running process")
            self.__private_method_1()

    さて、トレーサーは自動的にリソースにラップされている関数名、この場合は long_running_process をラベル付けしています。

  4. いくつかの HTTP リクエスト、特にいくつかの GET リクエストを再送します。

  5. トレースエクスプローラーで、新しい GET リクエストの 1 つをクリックすると、次のようなフレームグラフが表示されます。

    カスタムインスツルメンテーションを用いた GET トレースのフレームグラフ。

    get_notes 関数にカスタムトレースが追加され、スタックトレースがより詳細になったことに注意してください。

詳しくは、カスタムインストルメンテーションをご覧ください。

分散型トレーシングを見るために 2 つ目のアプリケーションを追加する

単一のアプリケーションをトレースすることは素晴らしいスタートですが、トレースの本当の価値は、リクエストがサービスを通じてどのように流れるかを見ることです。これは、_分散型トレーシング_と呼ばれています。

サンプルプロジェクトには calendar_app という 2 番目のアプリケーションが含まれており、呼び出されるたびにランダムな日付を返します。Notes アプリケーションの POST エンドポイントには、add_date という名前の 2 つ目のクエリパラメーターがあります。このパラメータが y に設定されると、Notes はカレンダーアプリケーションを呼び出して、ノートに追加する日付を取得します。

  1. 以下を実行することでカレンダーアプリケーションを起動します。

       DD_SERVICE=calendar DD_ENV=dev DD_VERSION=0.1.0 \
       ddtrace-run python -m calendar_app.app
       
  2. add_date パラメーターを指定して、POST リクエストを送信します。

curl -X POST 'localhost:8080/notes?desc=hello_again&add_date=y'
(2, hello_again with date 2022-11-06)
  1. トレースエクスプローラーで、この最新のトレースをクリックすると、2 つのサービス間の分散型トレーシングが表示されます。

    分散型トレーシングのフレームグラフ。

カスタムインスツルメンテーションの追加

コードを使って、カスタムのインスツルメンテーションを追加することができます。例えば、カレンダサービスをさらにインスツルメンテーションして、トレースを見やすくしたいとします。

  1. notes_app/notes_logic.py を開きます。

  2. 以下のインポートを追加します。

    from ddtrace import tracer
    
  3. try ブロックの内部、28 行目あたりに、次の with ステートメントを追加してください。

    with tracer.trace(name="notes_helper", service="notes_helper", resource="another_process") as span:
    

    その結果、こうなりました。

    def create_note(self, desc, add_date=None):
            if (add_date):
                if (add_date.lower() == "y"):
                    try:
                        with tracer.trace(name="notes_helper", service="notes_helper", resource="another_process") as span:
                            self.nh.another_process()
                        note_date = requests.get(f"http://localhost:9090/calendar")
                        note_date = note_date.text
                        desc = desc + " with date " + note_date
                        print(desc)
                    except Exception as e:
                        print(e)
                        raise IOError("Cannot reach calendar service.")
            note = Note(description=desc, id=None)
            note.id = self.db.create_note(note)

  4. 引数 add_date を指定して、より多くの HTTP リクエスト、特に POST リクエストを送信します。

  5. トレースエクスプローラーで、これらの新しい POST トレースをクリックすると、複数のサービスにわたるカスタムトレースが表示されます。

    カスタムインスツルメンテーションを用いた分散型トレーシングのフレームグラフ。
    新しいスパンには notes_helper.another_process というラベルが付けられていることに注意してください。

もし、期待通りのトレースが受信できない場合は、Python パッケージの ddtrace でデバッグモードを設定してください。詳しくはデバッグモードの有効化を読んでください。

その他の参考資料