こんにちは!実務でPython、Djangoを使って、機械学習やWebアプリケーションの開発をしているかるでねです!
今回はPythonの「Logging」の応用について解説していこうと思います!
「Logging」の基礎についてはこちらの記事にまとめてあります。
「Logging」はPythonの実務などでよく使われるので、Pythonエンジニアを目指している方は是非この記事でマスターしていってください!
この記事はPythonの公式ドキュメントを参考にしています。
Pythonに限らず、プログラミング言語の公式ドキュメントは非常に読みにくいので、この記事でわかりやすく解説していきます。
「Loggingよく見かけるんだけど全くわからない...」
「Pythonエンジニアを目指して学習中」
「Pythonスキルのレベルアップをしたい!」
このような人のお役に立てれば幸いです。
それでは早速本題に入っていきましょう!
「Logging」はPythonの基礎を理解していないと難しいので、Pythonの基礎を理解していない人は以下を参考に学習してください!
ロギングとは?
まずは「Logging」がどういったものなのかなどの概要から見ていきましょう。
「Logging」とは以下のようなものです。
ポイント
- プログラムが実行されているときに起こったイベントを追跡してくれる。
- 重要度を設定して、特定のイベントが発生したことをメッセージと共に知らせてくれる。
より詳しくはこちらにまとめてあります。
細かくみてみる
ロギングは「Logger」クラスのインスタンスにメソッドに呼び出して実行されています。
「ロガー」は、どこでイベントのログが取られたかを明らかにしてくれます。
名前の確認
まずは「ロガー」の名前を確認してきましょう。
logger = logging.getLogger(__name__)
print(logger)
# <Logger __main__ (WARNING)>
__main__
とWARNINIG
が表示されていますね。
__main__
は実行されているファイルのことで、WARNINIG
はデフォルトの重要度レベルのことです。
__name__
と指定することが推奨されています。
明示的に自分で指定
次に明示的に「ロガー」の名前を指定してみましょう。
logger = logging.getLogger("かるでね")
print(logger)
# <Logger かるでね (WARNING)>
自分が指定した通り、「かるでね」と出力されていますね。
重要度レベル変更してみる
では次に重要度レベルを変更してみましょう。
import logging
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
print(logger)
# <Logger __main__ (ERROR)>
3行目でERROR
に設定してみると、出力部分がWARNINIG
からERROR
に変わってますね。
他のファイルで実行されたロガー名を確認
名前が__main__
だとよくわからないので、別ファイルの「ロガー」名を確認してみましょう。
まずはtest.py
に以下を記入してください。
import logging
def log_info():
logger = logging.getLogger(__name__)
return logger
次にmain.py
に以下を記入してください。
import logging
import test
logger = logging.getLogger(__name__)
print(logger)
print(test.log_info())
# <Logger __main__ (WARNING)>
# <Logger test (WARNING)>
main.py
の「ロガー」は__main__
と表示されています。(ファイル名がmain.py
だからではなく、「実行されているファイル」という意味です。
test.py
の方はtest
と表示されています。
これで__name__
が実行されているファイル名を参照していることがわかります。
root
root
とは「ロガー」階層の一番上に位置しているものです。
こんな感じのイメージですね。
ちなみに普通に「ロギング」で出力されたメッセージを見るとひっそり出力されているのが確認できます。
import logging
logging.warning('Warningだよ!!!')
# WARNING:root:Warningだよ!!!
真ん中にroot
というのがしっかりありますね。
ロガー
「ロガー」には以下のような特徴があります。
ポイント
- プログラム実行中にメッセージを記録できるように、メソッドを呼べるようにしている。
- 重要度やフィルターなどを定義して、どのメッセージについて動作するか決める。
- 重要度に基づいてハンドラに関連するログメッセージを出力する。
setLevel
基本的な使い方
setLevel
は「ロガー」の重要度レベルを変更できます。
import logging
logging.basicConfig(level=logging.ERROR)
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
# ERROR:root:Errorだよ!!!!
# CRITICAL:root:Criticalだよ!!!!
これは「ロギング」の重要度レベルをERROR
に設定しています。
では次に「ロガー」の方で設定していきましょう。
logger = logging.getLogger(__name__)
print(logger)
# <Logger __main__ (WARNING)>
logger.setLevel(logging.INFO)
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
# INFO:__main__:Infoだよ!!
# WARNING:__main__:Warningだよ!!!
# ERROR:__main__:Errorだよ!!!!
# CRITICAL:__main__:Criticalだよ!!!!
「ロギング」の方でERROR
に設定していますが、「ロガー」の方ではINFO
に設定しているのでINFO
以上のメッセージが出力されていますね。
このように、「全体設定ファイルで全体の重要度を設定して、個別のファイルで取得するイベントの重要度を変更していく」使われ方がされます。
以下で実際に見てみましょう。
test.py
とmain.py
の2つのファイルを用意してください。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
def test_logger():
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
return logger
import logging
import test
logging.basicConfig(level=logging.ERROR)
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
test.test_logger()
では出力を確認しましょう。
# ==========《main.py》==========
# ERROR:root:Errorだよ!!!!
# CRITICAL:root:Criticalだよ!!!!
# ====================
# ==========《test.py》==========
# INFO:test:Infoだよ!!
# WARNING:test:Warningだよ!!!
# ERROR:test:Errorだよ!!!!
# CRITICAL:test:Criticalだよ!!!!
# ====================
main.py
では「ロギング」 が使われているので、ERROR
以上のメッセージが出力されています。
一方、test.py
では「ロガー」が使われているので、INFO
以上のメッセージが出力されています。
setLevelを明示的に指定しない
setLevel
を明示的に指定しないと親をたどっていき、有効な重要度レベルを探します。
test.py
とmain.py
の2つのファイルを用意してください。
import logging
logger = logging.getLogger(__name__)
def test_logger():
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
return logger
import logging
import test
logging.basicConfig(level=logging.ERROR)
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
test.test_logger()
では出力を確認しましょう。
# ==========《main.py》==========
# ERROR:root:Errorだよ!!!!
# CRITICAL:root:Criticalだよ!!!!
# ====================
# ==========《test.py》==========
# ERROR:test:Errorだよ!!!!
# CRITICAL:test:Criticalだよ!!!!
# ====================
test.py
の親であるmain.py
で設定されている ERROR レベルが採用されているのが確認できますね。
getLogger
一番最初に使用したgetLogger
をもう少し詳しくみていきましょう。
import logging
logging.basicConfig(level=logging.ERROR)
print(logging.getLogger(__name__))
# <Logger __main__ (ERROR)>
実行されているロガーの情報が確認できます。
しっかり重要度レベルも設定したようにERROR
になっていますね。
import logging
logging.basicConfig(level=logging.ERROR)
print(logging.getLogger())
# <RootLogger root (ERROR)>
getLogger
に何も渡さないと root
「 ロガー」の情報を取得していますね。
ハンドラ
「ハンドラ」とは以下のようなものです。
ハンドラの指定された出力先にログメッセージを出力する。
AddHandler
「ロガー」に設定を追加する。
ではこのaddHandler
機能を使用してファイル出力してみましょう。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
fh = logging.FileHandler("logging.log")
logger.addHandler(fh)
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
7行目のFileHandler
でメッセージの出力先ファイルを指定しています。
今回はlogging.log
というファイルに出力します。(実行時に自動で作成してくれます。)
8行目では「ロガー」にFileHandler
を追加しています。
ではしっかりlogging.log
ファイルに出力されているか確認してみましょう。
Infoだよ!!
Warningだよ!!!
Errorだよ!!!!
Criticalだよ!!!!
ちゃんと出力されていますね。
RemoveHandler
「ロガー」から設定を取り除く。
今回はtest.py
とmain.py
の2つのファイルを用意してください。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
fh = logging.FileHandler("logging.log")
logger.addHandler(fh)
def test_logger(output=True):
if not output:
logger.removeHandler(fh)
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
10行目でoutput
という引数を受け取っています。
デフォルトではTrue
となっていますが、もしFalse
が渡されるとremoveHandler
が呼び出されて、「ロガー」からファイル出力設定を取り除きます。
import logging
import test
logging.basicConfig(level=logging.ERROR)
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
test.test_logger(False)
12行目でtest.py
のtest_logger
関数を呼び出し、引数にFalse
を渡しています。
では出力されていないかlogging.log
を見てみましょう。
何も出力されていないですね。
StreamHandler
ログメッセージをコンソールに出力してくれる。
早速みてみましょう。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
st = logging.StreamHandler()
logger.addHandler(st)
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
7行目でStreamHandler
を定義してコンソール出力するようにしています。
8行目でStreamHandler
の設定を追加しています。
では出力結果をコンソールで確認してみましょう。
Infoだよ!!
Warningだよ!!!
Errorだよ!!!!
Criticalだよ!!!!
ちゃんと想定通りコンソールに出力されています。
他にも「ハンドラー」はいくつかあるので以下を参考にしてください。
フォーマッタ
ログメッセージ出力をカスタムできる。
名前の通りログメッセージの出力表現を自分好みにカスタムできます。
早速みていきましょう。
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
sh = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
sh.setFormatter(formatter)
logger.addHandler(sh)
logger.debug('Debugだよ!')
logger.info('Infoだよ!!')
logger.warning('Warningだよ!!!')
logger.error('Errorだよ!!!!')
logger.critical('Criticalだよ!!!!')
9行目で出力のメッセージを定義しています。
asctime
は、「日時」を出力します。
name
は、「ロガー」の名前を出力してくれます。
levelname
は、「重要度」を出力してくれます。
message
は、「ログメッセージ」のことです。
11行目で先ほど定義したformatter
をセットしています。
13行目で「ロガー」に渡しています。
では早速出力を見てみましょう。
2022-02-20 12:01:23,202 - __main__ - INFO - Infoだよ!!
2022-02-20 12:01:23,202 - __main__ - WARNING - Warningだよ!!!
2022-02-20 12:01:23,202 - __main__ - ERROR - Errorだよ!!!!
2022-02-20 12:01:23,202 - __main__ - CRITICAL - Criticalだよ!!!!
ちゃんと自分が設定したように出力されていますね。
コンフィグ
「ロガー」設定をまとめて定義。
「コンフィグ」では、「ロガー」の設定を別ファイルや辞書型など、まとめて定義します。
Webアプリケーションなどではこのような方法で使われることが多いので確認しておきましょう。
設定ファイル
まずは設定ファイルに「ロガー」をまとめて定義する方法を見ていきましょう。
logging.conf
というファイルを作成してください。
[loggers]
keys=root,CardeneHandler
[handlers]
keys=streamHandler
[formatters]
keys=formatter
[logger_root]
level=INFO
handlers=streamHandler
[logger_CardeneHandler]
level=DEBUG
handlers=streamHandler
qualname=cardeneHandler
propagate=0
[handler_streamHandler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stdout,)
[formatter_formatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
1つ1つ解説すると長くなってしまうので、ここまでの知識でなんとなく理解してください。
ではこの設定ファイルを読み込んで「ロガー」を実行してみましょう。
import logging
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('CardeneHandler')
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
4行目で先ほど定義したlogging.conf
ファイルを読み込んでいます。
6行目では、logging.conf
で定義してあるCardeneHandler
という「ハンドラー」を呼び出しています。
では出力を見てみましょう。
2022-02-20 13:59:25,418 - root - INFO - Infoだよ!!
2022-02-20 13:59:25,418 - root - WARNING - Warningだよ!!!
2022-02-20 13:59:25,418 - root - ERROR - Errorだよ!!!!
2022-02-20 13:59:25,418 - root - CRITICAL - Criticalだよ!!!!
ちゃんと出力されていますね。
辞書型
辞書型で定義する方法は「Django」などのWebアプリケーションを作成できるフレームワークでよく使われます。
では早速みていきましょう。
import logging
import logging.config
logging.config.dictConfig({
'version': 1,
'formatters': {
'cardeneFormatter': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
},
'handlers': {
'cardeneHandlers': {
'class': 'logging.StreamHandler',
'formatter': 'cardeneFormatter',
'level': logging.INFO
}
},
'root': {
'handlers': ['cardeneHandlers'],
'level': logging.WARNING,
},
'loggers': {
'cardeneHandler': {
'handlers': ['cardeneHandlers'],
'level': logging.INFO,
'propagate': 0
}
}
})
# create logger
logger = logging.getLogger('cardeneHandler')
logging.debug('Debugだよ!')
logging.info('Infoだよ!!')
logging.warning('Warningだよ!!!')
logging.error('Errorだよ!!!!')
logging.critical('Criticalだよ!!!!')
めちゃくちゃ長いですね...。
こちらも先ほどと設定内容はほとんど同じなので細かい説明は省きます。
では早速実行してみましょう。
2022-02-20 14:12:17,174 - root - WARNING - Warningだよ!!!
2022-02-20 14:12:17,175 - root - ERROR - Errorだよ!!!!
2022-02-20 14:12:17,175 - root - CRITICAL - Criticalだよ!!!!
ちゃんと出力できました。
今回はWARNING
以上に重要度レベルを設定しました。
さらに詳しく
より詳しく知りたいときは公式のドキュメントを読み進めてみてください!
最後に
今回は「Loggingに挑戦してみよう!《応用編》」と」いうテーマで「Logging」の使い方をみてきました。
いかかだったでしょうか?
「Logging」を実装するまでの力がついたと思います。
普段はPythonやブロックチェーンメインに情報発信をしています。
Twiiterでは図解でわかりやすく解説する投稿をしているのでぜひフォローしてくれると嬉しいです!
Tweets by cardene777