🔥

Pythonでjson形式のログを出力する

2024/11/05に公開

1. loggingモジュール と jsonモジュールを使う

ソースコード

import logging
import json

class JsonFormatter(logging.Formatter):
    def format(self, record):
        # JSONに含めたいフィールドを辞書として定義
        log_record = {
            "timestamp": self.formatTime(record),
            "logger": record.name,
            "level": record.levelname,
            "message": record.getMessage(),
        }
        # record.__dict__から追加情報を取得し、log_recordに追加
        log_record.update({key: value for key, value in record.__dict__.items() if key not in log_record})

        return json.dumps(log_record)

# ロガーの設定
logger = logging.getLogger("json_logger")
logger.setLevel(logging.DEBUG)

# ハンドラーとフォーマッタの設定
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)

# ログ出力(追加情報を渡す例)
logger.debug("This is a DEBUG message", extra={"user_id": 123, "transaction_id": "abc123"})
logger.info("This is an INFO message", extra={"user_id": 456, "transaction_id": "def456"})
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message", extra={"error_code": 500})
logger.critical("This is a CRITICAL message", extra={"urgent": True})

出力結果

{"timestamp": "2024-11-04 22:23:53,338", "logger": "json_logger", "level": "DEBUG", "message": "This is a DEBUG message", "name": "json_logger", "msg": "This is a DEBUG message", "args": [], "levelname": "DEBUG", "levelno": 10, "pathname": "<ipython-input-1-4ca55e575783>", "filename": "<ipython-input-1-4ca55e575783>", "module": "<ipython-input-1-4ca55e575783>", "exc_info": null, "exc_text": null, "stack_info": null, "lineno": 28, "funcName": "<module>", "created": 1730726633.338757, "msecs": 338.0, "relativeCreated": 1819.5860385894775, "thread": 8681610752, "threadName": "MainThread", "processName": "MainProcess", "process": 47518, "taskName": null, "user_id": 123, "transaction_id": "abc123"}
{"timestamp": "2024-11-04 22:23:53,338", "logger": "json_logger", "level": "INFO", "message": "This is an INFO message", "name": "json_logger", "msg": "This is an INFO message", "args": [], "levelname": "INFO", "levelno": 20, "pathname": "<ipython-input-1-4ca55e575783>", "filename": "<ipython-input-1-4ca55e575783>", "module": "<ipython-input-1-4ca55e575783>", "exc_info": null, "exc_text": null, "stack_info": null, "lineno": 29, "funcName": "<module>", "created": 1730726633.338881, "msecs": 338.0, "relativeCreated": 1819.7100162506104, "thread": 8681610752, "threadName": "MainThread", "processName": "MainProcess", "process": 47518, "taskName": null, "user_id": 456, "transaction_id": "def456"}
{"timestamp": "2024-11-04 22:23:53,339", "logger": "json_logger", "level": "WARNING", "message": "This is a WARNING message", "name": "json_logger", "msg": "This is a WARNING message", "args": [], "levelname": "WARNING", "levelno": 30, "pathname": "<ipython-input-1-4ca55e575783>", "filename": "<ipython-input-1-4ca55e575783>", "module": "<ipython-input-1-4ca55e575783>", "exc_info": null, "exc_text": null, "stack_info": null, "lineno": 30, "funcName": "<module>", "created": 1730726633.3390179, "msecs": 339.0, "relativeCreated": 1819.8468685150146, "thread": 8681610752, "threadName": "MainThread", "processName": "MainProcess", "process": 47518, "taskName": null}
{"timestamp": "2024-11-04 22:23:53,339", "logger": "json_logger", "level": "ERROR", "message": "This is an ERROR message", "name": "json_logger", "msg": "This is an ERROR message", "args": [], "levelname": "ERROR", "levelno": 40, "pathname": "<ipython-input-1-4ca55e575783>", "filename": "<ipython-input-1-4ca55e575783>", "module": "<ipython-input-1-4ca55e575783>", "exc_info": null, "exc_text": null, "stack_info": null, "lineno": 31, "funcName": "<module>", "created": 1730726633.339106, "msecs": 339.0, "relativeCreated": 1819.9350833892822, "thread": 8681610752, "threadName": "MainThread", "processName": "MainProcess", "process": 47518, "taskName": null, "error_code": 500}
{"timestamp": "2024-11-04 22:23:53,339", "logger": "json_logger", "level": "CRITICAL", "message": "This is a CRITICAL message", "name": "json_logger", "msg": "This is a CRITICAL message", "args": [], "levelname": "CRITICAL", "levelno": 50, "pathname": "<ipython-input-1-4ca55e575783>", "filename": "<ipython-input-1-4ca55e575783>", "module": "<ipython-input-1-4ca55e575783>", "exc_info": null, "exc_text": null, "stack_info": null, "lineno": 32, "funcName": "<module>", "created": 1730726633.339234, "msecs": 339.0, "relativeCreated": 1820.0631141662598, "thread": 8681610752, "threadName": "MainThread", "processName": "MainProcess", "process": 47518, "taskName": null, "urgent": true}

2. loggingモジュール と python-json-loggerモジュールを使う

  • python-json-logger 2023年2月22日 v2.0.7 リリース以降更新が止まっていそう

ソースコード

import logging
from pythonjsonlogger import jsonlogger

# ロガーの設定
logger = logging.getLogger("json_logger")
logger.setLevel(logging.DEBUG)

# ハンドラーとフォーマッタの設定
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
    fmt="%(asctime)s %(name)s %(levelname)s %(message)s"
)
handler.setFormatter(formatter)
logger.addHandler(handler)

# ログ出力(追加情報を渡す例)
logger.debug("This is a DEBUG message", extra={"user_id": 123, "transaction_id": "abc123"})
logger.info("This is an INFO message", extra={"user_id": 456, "transaction_id": "def456"})
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message", extra={"error_code": 500})
logger.critical("This is a CRITICAL message", extra={"urgent": True})

出力結果

{"asctime": "2024-11-04 22:32:40,700", "name": "json_logger", "levelname": "DEBUG", "message": "This is a DEBUG message", "taskName": null, "user_id": 123, "transaction_id": "abc123"}
{"asctime": "2024-11-04 22:32:40,700", "name": "json_logger", "levelname": "INFO", "message": "This is an INFO message", "taskName": null, "user_id": 456, "transaction_id": "def456"}
{"asctime": "2024-11-04 22:32:40,700", "name": "json_logger", "levelname": "WARNING", "message": "This is a WARNING message", "taskName": null}
{"asctime": "2024-11-04 22:32:40,700", "name": "json_logger", "levelname": "ERROR", "message": "This is an ERROR message", "taskName": null, "error_code": 500}
{"asctime": "2024-11-04 22:32:40,700", "name": "json_logger", "levelname": "CRITICAL", "message": "This is a CRITICAL message", "taskName": null, "urgent": true}

3. structlogを使う

  • structlog 2024年7月17日 v24.4.0 リリース

ソースコード

import structlog
import logging

# Pythonの標準ログをJSONに出力するための設定
logging.basicConfig(level=logging.DEBUG)
structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso", utc=False),
        structlog.processors.JSONRenderer()
    ],
    logger_factory=structlog.stdlib.LoggerFactory(),
)

# ロガーの取得
logger = structlog.get_logger("json_logger")

# ログ出力(追加情報を渡す例)
logger.debug("This is a DEBUG message", user_id=123, transaction_id="abc123")
logger.info("This is an INFO message", user_id=456, transaction_id="def456")
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message", error_code=500)
logger.critical("This is a CRITICAL message", urgent=True)

出力結果

DEBUG:json_logger:{"user_id": 123, "transaction_id": "abc123", "event": "This is a DEBUG message", "timestamp": "2024-11-04T22:37:43.273906"}
INFO:json_logger:{"user_id": 456, "transaction_id": "def456", "event": "This is an INFO message", "timestamp": "2024-11-04T22:37:43.274380"}
WARNING:json_logger:{"event": "This is a WARNING message", "timestamp": "2024-11-04T22:37:43.274477"}
ERROR:json_logger:{"error_code": 500, "event": "This is an ERROR message", "timestamp": "2024-11-04T22:37:43.274561"}
CRITICAL:json_logger:{"urgent": true, "event": "This is a CRITICAL message", "timestamp": "2024-11-04T22:37:43.274663"}

ソースコード 2

import structlog
import logging
from pythonjsonlogger import jsonlogger

# 標準の logging の設定
log_handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
    fmt='%(asctime)s %(name)s %(levelname)s %(message)s'
)
log_handler.setFormatter(formatter)

logging.basicConfig(handlers=[log_handler], level=logging.DEBUG)

# structlog の設定
structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.processors.JSONRenderer()
    ],
    wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    cache_logger_on_first_use=True,
)

# ロガーの取得
logger = structlog.get_logger("json_logger")

# ログ出力例
logger.debug("This is a DEBUG message", user_id=123, transaction_id="abc123")
logger.info("This is an INFO message", user_id=456, transaction_id="def456")
logger.warning("This is a WARNING message")
logger.error("This is an ERROR message", error_code=500)
logger.critical("This is a CRITICAL message", urgent=True)

出力結果2

{"asctime": "2024-11-04 22:41:59,173", "name": "json_logger", "levelname": "DEBUG", "message": "{\"user_id\": 123, \"transaction_id\": \"abc123\", \"event\": \"This is a DEBUG message\", \"timestamp\": \"2024-11-04T13:41:59.172661Z\", \"logger\": \"json_logger\", \"level\": \"debug\"}", "taskName": null}
{"asctime": "2024-11-04 22:41:59,173", "name": "json_logger", "levelname": "INFO", "message": "{\"user_id\": 456, \"transaction_id\": \"def456\", \"event\": \"This is an INFO message\", \"timestamp\": \"2024-11-04T13:41:59.173167Z\", \"logger\": \"json_logger\", \"level\": \"info\"}", "taskName": null}
{"asctime": "2024-11-04 22:41:59,173", "name": "json_logger", "levelname": "WARNING", "message": "{\"event\": \"This is a WARNING message\", \"timestamp\": \"2024-11-04T13:41:59.173267Z\", \"logger\": \"json_logger\", \"level\": \"warning\"}", "taskName": null}
{"asctime": "2024-11-04 22:41:59,173", "name": "json_logger", "levelname": "ERROR", "message": "{\"error_code\": 500, \"event\": \"This is an ERROR message\", \"timestamp\": \"2024-11-04T13:41:59.173346Z\", \"logger\": \"json_logger\", \"level\": \"error\"}", "taskName": null}
{"asctime": "2024-11-04 22:41:59,173", "name": "json_logger", "levelname": "CRITICAL", "message": "{\"urgent\": true, \"event\": \"This is a CRITICAL message\", \"timestamp\": \"2024-11-04T13:41:59.173423Z\", \"logger\": \"json_logger\", \"level\": \"critical\"}", "taskName": null}

4. django-structlogを使う

Discussion