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:
@@ -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
21
src/derp/log.py
Normal 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=(",", ":"))
|
||||
Reference in New Issue
Block a user