【俺的】Pythonロギングまとめ～ログファイル／標準出力

Pythonでロギングする際の俺的ベストプラクティス
パターンは以下の通り

1. ファイル出力(全て同一ログファイルに出力する)
1. ファイル出力(ログレベルに応じてログファイルを分ける)
1. 標準出力

基本的に、ログファイル出力と標準出力を併用するケースって少ないと思います。
標準出力のパターンは、コンテナ環境でfluentdドライバなどで外部転送したいケースがあるかなと思います。

参考にさせて頂きました → [pythonのlog出力](https://qiita.com/yopya/items/63155923602bf97dec53) by 
@yopya

# ファイル出力(全て同一ログファイルに出力する)

```python
from logging import getLogger, Formatter, handlers, DEBUG


class FileLogger:
    def __init__(self, file='log/app.log'):
        self.logger = getLogger(__name__)
        self.logger.setLevel(DEBUG)
        formatter = Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s')
        handler = handlers.RotatingFileHandler(
            filename=file,
            maxBytes=1048576,
            backupCount=3
        )
        handler.setFormatter(formatter)
        handler.setLevel(DEBUG)
        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)
```

# ファイル出力(ログレベルに応じてログファイルを分ける)

ERROR以上のレベルの場合に`err.log`に出力

```python
from logging import getLogger, Formatter, handlers, DEBUG, ERROR


class FileLogger:
    def __init__(self, debug_file='log/app.log', error_file='log/err.log'):
        self.logger = getLogger(__name__)
        self.logger.setLevel(DEBUG)
        formatter = Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s')
        # DEBUGレベルハンドラ定義
        debug_handler = handlers.RotatingFileHandler(
            filename=debug_file,
            maxBytes=1048576,
            backupCount=3
        )
        debug_handler.setFormatter(formatter)
        debug_handler.setLevel(DEBUG)
        self.logger.addHandler(debug_handler)
        # ERRORレベルハンドラ
        error_handler = handlers.RotatingFileHandler(
            filename=error_file,
            maxBytes=1048576,
            backupCount=3
        )
        error_handler.setFormatter(formatter)
        error_handler.setLevel(ERROR)
        self.logger.addHandler(error_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)
```

# 標準出力

```python
from logging import getLogger, Formatter, StreamHandler, DEBUG


class StreamLogger:
    def __init__(self):
        self.logger = getLogger(__name__)
        self.logger.setLevel(DEBUG)
        formatter = Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s')
        handler = StreamHandler()
        handler.setFormatter(formatter)
        handler.setLevel(DEBUG)
        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)
```

# 使い方

```python
logger = StreamLogger()
message = 'hello world!'
logger.info(message)
# => '2019-10-03 12:47:46,667 INFO __main__: hello world!'
```

`FileLogger`の場合は同様のメッセージが`/log/app.log`に出力されていく

# まとめ

「標準出力」に加えて「標準エラー出力」も必要だということに記事書いてて気づいたので後で対応する。