概要
loggingモジュールのFormatterクラスを拡張してログに含まれる改行文字をエスケープする方法を紹介します。
背景
Splunkのようなプラットフォームでログを分析するとき、スタックトレースのような複数行に渡って出力されるログが行単位でイベントに分割されていると、ログの検索がしづらいという課題があります。
方針
ロガーを呼び出す直前で自前でメッセージを組み立てたりはしなくありません。
Pythonのloggingモジュールにはログの出力フォーマットを設定するためのフォーマッタクラスがあるので、これを拡張して実装することにします。
実装
import logging
FORMAT = '%(asctime)-15s [%(name)s] %(message)s'
class OneLineFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
return super().format(
record).replace('\n', '\\n')
def test(formatter: logging.Formatter):
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
logger = logging.getLogger(
formatter.__class__.__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
try:
raise RuntimeError('exception message')
except RuntimeError:
logger.exception('Line 1\nLine 2')
def main():
# 標準のフォーマッタを使うとログが改行される
test(logging.Formatter(FORMAT))
# 実装したフォーマッタでは改行がエスケープされる
test(OneLineFormatter(FORMAT))
if __name__ == '__main__':
main()
動作確認
最初のログが標準のフォーマッタで出力したログで、2つ目のログが実装したフォーマッタで出力したログになります。
$ python3 main.py
2021-04-02 09:30:23,560 [Formatter] Line 1
Line 2
Traceback (most recent call last):
File "/Users/massakai/main.py", line 23, in test
raise RuntimeError('exception message')
RuntimeError: exception message
2021-04-02 09:30:23,561 [OneLineFormatter] Line 1\nLine 2\nTraceback (most recent call last):\n File "/Users/massakai/main.py", line 23, in test\n raise RuntimeError('exception message')\nRuntimeError: exception message