mcpField
P
python-observability

python-observability

github.com/wshobson開発41.9k11.7k 回インストール2 週間前に更新
リポジトリ

python-observability について

本番アプリケーションにオブザーバビリティを追加するPython開発者向けのガイドです。構造化ログ、Prometheusメトリクス、分散トレース、相関IDを使ったサービス横断の問題診断を扱います。

主な機能

  1. 01構造化JSONログ
  2. 02Prometheusメトリクス収集
  3. 03サービス横断の分散トレース
  4. 04相関IDの伝播
  5. 05メトリクスラベルのカーディナリティ制御

活用シーン

  1. 01Pythonアプリへの構造化ログの追加
  2. 02遅延、トラフィック、エラー、飽和度の監視
  3. 03サービス間リクエストの追跡による本番障害の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshobson/agents --skill python-observability

インストール後、Claude Code で/python-observabilityを入力して読み込まれたか確認します。

同梱ファイルreferences/details.md

詳細

Python オブザーバビリティ

構造化ログ、メトリクス、トレースを使用して Python アプリケーションを計装します。本番環境で問題が発生したとき、新しいコードをデプロイすることなく「何が、どこで、なぜ」を答えられる必要があります。

このスキルを使用する場面

  • アプリケーションに構造化ログを追加する
  • Prometheus を使用したメトリクス収集を実装する
  • サービス間の分散トレーシングをセットアップする
  • リクエストチェーンを通じて相関 ID を伝播する
  • 本番環境の問題をデバッグする
  • オブザーバビリティダッシュボードを構築する

中核となる概念

1. 構造化ログ

本番環境では、一貫したフィールドを持つ JSON としてログを出力します。機械可読なログは、強力なクエリとアラートを可能にします。ローカル開発では、人間が読める形式を検討してください。

2. 4つのゴールデンシグナル

すべてのサービス境界で、レイテンシ、トラフィック、エラー、飽和度を追跡します。

3. 相関 ID

単一のリクエストに対して、すべてのログとスパンを通じて一意の ID を引き回し、エンドツーエンドのトレーシングを可能にします。

4. 有界なカーディナリティ

メトリクスのラベル値を有界に保ちます。無界なラベル(ユーザー ID など)はストレージコストを爆発させます。

クイックスタート

import structlog

structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.JSONRenderer(),
    ],
)

logger = structlog.get_logger()
logger.info("Request processed", user_id="123", duration_ms=45)

基本的なパターン

パターン 1: Structlog による構造化ログ

一貫したフィールドを持つ JSON 出力のために structlog を構成します。

import logging
import structlog

def configure_logging(log_level: str = "INFO") -> None:
    """Configure structured logging for the application."""
    structlog.configure(
        processors=[
            structlog.contextvars.merge_contextvars,
            structlog.processors.add_log_level,
            structlog.processors.TimeStamper(fmt="iso"),
            structlog.processors.StackInfoRenderer(),
            structlog.processors.format_exc_info,
            structlog.processors.JSONRenderer(),
        ],
        wrapper_class=structlog.make_filtering_bound_logger(
            getattr(logging, log_level.upper())
        ),
        context_class=dict,
        logger_factory=structlog.PrintLoggerFactory(),
        cache_logger_on_first_use=True,
    )

# Initialize at application startup
configure_logging("INFO")
logger = structlog.get_logger()
パターン 2: 一貫したログフィールド

すべてのログエントリには、フィルタリングと相関のための標準フィールドを含める必要があります。

import structlog
from contextvars import ContextVar

# Store correlation ID in context
correlation_id: ContextVar[str] = ContextVar("correlation_id", default="")

logger = structlog.get_logger()

def process_request(request: Request) -> Response:
    """Process request with structured logging."""
    logger.info(
        "Request received",
        correlation_id=correlation_id.get(),
        method=request.method,
        path=request.path,
        user_id=request.user_id,
    )

    try:
        result = handle_request(request)
        logger.info(
            "Request completed",
            correlation_id=correlation_id.get(),
            status_code=200,
            duration_ms=elapsed,
        )
        return result
    except Exception as e:
        logger.error(
            "Request failed",
            correlation_id=correlation_id.get(),
            error_type=type(e).__name__,
            error_message=str(e),
        )
        raise
パターン 3: 意味のあるログレベル

アプリケーション全体でログレベルを一貫して使用します。

レベル目的例
DEBUG開発時の診断変数の値、内部状態
INFOリクエストのライフサイクル、操作リクエストの開始/終了、ジョブの完了
WARNING回復可能な異常リトライの試行、フォールバックの使用
ERROR注意が必要な障害例外、サービス利用不可
# DEBUG: Detailed internal information
logger.debug("Cache lookup", key=cache_key, hit=cache_hit)

# INFO: Normal operational events
logger.info("Order created", order_id=order.id, total=order.total)

# WARNING: Abnormal but handled situations
logger.warning(
    "Rate limit approaching",
    current_rate=950,
    limit=1000,
    reset_seconds=30,
)

# ERROR: Failures requiring investigation
logger.error(
    "Payment processing failed",
    order_id=order.id,
    error=str(e),
    payment_provider="stripe",
)

予期された動作を ERROR でログに記録しないでください。ユーザーが誤ったパスワードを入力することは INFO であり、ERROR ではありません。

パターン 4: 相関 ID の伝播

イングレスで一意の ID を生成し、すべての操作を通じて引き回します。

from contextvars import ContextVar
import uuid
import structlog

correlation_id: ContextVar[str] = ContextVar("correlation_id", default="")

def set_correlation_id(cid: str | None = None) -> str:
    """Set correlation ID for current context."""
    cid = cid or str(uuid.uuid4())
    correlation_id.set(cid)
    structlog.contextvars.bind_contextvars(correlation_id=cid)
    return cid

# FastAPI middleware example
from fastapi import Request

async def correlation_middleware(request: Request, call_next):
    """Middleware to set and propagate correlation ID."""
    # Use incoming header or generate new
    cid = request.headers.get("X-Correlation-ID") or str(uuid.uuid4())
    set_correlation_id(cid)

    response = await call_next(request)
    response.headers["X-Correlation-ID"] = cid
    return response

送信リクエストに伝播します:

import httpx

async def call_downstream_service(endpoint: str, data: dict) -> dict:
    """Call downstream service with correlation ID."""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            endpoint,
            json=data,
            headers={"X-Correlation-ID": correlation_id.get()},
        )
        return response.json()

詳細な実例とパターン

詳細なセクション(## Advanced Patterns で始まるもの)は references/details.md にあります。上記のナビゲーション概要では不十分な場合は、そのファイルを参照してください。

ベストプラクティスのまとめ

  1. 構造化ログを使用する - 一貫したフィールドを持つ JSON ログ
  2. 相関 ID を伝播する - すべてのリクエストとログを通じて引き回す
  3. 4つのゴールデンシグナルを追跡する - レイテンシ、トラフィック、エラー、飽和度
  4. ラベルのカーディナリティを有界にする - 無界な値をメトリクスラベルとして使用しない
  5. 適切なレベルでログを記録する - ERROR で空騒ぎしない
  6. コンテキストを含める - ログにユーザー ID、リクエスト ID、操作名を含める
  7. コンテキストマネージャーを使用する - 一貫したタイミングとエラー処理
  8. 関心を分離する - オブザーバビリティのコードでビジネスロジックを汚染しない
  9. オブザーバビリティをテストする - 統合テストでログとメトリクスを検証する
  10. アラートを設定する - アラートがなければメトリクスは役に立たない

コメント