どうも、おのでらです。
今回の記事は
「loggingとは?」
「djangoでlogを出力する方法」
についてです。
自分が案件でloggingの実装を始めたときの過去の自分への備忘録としてまとめました。
この記事を読んで貰えれば
loggingについて、だいたいのイメージができると思います。
1. そもそもログはなんで必要?
ログを記録する理由は
不具合の修正を迅速に行うためです。
次のようなログが記録されたとします。
timestamp='2019-04-13T19:39:31.089925Z' level='info' event='an_error_occurred' logger='my_awesome_project.my_awesome_module' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' bar='Buz'
このlogからは
・エラーが起きた時間
・エラーの内容
・エラーが起きた場所
・エラーを起こしたユーザー
の情報を取得できます。
もしも、logなしで不具合を調査が出た場合、
調査だけで莫大な時間がかかってしまいます。
2. django-structlogとは
djangoでログをとるには
python標準のloggerがあります。
対して、django-structlogはdjangoに特化したloggerです。
django-structlogは、簡単な設定で多くのログ情報を出力してくれます。
loggerとdjango-structlog2つのログ出力を比較しましょう。
loggerの場合
入力
import logging
logger = logging.get_logger(__name__)
logger.info("An error occurred")
出力
An error occurred
django-structlogの場合
入力
>>> import structlog
>>> logger = structlog.get_logger(__name__)
>>> logger.info("an_error_occurred", bar="Buz")
出力
timestamp='2019-04-13T19:39:31.089925Z' level='info' event='an_error_occurred' logger='my_awesome_project.my_awesome_module' request_id='3a8f801c-072b-4805-8f38-e1337f363ed4' user_id=1 ip='0.0.0.0' bar='Buz'
django-structlogがデフォルトで
多くの情報を出力してくれることがわかりました。
3. LogLevelについて
Logにはそれぞれ重要度を表すLogLevelがあります。
・CRITICAL
・ERROR
・WARNING
・INFO
・DEBUG
などがLogLevelになります。
上に行くにつれ重要度が高いです。
500などのサーバーエラー→CRITICAL、
40~系のクライアントエラー→ERROR
などの使い分けができます。
4. 導入方法
https://django-structlog.readthedocs.io/en/latest/getting_started.html
公式ドキュメントより
まずはstructlogのインストール。
pip install django-structlog
次にsettings.pyのMIDDLEWAREの欄に下のように追加します。
MIDDLEWARE = [
# ...
'django_structlog.middlewares.RequestMiddleware',
]
そして次はstructlogの設定です。settings.pyから
import structlog
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
},
"plain_console": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(),
},
"key_value": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.KeyValueRenderer(key_order=['timestamp', 'level', 'event', 'logger']),
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "plain_console",
},
"json_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/json.log",
"formatter": "json_formatter",
},
"flat_line_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/flat_line.log",
"formatter": "key_value",
},
},
"loggers": {
"django_structlog": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
# Make sure to replace the following logger's name for yours
"django_structlog_demo_project": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
}
}
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.stdlib.filter_by_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
import structlog
logger = structlog.get_logger(__name__)
これでログが出力されます。
※公式ドキュメントの設定では
タイムスタンプの時刻が協定世界時のUTCなっています。
変更方法は
structlog.processors.TimeStamper(fmt="iso")
から
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False)
に変更することで日本標準時間に変更できます。
5. Loggingの定数について
Loggingの定数で重要なものは
・formatter
・handler
・logger
になります。
formatter
formatterはどんな情報をログ出力するかを決定します。
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
},
"plain_console": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(),
},
"key_value": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.KeyValueRenderer(key_order=['timestamp', 'level', 'event', 'logger']),
},
},
handlers
ログの出力先を設定します。(標準出力、ファイル、メール等)
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "plain_console",
},
"json_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/json.log",
"formatter": "json_formatter",
},
"flat_line_file": {
"class": "logging.handlers.WatchedFileHandler",
"filename": "logs/flat_line.log",
"formatter": "key_value",
},
},
・class
ログ出力に使用するクラスです。ファイルに出力するクラスを指定します。
例えば、'logging.StreamHandler'を指定するとコンソールにログが出力されます。
・filename
ログ出力するファイルのパスとファイル名を指定できます。
・formatter
定義したformatterのうちどれを使用するかを指定します。
※SMTPHandllerを用いてログをメール送信することもできるらしいです。
www.nullpo.io/2018/11/12/python-logging-mail/
logger
実際にdjangoアプリ内で使用するstructlogの設定になります。
"loggers": {
"django_structlog": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
"django_structlog_demo_project": {
"handlers": ["console", "flat_line_file", "json_file"],
"level": "INFO",
},
・handlers
先に定義したhandlersのどれを使用するか指定します。
・level
設定したログレベル以下のログは出力されなくなります。