分散システムにおいてリクエストが失敗した場合、相関キーがないと、サービス全体から関連するログを見つけることは困難です。Application Real-Time Monitoring Service (ARMS) は、OpenTelemetry のトレースコンテキスト (トレース ID、スパン ID、サービス名) を Python のログレコードに注入することで、この問題を解決します。これにより、Simple Log Service (SLS) でトレース ID を使用してログを検索し、根本原因を特定できます。
仕組み:ARMS の Python エージェントは、Python 標準の logging モジュールにフックし、各 LogRecord にトレースコンテキストフィールドを追加します。エージェントが担当するのは注入のみであり、ログの収集や転送は行いません。ログ配信は、別のログ収集パイプライン (通常は SLS) が処理します。
Python 標準の logging モジュールのみが、OTEL_PYTHON_LOG_CORRELATION 環境変数を使用した自動トレースコンテキスト注入をサポートしています。Structlog やその他のサードパーティフレームワークは、自動注入をサポートしていません。
クイックスタート
3 つの環境変数を設定し、アプリケーションを再起動します。
export OTEL_PYTHON_LOG_CORRELATION=true
export OTEL_PYTHON_LOG_LEVEL=info
export OTEL_PYTHON_LOG_FORMAT='%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s resource.service.name=%(otelServiceName)s trace_sampled=%(otelTraceSampled)s] - %(message)s'アプリケーションの再起動後、トレースされたリクエスト内で生成される各ログ行には、トレースコンテキストが含まれます。
2026-03-10 14:30:00,123 INFO [my_app] [app.py:42] [trace_id=ac1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6 span_id=1a2b3c4d5e6f7a8b resource.service.name=my-python-app trace_sampled=True] - Order created successfullySLS のバインドや検証を含む完全なセットアップについては、以下のセクションをご参照ください。
前提条件
ARMS によって監視される Python アプリケーション。詳細については、「Python アプリケーションの監視」をご参照ください。
SLS で収集されたアプリケーションログ。詳細については、「データ収集の概要」をご参照ください。
SLS を ARMS にバインド
SLS の Project と Logstore を ARMS にリンクし、コンソールでトレースとログを関連付けられるようにします。
ARMS コンソールにログインします。
左側のメニューで を選択します。
上部のメニューバーでリージョンを選択し、対象のアプリケーションをクリックします。
説明[Language] 列のアイコンは、プログラミング言語を示しています。-
: Java -
: Go -
: Python - [-] (ハイフン): Managed Service for OpenTelemetry で監視されているアプリケーション上部のメニューで [Configuration] > [Custom Configurations] を選択します。
[Application log Association configuration] セクションで、[Log Source] を [Log service SLS] に設定します。SLS がデプロイされているリージョンを選択し、Project と Logstore をバインドします。
トレースとログの相関の有効化
3 つの環境変数を設定し、トレースコンテキストの注入を有効にし、ログフォーマットを定義します。
環境変数
| 変数 | 用途 | デフォルト値 |
|---|---|---|
OTEL_PYTHON_LOG_CORRELATION | トレースとログの相関を有効または無効にします。有効にするには true に設定します。false に設定するか空白のままにすると、相関は無効になります。 | false |
OTEL_PYTHON_LOG_LEVEL | 相関を適用する最小ログレベル。有効な値: debug、info、warning、error。 | - |
OTEL_PYTHON_LOG_FORMAT | ログ出力のフォーマット文字列。ビジネス要件に基づいてカスタマイズできます。以下に示すトレースコンテキストフィールドを使用して、ログ出力にトレースコンテキストを含めます。 | - |
トレースコンテキストフィールド
ログ出力にトレースコンテキストを含めるには、OTEL_PYTHON_LOG_FORMAT フォーマット文字列で以下のフィールドを使用します。
| フィールド | 説明 |
|---|---|
%(otelTraceID)s | ログを分散トレースにリンクするトレース ID |
%(otelSpanID)s | 現在のログで生成されたスパンの ID |
%(otelTraceSampled)s | トレースがサンプリングされたかどうか (True または False) |
%(otelServiceName)s | ARMS に登録されたアプリケーション名 |
環境変数の設定
アプリケーションを起動する前に、以下の環境変数を設定します。
export OTEL_PYTHON_LOG_CORRELATION=true
export OTEL_PYTHON_LOG_LEVEL=info
export OTEL_PYTHON_LOG_FORMAT='%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s resource.service.name=%(otelServiceName)s trace_sampled=%(otelTraceSampled)s] - %(message)s'アプリケーションの起動後、ログ出力にはトレースコンテキストが含まれます。
Submitting thread
INFO: Started server process [271]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
2025-01-02 21:55:31,142 WARNING [__main__] [app.py:14] [trace_id=534xxx021eb span_id=15xxx32 resource.service.name=fastapi-agent-manual trace_sampled=True] - calling client
INFO: 127.0.0.1:59956 - "GET / HTTP/1.1" 200 OKカスタムハンドラへのトレースコンテキストの追加
トレースコンテキストは、ルートロガーにのみ自動的に注入されます。カスタムハンドラを使用する場合は、OTEL_PYTHON_LOG_FORMAT 環境変数から読み取るようにフォーマッタを設定します。
import os
import logging
# 環境変数からフォーマット文字列を読み取る
log_format = os.getenv('OTEL_PYTHON_LOG_FORMAT')
# トレースコンテキストフィールドを持つカスタムハンドラを作成
my_handler = logging.StreamHandler()
formatter = logging.Formatter(log_format)
my_handler.setFormatter(formatter)
# ロガーにハンドラをアタッチ
logger = logging.getLogger('my_module')
logger.addHandler(my_handler)
logger.setLevel(logging.INFO)structlog へのトレースコンテキストの追加
アプリケーションで structlog を使用している場合、自動トレースコンテキスト注入はサポートされていません。structlog パイプラインに、OpenTelemetry SDK からトレースコンテキストを読み取るカスタムプロセッサを追加します。
from opentelemetry import trace
import structlog
def add_otel_context(logger, method, event_dict):
span = trace.get_current_span()
ctx = span.get_span_context()
if ctx.is_valid:
event_dict['trace_id'] = format(ctx.trace_id, '032x')
event_dict['span_id'] = format(ctx.span_id, '016x')
return event_dict
structlog.configure(
processors=[
add_otel_context,
structlog.processors.JSONRenderer(),
]
)設定後、トレースされたリクエスト内で生成される各 structlog エントリには、trace_id および span_id フィールドが含まれます。SLS は、これらのフィールドを使用して、ログを対応する分散トレースと関連付けます。
ログ収集の設定 (オプション)
ARMS の Python エージェントは、ログレコードへのトレースコンテキストフィールドの注入のみを担当します。アプリケーションログの収集や転送は行いません。アプリケーションログは SLS によって直接収集されるため、OpenTelemetry コンソールへの追加レポートは不要です。
ARMS コンソールで関連付けられたログを表示するには、SLS を ARMS にバインドでバインドした SLS の Project と Logstore にログを収集および配信するように、SLS を設定します。
SLS のログ収集の詳細については、「Data collection overview」をご参照ください。
設定の確認
アプリケーションにテストリクエストを送信します。
アプリケーションのログ出力を確認します。トレースされたリクエスト内で生成される各ログ行には、ゼロ以外の
trace_id値が含まれます。2026-03-10 14:30:00,123 INFO [my_app] [app.py:42] [trace_id=ac1b2d3e4f5a6b7c8d9e0f1a2b3c4d5e6 span_id=1a2b3c4d5e6f7a8b resource.service.name=my-python-app trace_sampled=True] - Order created successfullyARMS コンソールで、リクエストのトレース詳細ページを開き、関連付けられたログに正しいトレース ID が表示されていることを確認します。