はじめに
このセクションでは、ロギングモジュールを簡単に紹介します。
This tutorial is from open-source community. Access the source code
💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください
このセクションでは、ロギングモジュールを簡単に紹介します。
logging
モジュールは、診断情報を記録するための標準ライブラリモジュールです。また、非常に大きなモジュールであり、洗練された機能が豊富です。その有用性を示すために、簡単な例を示します。
演習では、次のような関数 parse()
を書きました。
## fileparse.py
def parse(f, types=None, names=None, delimiter=None):
records = []
for line in f:
line = line.strip()
if not line: continue
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
return records
try-except
文に注目してください。except
ブロックで何をすべきでしょうか?
警告メッセージを表示するべきですか?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
それとも、黙って無視しますか?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
pass
どちらの解決策も満足のいくものではありません。なぜなら、多くの場合、両方の動作(ユーザーが選択可能)が必要だからです。
logging
モジュールを使うことでこの問題を解決できます。
## fileparse.py
import logging
log = logging.getLogger(__name__)
def parse(f,types=None,names=None,delimiter=None):
...
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
log.warning("Couldn't parse : %s", line)
log.debug("Reason : %s", e)
コードを修正して、警告メッセージを発行するか、特殊な Logger
オブジェクトを使うようにしました。logging.getLogger(__name__)
で作成されたものです。
ロガーオブジェクトを作成します。
log = logging.getLogger(name) ## name は文字列
ログメッセージを発行します。
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
各メソッドは、異なる重大度レベルを表します。
すべてのメソッドは、フォーマットされたログメッセージを作成します。args
は、メッセージを作成するために %
演算子とともに使用されます。
logmsg = message % args ## ログに書き込まれます
ロギングの動作は別途設定されます。
## main.py
...
if __name__ == '__main__':
import logging
logging.basicConfig(
filename = 'app.log', ## ログ出力ファイル
level = logging.INFO, ## 出力レベル
)
通常、これはプログラム起動時の一度だけの設定です。設定は、ロギング呼び出しを行うコードとは別です。
ロギングは高度に設定可能です。出力ファイル、レベル、メッセージ形式など、そのあらゆる側面を調整できます。ただし、ロギングを使用するコードはそれについて心配する必要はありません。
fileparse.py
には、入力データの不備による例外に関するエラー処理がいくつかあります。このようになっています。
## fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
診断メッセージを発行する print
文に注目してください。これらの print
をロギング操作に置き換えるのは比較的簡単です。コードを次のように変更します。
## fileparse.py
import csv
import logging
log = logging.getLogger(__name__)
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
log.warning("Row %d: Couldn't convert %s", rowno, row)
log.debug("Row %d: Reason %s", rowno, e)
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
これらの変更を行ったら、不適切なデータに対してコードをいくつか使用してみてください。
>>> import report
>>> a = report.read_portfolio('missing.csv')
Row 4: Bad row: ['MSFT', '', '51.23']
Row 7: Bad row: ['IBM', '', '70.44']
>>>
何もしなければ、WARNING
レベル以上のロギングメッセージのみが表示されます。出力は単純な print
文のように見えます。ただし、ロギングモジュールを設定すると、ロギングレベル、モジュールなどに関する追加情報が得られます。これを確認するには、次の手順を実行してください。
>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
>>>
log.debug()
操作の出力が表示されないことに気付くでしょう。レベルを変更するには、次を入力してください。
>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
DEBUG:fileparse:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
DEBUG:fileparse:Row 7: Reason: invalid literal for int() with base 10: ''
>>>
最も重要なロギングメッセージ以外はすべて無効にします。
>>> logging.getLogger('fileparse').setLevel(logging.CRITICAL)
>>> a = report.read_portfolio('missing.csv')
>>>
アプリケーションにロギングを追加するには、メインモジュールでロギングモジュールを初期化するための何らかのメカニズムが必要です。これを行う方法の1つは、次のようなセットアップコードを含めることです。
## このファイルでは、ロギングモジュールの基本設定を行います。
## 必要に応じてロギング出力を調整するには、ここで設定を変更してください。
import logging
logging.basicConfig(
filename = 'app.log', ## ログファイルの名前 (省略するとstderrを使用します)
filemode = 'w', ## ファイルモード ('a' を使用して追記する場合もあります)
level = logging.WARNING, ## ロギングレベル (DEBUG、INFO、WARNING、ERROR、またはCRITICAL)
)
同様に、これをプログラムの起動手順のどこかに配置する必要があります。たとえば、report.py
プログラムのどこにこれを配置するでしょうか。
おめでとうございます! あなたはロギングの実験を完了しました。あなたのスキルを向上させるために、LabExでさらに多くの実験を行って練習することができます。