はじめに
エミリーちゃん... お衣装だよ... nikkieです
Pythonのloggingの小ネタです。
ライブラリに用意されたロガーをアプリケーションコードで操作していきます。
アプリケーションコードという言い回しは、ライブラリを使うコードを指して使っています。
目次
httpxのロガー
以下の記事で見た事例をなぞっています
並行処理までサポートするHTTPクライアントライブラリ httpx1。
ロギングについてはドキュメントの以下にあります。
この実装ですが、httpxという名前のロガーが仕込まれています。
logger = logging.getLogger("httpx")
- https://github.com/encode/httpx/blob/0.27.0/httpx/_client.py#L90
- https://github.com/encode/httpx/blob/0.27.0/httpx/_config.py#L37
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
で設定済みの)ルートロガーのハンドラにより出力されます。
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つ見ました。
- アプリケーションコードでルートロガーを設定する
logging.basicConfig
- すべてのロガーの親のため、ログを確認したいライブラリ以外のログもpropagateされてくる
- アプリケーションコードで特定のライブラリのロガーを設定する
logging.getLogger
は同じ名前ならシングルトン- ライブラリのロガーにログレベルやハンドラを設定する(ハンドラにはフォーマッタも)
ライブラリの利用者がこのように設定できるからこそ、ライブラリ側のロガーはNullHandler
(もしくはハンドラを取り付けない6)でよいなと思います。
- このブログにもいくつか記事があります。 https://nikkie-ftnext.hatenablog.com/search?q=httpx↩
- 取り上げている記事 ↩
- 注で既出ですが、この記事にあります ↩
- Inline script metadataのサンプルコードをhttpxに書き換えました ↩
- https://github.com/search?q=repo%3Aencode%2Fhttpcore%20getLogger&type=code↩
-
ライブラリのロガーにハンドラを取り付けないとは、すなわち最終手段ハンドラが使われる状況です。
WARNING
以上のレベルでは出力され、私はそれをあまり好ましくとらえていないので、カッコ書きです↩