前書き
logging
モジュールは標準のPythonライブラリの一部であり、ソフトウェアの実行中に発生するイベントの追跡を提供します。 ロギングコールをコードに追加して、発生したイベントを示すことができます。
logging
モジュールでは、アプリケーションの操作に関連するイベントを記録する診断ログと、分析のためにユーザーのトランザクションのイベントを記録する監査ログの両方を使用できます。 特に、イベントをファイルに記録するために使用されます。
logging
モジュールを使用する理由
logging
モジュールは、プログラム内で発生するイベントの記録を保持し、ソフトウェアの実行時に発生するイベントのいずれかに関連する出力を確認できるようにします。
コード全体でprint()
ステートメントを使用することで、イベントが発生していることを確認することに慣れている場合があります。 print()
ステートメントdoesは、問題を解決するためにコードをデバッグするための基本的な方法を提供します。 コード全体にprint()
ステートメントを埋め込むと、実行フローとプログラムの現在の状態を追跡できますが、このソリューションは、いくつかの理由でlogging
モジュールを使用するよりも保守が難しいことがわかります。
-
2つが混在しているため、デバッグ出力と通常のプログラム出力を区別することが難しくなります。
-
コード全体に分散された
print()
ステートメントを使用する場合、デバッグ出力を提供するステートメントを無効にする簡単な方法はありません。 -
デバッグが終了すると、すべての
print()
ステートメントを削除することが困難になります -
すぐに利用できる診断情報を含むログレコードはありません
コードでlogging
モジュールを使用する習慣を身に付けることをお勧めします。これは、単純なPythonスクリプトを超えて成長し、デバッグへの持続可能なアプローチを提供するアプリケーションに適しているためです。
ログは時間の経過とともに動作とエラーを表示できるため、アプリケーション開発プロセスで行われていることの全体像をよりよく把握できます。
コンソールへのデバッグメッセージの印刷
print()
ステートメントを使用してプログラムで何が起こっているかを確認することに慣れている場合は、defines a classを実行し、次のようなオブジェクトをインスタンス化するプログラムを確認することに慣れている可能性があります。
pizza.py
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
print("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
print("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
print("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
上記のコードには、Pizza
クラスのオブジェクトのname
とprice
を定義する__init__
メソッドがあります。 次に、2つのメソッドがあります。1つはピザを作るためのmake()
と呼ばれ、もう1つはピザを食べるためのeat()
と呼ばれます。 これらの2つのメソッドは、1
で初期化されるquantity
のパラメーターを受け取ります。
それではプログラムを実行しましょう:
python pizza.py
次の出力が表示されます。
OutputPizza created: artichoke ($15)
Made 1 artichoke pizza(s)
Ate 1 pizza(s)
Pizza created: margherita ($12)
Made 2 margherita pizza(s)
Ate 1 pizza(s)
print()
ステートメントを使用すると、コードが機能していることを確認できますが、代わりにlogging
モジュールを使用してこれを行うことができます。
コード全体でprint()
ステートメントを削除またはコメントアウトし、ファイルの先頭にimport logging
を追加しましょう。
pizza.py
import logging
class Pizza():
def __init__(self, name, value):
self.name = name
self.value = value
...
logging
モジュールにはWARNING
のdefault levelがあります。これはDEBUG
より上のレベルです。 この例ではデバッグにlogging
モジュールを使用するため、logging.DEBUG
のレベルがコンソールに情報を返すように構成を変更する必要があります。 これを行うには、import statementの下に次の行を追加します。
pizza.py
import logging
logging.basicConfig(level=logging.DEBUG)
class Pizza():
...
このレベルのlogging.DEBUG
は、しきい値を設定するために上記のコードで参照する定数整数値を参照します。 DEBUG
のレベルは10です。
ここで、代わりにすべてのprint()
ステートメントをlogging.debug()
ステートメントに置き換えます。 定数であるlogging.DEBUG
とは異なり、logging.debug()
はlogging
モジュールのメソッドです。 このメソッドを使用する場合、以下に示すように、print()
に渡されたものと同じstringを使用できます。
pizza.py
import logging
logging.basicConfig(level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
この時点で、python pizza.py
コマンドを使用してプログラムを実行すると、次の出力が表示されます。
OutputDEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
ログメッセージには、重大度レベルDEBUG
と、Pythonモジュールのレベルを表す単語root
が埋め込まれています。 logging
モジュールは、異なる名前のロガーの階層で使用できるため、モジュールごとに異なるロガーを使用できます。
たとえば、異なる名前と異なる出力を持つ異なるロガーに等しいロガーを設定できます。
logger1 = logging.getLogger("module_1")
logger2 = logging.getLogger("module_2")
logger1.debug("Module 1 debugger")
logger2.debug("Module 2 debugger")
OutputDEBUG:module_1:Module 1 debugger
DEBUG:module_2:Module 2 debugger
logging
モジュールを使用してメッセージをコンソールに出力する方法を理解したので、次にlogging
モジュールを使用してメッセージをファイルに出力する方法に移ります。
メッセージをファイルに記録する
logging
モジュールの主な目的は、メッセージをコンソールではなくファイルに記録することです。 メッセージのファイルを保持することで、コードをどのように変更する必要があるかを確認できるように、時間をかけてデータを参照して定量化できます。
ファイルへのロギングを開始するには、filename
メソッドを変更してfilename
パラメーターを含めることができます。 この場合、ファイル名をtest.log
と呼びましょう。
pizza.py
import logging
logging.basicConfig(filename="test.log", level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("artichoke", 15)
pizza_01.make()
pizza_01.eat()
pizza_02 = Pizza("margherita", 12)
pizza_02.make(2)
pizza_02.eat()
上記のコードは前のセクションと同じですが、印刷するログのファイル名を追加した点が異なります。 python pizza.py
コマンドを使用してコードを実行すると、ディレクトリにtest.log
という新しいファイルが作成されます。
nano(または選択したテキストエディタ)でtest.log
ファイルを開きましょう:
nano test.log
ファイルが開くと、次のように表示されます。
test.log
DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
これは、前のセクションで検出したコンソール出力と似ていますが、test.log
ファイルにある点が異なります。
CTRL
+x
でファイルを閉じ、pizza.py
ファイルに戻って、コードを変更できるようにします。
コードの大部分は同じままにしますが、2つのピザインスタンスpizza_01
とpizza_02
のパラメータを変更します。
pizza.py
import logging
logging.basicConfig(filename="test.log", level=logging.DEBUG)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
# Modify the parameters of the pizza_01 object
pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)
# Modify the parameters of the pizza_02 object
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)
これらの変更を加えて、python pizza.py
コマンドを使用してプログラムを再度実行してみましょう。
プログラムが実行されたら、nanoを使用してtest.log
ファイルを再度開くことができます。
nano test.log
ファイルを見ると、いくつかの新しい行が追加され、プログラムが最後に実行された前の行が保持されていることがわかります。
test.log
DEBUG:root:Pizza created: artichoke ($15)
DEBUG:root:Made 1 artichoke pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: margherita ($12)
DEBUG:root:Made 2 margherita pizza(s)
DEBUG:root:Ate 1 pizza(s)
DEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)
この情報は確かに役立ちますが、LogRecord attributesを追加することで、ログをより有益なものにすることができます。 主に、LogRecordがいつ作成されたかを示す、人間が読み取れるタイムスタンプを追加します。
その属性をformat
というパラメーターに追加して、表に示すように文字列%(asctime)s
で参照できます。 さらに、DEBUG
レベル名を保持するには、文字列%(levelname)s
を含める必要があり、ロガーに印刷するように要求する文字列メッセージを保持するには、%(message)s
を含めます。 以下に追加するコードに示すように、これらの各属性はcolon
で区切られます。
pizza.py
import logging
logging.basicConfig(
filename="test.log",
level=logging.DEBUG,
format="%(asctime)s:%(levelname)s:%(message)s"
)
class Pizza():
def __init__(self, name, price):
self.name = name
self.price = price
logging.debug("Pizza created: {} (${})".format(self.name, self.price))
def make(self, quantity=1):
logging.debug("Made {} {} pizza(s)".format(quantity, self.name))
def eat(self, quantity=1):
logging.debug("Ate {} pizza(s)".format(quantity, self.name))
pizza_01 = Pizza("Sicilian", 18)
pizza_01.make(5)
pizza_01.eat(4)
pizza_02 = Pizza("quattro formaggi", 16)
pizza_02.make(2)
pizza_02.eat(2)
python pizza.py
コマンドで属性を追加して上記のコードを実行すると、%のレベル名に加えて人間が読めるタイムスタンプを含む新しい行がtest.log
ファイルに追加されます。 (t2)sおよび文字列としてロガーに渡される関連メッセージ。
OutputDEBUG:root:Pizza created: Sicilian ($18)
DEBUG:root:Made 5 Sicilian pizza(s)
DEBUG:root:Ate 4 pizza(s)
DEBUG:root:Pizza created: quattro formaggi ($16)
DEBUG:root:Made 2 quattro formaggi pizza(s)
DEBUG:root:Ate 2 pizza(s)
2017-05-01 16:28:54,593:DEBUG:Pizza created: Sicilian ($18)
2017-05-01 16:28:54,593:DEBUG:Made 5 Sicilian pizza(s)
2017-05-01 16:28:54,593:DEBUG:Ate 4 pizza(s)
2017-05-01 16:28:54,593:DEBUG:Pizza created: quattro formaggi ($16)
2017-05-01 16:28:54,593:DEBUG:Made 2 quattro formaggi pizza(s)
2017-05-01 16:28:54,593:DEBUG:Ate 2 pizza(s)
必要に応じて、プログラムファイルのログを自分に関連させるために、コードで追加のLogRecord attributesを使用することをお勧めします。
デバッグとその他のメッセージを別々のファイルに記録すると、時間の経過とともにPythonプログラムの全体的な理解が得られ、プログラムに加えられた履歴作業によって通知される方法でコードのトラブルシューティングと修正を行うことができます発生するイベントとトランザクション。
ロギングレベルの表
開発者は、重大度レベルを追加することにより、ロガーでキャプチャされるイベントの重要度を割り当てることができます。 重大度レベルを以下の表に示します。
ロギングレベルは技術的には整数(定数)であり、ロガーを数値0で初期化するNOTSET
から始まり、すべて10ずつ増加します。
事前定義されたレベルに関連して独自のレベルを定義することもできます。 同じ数値でレベルを定義すると、その値に関連付けられた名前が上書きされます。
次の表は、さまざまなレベル名、それらの数値、レベルを呼び出すために使用できる関数、およびそのレベルの用途を示しています。
レベル | 数値 | 関数 | 慣れている |
---|---|---|---|
|
50 |
|
重大なエラーを表示します。プログラムは実行を継続できない可能性があります |
|
40 |
|
より深刻な問題を表示する |
|
30 |
|
予期しないことが起こった、または起こる可能性があることを示します |
|
20 |
|
物事が期待どおりに機能していることを確認します |
|
10 |
|
問題を診断し、詳細情報を表示する |
logging
モジュールはデフォルトレベルをWARNING
に設定するため、WARNING
、ERROR
、およびCRITICAL
はすべてデフォルトでログに記録されます。 上記の例では、次のコードでDEBUG
レベルを含めるように構成を変更しました。
logging.basicConfig(level=logging.DEBUG)
コマンドとデバッガーの操作について詳しくは、official logging
documentationを参照してください。
結論
デバッグは、ソフトウェア開発プロジェクトの重要なステップです。 logging
モジュールは標準のPythonライブラリの一部であり、ソフトウェアの実行中に発生するイベントの追跡を提供し、これらのイベントを別のログファイルに出力して、コードの実行中に発生するイベントを追跡できます。 これにより、時間の経過に伴うプログラムの実行から発生するさまざまなイベントの理解に基づいてコードをデバッグできます。