Serialize log records as compact JSON objects, one per line.
| 51 | |
| 52 | |
| 53 | class JsonFormatter(logging.Formatter): |
| 54 | """Serialize log records as compact JSON objects, one per line.""" |
| 55 | |
| 56 | def format(self, record: logging.LogRecord) -> str: |
| 57 | payload: dict[str, Any] = { |
| 58 | "ts": datetime.fromtimestamp(record.created, tz=timezone.utc).strftime( |
| 59 | "%Y-%m-%dT%H:%M:%S.%fZ" |
| 60 | ), |
| 61 | "level": record.levelname, |
| 62 | "msg": record.getMessage(), |
| 63 | } |
| 64 | # Anything passed via logger.info(..., extra={"foo": 1}) ends up as |
| 65 | # a record attribute — copy whichever extras we can JSON-encode. |
| 66 | for key, value in record.__dict__.items(): |
| 67 | if key in _RESERVED_LOGRECORD_ATTRS or key in payload: |
| 68 | continue |
| 69 | try: |
| 70 | json.dumps(value) |
| 71 | except (TypeError, ValueError): |
| 72 | value = str(value) |
| 73 | payload[key] = value |
| 74 | if record.exc_info: |
| 75 | payload["exc"] = self.formatException(record.exc_info) |
| 76 | return json.dumps(payload, ensure_ascii=False) |
| 77 | |
| 78 | |
| 79 | def configure(home: str | Path | None = None) -> logging.Logger: |