Format log record as JSON.
(self, record: logging.LogRecord)
| 615 | """ |
| 616 | |
| 617 | def format(self, record: logging.LogRecord) -> str: |
| 618 | """Format log record as JSON.""" |
| 619 | import json |
| 620 | from datetime import timezone |
| 621 | |
| 622 | # Get context variables |
| 623 | try: |
| 624 | req_id = request_id_var.get() |
| 625 | except LookupError: |
| 626 | req_id = "" |
| 627 | |
| 628 | try: |
| 629 | source = source_var.get() |
| 630 | except LookupError: |
| 631 | source = "SYS" |
| 632 | |
| 633 | source_normalized = normalize_source(source) |
| 634 | |
| 635 | # Build timestamp in ISO 8601 format with milliseconds |
| 636 | now = datetime.now(timezone.utc) |
| 637 | timestamp = ( |
| 638 | now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{int(now.microsecond / 1000):03d}Z" |
| 639 | ) |
| 640 | |
| 641 | # Base log entry |
| 642 | log_entry: dict[str, Any] = { |
| 643 | "timestamp": timestamp, |
| 644 | "level": record.levelname, |
| 645 | "source": source_normalized.strip(), |
| 646 | "message": record.getMessage(), |
| 647 | } |
| 648 | |
| 649 | # Add request ID if present |
| 650 | if req_id and req_id.strip(): |
| 651 | log_entry["request_id"] = req_id.strip() |
| 652 | |
| 653 | # Add logger name |
| 654 | if record.name: |
| 655 | log_entry["logger"] = record.name |
| 656 | |
| 657 | # Add exception info if present |
| 658 | if record.exc_info and record.exc_info[1] is not None: |
| 659 | import traceback as tb |
| 660 | |
| 661 | log_entry["exception"] = { |
| 662 | "type": type(record.exc_info[1]).__name__, |
| 663 | "message": str(record.exc_info[1]), |
| 664 | "traceback": "".join(tb.format_exception(*record.exc_info)), |
| 665 | } |
| 666 | |
| 667 | # Add extra fields from record |
| 668 | # Common extra fields that might be useful |
| 669 | for key in ("funcName", "lineno", "pathname"): |
| 670 | value = getattr(record, key, None) |
| 671 | if value: |
| 672 | log_entry[key] = value |
| 673 | |
| 674 | return json.dumps(log_entry, ensure_ascii=False, default=str) |
nothing calls this directly
no test coverage detected