LoginSignup
1
1

More than 1 year has passed since last update.

Pythonのloggingモジュールを使用してログ出力する際のTips

Posted at

Pythonのloggingモジュールを使っているうちに知見が溜まってきたので記事にします。

設定はfileConfigよりdictConfigで行う

ログレベルやフォーマッタ等の設定を行う際、fileConfig関数とdictConfig関数が利用できる。

dictConfig関数の方が柔軟に設定を行うことができる。

使用例

from logging import getLogger
from logging.config import dictConfig

CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "default": {
            "format": "%(asctime)s %(name)s %(levelname)-5s: %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
        },
    },
    "root": {
        "level": "INFO",
        "handlers": ["console"],
    },
}

logger = getLogger(__name__)


if __name__ == '__main__':
    dictConfig(CONFIG)
    logger.info('hello')

出力項目を追加したりできる(fileConfigには無い機能)

from logging import getLogger, Filter
from logging.config import dictConfig

import flask


class IpAddressFilter(Filter):
    """クライアントのIPアドレスをログレコードに追加"""
    def filter(self, record):
        record.remote_addr = flask.request.headers.get('REMOTE_ADDR')
        return True

CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "default": {
            "format": "%(asctime)s %(name)s %(remote_addr)s %(levelname)-5s: %(message)s"
        }
    },
    "filters": {
        "addr": {
            "()": IpAddressFilter,
        }
    }
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
            "filters": ["addr"],
        },
    },
    "root": {
        "level": "INFO",
        "handlers": ["console"],
    },
}

app = flask.Flask(__name__)
dictConfig(CONFIG)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

ロガー設定はモジュールレベルでは行わない

pytestでcaplogを使用してログ出力のテストをする際、テスト対象モジュールをimportするだけでdictConfigやfileConfigによるロガー設定が行われてしまうと、正しくcaplogが動作しない。

dictConfigやfileConfigによるロガー設定はできる限り実際のプログラムとして起動する場合のみ行う。

mainで起動するコマンドラインツールの場合

...

if __name__ == '__main__':
    dictConfig(CONFIG)
    main()

FlaskやFastAPIなどのアプリケーション変数初期化を行う場合の例

main.py

...
app = create_app()
dictConfig(CONFIG)

# $ export FLASK_APP=main
# $ flask run

app.py

def create_app():
    return Flask('app_name')

tests/fixtures/app.py

@fixture
def client():
    """テストクライアント"""
    app = create_app()
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1