> ## Documentation Index
> Fetch the complete documentation index at: https://docs.logbrew.co/llms.txt
> Use this file to discover all available pages before exploring further.

# LogBrew で Flask のログと例外を扱う

> Flask のログを設定し、未処理の例外とリクエストのトレースを取得して、LogBrew CLI で保存された各シグナルを確認します。

`logbrew-flask` 0.1.6 以降を使います。このパッケージは、リクエストのスパンと未処理の例外を取得します。任意のロギングハンドラーを使うと、Python のログをリクエストのトレースに結び付けられます。リクエストの所要時間のメトリクスも任意です。

このパッケージには、Python 3.10 以降と Flask 3.1 以降が必要です。

<Note>
  アプリケーションでは、プロジェクト単位のサーバー用取り込みキーを使ってください。CLI を認可するアカウントの認証情報は使わないでください。
</Note>

`<project_id>`、`<trace_id>`、`<issue_id>` は、LogBrew の ID に置き換えます。テストのリクエストには開発用のプロジェクトを使います。シェルの例は Bash です。

## 最初のリクエストを送信する

<Steps>
  <Step title="プロジェクトのキーを作成または選択する">
    [CLI](/ja/guides/cli) でサインインします。次に、新しいプロジェクトか既存のプロジェクトを使います。両方の例を実行しないでください。新しいプロジェクトの場合は、取り込みキーを所有者だけが読める新しいファイルに保存します。

    ```bash theme={null}
    install -d -m 700 "$HOME/.logbrew"
    logbrew projects create my-flask-app \
      --runtime flask \
      --environment development \
      --ingest-key-file "$HOME/.logbrew/my-flask-app.ingest" \
      --json
    ```

    既存のプロジェクトの場合は、`logbrew projects --json` でプロジェクトの ID を確認します。次に、別のサーバー用キーを作成します。

    ```bash theme={null}
    logbrew projects keys create "<project_id>" \
      --kind server \
      --label "Flask server" \
      --ingest-key-file "$HOME/.logbrew/my-flask-app.ingest" \
      --json
    ```

    CLI は、取り込みキーを所有者だけのアクセス権で保存します。キーやファイルのパスは表示しません。本番環境では、このローカルファイルではなく、デプロイ先のプラットフォームのシークレット管理機能を使ってください。

    作成リクエストの結果がはっきりしない場合は、同じコマンドをそのままもう一度実行します。
  </Step>

  <Step title="リリース済みの連携をインストールする">
    アプリケーションの仮想環境を作成または有効化し、フレームワークのパッケージをインストールします。互換性のあるコア SDK も依存関係としてインストールされます。

    ```bash theme={null}
    python3 -m pip install "logbrew-flask>=0.1.6,<0.2"
    ```
  </Step>

  <Step title="デプロイの識別情報を設定する">
    プロジェクトのキーを、表示せずにローカルで読み込みます。すべてのシグナルが役立つデプロイのコンテキストを持つように、変わらないサービス、環境、リリースを設定します。

    ```bash theme={null}
    export LOGBREW_SERVER_API_KEY="$(tr -d '\r\n' < "$HOME/.logbrew/my-flask-app.ingest")"
    export LOGBREW_SERVICE_NAME="my-flask-app"
    export LOGBREW_ENVIRONMENT="development"
    export LOGBREW_RELEASE="my-flask-app@1.0.0"
    ```

    `init_logbrew()` は、まず明示的な引数を確認し、次に Flask アプリケーションの設定、最後にこれらの環境変数を確認します。サービス名が設定されていない場合は、Flask のインポート名を使います。
  </Step>

  <Step title="Flask を1回だけ初期化する">
    新しいアプリの場合は、例を `app.py` として保存します。初期化処理は、Flask アプリケーションを作成した後に追加します。ロギングハンドラーは任意です。これを使うと、アプリケーションのログがアクティブなリクエストのトレースとスパンを共有します。

    ```python theme={null}
    import logging

    from flask import Flask
    from logbrew_flask import init_logbrew
    from logbrew_sdk import LogBrewLoggingHandler

    app = Flask(__name__)
    logbrew = init_logbrew(app, capture_request_metrics=True)

    logger = logging.getLogger("my-flask-app")
    logger.setLevel(logging.INFO)
    logger.addHandler(LogBrewLoggingHandler(logbrew.client))


    @app.get("/health")
    def health() -> dict[str, bool]:
        logger.info("health check completed")
        return {"ok": True}
    ```

    `init_logbrew(app)` を何度呼び出しても、同じ Flask 拡張が返ります。リクエストや例外のフックが重複してインストールされることはありません。初期化処理はロギングハンドラーを追加しません。ハンドラーは、アプリが所有するロガーに1回だけ追加します。
  </Step>

  <Step title="リクエストを1件実行する">
    アプリを起動し、別のターミナルからルートを呼び出します。アプリケーションのモジュール名が違う場合は、コマンドの `app` を置き換えます。

    ```bash theme={null}
    flask --app app run
    ```

    ```bash theme={null}
    curl -fsS http://127.0.0.1:5000/health
    ```

    送信はバックグラウンドのワーカーで行われ、レスポンスを待たせません。
  </Step>

  <Step title="保存された証拠を確認する">
    正確なプロジェクトとデプロイの範囲で読み取ります。ヘルスチェックのリクエストのトレースと、例のロギングハンドラーが送った関連するログを探します。

    ```bash theme={null}
    logbrew read traces \
      --project "<project_id>" \
      --service my-flask-app \
      --environment development \
      --release my-flask-app@1.0.0 \
      --since 1h \
      --json

    logbrew read logs \
      --project "<project_id>" \
      --service my-flask-app \
      --environment development \
      --release my-flask-app@1.0.0 \
      --search "health check completed" \
      --since 1h \
      --json
    ```

    返されたトレース ID を、範囲を限った調査コマンドにコピーします。

    ```bash theme={null}
    logbrew explain trace "<trace_id>" --json
    ```

    読み取りで一致すれば、LogBrew がシグナルを保存したことを確認できます。ローカルの `200` レスポンスが証明するのは、Flask がリクエストを処理したことだけです。
  </Step>
</Steps>

## 必要なシグナルを選ぶ

初期化処理では、最初からリクエストのトレースと未処理の例外の取得が有効です。リクエストのメトリクスと Python のログの転送は、別に選びます。

| オプション | デフォルト値 | 使う場面 |
| - | - | - |
| `capture_successful_requests` | `True` | 完了したリクエストごとにスパンを1つ作る場合。このオプションがオフでも、ステータスが `500` 以上のレスポンスではスパンが作られます。 |
| `capture_exceptions` | `True` | Flask が未処理として報告する例外について、型付きの課題と、対応するエラースパンを作る場合。 |
| `capture_request_metrics` | `False` | 完了したリクエストの所要時間のヒストグラムが必要な場合。 |
| `LogBrewLoggingHandler` | 未設定 | 選択した Python のログレコードを LogBrew のログにする場合。 |
| `flush_on_response` | `False` | バックグラウンドでの送信には、このデフォルト値のままにします。レスポンスの経路での送信を確認する必要がある、管理された診断の場合にだけ有効にします。 |

デフォルト値から始めます。レイテンシーを見る必要があるときに、リクエストのメトリクスを追加します。ロギングハンドラーは、調査に役立つロガーにだけ追加します。

デフォルトでは、初期化処理が HTTP トランスポートを持ちます。各イベントはキューに入り、キューにイベントが1件入るとすぐに、バックグラウンドのワーカーが送信を始めます。送信はレスポンスを待たせません。トランスポートが失敗しても、Flask のレスポンスは壊れません。

`flush_on_response=True` にすると、キュー内のイベントをレスポンスの経路で送信します。このオプションと合わせて `raise_flush_errors=True` を使うと、送信エラーが例外として発生します。どちらも、管理された診断にだけ使ってください。

## ログの内容を管理する

必要なメッセージを出すロガーに、`LogBrewLoggingHandler` を1つ追加します。このハンドラーは、アプリのコンソールやファイルのハンドラーを置き換えません。Flask や Python の logging モジュールにパッチを当てることもありません。同じハンドラーを2回追加すると、ログが重複します。アプリがルートロガーも使っている場合は、2つ目のハンドラーを追加する前に伝搬を確認してください。

リクエスト内のログは、アクティブなトレースとスパンを引き継ぎます。リクエスト外のログには、リクエストのトレースがありません。

ハンドラーは、整形されたメッセージ、ロガー名、LogBrew のレベルを送信します。また、ソースのファイル名、モジュール、関数、行、スレッド名、プロセス名も送信しますが、ソースの完全なパスは送信しません。`extra={...}` のプリミティブな値はメタデータになります。ユーザー向けのリクエストからログを出す前に、それらの値を確認してください。

Python の `DEBUG` レコードは、LogBrew の `info` になります。`WARNING` は `warning` に、`CRITICAL` は `critical` になります。元の Python のレベル名と数値はメタデータに残ります。送信するレコードを減らすには、`LogBrewLoggingHandler(logbrew.client, level=logging.WARNING)` のように、ハンドラーに独自のレベルを設定します。

コンソールと LogBrew が同じレコードを受け取ることもできます。たとえば、Flask アプリを作成する前にコンソールを設定します。

```python theme={null}
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)
```

コンソールには整形されたテキストが出力されます。LogBrew は、メッセージ、レベル、ロガー、メタデータを別々のフィールドとして保持します。

`exc_info` を持つログレコードには、例外の型とメッセージが含まれます。例外の全文が送信されるのは、ハンドラーに `include_exception_text=True` を設定した場合だけです。これは自動の例外取得とは別のもので、自動の例外取得は例外のメッセージを一切送信しません。ログや例外のメッセージに、認証情報、個人データ、リクエストのボディを入れないでください。

## 連携が取得するもの

| シグナル | デフォルトの動作 |
| - | - |
| リクエストのトレース | メソッド、Flask のルートテンプレート、ステータスコード、所要時間を持つサーバースパン1つ。 |
| 例外の課題 | タイトル `<METHOD> <route template> failed`、未処理の例外の型、固定の要約 `Unhandled exception`、メカニズム `flask.middleware`、処理済みかどうか、サニタイズされた最大32個のフレーム。 |
| アプリケーションのログ | `LogBrewLoggingHandler` で有効にします。リクエスト内のログは、アクティブなトレースとスパンを引き継ぎます。 |
| リクエストのメトリクス | `capture_request_metrics=True` で有効にします。説明が `Duration of one completed server request.` の `http.server.duration` デルタヒストグラムを送信します。 |
| 受信したトレース | 有効な W3C `traceparent` を、新しい子スパンで引き継ぎます。 |

スパン名には、HTTP メソッドと一致したルートテンプレートを使います。たとえば、`/orders/100` と `/orders/200` は、どちらも `GET /orders/<int:order_id>` になります。どのルートにも一致しないリクエストには、固定のラベル `<unmatched>` を使います。`traceparent` がないか無効な場合は、新しいトレースを開始します。

スパンのメタデータには、メソッド、ルートテンプレート、エンドポイント、ステータスコード、所要時間が含まれます。具体的なパスやクエリ文字列は含まれません。

0.1.6 以降では、リクエストのスパンが Flask のディスパッチ全体を対象にします。before-request フック、エラーハンドラー、teardown は、リクエストのトレースを共有します。ストリーミングのレスポンスでは、ボディが終わるか閉じられたときにスパンが終了します。

### 例外の取得をテストする

`4xx` や `5xx` のステータスは、リクエストのスパンに表示されます。ステータスだけでは、例外が発生したことの証明にはなりません。障害を診断する前に、トレースと課題の両方を読んでください。LogBrew は、Flask のエラーハンドラーを置き換えたり、レスポンスのステータスを変えたりしません。

開発用のプロジェクトで、一時的なルートを追加します。1回だけ呼び出します。デプロイする前に削除してください。

```python theme={null}
@app.get("/logbrew-test-error")
def handle_test_error() -> None:
    raise RuntimeError("local capture check")
```

リクエストはステータス `500` を返します。新しい課題とリクエストのメトリクスを探します。

```bash theme={null}
logbrew read issues --project "<project_id>" --since 1h --json
logbrew explain issue "<issue_id>" --json

logbrew explain metric http.server.duration \
  --project "<project_id>" \
  --service my-flask-app \
  --environment development \
  --release my-flask-app@1.0.0 \
  --since 1h \
  --json
```

課題のサービス、環境、リリース、例外の型、リクエストのトレースを確認します。課題に `local capture check` というテキストが含まれていてはいけません。

### 外部への処理をリクエストに結び付ける

Python SDK には、`requests`、`urllib`、`httpx`、`aiohttp`、データベース、キャッシュ、キューの処理のための、アプリ側で使うヘルパーがあります。各ヘルパーは、アクティブなリクエストのトレースの子スパンを作成します。SDK は、これらのライブラリにグローバルなパッチを当てません。例は [`logbrew-flask` パッケージ](https://pypi.org/project/logbrew-flask/)にあります。

各ヘルパーには、変わらない処理名と、ルートまたはステートメントのテンプレートを指定します。それらの名前に、レコード ID、顧客の値、SQL のバインド値、メッセージのペイロードを入れないでください。子スパンは、リクエストにとって重要な依存先にだけ追加します。小さな呼び出しのたびにスパンを作ると、トレースが読みにくくなります。外部への HTTP ヘルパーは、W3C `traceparent` ヘッダーを1つ追加します。完全な URL、クエリ文字列、ボディ、Cookie、任意のヘッダーは取得しません。データベース、キャッシュ、キューのヘルパーは、バインド値、結果、キャッシュの値、キューのペイロードを取得しません。

## プライバシーとプロセスのライフサイクル

Flask の自動取得では、具体的なリクエストのパス、クエリ文字列、リクエストとレスポンスのボディ、Cookie、任意のヘッダー、`traceparent` の生の値、baggage、tracestate を除外します。

自動の例外取得は、`str(error)` を読み取ることも送信することもありません。固定の要約 `Unhandled exception` を使い、例外チェーンの各メッセージを秘匿済みとしてマークします。フレームには、ファイルのベース名と、範囲を限ったコードの識別情報が入ります。トレースバックの生のテキスト、ソースコード、ローカル変数、絶対パスは入りません。

アプリと LogBrew クライアントは、各ワーカープロセスの中で作成します。Gunicorn のデフォルトの、ワーカーごとにアプリを読み込む方式で動作します。親プロセスで作成したクライアントと一緒に Gunicorn の `--preload` を使わないでください。uWSGI では、`--lazy-apps` など、ワーカーごとにアプリを読み込む方式を使います。Python クライアントは、プロセスのフォーク後の再利用を拒否します。

プロセスマネージャーのワーカーごとの終了フックから、`logbrew.client.shutdown()` を呼び出します。これにより、キューに残ったイベントを送信し、クライアントを閉じます。リクエストのたびに呼び出さないでください。終了後、クライアントはそのワーカーからの新しいイベントを拒否します。

ワーカーがキューの最後のイベントを送信する前に、レスポンスが終わることがあります。デプロイやスケールダウンのときは、正常な終了処理によって、それらのイベントに最後の送信の機会が与えられます。

## 設定の問題を解決する

| 症状 | 確認すること | 次の操作 |
| - | - | - |
| `LOGBREW_SERVER_API_KEY is required` | 初期化処理が、空でないプロジェクトのキーを受け取っていません。 | Flask の設定、明示的な引数、環境変数のいずれかで、プロジェクト単位のサーバー用キーを設定します。 |
| トレースが表示されない | プロジェクト、サービス、環境、リリース、時間のフィルターが一致していません。 | `logbrew doctor --project "<project_id>" --json` を実行し、範囲を限ったトレースの読み取りをもう一度行います。 |
| トレースは表示されるがログが表示されない | リクエストの計測は、Python のロギングハンドラーを自動では追加しません。 | アプリが所有するロガーに `LogBrewLoggingHandler` を1つ追加します。 |
| リクエストは動くが、終了時に最後のイベントが欠ける | バックグラウンドのワーカーが残りのイベントを送信する前に、プロセスが終了しています。 | ワーカーの終了フックから `logbrew.client.shutdown()` を呼び出します。 |
| ワーカーが `process_ownership_error` を報告する | ワーカープロセスがフォークする前に、クライアントが作成されています。 | ワーカーごとにアプリを読み込み、そのプロセスで新しいクライアントを作成します。 |

アカウントのアクセス権、キー、読み戻しのエラーについては、[トラブルシューティング](/ja/guides/troubleshooting)を参照してください。他のランタイムについては、[SDK 連携](/ja/guides/sdk-ingestion)を参照してください。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.