Python logging 解説(初心者向け)

# はじめに

Pythonのloggingについての記事です。


:::note info
loggingについて詳しくない人向けの記事です。ログの初歩から始めて、図やPythonコードを見ながら進めていきます。
:::

# ログとは

ログとは、アプリケーションやシステムが実行中に生成する記録やメッセージのことです。

よくあるのはこういうログ。
| ログ名 | 説明 |
|:-:|:-:|
| エラーログ  | システムやアプリケーションのエラーに関する情報。 |
| イベントログ |  特定のイベントやアクションに関する記録。 |
|  デバッグログ |  開発中のデバッグ情報。詳細な実行状況を記録する。 |
| 情報ログ  |  一般的な運用情報。システムの状態や進行状況を示す。 |

# 実際に動かしてみる

まずはPythonのloggingがどのような出力をするのか見てみます。

```Python: Python
import logging

logging.warning("This is warning message!!")
print("reached here")
```

```bash: 出力結果
WARNING:root:This is warning message!!
reached here
```

`logging.warning(message)` で、コンソールに警告メッセージを出力できます。

ただし、これ自体はエラーでもなんでもありません。実際に `print("reached here")` は実行されています。

プログラム中のどこにでもログは仕込ませることができ、文字列を出力できます。

# ログレベル

warning、つまり警告ばかりを出力するわけではありません。開発中のデバッグ情報などもログとして出力できます。

次の表は、上に行けば行くほど、重大なエラーであることを表しています。

| エラーレベル | 説明 |
|:-:|:-:|
|  `CRITICAL` | プログラム自体が実行を続けられないことを表す、重大なエラー。  |
|  `ERROR` | より重大な問題により、ソフトウェアがある機能を実行できないこと。  |
|  `WARNING` |  想定外のことが起こった、または問題が近く起こりそうであることの表示。 |
|  `INFO` | 想定された通りのことが起こったことの確認。  |
|  `DEBUG` | おもに問題を診断するときにのみ関心があるような、詳細な情報。  |

(参考: [Pythonチュートリアル Logging HOWTO](https://docs.python.org/ja/3/howto/logging.html))

例えば `CRITICAL` のエラーだとこのように書けます。

```Python: Python
import logging

logging.critical(f"This is critical message!!")
```

```bash: 出力結果
CRITICAL:root:This is critical message!!
```


# logger, handler, formatter

loggerは、ログメッセージを生成します。
handlerは、loggerが生成したメッセージを出力します。
formatterは、メッセージのフォーマットを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894702/d2ea8582-59b9-cbe1-976e-a4bb07869720.png)


この3つを使うことでログ生成を行います。

コードにすると次のようになります。

```Python: main.py
from logging import getLogger, StreamHandler, Formatter, DEBUG

# create logger
logger = getLogger("__name__")
logger.setLevel(DEBUG)  # DEBUG以上のログメッセージを生成する設定

# create console handler and set level to debug
handler = StreamHandler()
handler.setLevel(DEBUG)  # DEBUG以上のログメッセージをコンソールに出力する設定

# create formatter
formatter = Formatter("[%(levelname)s] (%(asctime)s) %(name)s: %(message)s")

# add formatter to handler
handler.setFormatter(formatter)

# add handler to logger
logger.addHandler(handler)

# application code
logger.debug("debug message")
logger.info("info message")
logger.warning("warn message")
logger.error("error message")
logger.critical("critical message")
```

```bash: 出力結果
[DEBUG] (2024-09-28 01:36:18,365) __main__: debug message
[INFO] (2024-09-28 01:36:18,365) __main__: info message
[WARNING] (2024-09-28 01:36:18,365) __main__: warn message
[ERROR] (2024-09-28 01:36:18,365) __main__: error message
[CRITICAL] (2024-09-28 01:36:18,365) __main__: critical message
```

# loggingの設定をconfファイルで管理する

先ほどのコードだと、ログを仕込もうとする度にPythonコードでの設定が必要になります。毎回あんなことやってられません。

その設定をconfファイルにまとめて、Pythonファイル上ではconfファイルを呼び出すだけで設定が完了するようにできます。

```Python: main.py
from logging import config, getLogger

config.fileConfig("logging.conf")

# create logger
logger = getLogger(__name__)

# application code
logger.debug("debug message")
logger.info("info message")
logger.warning("warn message")
logger.error("error message")
logger.critical("critical message")
```

```conf: logger.conf
[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=[%(levelname)s] (%(asctime)s) %(name)s: %(message)s

```

```bash: 出力結果
[DEBUG] (2024-09-28 02:12:31,873) __main__: debug message
[INFO] (2024-09-28 02:12:31,873) __main__: info message
[WARNING] (2024-09-28 02:12:31,873) __main__: warn message
[ERROR] (2024-09-28 02:12:31,873) __main__: error message
[CRITICAL] (2024-09-28 02:12:31,873) __main__: critical message
```

# 参考文献
[Pythonチュートリアル Logging HOWTO](https://docs.python.org/ja/3/howto/logging.html)