Pythonでちょっと高級なAppを作るときに使用するlogger
loggerに渡した文字列に細工をしたいことありませんか?
今回はloggerにわたす文字列を加工して出力するloggerを作成しました
コード
from logging import LogRecord, Logger, Formatter, StreamHandler, FileHandler, DEBUG, INFO
def change_message(s: str) -> str:
if s == "\\\n":
return "\n\n\n\n\n"
s = s.upper().replace("\n", "\t")
if "CAT" in s:
s += "ฅ^•ω•^ฅニャー"
return s
class OriginalFormatter(Formatter):
def format(self, record: LogRecord) -> str:
record.message = change_message(record.getMessage())
if self.usesTime():
record.asctime = self.formatTime(record, self.datefmt)
s = self.formatMessage(record)
if record.exc_info:
# Cache the traceback text to avoid converting it multiple times
# (it's constant anyway)
if not record.exc_text:
record.exc_text = self.formatException(record.exc_info)
if record.exc_text:
if s[-1:] != "\n":
s = s + "\n"
s = s + record.exc_text
if record.stack_info:
if s[-1:] != "\n":
s = s + "\n"
s = s + self.formatStack(record.stack_info)
return s
def init_logger(logger: Logger, file_name: str) -> Logger:
log_format = OriginalFormatter("%(asctime)s %(name)s:%(lineno)s %(funcName)s [%(levelname)s]: %(message)s")
st = StreamHandler()
st.setFormatter(log_format)
st.setLevel(INFO)
fl = FileHandler(file_name)
fl.setFormatter(log_format)
fl.setLevel(DEBUG)
logger.setLevel(DEBUG)
logger.addHandler(st)
logger.addHandler(fl)
return logger
change_message関数
ここに任意の文字列を加工する関数を書いてください
OriginalFormatterクラス
loggerのFormat関数にchange_message関数をいれています
init_logger関数
ここでOriginalFormatterを用いloggerの設定をしています
お試し
if __name__=="__main__":
from logging import getLogger
import traceback
logger = getLogger(__name__)
logger = init_logger(logger, "test.log")
logger.debug("logger debug")
logger.info("logger info")
# 2023-08-03 12:22:42,819 __main__:59 <module> [INFO]: LOGGER INFO
logger.warning("logger warn")
# 2023-08-03 12:22:42,819 __main__:60 <module> [WARNING]: LOGGER WARN
logger.error("logger error")
# 2023-08-03 12:22:42,819 __main__:61 <module> [ERROR]: LOGGER ERROR
loggerの設定と、実際に出力したものです
今回の関数は小文字を大文字にする機能がありますが、適切に変化しています
try:
raise("raise!")
except:
logger.exception("logger exception", exc_info=True) # traceback に関数を適用しない
logger.error("except log\n" + traceback.format_exc()) # 適用する
# 2023-08-03 12:22:42,819 __main__:66 <module> [ERROR]: LOGGER EXCEPTION
# Traceback (most recent call last):
# File "/Users/~~~~~/original_logger.py", line 64, in <module>
# raise("raise!")
# TypeError: exceptions must derive from BaseException
# 2023-08-03 12:22:42,819 __main__:67 <module> [ERROR]: EXCEPT LOG TRACEBACK (MOST RECENT CALL LAST): FILE "/USERS/~~~~~/ORIGINAL_LOGGER.PY", LINE 64, IN <MODULE> RAISE("RAISE!") TYPEERROR: EXCEPTIONS MUST DERIVE FROM BASEEXCEPTION
logger.exceptionによって traceback を出力すると、traceback に関数は適用されません
下の方法だと適用されています
ここでも、大文字にする機能と、改行をタブに変換する機能が動作しているのを確認できます
def test(logger: Logger):
logger.debug("logger debug in test")
logger.info("logger info in test")
logger.warning("logger warn in test")
logger.error("logger error in test")
try:
raise("raise in test!")
except:
logger.exception("logger exception in test", exc_info=True)
logger.error("except log in test\n" + traceback.format_exc())
test(logger)
# 2023-08-03 12:22:42,820 __main__:71 test [INFO]: LOGGER INFO IN TEST
# 2023-08-03 12:22:42,820 __main__:72 test [WARNING]: LOGGER WARN IN TEST
# 2023-08-03 12:22:42,820 __main__:73 test [ERROR]: LOGGER ERROR IN TEST
# 2023-08-03 12:22:42,820 __main__:78 test [ERROR]: LOGGER EXCEPTION IN TEST
# Traceback (most recent call last):
# File "/Users/~~~~~/original_logger.py", line 76, in test
# raise("raise in test!")
# TypeError: exceptions must derive from BaseException
# 2023-08-03 12:22:42,820 __main__:79 test [ERROR]: EXCEPT LOG IN TEST TRACEBACK (MOST RECENT CALL LAST): FILE "/USERS/~~~~~/ORIGINAL_LOGGER.PY", LINE 76, IN TEST RAISE("RAISE IN TEST!") TYPEERROR: EXCEPTIONS MUST DERIVE FROM BASEEXCEPTION
関数内でも適切に動いています
logger.info("\\\n")
logger.info("cat")
# 2023-08-03 12:22:42,820 __main__:83 <module> [INFO]:
# 2023-08-03 12:22:42,820 __main__:84 <module> [INFO]: CATฅ^•ω•^ฅニャー
\\\nを改行5回に変換する機能も無事に動いています
cat | CAT という文字列があったとき、末尾にねこを追加する機能も動作しているようですฅ^•ω•^ฅニャー
ファイルハンドラ
今回は省略しますが、FileHandlerでも適切に関数が適用されていました
もし、FileとStreamで別々の関数を適用したい場合は、2つFormatterを用意すればよいと思います
まとめ
これでloggerを便利に活用できます!!
参考文献
Pythonでprintを卒業してログ出力をいい感じにする - Qiita
https://qiita.com/FukuharaYohei/items/92795107032c8c0bfd19
[Tips] Pythonのロガーでユーザーが定義したキーを使って値を出力する方法 | DevelopersIO
https://dev.classmethod.jp/articles/python-logger-kwarg-extra/