feat: add structured JSON logging

New JsonFormatter emits one JSON object per log line (JSONL). Activated
via format = "json" in [logging] config. Default remains "text".
This commit is contained in:
user
2026-02-15 04:16:45 +01:00
parent 7bbfa9b345
commit b32c9efb8a
2 changed files with 31 additions and 3 deletions

View File

@@ -10,6 +10,7 @@ import sys
from derp import __version__
from derp.bot import Bot
from derp.config import resolve_config
from derp.log import JsonFormatter
from derp.plugin import PluginRegistry
LOG_FORMAT = "%(asctime)s %(levelname)-5s %(name)s %(message)s"
@@ -60,11 +61,17 @@ def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
level = logging.DEBUG if args.verbose else logging.INFO
logging.basicConfig(format=LOG_FORMAT, datefmt=LOG_DATE, level=level)
config = resolve_config(args.config)
level = logging.DEBUG if args.verbose else logging.INFO
log_fmt = config.get("logging", {}).get("format", "text")
if log_fmt == "json":
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logging.basicConfig(handlers=[handler], level=level)
else:
logging.basicConfig(format=LOG_FORMAT, datefmt=LOG_DATE, level=level)
log = logging.getLogger("derp")
log.info("derp %s starting", __version__)

21
src/derp/log.py Normal file
View File

@@ -0,0 +1,21 @@
"""Structured logging formatters."""
from __future__ import annotations
import json
import logging
class JsonFormatter(logging.Formatter):
"""Emit log records as single-line JSON objects."""
def format(self, record: logging.LogRecord) -> str:
entry: dict[str, str] = {
"ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S"),
"level": record.levelname.lower(),
"logger": record.name,
"msg": record.getMessage(),
}
if record.exc_info and not isinstance(record.exc_info, bool) and record.exc_info[0]:
entry["exc"] = self.formatException(record.exc_info)
return json.dumps(entry, ensure_ascii=False, separators=(",", ":"))