Source code for _errortools.logging.record

"""Log record — the structured object passed to every sink."""

from __future__ import annotations

import sys
import threading
import traceback
from dataclasses import dataclass, field
from datetime import datetime
from types import TracebackType
from typing import Any, Union

if sys.version_info >= (3, 9):
    from datetime import UTC
else:
    from datetime import timezone

    UTC = timezone.utc

from .level import Level


[docs] @dataclass class Record: """Immutable snapshot of a single log event. Mirrors loguru's record dict but as a typed dataclass. Attributes: time: UTC timestamp when the record was created. level: The `logging.level.Level` of this event. message: The formatted log message (after ``str.format`` / f-string). name: Logger name (set by the caller or auto-detected). file: Source file name (``__file__``). line: Source line number. function: Calling function name. thread_id: OS thread identifier. thread_name: Thread name. process_id: OS process id. exception: The active exception info tuple, or ``None``. extra: Arbitrary key/value context bound via ``logger.bind()``. """ time: datetime level: Level message: str name: str file: str line: int function: str thread_id: int thread_name: str process_id: int exception: Union[tuple[type[BaseException], BaseException, Union[TracebackType, None]], None] extra: dict[str, Any] = field(default_factory=dict) # ------------------------------------------------------------------ # Convenience helpers # ------------------------------------------------------------------ @property def exc_text(self) -> Union[str, None]: """Formatted traceback string, or ``None`` if no exception.""" if self.exception is None: return None return "".join(traceback.format_exception(*self.exception)) def __str__(self) -> str: return self.message
def _current_frame_info(depth: int = 2) -> tuple[str, int, str]: """Return (filename, lineno, function_name) *depth* frames up the call stack.""" frame = sys._getframe(depth) return frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name def make_record( level: Level, message: str, name: str, depth: int, exception: bool, extra: dict[str, Any], ) -> Record: """Build a `Record` for the given log call. Args: level: Log level. message: Already-formatted message string. name: Logger name. depth: How many frames to skip when locating caller info. exception: Whether to capture the current exception info. extra: Bound context from ``logger.bind()``. """ file, line, func = _current_frame_info(depth) exc_info = sys.exc_info() if exception else None if exc_info == (None, None, None): exc_info = None t = threading.current_thread() import os return Record( time=datetime.now(UTC), level=level, message=message, name=name, file=file, line=line, function=func, thread_id=t.ident or 0, thread_name=t.name, process_id=os.getpid(), exception=exc_info, # type: ignore[arg-type] extra=dict(extra), )