ロギングHOWTO—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.9/howto/logging
移動先:案内検索

ロギングHOWTO

著者
ビナイサジップ

基本的なロギングチュートリアル

ロギングは、一部のソフトウェアの実行時に発生するイベントを追跡する手段です。 ソフトウェアの開発者は、特定のイベントが発生したことを示すために、コードにロギング呼び出しを追加します。 イベントは、オプションで変数データを含めることができる説明メッセージによって記述されます(つまり、 イベントの発生ごとに異なる可能性のあるデータ)。 イベントには、開発者がイベントに帰する重要性もあります。 重要度は、レベルまたは重大度とも呼ばれます。

ロギングを使用する場合

ロギングは、簡単なロギングの使用のための便利な関数のセットを提供します。 これらは、 debug()info()warning()error()、および critical()です。 ]。 ロギングをいつ使用するかを決定するには、以下の表を参照してください。この表には、一連の一般的なタスクのそれぞれについて、ロギングに使用するのに最適なツールが記載されています。

実行したいタスク タスクに最適なツール
コマンドラインスクリプトまたはプログラムの通常の使用のためのコンソール出力を表示します print()
プログラムの通常の操作中に発生するイベントを報告します(例: 状態監視または障害調査用) logging.info()(または診断目的の非常に詳細な出力の場合は logging.debug()
特定のランタイムイベントに関する警告を発行します

問題が回避可能であり、警告を排除するためにクライアントアプリケーションを変更する必要がある場合は、ライブラリコードの warnings.warn()

logging.warning()クライアントアプリケーションが状況について何もできない場合でも、イベントに注意する必要があります

特定のランタイムイベントに関するエラーを報告する 例外を発生させる
例外を発生させずにエラーの抑制を報告します(例: 長時間実行されるサーバープロセスのエラーハンドラー) 特定のエラーおよびアプリケーションドメインに応じて、 logging.error()logging.exception()、または logging.critical()

ロギング機能は、追跡に使用されるイベントのレベルまたは重大度にちなんで名付けられています。 標準レベルとその適用性を以下に説明します(重大度の昇順)。

レベル 使用時
DEBUG 詳細情報。通常、問題を診断する場合にのみ関心があります。
INFO 物事が期待どおりに機能していることの確認。
WARNING 予期しないことが起こったことの兆候、または近い将来に何らかの問題が発生したことの兆候(例: 「ディスク容量が少ない」)。 ソフトウェアはまだ期待どおりに機能しています。
ERROR より深刻な問題のため、ソフトウェアはいくつかの機能を実行できませんでした。
CRITICAL プログラム自体が実行を継続できない可能性があることを示す重大なエラー。

デフォルトのレベルはWARNINGです。これは、ロギングパッケージが別の方法で構成されていない限り、このレベル以上のイベントのみが追跡されることを意味します。

追跡されるイベントは、さまざまな方法で処理できます。 追跡されたイベントを処理する最も簡単な方法は、それらをコンソールに出力することです。 もう1つの一般的な方法は、それらをディスクファイルに書き込むことです。


簡単な例

非常に簡単な例は次のとおりです。

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

これらの行をスクリプトに入力して実行すると、次のように表示されます。

WARNING:root:Watch out!

コンソールに印刷されます。 デフォルトのレベルがWARNINGであるため、INFOメッセージは表示されません。 印刷されたメッセージには、レベルの表示と、ロギング呼び出しで提供されたイベントの説明が含まれます。 '気を付けて!'。 今のところ「ルート」の部分については心配しないでください。後で説明します。 実際の出力は、必要に応じて非常に柔軟にフォーマットできます。 書式設定オプションについても後で説明します。


ファイルへのロギング

非常に一般的な状況は、ログイベントをファイルに記録する状況です。次に、それを見てみましょう。 新しく起動したPythonインタープリターで次のことを試してください。上記のセッションから続行しないでください。

import logging
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.error('And non-ASCII stuff, too, like Øresund and Malmö')

バージョン3.9で変更: encoding 引数が追加されました。 以前のPythonバージョンでは、または指定されていない場合、使用されるエンコーディングは open()で使用されるデフォルト値です。 上記の例には示されていませんが、 errors 引数も渡すことができるようになりました。これにより、エンコードエラーの処理方法が決まります。 使用可能な値とデフォルトについては、 open()のドキュメントを参照してください。


そして今、ファイルを開いて私たちが持っているものを見ると、ログメッセージを見つけるはずです:

DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
ERROR:root:And non-ASCII stuff, too, like Øresund and Malmö

この例は、追跡のしきい値として機能するロギングレベルを設定する方法も示しています。 この場合、しきい値をDEBUGに設定したため、すべてのメッセージが出力されました。

次のようなコマンドラインオプションからログレベルを設定する場合:

--log=INFO

--logに渡されたパラメーターの値が、いくつかの変数 loglevel にある場合は、次を使用できます。

getattr(logging, loglevel.upper())

level 引数を介して basicConfig()に渡す値を取得します。 次の例のように、ユーザー入力値をエラーチェックすることをお勧めします。

# assuming loglevel is bound to the string value obtained from the
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)

basicConfig()の呼び出しは、 debug()info()などの呼び出しの前に行う必要があります。 これは1回限りの単純な構成機能として意図されているため、最初の呼び出しのみが実際に何も実行しません。後続の呼び出しは事実上何も実行されません。

上記のスクリプトを複数回実行すると、連続して実行されたメッセージがファイル example.log に追加されます。 以前の実行からのメッセージを記憶せずに、各実行を新たに開始する場合は、上記の例の呼び出しを次のように変更することで、 filemode 引数を指定できます。

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)

出力は以前と同じになりますが、ログファイルが追加されなくなったため、以前の実行からのメッセージは失われます。


複数のモジュールからのロギング

プログラムが複数のモジュールで構成されている場合、ログインを整理する方法の例を次に示します。

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
# mylib.py
import logging

def do_something():
    logging.info('Doing something')

myapp.py を実行すると、 myapp.log に次のように表示されます。

INFO:root:Started
INFO:root:Doing something
INFO:root:Finished

うまくいけば、あなたが期待していたものです。 mylib.py のパターンを使用して、これを複数のモジュールに一般化できます。 この単純な使用パターンでは、イベントの説明を見る以外に、ログファイルを調べても、アプリケーションのどこからメッセージが送信されたのかがわからないことに注意してください。 メッセージの場所を追跡する場合は、チュートリアルレベルを超えてドキュメントを参照する必要があります。高度なログチュートリアルを参照してください。


変数データのロギング

変数データをログに記録するには、イベントの説明メッセージにフォーマット文字列を使用し、変数データを引数として追加します。 例えば:

import logging
logging.warning('%s before you %s', 'Look', 'leap!')

表示されます:

WARNING:root:Look before you leap!

ご覧のとおり、変数データをイベントの説明メッセージにマージするには、古い%-sスタイルの文字列形式が使用されます。 これは下位互換性のためです。ロギングパッケージは、 str.format()string.Template などの新しいフォーマットオプションよりも前のものです。 これらの新しいフォーマットオプションでサポートされていますが、それらの調査はこのチュートリアルの範囲外です。詳細については、アプリケーション全体での特定のフォーマットスタイルの使用を参照してください。


表示されるメッセージの形式を変更する

メッセージの表示に使用される形式を変更するには、使用する形式を指定する必要があります。

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')

これは印刷されます:

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too

前の例で表示された「ルート」が消えていることに注意してください。 フォーマット文字列に表示される可能性のあるものの完全なセットについては、 LogRecord属性のドキュメントを参照できますが、簡単な使用法では、 levelname (重大度)、[ X201X]メッセージ(変数データを含むイベントの説明)およびおそらくイベントが発生したときに表示します。 これについては、次のセクションで説明します。


メッセージに日付/時刻を表示する

イベントの日付と時刻を表示するには、フォーマット文字列に「%(asctime)s」を配置します。

import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')

これは次のようなものを印刷するはずです:

2010-12-12 11:41:42,612 is when this event was logged.

日付/時刻表示のデフォルトの形式(上に表示)は、ISO8601または RFC 3339 のようなものです。 日付/時刻のフォーマットをさらに制御する必要がある場合は、次の例のように、 datefmt 引数をbasicConfigに指定します。

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')

これは次のようなものを表示します:

12/12/2010 11:46:36 AM is when this event was logged.

datefmt 引数の形式は、 time.strftime()でサポートされているものと同じです。


次のステップ

これで基本的なチュートリアルは終わりです。 ロギングを開始して実行するには十分なはずです。 ロギングパッケージが提供するものは他にもたくさんありますが、それを最大限に活用するには、次のセクションを読むことにもう少し時間を費やす必要があります。 その準備ができたら、お気に入りの飲み物をいくつか手に入れて続けてください。

ロギングのニーズが単純な場合は、上記の例を使用してロギングを独自のスクリプトに組み込みます。問題が発生した場合や何かがわからない場合は、comp.lang.python Usenetグループ([で入手可能)に質問を投稿してください。 X242X] https://groups.google.com/forum/#!forum/comp.lang.python )そしてあなたはすぐに助けを受け取るはずです。

まだここ? 次のいくつかのセクションを読み続けることができます。これらのセクションでは、上記の基本的なチュートリアルよりも少し高度で詳細なチュートリアルが提供されます。 その後、 Logging Cookbook をご覧ください。


高度なロギングチュートリアル

ロギングライブラリはモジュラーアプローチを採用しており、ロガー、ハンドラー、フィルター、フォーマッターなど、いくつかのカテゴリのコンポーネントを提供します。

  • ロガーは、アプリケーションコードが直接使用するインターフェイスを公開します。
  • ハンドラーは、ログレコード(ロガーによって作成された)を適切な宛先に送信します。
  • フィルタは、出力するログレコードを決定するためのよりきめ細かい機能を提供します。
  • フォーマッタは、最終出力のログレコードのレイアウトを指定します。

ログイベント情報は、 LogRecord インスタンスのロガー、ハンドラー、フィルター、およびフォーマッターの間で渡されます。

ロギングは、 Logger クラス(以下、 loggers と呼びます)のインスタンスでメソッドを呼び出すことによって実行されます。 各インスタンスには名前があり、ドット(ピリオド)を区切り文字として使用して、概念的に名前空間階層に配置されます。 たとえば、「scan」という名前のロガーは、「scan.text」、「scan.html」、および「scan.pdf」のロガーの親です。 ロガー名は任意の名前にすることができ、ログに記録されたメッセージが発信されるアプリケーションの領域を示します。

ロガーに名前を付けるときに使用する良い規則は、次のように名前が付けられた、ロギングを使用する各モジュールでモジュールレベルのロガーを使用することです。

logger = logging.getLogger(__name__)

これは、ロガー名がパッケージ/モジュール階層を追跡することを意味し、イベントがロガー名からログに記録される場所が直感的にわかります。

ロガーの階層のルートは、ルートロガーと呼ばれます。 これは、関数 debug()info()warning()error()、およびで使用されるロガーです。クリティカル()。これは、ルートロガーの同じ名前のメソッドを呼び出すだけです。 関数とメソッドのシグネチャは同じです。 ルートロガーの名前は、ログ出力に「root」として出力されます。

もちろん、さまざまな宛先にメッセージを記録することは可能です。 パッケージには、ログメッセージをファイル、HTTP GET / POSTの場所、SMTP経由の電子メール、汎用ソケット、キュー、またはsyslogやWindowsNTイベントログなどのOS固有のログメカニズムに書き込むためのサポートが含まれています。 宛先はハンドラークラスによって提供されます。 組み込みのハンドラークラスのいずれも満たさない特別な要件がある場合は、独自のログ宛先クラスを作成できます。

デフォルトでは、ログメッセージの宛先は設定されていません。 チュートリアルの例のように basicConfig()を使用して、宛先(コンソールやファイルなど)を指定できます。 関数 debug()info()warning()error()critical( )、宛先が設定されていないかどうかを確認します。 設定されていない場合は、コンソールの宛先(sys.stderr)と表示されるメッセージのデフォルト形式を設定してから、ルートロガーに委任して実際のメッセージ出力を実行します。

basicConfig()によってメッセージに設定されるデフォルトの形式は次のとおりです。

severity:logger name:message

これを変更するには、 format キーワード引数を指定してフォーマット文字列を basicConfig()に渡します。 フォーマット文字列の作成方法に関するすべてのオプションについては、 Formatter Objects を参照してください。

ロギングフロー

次の図に、ロガーとハンドラーでのログイベント情報のフローを示します。

../_images/logging_flow.png

ロガー

Logger オブジェクトには3つの役割があります。 まず、アプリケーションが実行時にメッセージをログに記録できるように、いくつかのメソッドをアプリケーションコードに公開します。 次に、ロガーオブジェクトは、重大度(デフォルトのフィルタリング機能)またはフィルターオブジェクトに基づいて、どのログメッセージに基づいて動作するかを決定します。 第3に、ロガーオブジェクトは、関連するログメッセージをすべての関心のあるログハンドラーに渡します。

ロガーオブジェクトで最も広く使用されている方法は、構成とメッセージ送信の2つのカテゴリに分類されます。

最も一般的な構成方法は次のとおりです。

  • Logger.setLevel()は、ロガーが処理する最低の重大度のログメッセージを指定します。ここで、debugは最低の組み込み重大度レベルであり、criticalは最高の組み込み重大度です。 たとえば、重大度レベルがINFOの場合、ロガーはINFO、WARNING、ERROR、およびCRITICALメッセージのみを処理し、DEBUGメッセージを無視します。
  • Logger.addHandler()および Logger.removeHandler()は、ロガーオブジェクトにハンドラーオブジェクトを追加および削除します。 ハンドラーについては、ハンドラーで詳しく説明しています。
  • Logger.addFilter()および Logger.removeFilter()は、ロガーオブジェクトにフィルターオブジェクトを追加および削除します。 フィルタについては、フィルタオブジェクトで詳しく説明されています。

作成するすべてのロガーでこれらのメソッドを常に呼び出す必要はありません。 このセクションの最後の2つの段落を参照してください。

ロガーオブジェクトを構成した状態で、次のメソッドがログメッセージを作成します。

  • Logger.debug()Logger.info()Logger.warning()Logger.error()、および[ X105X] Logger.critical()はすべて、それぞれのメソッド名に対応するメッセージとレベルでログレコードを作成します。 メッセージは実際にはフォーマット文字列であり、%s%d%fなどの標準の文字列置換構文が含まれている場合があります。 それらの引数の残りは、メッセージの置換フィールドに対応するオブジェクトのリストです。 **kwargsに関しては、ロギングメソッドはexc_infoのキーワードのみを考慮し、それを使用して例外情報をログに記録するかどうかを決定します。
  • Logger.exception()は、 Logger.error()と同様のログメッセージを作成します。 違いは、 Logger.exception()がスタックトレースを一緒にダンプすることです。 このメソッドは、例外ハンドラーからのみ呼び出します。
  • Logger.log()は、明示的な引数としてログレベルを取ります。 これは、上記のログレベルの便利な方法を使用するよりもメッセージのログ記録について少し冗長ですが、これはカスタムログレベルでログを記録する方法です。

getLogger()は、指定された名前のロガーインスタンスへの参照を返します(指定されている場合)。指定されていない場合はrootを返します。 名前はピリオドで区切られた階層構造です。 同じ名前で getLogger()を複数回呼び出すと、同じロガーオブジェクトへの参照が返されます。 階層リストのさらに下にあるロガーは、リストの上位にあるロガーの子です。 たとえば、fooという名前のロガーがある場合、foo.barfoo.bar.baz、およびfoo.bamという名前のロガーはすべて [の子孫です。 X139X]。

ロガーには、有効レベルの概念があります。 ロガーにレベルが明示的に設定されていない場合は、代わりにその親のレベルが有効なレベルとして使用されます。 親に明示的なレベルが設定されていない場合、 its の親が調べられ、以下同様に続きます。明示的に設定されたレベルが見つかるまで、すべての祖先が検索されます。 ルートロガーには常に明示的なレベルセットがあります(デフォルトではWARNING)。 イベントを処理するかどうかを決定するとき、ロガーの有効レベルを使用して、イベントがロガーのハンドラーに渡されるかどうかが決定されます。

子ロガーは、祖先ロガーに関連付けられたハンドラーまでメッセージを伝播します。 このため、アプリケーションが使用するすべてのロガーのハンドラーを定義および構成する必要はありません。 トップレベルのロガーのハンドラーを構成し、必要に応じて子ロガーを作成するだけで十分です。 (ただし、ロガーの propagate 属性をFalseに設定することで、伝播をオフにすることができます。)


ハンドラー

Handler オブジェクトは、適切なログメッセージ(ログメッセージの重大度に基づく)をハンドラーの指定された宛先にディスパッチする役割を果たします。 Logger オブジェクトは、 addHandler()メソッドを使用して、0個以上のハンドラーオブジェクトをオブジェクトに追加できます。 シナリオ例として、アプリケーションは、すべてのログメッセージをログファイルに送信し、エラー以上のすべてのログメッセージをstdoutに送信し、重要なすべてのメッセージを電子メールアドレスに送信したい場合があります。 このシナリオでは、3つの個別のハンドラーが必要であり、各ハンドラーは特定の重大度のメッセージを特定の場所に送信する責任があります。

標準ライブラリには、かなりの数のハンドラタイプが含まれています(便利なハンドラを参照)。 チュートリアルでは、例で主に StreamHandlerFileHandler を使用しています。

ハンドラーには、アプリケーション開発者が気にするメソッドはほとんどありません。 組み込みのハンドラーオブジェクトを使用している(つまり、カスタムハンドラーを作成していない)アプリケーション開発者に関連すると思われるハンドラーメソッドは、次の構成メソッドのみです。

  • setLevel()メソッドは、ロガーオブジェクトの場合と同様に、適切な宛先にディスパッチされる最低の重大度を指定します。 setLevel()メソッドが2つあるのはなぜですか? ロガーに設定されたレベルによって、ハンドラーに渡すメッセージの重大度が決まります。 各ハンドラーに設定されたレベルによって、そのハンドラーが送信するメッセージが決まります。
  • setFormatter()は、このハンドラーが使用するFormatterオブジェクトを選択します。
  • addFilter()および removeFilter()は、それぞれハンドラーのフィルターオブジェクトを構成および構成解除します。

アプリケーションコードは、 Handler のインスタンスを直接インスタンス化して使用しないでください。 代わりに、 Handler クラスは、すべてのハンドラーが持つ必要のあるインターフェイスを定義し、子クラスが使用(またはオーバーライド)できるデフォルトの動作を確立する基本クラスです。


フォーマッター

フォーマッタオブジェクトは、ログメッセージの最終的な順序、構造、および内容を構成します。 基本の logging.Handler クラスとは異なり、アプリケーションコードはフォーマッタークラスをインスタンス化できますが、アプリケーションで特別な動作が必要な場合は、フォーマッターをサブクラス化できます。 コンストラクターは、メッセージ形式の文字列、日付形式の文字列、およびスタイルインジケーターの3つのオプションの引数を取ります。

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

メッセージ形式の文字列がない場合、デフォルトでは生のメッセージが使用されます。 日付形式の文字列がない場合、デフォルトの日付形式は次のとおりです。

%Y-%m-%d %H:%M:%S

最後にミリ秒が追加されます。 styleは、%、 '{'、または '$'のいずれかです。 これらのいずれかが指定されていない場合は、「%」が使用されます。

styleが '%'の場合、メッセージ形式の文字列は%(<dictionary key>)sスタイルの文字列置換を使用します。 可能なキーは、 LogRecord属性に記載されています。 スタイルが「{」の場合、メッセージ形式の文字列は str.format()と互換性があると見なされます(キーワード引数を使用)。一方、スタイルが「$」の場合、メッセージ形式の文字列は準拠する必要があります。 string.Template.substitute()で期待されるものに。

バージョン3.2で変更: styleパラメーターが追加されました。


次のメッセージ形式の文字列は、人間が読める形式で時刻、メッセージの重大度、メッセージの内容をこの順序でログに記録します。

'%(asctime)s - %(levelname)s - %(message)s'

フォーマッターは、ユーザーが構成可能な関数を使用して、レコードの作成時間をタプルに変換します。 デフォルトでは、 time.localtime()が使用されます。 特定のフォーマッタインスタンスに対してこれを変更するには、インスタンスのconverter属性を、 time.localtime()または time.gmtime()[と同じシグネチャを持つ関数に設定します。 X230X]。 すべてのフォーマッタで変更するには、たとえば、すべてのロギング時間をGMTで表示する場合は、フォーマッタクラスのconverter属性を設定します(GMT表示の場合はtime.gmtimeに設定します)。


ロギングの構成

プログラマーは、次の3つの方法でロギングを構成できます。

  1. 上記の構成メソッドを呼び出すPythonコードを使用して、ロガー、ハンドラー、およびフォーマッターを明示的に作成します。
  2. ロギング設定ファイルを作成し、 fileConfig()関数を使用して読み取ります。
  3. 構成情報のディクショナリを作成し、それを dictConfig()関数に渡します。

最後の2つのオプションのリファレンスドキュメントについては、構成関数を参照してください。 次の例では、Pythonコードを使用して、非常に単純なロガー、コンソールハンドラー、および単純なフォーマッターを構成します。

import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

このモジュールをコマンドラインから実行すると、次の出力が生成されます。

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

次のPythonモジュールは、上記の例とほぼ同じロガー、ハンドラー、フォーマッターを作成しますが、オブジェクトの名前だけが異なります。

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

これがlogging.confファイルです:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

出力は、非構成ファイルベースの例の出力とほぼ同じです。

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

構成ファイルのアプローチには、Pythonコードのアプローチに比べていくつかの利点があります。主に、構成とコードを分離し、非コーダーがロギングプロパティを簡単に変更できることです。

警告

fileConfig()関数は、デフォルトのパラメーターdisable_existing_loggersを取ります。これは、下位互換性のためにデフォルトでTrueになります。 fileConfig()呼び出しの前に存在する非ルートロガー(または祖先)が構成で明示的に指定されていない限り、これが無効になるため、これは希望する場合とそうでない場合があります。 詳細については、リファレンスドキュメントを参照し、必要に応じて、このパラメータにFalseを指定してください。

dictConfig()に渡されるディクショナリは、キーdisable_existing_loggersでブール値を指定することもできます。ディクショナリで明示的に指定されていない場合、デフォルトでTrueとして解釈されます。 これにより、上記のロガーを無効にする動作が発生しますが、これは希望どおりではない可能性があります。その場合は、キーにFalseの値を明示的に指定してください。


構成ファイルで参照されるクラス名は、ロギングモジュールに相対的であるか、通常のインポートメカニズムを使用して解決できる絶対値である必要があることに注意してください。 したがって、 WatchedFileHandler (ロギングモジュールに対して)またはmypackage.mymodule.MyHandler(パッケージmypackageおよびモジュールmymoduleで定義されたクラスの場合)のいずれかを使用できます。 mypackageはPythonインポートパスで利用できます)。

Python 3.2では、辞書を使用して構成情報を保持する、ロギングを構成する新しい手段が導入されました。 これは、上記で概説した構成ファイルベースのアプローチの機能のスーパーセットを提供し、新しいアプリケーションと展開に推奨される構成方法です。 Pythonディクショナリは構成情報を保持するために使用され、さまざまな方法を使用してそのディクショナリにデータを入力できるため、構成のオプションが増えます。 たとえば、JSON形式の構成ファイルを使用できます。YAML処理機能にアクセスできる場合は、YAML形式のファイルを使用して構成ディクショナリにデータを入力できます。 または、もちろん、Pythonコードで辞書を作成したり、ソケットを介してピクルス形式で辞書を受け取ったり、アプリケーションに適したアプローチを使用したりすることもできます。

これは、新しい辞書ベースのアプローチのためのYAML形式での上記と同じ構成の例です。

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]

辞書を使用したロギングの詳細については、構成関数を参照してください。


構成が提供されていない場合はどうなりますか

ロギング構成が提供されていない場合、ロギングイベントを出力する必要がある状況が発生する可能性がありますが、イベントを出力するハンドラーが見つかりません。 このような状況でのロギングパッケージの動作は、Pythonのバージョンによって異なります。

3.2より前のバージョンのPythonの場合、動作は次のとおりです。

  • logging.raiseExceptionsFalse(本番モード)の場合、イベントはサイレントにドロップされます。
  • logging.raiseExceptionsTrue(開発モード)の場合、「ロガーXYZのハンドラーが見つかりませんでした」というメッセージが1回出力されます。

Python 3.2以降では、動作は次のとおりです。

  • イベントは、logging.lastResortに格納されている「最後の手段のハンドラー」を使用して出力されます。 この内部ハンドラーはどのロガーにも関連付けられておらず、 StreamHandler のように機能します。 StreamHandler は、イベントの説明メッセージをsys.stderrの現在の値に書き込みます(したがって、有効なリダイレクトを尊重します)。 メッセージのフォーマットは行われず、ベアイベントの説明メッセージのみが出力されます。 ハンドラーのレベルはWARNINGに設定されているため、これ以上の重大度のすべてのイベントが出力されます。

3.2より前の動作を取得するには、logging.lastResortNoneに設定できます。


ライブラリのログの構成

ロギングを使用するライブラリを開発するときは、ライブラリがロギングをどのように使用するか(たとえば、使用されるロガーの名前)を文書化するように注意する必要があります。 ロギング構成についても考慮する必要があります。 使用しているアプリケーションがロギングを使用せず、ライブラリコードがロギング呼び出しを行う場合、(前のセクションで説明したように)重大度WARNING以上のイベントがsys.stderrに出力されます。 これは、最良のデフォルトの動作と見なされます。

何らかの理由で、ログ設定がない場合にこれらのメッセージを印刷したくない場合は、ライブラリの最上位ロガーに何もしないハンドラーをアタッチできます。 これにより、ライブラリのイベントのハンドラーが常に検出されるため、メッセージが出力されなくなります。出力が生成されないだけです。 ライブラリユーザーがアプリケーションで使用するためにロギングを構成する場合、おそらくその構成によっていくつかのハンドラーが追加され、レベルが適切に構成されている場合、ライブラリコードで行われたロギング呼び出しは、通常どおりそれらのハンドラーに出力を送信します。

何もしないハンドラーがロギングパッケージに含まれています: NullHandler (Python 3.1以降)。 このハンドラーのインスタンスは、ライブラリーが使用するロギング名前空間のトップレベルのロガーに追加できます( if がない場合、ライブラリーのログに記録されたイベントがsys.stderrに出力されないようにしますロギング構成の)。 ライブラリ foo によるすべてのロギングが、「foo.x」、「foo.x.y」などに一致する名前のロガーを使用して行われる場合。 次にコード:

import logging
logging.getLogger('foo').addHandler(logging.NullHandler())

望ましい効果があるはずです。 組織が多数のライブラリを作成している場合、指定するロガー名は「foo」だけでなく「orgname.foo」にすることができます。

ノート

ライブラリのロガー NullHandler 以外のハンドラーを追加しないことを強くお勧めします。 これは、ハンドラーの構成が、ライブラリーを使用するアプリケーション開発者の特権であるためです。 アプリケーション開発者は、ターゲットオーディエンスと、アプリケーションに最も適したハンドラーを知っています。「内部」でハンドラーを追加すると、単体テストを実行して要件に合ったログを配信する機能が妨げられる可能性があります。


ロギングレベル

ロギングレベルの数値を次の表に示します。 これらは、独自のレベルを定義し、事前定義されたレベルに関連する特定の値を持つ必要がある場合に主に役立ちます。 同じ数値でレベルを定義すると、事前定義された値が上書きされます。 事前定義された名前は失われます。

レベル 数値
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

レベルはロガーに関連付けることもでき、開発者が設定するか、保存されたロギング構成をロードすることで設定できます。 ロギングメソッドがロガーで呼び出されると、ロガーは自身のレベルをメソッド呼び出しに関連付けられたレベルと比較します。 ロガーのレベルがメソッド呼び出しのレベルよりも高い場合、ログメッセージは実際には生成されません。 これは、ロギング出力の冗長性を制御する基本的なメカニズムです。

ロギングメッセージは、 LogRecord クラスのインスタンスとしてエンコードされます。 ロガーが実際にイベントをログに記録することを決定すると、ログメッセージから LogRecord インスタンスが作成されます。

ロギングメッセージは、ハンドラークラスのサブクラスのインスタンスであるハンドラーを使用してディスパッチメカニズムの対象になります。 ハンドラーは、ログに記録されたメッセージ( LogRecord の形式)が特定の場所(または場所のセット)に到達することを保証する責任があります。これは、そのメッセージのターゲットオーディエンス(エンドユーザーなど)に役立ちます。 、サポートデスクのスタッフ、システム管理者、開発者)。 ハンドラーには、特定の宛先を対象とした LogRecord インスタンスが渡されます。 各ロガーには、ゼロ、1つ以上のハンドラーを関連付けることができます(ロガーaddHandler()メソッドを介して)。 ロガーに直接関連付けられているハンドラーに加えて、ロガーのすべての祖先に関連付けられているすべてのハンドラーが呼び出され、メッセージがディスパッチされます(ロガーの propagate フラグがに設定されている場合を除く) false値。この時点で、祖先ハンドラーへの受け渡しが停止します)。

ロガーの場合と同様に、ハンドラーにはレベルを関連付けることができます。 ハンドラーのレベルは、ロガーのレベルと同じようにフィルターとして機能します。 ハンドラーが実際にイベントをディスパッチすることを決定した場合、 emit()メソッドを使用してメッセージを宛先に送信します。 Handler のほとんどのユーザー定義サブクラスは、この emit()をオーバーライドする必要があります。

カスタムレベル

独自のレベルを定義することは可能ですが、既存のレベルは実際の経験に基づいて選択されているため、必須ではありません。 ただし、カスタムレベルが必要であると確信している場合は、これを行う際に細心の注意を払う必要があります。ライブラリを開発している場合は、カスタムレベルを定義することは非常に悪い考えです。 これは、複数のライブラリ作成者がすべて独自のカスタムレベルを定義している場合、特定の数値は異なる意味を持つ可能性があるため、一緒に使用されるそのような複数のライブラリからのログ出力は、使用する開発者が制御および/または解釈するのが難しい可能性があるためです。さまざまなライブラリ用。


便利なハンドラー

基本の Handler クラスに加えて、多くの便利なサブクラスが提供されています。

  1. StreamHandler インスタンスは、メッセージをストリーム(ファイルのようなオブジェクト)に送信します。
  2. FileHandler インスタンスは、ディスクファイルにメッセージを送信します。
  3. BaseRotatingHandler は、特定の時点でログファイルをローテーションするハンドラーの基本クラスです。 直接インスタンス化することを意図したものではありません。 代わりに、 RotatingFileHandler または TimedRotatingFileHandler を使用してください。
  4. RotatingFileHandler インスタンスは、最大ログファイルサイズとログファイルローテーションをサポートして、メッセージをディスクファイルに送信します。
  5. TimedRotatingFileHandler インスタンスは、メッセージをディスクファイルに送信し、特定の時間間隔でログファイルをローテーションします。
  6. SocketHandler インスタンスはTCP / IPソケットにメッセージを送信します。 3.4以降、Unixドメインソケットもサポートされています。
  7. DatagramHandler インスタンスはUDPソケットにメッセージを送信します。 3.4以降、Unixドメインソケットもサポートされています。
  8. SMTPHandler インスタンスは、指定された電子メールアドレスにメッセージを送信します。
  9. SysLogHandler インスタンスは、おそらくリモートマシン上のUnixsyslogデーモンにメッセージを送信します。
  10. NTEventLogHandler インスタンスは、Windows NT / 2000 / XPイベントログにメッセージを送信します。
  11. MemoryHandler インスタンスは、特定の基準が満たされるたびにフラッシュされるメモリ内のバッファにメッセージを送信します。
  12. HTTPHandler インスタンスは、GETまたはPOSTセマンティクスのいずれかを使用してHTTPサーバーにメッセージを送信します。
  13. WatchedFileHandler インスタンスは、ログに記録しているファイルを監視します。 ファイルが変更された場合は、ファイル名を使用して閉じられ、再度開かれます。 このハンドラーは、Unixライクなシステムでのみ役立ちます。 Windowsは、使用される基本的なメカニズムをサポートしていません。
  14. QueueHandler インスタンスは、 queue または multiprocessing モジュールに実装されているようなメッセージをキューに送信します。
  15. NullHandler インスタンスは、エラーメッセージに対して何もしません。 これらは、ロギングを使用したいが、ライブラリユーザーがロギングを構成していない場合に表示される可能性がある「ロガーXXXのハンドラーが見つかりませんでした」というメッセージを避けたいライブラリ開発者によって使用されます。 詳細については、ライブラリのログの構成を参照してください。

バージョン3.1の新機能: NullHandler クラス。


バージョン3.2の新機能: QueueHandler クラス。


NullHandlerStreamHandler 、および FileHandler クラスは、コアロギングパッケージで定義されています。 他のハンドラーは、サブモジュール logging.handlers で定義されています。 (構成機能用に、別のサブモジュール logging.config もあります。)

ログに記録されたメッセージは、 Formatter クラスのインスタンスを介して表示するためにフォーマットされます。 これらは、% o peratorおよび辞書での使用に適したフォーマット文字列で初期化されます。

複数のメッセージをまとめてフォーマットする場合は、BufferingFormatterのインスタンスを使用できます。 フォーマット文字列(バッチ内の各メッセージに適用される)に加えて、ヘッダーおよびトレーラーのフォーマット文字列が用意されています。

ロガーレベルやハンドラーレベルに基づくフィルタリングでは不十分な場合は、 Filter のインスタンスを Logger インスタンスと Handler インスタンスの両方に追加できます([X185X ] addFilter()メソッド)。 メッセージをさらに処理することを決定する前に、ロガーとハンドラーの両方が許可を得るためにすべてのフィルターを調べます。 フィルタがfalse値を返す場合、メッセージはそれ以上処理されません。

基本的な Filter 機能により、特定のロガー名によるフィルタリングが可能になります。 この機能を使用すると、指定されたロガーとその子に送信されたメッセージはフィルターを通過でき、他のすべてのメッセージはドロップされます。


ロギング中に発生した例外

ロギングパッケージは、本番環境へのログイン中に発生する例外を飲み込むように設計されています。 これは、ロギングイベントの処理中に発生するエラー(ロギングの設定ミス、ネットワーク、またはその他の同様のエラーなど)によって、ロギングを使用するアプリケーションが途中で終了しないようにするためです。

SystemExit および KeyboardInterrupt 例外が飲み込まれることはありません。 Handler サブクラスの emit()メソッド中に発生するその他の例外は、その handleError()メソッドに渡されます。

HandlerhandleError()のデフォルトの実装は、モジュールレベルの変数raiseExceptionsが設定されているかどうかを確認します。 設定されている場合、トレースバックは sys.stderr に出力されます。 設定されていない場合、例外は飲み込まれます。

ノート

raiseExceptionsのデフォルト値はTrueです。 これは、開発中に、通常、発生した例外について通知を受ける必要があるためです。 本番環境で使用する場合は、raiseExceptionsFalseに設定することをお勧めします。


メッセージとして任意のオブジェクトを使用する

前のセクションと例では、イベントのログ記録時に渡されるメッセージは文字列であると想定されています。 ただし、これが唯一の可能性ではありません。 任意のオブジェクトをメッセージとして渡すことができ、その __ str __()メソッドは、ロギングシステムがそれを文字列表現に変換する必要があるときに呼び出されます。 実際、必要に応じて、文字列表現の計算を完全に回避できます。たとえば、 SocketHandler は、イベントをピクルしてネットワーク経由で送信することにより、イベントを発行します。


最適化

メッセージ引数のフォーマットは、回避できなくなるまで延期されます。 ただし、loggingメソッドに渡される引数の計算にもコストがかかる可能性があるため、ロガーがイベントを破棄するだけの場合は、計算を避けたい場合があります。 何をするかを決定するには、 isEnabledFor()メソッドを呼び出すことができます。このメソッドは、レベル引数を取り、そのレベルの呼び出しに対してロガーによってイベントが作成される場合はtrueを返します。 次のようなコードを書くことができます:

if logger.isEnabledFor(logging.DEBUG):
    logger.debug('Message with %s, %s', expensive_func1(),
                                        expensive_func2())

そのため、ロガーのしきい値がDEBUGより上に設定されている場合、expensive_func1()およびexpensive_func2()への呼び出しは行われません。

ノート

場合によっては、 isEnabledFor()自体が希望よりも高くなる可能性があります(例: 明示的なレベルがロガー階層の上位にのみ設定されている、深くネストされたロガーの場合)。 このような場合(またはタイトループでメソッドを呼び出さないようにしたい場合)、 isEnabledFor()の呼び出し結果をローカル変数またはインスタンス変数にキャッシュし、を呼び出す代わりにそれを使用できます。毎回メソッド。 このようなキャッシュされた値は、アプリケーションの実行中にロギング構成が動的に変更された場合にのみ再計算する必要があります(これはそれほど一般的ではありません)。


収集されるロギング情報をより正確に制御する必要がある特定のアプリケーションに対して行うことができる他の最適化があります。 ロギング中の不要な処理を回避するために実行できることのリストを次に示します。

集めたくないもの それを収集しないようにする方法
どこから電話がかけられたかについての情報。 logging._srcfileNoneに設定します。 これにより、 sys._getframe()の呼び出しが回避され、PyPy( sys._getframe()を使用するコードを高速化できない)などの環境でコードを高速化するのに役立つ場合があります。
スレッド情報。 logging.logThreads0に設定します。
プロセス情報。 logging.logProcesses0に設定します。

また、コアロギングモジュールには基本的なハンドラーのみが含まれていることに注意してください。 logging.handlerslogging.config をインポートしない場合、それらはメモリを消費しません。

も参照してください

モジュールロギング
ロギングモジュールのAPIリファレンス。
モジュール logging.config
ロギングモジュールの構成API。
モジュール logging.handlers
ロギングモジュールに含まれている便利なハンドラー。

ロギングクックブック