1.はじめに
systemdなどから起動するバックグラウンド動作アプリを作る機会が増えてきて、ログ(logging)の扱い方を整理しました。
2.やりたいこと
- loggingモジュールの挙動を、外部のyamlファイルから定義したい
- ログファイルの肥大化を抑制したい(自動ローテートしたい)
- クラス・モジュールごとの表示レベルを調整したい
- ログの出力先を、画面とログファイルそれぞれに保存したい
2.実験
(1)環境づくり
#作業ディレクトリに入る $ cd ~/work #プロジェクトのディレクトリ $ mkdir loggeryaml $ cd loggeryaml #設定ファイルの置き場 $ mkdir etc #仮想環境を立ち上げ $ python3 -m venv env #yamlファイルを扱うので必須です $ pip3 install pyyaml
(2)yamlファイルの設定
参考 - https://kokiblog.com/2019/08/03/python_logging/ - https://blog.hiros-dot.net/?p=10297 - https://docs.python.org/ja/3/library/logging.handlers.html
ファイルを生成
$ touch etc/logging.yaml $ code !$
# -*- coding: utf-8 -*- # --------------------------------------------------------- # ログ出力設定ファイル version: 1 # ルートロガーの設定 root: # 出力対象のレベルを選択します。 # DEBUG, INFO (default), WARNING, ERROR level: DEBUG #コンソール出力だけのときはこちらを有効に #handlers: [console] #コンソール、ファイル両方に出力のときはこちらを有効に handlers: [console, file] # サブモジュールのロガー設定 loggers: # from xxx import yyy でインポートした特定のモジュールだけを制御 # xxx.yyy: # level: ERROR # 書式 formatters: simple_fmt: # "〇s"のところは文字列を出力。マイナスがつくと右寄せ format: "%(asctime)s [%(levelname)-7s] (%(name)-20s) %(message)s" # ハンドラ handlers: # コンソール出力時 console: class: logging.StreamHandler formatter: simple_fmt stream: ext://sys.stdout # ファイル出力時 file: #ログローテート class: logging.handlers.RotatingFileHandler #ファイル名 filename: var/log/app.log formatter: simple_fmt encoding: utf-8 #ログの上限容量、これを超えるとローテーション maxBytes: 100000 #ログを残す数量 backupCount: 20 # ロガーの設定をロードしない場合の挙動 # true: 現在のロガーを無効 # false: ルートロガーの設定を引き継いで、現在のロガーを有効 disable_existing_loggers: false
(3)Python側の書き方
ファイル main.py
$ touch app/__main__.py $ code !$
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import yaml from logging import config, getLogger logger = getLogger(__name__) def main(): """エントリーポイント """ logger.debug(f"DEBUG") logger.info(f"INFO") logger.warning(f"WARNING") logger.error(f"ERROR") if __name__ == "__main__": try: # ロガーの設定読み込み config.dictConfig( yaml.load(open("./etc/logging.yaml", encoding="utf-8").read(), Loader=yaml.SafeLoader)) # メイン処理開始 main() except KeyboardInterrupt: # [Ctrl-C]が押されたときの終了捕捉 print("SIGINT - Exit") except SystemExit: # sys.exit関数による終了捕捉 print("SystemExit - Exit") except: # 例外発生時にメッセージ import traceback traceback.print_exc() finally: pass
ファイル appclass.py
$ touch app/appclass.py $ code !$
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # --------------------------------------------------------------------------- # IMPORTS # --------------------------------------------------------------------------- #この部分は、各ファイルごとに書く必要があります。 #(__main__.pyで読み込んだ設定が引き継がれます) import sys from logging import config, getLogger logger = getLogger(__name__) # --------------------------------------------------------------------------- # CLASSES # --------------------------------------------------------------------------- class AppClass_A: """クラスA""" def __init__(self): """コンストラクタ""" logger.debug(f"func: {sys._getframe().f_code.co_name}") def func_a(self): """A""" logger.debug(f"func: {sys._getframe().f_code.co_name}") logger.debug(f"message debug") class AppClass_B: """クラスB""" def __init__(self): """コンストラクタ""" logger.debug(f"func: {sys._getframe().f_code.co_name}") def func_b(self): """B""" logger.debug(f"func: {sys._getframe().f_code.co_name}") logger.debug(f"message debug")
ここまで準備すると、このような構成になります。
(4)実行してみる
2022-06-29 16:16:40,312 [DEBUG ] (__main__ ) message debug 2022-06-29 16:16:40,313 [INFO ] (__main__ ) message info /Users/miyake/gitwork/python/loggeryaml/app/__main__.py:24: DeprecationWarning: The 'warn' method is deprecated, use 'warning' instead logger.warn(f"message warning") 2022-06-29 16:16:40,313 [WARNING] (__main__ ) message warning 2022-06-29 16:16:40,313 [ERROR ] (__main__ ) message error 2022-06-29 16:16:40,313 [DEBUG ] (app.appclass ) func: __init__ 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: func_a 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) message debug 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: __init__ 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: func_b 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) message debug
ログの内容
$ less var/log/app.log
2022-06-29 16:16:40,312 [DEBUG ] (__main__ ) message debug 2022-06-29 16:16:40,313 [INFO ] (__main__ ) message info 2022-06-29 16:16:40,313 [WARNING] (__main__ ) message warning 2022-06-29 16:16:40,313 [ERROR ] (__main__ ) message error 2022-06-29 16:16:40,313 [DEBUG ] (app.appclass ) func: __init__ 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: func_a 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) message debug 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: __init__ 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) func: func_b 2022-06-29 16:16:40,314 [DEBUG ] (app.appclass ) message debug
コンソールに出ていた内容が、ファイルに出力できています。
4.まとめ
yamlファイルを使ってのlogger制御の例が少なかったので、簡単にまとめてみました。