• ログイン無料アカウント

本書は、お客様のご参考のために原文の英語版を機械翻訳したものです。

英語版と齟齬がある場合、英語版の定めが優先するものとします。より詳しい情報については、本リンクをご参照ください。

問題を作成する

非ウェブスクリプト、ワーカープロセス、タスク、およびファンクションの監視

Pythonエージェント は、主にWebアプリとそのWebリクエストを監視するために設計されているため、WSGIベースのWebアプリや一般的なWebフレームワーク用のインスツルメンテーションが組み込まれています。

しかし、 background_task メソッドを使用して、任意のジョブベースのシステム、スタンドアロンのスクリプト、ワーカープロセス、および非ウェブトランザクションを監視することもできます。これらのトランザクションは、New Relic の UI で non-web transactions として表示されます。

Pythonエージェントには、タスクキューイングシステム Celery とGearmanのインスツルメンテーションも組み込まれています。

Pythonエージェントの統合

最初の要件は、エージェントを初期化し、監視対象のプロセスで実行することです。これは、 標準的なPythonのインストール で説明されている、Webアプリケーションの場合と同じプロセスです。

監視対象のタスクが、既存の監視対象のWebアプリケーションプロセスのバックグラウンドスレッドで実行されている場合、エージェントの初期化はすでに実行されているので、このステップを繰り返す必要はありません。

Webトラフィックを処理していないアプリケーションをインストルメントする場合は、WSGIアプリケーションのエントリーポイントをラップする必要はありません。

監視対象となるタスクをラップする

WSGIアプリケーションのエントリーポイントをラップする代わりに、追跡したいバックグラウンドタスクを実行するすべての関数をラップする必要があります。例えば、以下のようになります。

import newrelic.agent
@newrelic.agent.background_task()
def execute_task():
...

デフォルトでは、タスクの名前はデコレーターが適用された関数の名前になります。

タスク名を上書きしたい場合は、デコレーターの名前付き引数で指定することができます。また、デフォルトの Function の代わりに別のグループを指定することもできます。

import newrelic.agent
@newrelic.agent.background_task(name='database-update', group='Task')
def database_update():
...

タスクの名前を動的に設定する必要がある場合は、代わりにコンテキスト・マネージャ・オブジェクトを使用する必要があります。コンテキストマネージャオブジェクトを使用する際には、まず、データが報告されるアプリケーションに対応するアプリケーションオブジェクトを取得する必要があります。アプリケーション・オブジェクトを取得する際に、アプリケーションの名前を省略すると、エージェント構成で指定されたデフォルト・アプリケーションに対応するものが使用されます。

import newrelic.agent
def execute_task(task_name):
application = newrelic.agent.application()
with newrelic.agent.BackgroundTask(application, name=task_name, group='Task'):
...

バックグラウンド・タスク・デコレーターまたはコンテキスト・マネージャー・オブジェクトによってラップされるものは、実行時間が有限であるタスクでなければならないことに注意してください。個々のタスクが実行されているプロセスのメインループをラップしてはいけません。個々のタスクの実行のみをラップしてください。不適切にメインループをラップした場合、メインループが戻ることはないため、データは取得されますが、データの集計や報告はタスクが実際に完了した後にしか行われないため、報告されることはありません。

そのため、追跡したい特定の粒度の高いタスクをラップします。このようなタスクは、プロセスの寿命の間に複数回実行することができ、すべてが報告されます。

Pythonエージェントの強制登録

デフォルトでは、エージェントは、(ウェブまたは非ウェブの)トランザクションが初めて実行されたときに自分自身を登録します。エージェントが自分自身を登録するまでデータは実際には蓄積されず、それは通常、並列タスクとして行われるため、最初のトランザクションのデータは取得されません。

長時間稼働するプロセスの場合、これは一般的に許容されることであり、登録が行われるまでウェブサーバーの起動を遅らせてリクエストを処理できないようにするよりも良いでしょう。

しかし、ウェブ以外のトランザクションでは、スクリプトが1つのタスクのみを実行し、長時間実行されることがよくあります。このような理由から、エージェントの登録を待つための起動時の遅延は、そのデータが確実に取得されるため、許容されます。

このような場合、2つの方法のいずれかで登録を強制することができます。最も簡単な方法は、エージェントの設定で起動時のタイムアウトを指定することです。このタイムアウトは、エージェントの遅延登録を行う際に、エージェントが登録を待ってから処理を行う時間です。これは、待機する最大時間を表しています。登録にかかる時間がタイムアウトで指定された時間よりも短い場合は、すぐに登録を続行します。

エージェント設定ファイルを使用している場合は、エージェント設定ファイルの newrelic セクションに以下のエントリを追加することで行います。

startup_timeout = 10.0

また、起動時のタイムアウトは、 NEW_RELIC_STARTUP_TIMEOUT 環境変数を使って指定することもできます。

Webアプリケーションでこの起動タイムアウトを使用すると、最初のWebリクエストを処理する際にWebサーバーがストールしてしまい、ユーザーがレスポンスを得るまでの時間が長くなってしまうため、注意が必要です。

必要に応じて、アプリケーションコード内のコードでエージェントの強制登録を行うこともできます。

import newrelic.agent
application = newrelic.agent.register_application(timeout=10.0)
def execute_task(task_name):
with newrelic.agent.BackgroundTask(application, name=task_name, group='Task'):
...

タイムアウトは、 register_application() 関数の名前付きパラメータにする必要があります。エージェント構成で指定されたアプリケーション名以外のアプリケーション名を使用する必要がある場合は、 register_application() 関数の第一引数に指定する必要があります。

アプリケーションに対するエージェントの登録時の結果は、タイムアウト期間内に成功したかどうかに関わらず、対応するアプリケーションオブジェクトとなります。これは、バックグラウンド・タスク・コンテキスト・マネージャーで使用することができます。

プロセス停止時のデータアップロード

エージェントは、長時間実行されるプロセスで使用されることを意図しています。単発のタスクを実行するスクリプトに使用することもできますが、上述のようにアプリケーションに対してエージェントの登録を強制する必要があります。これにより、スクリプトで実行される最初のタスクのデータが確実に取得されます。

とはいえ、タスクの詳細が報告されることが保証されているわけではありません。

これは、タスクが完了した時点ではデータが報告されないためです。その代わり、データは蓄積され、通常は1分に1回報告されます。しかし、1回限りのタスクを実行するスクリプトでは、バックグラウンドでのデータ収集とデータコレクタへのデータの報告を実行するために、スクリプト全体が十分な時間実行されないことがあります。

部分的な収穫期間のデータに対応するために、エージェントはスクリプトが終了する際のエージェントのシャットダウン時に、そのような部分的なデータのアップロードを試みます。この処理に時間がかかる場合はタイムアウトし、プロセスのシャットダウンを無制限に遅らせることはありません。

この場合、デフォルトのシャットダウンタイムアウトは2.5秒となっています。この値は、Apacheウェブサーバー下のPythonウェブアプリケーションでエージェントを実行する際の時間的な制約を考慮して選択されています。Apacheの場合、Apacheは3秒後にプロセスを強制終了するため、タイムアウトを長くすることはできません。そのため、エージェントはその少し前に終了し、Apacheがプロセスを殺す前に、終了時に実行する必要のある他のコードにチャンスを与えます。

実際には、スクリプトが実行されるホストとデータコレクタの間に低速のネットワークやファイアウォールがある場合など、スクリプトの終了時にデータを報告するには、このタイムアウトが十分に長くない場合があります。

この場合、スクリプトのワンオフタスクからのデータが実際に報告されるように、シャットダウンのタイムアウトを増やす必要があるかもしれません。

エージェント設定ファイルを使用している場合は、エントリを追加することで変更されます。

shutdown_timeout = 2.5

を、エージェント設定ファイルの [newrelic] セクションに記述してください。

また、シャットダウンのタイムアウトは、 NEW_RELIC_SHUTDOWN_TIMEOUT 環境変数を使って指定することもできます。

このシャットダウンのタイムアウトを変更するのは、それが必要であるとわかった場合のみです。

モニターDjangoの管理コマンド

よくあるユースケースとして、Django の管理コマンドの監視があります。これは一般的なシナリオなので、我々はこれをビルトインでサポートしています。しかし、すべての Django 管理コマンドを自動的に監視することはできないので、オプションで有効にしています。というのも、 runserverrun_gunicorn のように、無限ループになっているものがあるからです。特にこの二つをインストゥルメンテーションすると、Web アプリケーションとしてのそれらを監視するための通常のインストゥルメンテーションにも支障をきたします。

監視できる Django 管理コマンドに制限があるため、エージェントの設定ファイルに特別な設定セクション [import-hook:django] を追加する必要があります。このセクションの下に、 instrumentation.scripts.django_admin という設定で、スペースで区切られたリストを用意する必要があります。

[import-hook:django]
instrumentation.scripts.django_admin = syncdb sqlflush

デフォルトでは、Django の管理コマンドを監視する際、起動タイムアウトは自動的に 10.0 秒に指定されます。起動時のタイムアウトを上書きする必要がある場合は、同じ import-hook:django 設定セクション内で instrumentation.background_task.startup_timeout を設定してください。

追加の設定を指定した後は、私たちの newrelic-admin ラッパースクリプトでラップした Django 管理コマンドを実行できます。

NEW_RELIC_CONFIG_FILE=newrelic.ini newrelic-admin run-python manage.py syncdb
Copyright © 2022 New Relic株式会社。