structlog
設定例
structlog公式
code:python
import logging
import structlog
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.StackInfoRenderer(),
structlog.dev.set_exc_info,
structlog.processors.TimeStamper(),
structlog.dev.ConsoleRenderer()
],
wrapper_class=structlog.make_filtering_bound_logger(logging.NOTSET),
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
cache_logger_on_first_use=False
)
log = structlog.get_logger()
django-structlog
code:python
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,
},
},
"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": {
"level": "INFO",
},
# Make sure to replace the following logger's name for yours
"django_structlog_demo_project": {
"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(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
設定の説明
code:python
structlog.configure(
processors=[
structlog.stdlib.filter_by_level, # ログ出力対象でないレベルを早い段階でフィルタする
structlog.processors.TimeStamper(fmt="iso"), # 時刻情報の出力
structlog.stdlib.add_logger_name, # ロガー名(__name__)出力
structlog.stdlib.add_log_level, # ログレベル出力
structlog.stdlib.PositionalArgumentsFormatter(), # log.info("%s", value) をフォーマットする
structlog.processors.StackInfoRenderer(), # log.info(..., stack_info=True)でtracebackを出力
structlog.processors.format_exc_info, # exc_infoキーをロガーに渡したらtracebackを出力
structlog.processors.UnicodeDecoder(), # ログ出力に b"abc" 等が出るのを 'b"abc"' にする
structlog.stdlib.ProcessorFormatter.wrap_for_formatter, # ProcessorFormatter用に変形
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
structlog.contextvars.merge_contextvars
常に書いておけば良い
structlog.stdlib.filter_by_level
重いprocessor処理を省略できるようになる。重いかどうか関係なく設定しておけば良さそう
structlog.stdlib.PositionalArgumentsFormatter()
log.info("%s", value) をフォーマットする。設定しておけば良さそう
structlog.processors.StackInfoRenderer()
log.info(..., stack_info=True) でtracebackを出力。設定しておけばよさそう
structlog.processors.format_exc_info
exc_infoキーをロガーに渡したらtracebackを出力。設定しておけばよさそう
structlog.processors.UnicodeDecoder()
ログ出力に b"abc" 等が出るのを 'b"abc"' にする。JSONRendere を使えば、あってもなくても同じ。
あってもなくても同じなら設定しておけばよさそう。
structlog.stdlib.ProcessorFormatter.wrap_for_formatter
ProcessorFormatter用に変形
processorsでフォーマット変換せずにlogging.formatterの方で変換する場合に必要
Python標準のloggingでログ出力するコードがある場合は、formatterに ProcessorFormatter を指定し、そこにRendererを指定する使い方をする。
実行例
code:python
>> import structlog
>> import logging
>> logging.basicConfig(level=logging.INFO)
>> structlog.configure(
... processors=[
... structlog.contextvars.merge_contextvars,
... structlog.processors.TimeStamper(fmt="iso"),
... structlog.stdlib.add_logger_name,
... structlog.stdlib.add_log_level,
... structlog.processors.JSONRenderer(ensure_ascii=False),
... ],
... logger_factory=structlog.stdlib.LoggerFactory(),
... wrapper_class=structlog.stdlib.BoundLogger,
... )
>> log.info('hello', binary=b'abc')
INFO:__main__:{"binary": "b'abc'", "event": "hello", "timestamp": "2022-04-25T02:39:45.474130Z", "logger": "__main__", "level": "info"}
>> logging.info('hello')
INFO:root:hello
loggingのformatterでログレベル等を出さないように調整
code:python
>> logging.basicConfig(level=logging.INFO, format="%(message)s", force=True)
>> logging.info('hello')
hello
>> log.info('hello')
{"event": "hello", "timestamp": "2022-04-25T04:42:04.150653Z", "logger": "__main__", "level": "info"}
Python標準のloggingを使っている外部ライブラリなどのログ出力も構造化ログにするには、formatterの指定が必要。
loggingのformatterにstructlogのProcessorFormatterを指定
code:python
>> foreign_pre_chains = [
... structlog.processors.TimeStamper(fmt="iso"),
... structlog.stdlib.add_logger_name,
... structlog.stdlib.add_log_level,
... ]
>> formatter = structlog.stdlib.ProcessorFormatter(processor=structlog.processors.JSONRenderer(), foreign_pre_chain=foreign_pre_chains)
>> logging.root.handlers0.formatter = formatter >> structlog.configure(
... processors=[
... structlog.contextvars.merge_contextvars,
... structlog.processors.TimeStamper(fmt="iso"),
... structlog.stdlib.add_logger_name,
... structlog.stdlib.add_log_level,
... structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
... ],
... logger_factory=structlog.stdlib.LoggerFactory(),
... wrapper_class=structlog.stdlib.BoundLogger,
... cache_logger_on_first_use=True,
... )
>> logging.info('hello') # python標準rootロガー
{"event": "hello", "timestamp": "2022-04-25T04:53:25.968477Z", "logger": "root", "level": "info"}
>> log.info('hello') # structlog
{"event": "hello", "timestamp": "2022-04-25T04:53:30.098200Z", "logger": "__main__", "level": "info"}
タグ