nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonのライブラリに用意されたロガーを設定してログを出力する(httpxを例に)

はじめに

エミリーちゃん... お衣装だよ... nikkieです

Pythonのloggingの小ネタです。
ライブラリに用意されたロガーをアプリケーションコードで操作していきます。
アプリケーションコードという言い回しは、ライブラリを使うコードを指して使っています。

目次

httpxのロガー

以下の記事で見た事例をなぞっています

並行処理までサポートするHTTPクライアントライブラリ httpx1
ロギングについてはドキュメントの以下にあります。

www.python-httpx.org

この実装ですが、httpxという名前のロガーが仕込まれています。

logger = logging.getLogger("httpx")

logging.getLoggerのドキュメントにありますが、

与えられた名前に対して、この関数はどの呼び出しでも同じロガーインスタンスを返します。

httpxという名前のロガーは1つだけ(シングルトン)となります。

httpxロガーには

がありました。

httpxロガーはハンドラが設定されていません。
なので、「最終手段ハンドラ」が使われます2
https://docs.python.org/ja/3/howto/logging.html#what-happens-if-no-configuration-is-provided
ですが、WARNINGレベル未満のロギングのため、ログ出力は行われません。

方法1:ルートロガーを設定する

httpxのドキュメントでも案内されている方法です。

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format="%(levelname)s [%(asctime)s] %(name)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

httpxロガーのログレベルはNOTSETです。
親のルートロガーのログレベルをDEBUGとすることで、httpxロガーはDEBUG以上のレベルをロギングします3
httpxロガーにハンドラはありませんが、ルートロガーにpropagateして、(basicConfigで設定済みの)ルートロガーのハンドラにより出力されます。

スクリプト全体4

uvで試していきます。

% uv --version
uv 0.3.3 (Homebrew 2024-08-23)
% uv run python -V
Python 3.12.5
% uv run script.py

httpxのログが確認できます

DEBUG [2024-08-25 20:48:25] httpx - load_ssl_context verify=True cert=None trust_env=True http2=False
DEBUG [2024-08-25 20:48:25] httpx - load_verify_locations cafile='/Users/.../Library/Caches/uv/archive-v0/UKa20n18cbeMI11dnBLSO/lib/python3.12/site-packages/certifi/cacert.pem'
INFO [2024-08-25 20:48:25] httpx - HTTP Request: GET https://peps.python.org/api/peps.json "HTTP/1.1 200 OK"

ルートロガーはすべての親なので、httpcoreのロガー5のログも見えています(httpxのドキュメントにあるとおりです)

方法2:httpxロガーを設定する

再掲ですが、こちらの記事で知った方法です。

httpx_logger = logging.getLogger("httpx")
httpx_logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_formatter = logging.Formatter(
    "%(levelname)s [%(asctime)s] %(name)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
console_handler.setFormatter(console_formatter)
httpx_logger.addHandler(console_handler)

方法1でルートロガーに設定したのと同様にhttpxロガーに(アプリケーションコードで)設定しました

  • ログレベルはDEBUG
  • 標準エラー出力に出力(StreamHandler
    • フォーマッタにformatやdatefmtを指定
    • ハンドラのレベルはNOTSET(全部出力する)で問題なさそうなので触らず(※元記事から変えた点)

スクリプト全体

httpxライブラリのログだけが出力されました!

DEBUG [2024-08-25 21:00:05] httpx - load_ssl_context verify=True cert=None trust_env=True http2=False
DEBUG [2024-08-25 21:00:05] httpx - load_verify_locations cafile='/Users/.../Library/Caches/uv/archive-v0/UKa20n18cbeMI11dnBLSO/lib/python3.12/site-packages/certifi/cacert.pem'
INFO [2024-08-25 21:00:05] httpx - HTTP Request: GET https://peps.python.org/api/peps.json "HTTP/1.1 200 OK"

まとめ

この記事ではhttpxを例に、ライブラリに設定されたロガーをアプリケーションコードで操作する方法を2つ見ました。

  1. アプリケーションコードでルートロガーを設定する
    • logging.basicConfig
    • すべてのロガーの親のため、ログを確認したいライブラリ以外のログもpropagateされてくる
  2. アプリケーションコードで特定のライブラリのロガーを設定する
    • logging.getLoggerは同じ名前ならシングルトン
    • ライブラリのロガーにログレベルやハンドラを設定する(ハンドラにはフォーマッタも)

ライブラリの利用者がこのように設定できるからこそ、ライブラリ側のロガーはNullHandler(もしくはハンドラを取り付けない6)でよいなと思います。


  1. このブログにもいくつか記事があります。 https://nikkie-ftnext.hatenablog.com/search?q=httpx
  2. 取り上げている記事
  3. 注で既出ですが、この記事にあります
  4. Inline script metadataのサンプルコードをhttpxに書き換えました
  5. https://github.com/search?q=repo%3Aencode%2Fhttpcore%20getLogger&type=code
  6. ライブラリのロガーにハンドラを取り付けないとは、すなわち最終手段ハンドラが使われる状況です。WARNING以上のレベルでは出力され、私はそれをあまり好ましくとらえていないので、カッコ書きです