私は Python で Web アプリケーション フレームワークのプロトタイプを作成しています (主に教育目的のため)。長い間欲しかった 1 つの機能、つまりルートごとのログ レベルに行き詰まっています。
この機能の目的は、診断を実行している特定のエントリ ポイントを特定することです。たとえば、発信者が をヒットしたときに何が起こっているかを追跡したいと考えていPOST /sessions/login
ます。ここで、この URL のリクエスト処理によってヒットしたコードのログ エントリを 100% 取得したいと考えています。これは、サードパーティのアプリケーションで行われることを含め、すべてを意味します。
例: 架空のアプリケーションには と の 2 つのルートが/sessions/login
あり/sessions/info
ます。両方のリクエスト ハンドラーが、users
logger を使用する package 内の同じデータベース コードにヒットしますmyapp.users.db
。のリクエスト処理は/sessions/login
loggermyapp.users.db
でログ メッセージを出力する必要がありますが、 のリクエスト処理は/sessions/info
すべきではありません。
問題は、これが Python のロギング ライブラリにうまく適合しないことです。このライブラリはロギングを階層的に分解し、階層化に適しています (たとえば、アプリケーション レイヤによるログ レベルの制御)。
私が本当に欲しいのは、コンテキスト依存のログ レベルです。頭に浮かぶ自然な実装はlogger.getEffectiveLevel()
、スレッド ローカルのログ レベルを返すようにするものです (リクエスト URL がデバッグの対象である場合、デバッグ ミドルウェアは条件付きでログ レベルをデバッグに下げます)。しかし、私は Python ドキュメントのロギング フローを見ていますが、さまざまな種類の構成フックのいずれかを使用してこれを実装する方法がわかりません。
質問: Python でコンテキスト依存のログ レベルをどのように実装しますか?
更新:部分的な解決策が見つかりました。
context = threading.local()
class ContextualLogger(logging.Logger):
def getEffectiveLevel(self):
global context
level = getattr(context, 'log_level', logging.NOTSET)
if level == logging.NOTSET:
level = super(ContextualLogger, self).getEffectiveLevel()
return level
logging.setLoggerClass(ContextualLogger)
ただし、これはルート ロガーでは機能しません。何か案は?
更新: 関数にモンキー パッチを適用することもできgetEffectiveLevel()
ます。
context = threading.local()
# Monkey patch "getEffectiveLevel()" to consult the current setting in the
# `context.log_level` thread-local storage. If that value is present, use
# it to override the current value; else, compute the level using the usual
# infrastructure.
default_getEffectiveLevel = logging.Logger.getEffectiveLevel
def patched_getEffectiveLevel(self):
level = getattr(context, 'log_level', logging.NOTSET)
if level == logging.NOTSET:
level = default_getEffectiveLevel(self)
return level
logging.Logger.getEffectiveLevel = patched_getEffectiveLevel
現在、これはルートロガーでも機能します。この関数にモンキー パッチを適用することに少し抵抗があることは認めざるを得ませんが、通常のインフラストラクチャにフォールバックするため、実際には見た目ほど汚れていません。