pythonのloggingについてのメモ

# loggingについて


python標準のlog書き出しライブラリであるloggingパッケージの使い方メモです


loggingの公式ドキュメント
https://docs.python.org/ja/3/library/logging.html#handler-objects


# 参考にしたページ


ログ出力のための print と import logging はやめてほしい
https://qiita.com/amedama/items/b856b2f30c2f38665701

python logging best practice
https://pieces.openpolitics.com/2012/04/python-logging-best-practices/

pythonのlog出力
https://qiita.com/yopya/items/63155923602bf97dec53


# 使い方


Loggerにレベルとフォーマットの管理をしてもらい、Handlerに書き出し先を管理してもらいます。


#### Logger


logging.getLogger()によりloggerインスタンスを作ることができます。引数としてlogger名を与えてフォーマットに含めるとここで指定したlogger名をlogに書き出すことができます


```python
from logging import getLogger
logger = getLogger('hoge')
```


#### レベル


書き出しレベルを設定しておいて、閾値以下のlogは書き出さないようにすることが出来ます。細々とした情報はDEBUGのときにしか必要ないので、実運用時にはWARNING以上のlogしか書き出さない、というような事が出来るわけです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/9220f1cf-a4cf-667d-fdd2-db81c18f26e1.png" width=30%>
これを上記で作ったloggerに追加することでレベルの閾値を指定できます。

```python
from logging import DEBUG
logger.setLevel(DEBUG)
```


#### Handler


logging.StreamHandler()によりstdoutに、logging.handlers.RotatingFileHandler()によりテキストファイルにlogを書き出せます。


```python:stdout
from logging import StreamHandler
handler = StreamHandler()
```


```python:file出力
from logging import handlers
handler = handlers.RotatingFileHandler(filename='./hoge.log')
```


#### フォーマット


logのフォーマットも自由に指定することが出来ます


```python
from logging import Formatter
formatter = Formatter("[%(asctime)s] [%(process)d] [%(name)s] [%(levelname)s] %(message)s")
handler.setFormatter(formatter)
```


#### 書き出し


以下のようにレベルを指定して書き出します

```python
logger.debug('debugレベルで書き出します')
logger.info('infoレベルで書き出します')
logger.warning('warnレベルで書き出します')
logger.error('errorレベルで書き出します')
logger.critical('criticalレベルで書き出します')
```


# 実際の使い方


こちらのページのやり方が分かりやすかったのでお借りしました。
https://qiita.com/yopya/items/63155923602bf97dec53

上記のあれこれをClassにしておいて一括で使おうというやり方ですね。


```python:logger.py
from logging import Formatter, handlers, StreamHandler, getLogger, DEBUG


class Logger:
    def __init__(self, name=__name__, filename='./fuga.log', level=DEBUG):
        assert isinstance(filename, str), 'filename must be string: {}'.format(filename)

        self.logger = getLogger(name)
        self.logger.setLevel(level)
        formatter = Formatter("[%(asctime)s] [%(process)d] [%(name)s] [%(levelname)s] %(message)s")

        # stdout
        handler = StreamHandler()
        handler.setLevel(level)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

        # file
        handler = handlers.RotatingFileHandler(filename=filename,
                                               maxBytes=1048576,
                                               backupCount=3)
        handler.setLevel(level)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def debug(self, msg):
        self.logger.debug(msg)

    def info(self, msg):
        self.logger.info(msg)

    def warn(self, msg):
        self.logger.warning(msg)

    def error(self, msg):
        self.logger.error(msg)

    def critical(self, msg):
        self.logger.critical(msg)
```


上記をimportして使ってみます


```python:main.py
import logger


def main():
    log = logger.Logger('hoge')
    log.debug('test debug')
    log.info('test info')
    log.warn('test warn')
    log.error('test error')
    log.critical('test critical')


if __name__ == '__main__':
    main()
```


hogeと書かれているのはloggerを作成したときに指定したlogger名なので、モジュールごとにこれを変えておけばどのモジュールが書いたログなのかも分かるようになります。


```terminal:fuga.log
[2020-11-26 18:10:53,002] [5760] [hoge] [DEBUG] test debug
[2020-11-26 18:10:53,002] [5760] [hoge] [INFO] test info
[2020-11-26 18:10:53,003] [5760] [hoge] [WARNING] test warn
[2020-11-26 18:10:53,003] [5760] [hoge] [ERROR] test error
[2020-11-26 18:10:53,003] [5760] [hoge] [CRITICAL] test critical
```


また、levelをDEBUGではなくWARNに変更すると以下のように出力が制限されます。

```terminal:fuga.log
[2020-11-26 18:10:53,003] [5760] [hoge] [WARNING] test warn
[2020-11-26 18:10:53,003] [5760] [hoge] [ERROR] test error
[2020-11-26 18:10:53,003] [5760] [hoge] [CRITICAL] test critical
```


# まとめ


とりあえず使うだけならとても簡単だけど、込み入ったモジュールでバグを素早く見つけられるようにするには結構ノウハウが必要になりそうなので、スマートな使い方を模索していきたい所存でござる