nano_gpt.log

Logging utilities.

This module provides a logging utility that can be used to log messages to a file or standard output. This uses a structured log format that can be parsed by tools for parsing and metrics gathering.

 1"""Logging utilities.
 2
 3This module provides a logging utility that can be used to log messages to a file or
 4standard output. This uses a structured log format that can be parsed by tools
 5for parsing and metrics gathering.
 6"""
 7
 8import logging
 9import pathlib
10from abc import ABC, abstractmethod
11from dataclasses import dataclass
12from typing import Any
13
14_LOGGER = logging.getLogger(__name__)
15
16
17@dataclass(frozen=True, kw_only=True)
18class LogRecord(ABC):
19    """A structured log record."""
20
21    log_type: str
22    data: dict[str, Any]
23
24    @property
25    def message(self) -> str:
26        """String representation of the data."""
27        return " | ".join(f"{key}: {value}" for key, value in self.data.items())
28
29    def __str__(self) -> str:
30        """String representation."""
31        return f"{self.log_type}: {self.message}"
32
33
34class TrainLog(ABC):
35    """Train logger abstract base class."""
36
37    @abstractmethod
38    def log(self, message: LogRecord) -> None:
39        """Log a message."""
40
41
42class LogFile(TrainLog):
43    """Train logger that logs to a file."""
44
45    def __init__(self, log_file: pathlib.Path) -> None:
46        """Initialize the train logger."""
47        self.log_file = log_file
48        self.log_file.parent.mkdir(parents=True, exist_ok=True)
49        self.log_file.unlink(missing_ok=True)
50
51    def log(self, message: LogRecord) -> None:
52        """Log a message."""
53        with self.log_file.open("a") as f:
54            f.write(str(message))
55            f.write("\n")
56            f.flush()
57
58
59class LogStdout(TrainLog):
60    """Train logger that logs to standard output."""
61
62    def log(self, message: LogRecord) -> None:
63        """Log a message."""
64        print(str(message))
65
66
67class LogMulti(TrainLog):
68    """Train logger that logs to multiple loggers."""
69
70    def __init__(self, logs: list[TrainLog]) -> None:
71        """Initialize the train logger."""
72        self.logs = logs
73
74    def log(self, message: LogRecord) -> None:
75        """Log a message."""
76        for log in self.logs:
77            log.log(message)
78
79
80def create_log(
81    log_file: pathlib.Path | None = None, log_stdout: bool = True
82) -> TrainLog:
83    """Create a train logger that logs to a file and standard output."""
84    logs: list[TrainLog] = []
85    if log_file:
86        logs.append(LogFile(log_file))
87    if log_stdout:
88        logs.append(LogStdout())
89    return LogMulti(logs)
@dataclass(frozen=True, kw_only=True)
class LogRecord(abc.ABC):
18@dataclass(frozen=True, kw_only=True)
19class LogRecord(ABC):
20    """A structured log record."""
21
22    log_type: str
23    data: dict[str, Any]
24
25    @property
26    def message(self) -> str:
27        """String representation of the data."""
28        return " | ".join(f"{key}: {value}" for key, value in self.data.items())
29
30    def __str__(self) -> str:
31        """String representation."""
32        return f"{self.log_type}: {self.message}"

A structured log record.

LogRecord(*, log_type: str, data: dict[str, typing.Any])
log_type: str
data: dict[str, typing.Any]
message: str
25    @property
26    def message(self) -> str:
27        """String representation of the data."""
28        return " | ".join(f"{key}: {value}" for key, value in self.data.items())

String representation of the data.

class TrainLog(abc.ABC):
35class TrainLog(ABC):
36    """Train logger abstract base class."""
37
38    @abstractmethod
39    def log(self, message: LogRecord) -> None:
40        """Log a message."""

Train logger abstract base class.

@abstractmethod
def log(self, message: LogRecord) -> None:
38    @abstractmethod
39    def log(self, message: LogRecord) -> None:
40        """Log a message."""

Log a message.

class LogFile(TrainLog):
43class LogFile(TrainLog):
44    """Train logger that logs to a file."""
45
46    def __init__(self, log_file: pathlib.Path) -> None:
47        """Initialize the train logger."""
48        self.log_file = log_file
49        self.log_file.parent.mkdir(parents=True, exist_ok=True)
50        self.log_file.unlink(missing_ok=True)
51
52    def log(self, message: LogRecord) -> None:
53        """Log a message."""
54        with self.log_file.open("a") as f:
55            f.write(str(message))
56            f.write("\n")
57            f.flush()

Train logger that logs to a file.

LogFile(log_file: pathlib.Path)
46    def __init__(self, log_file: pathlib.Path) -> None:
47        """Initialize the train logger."""
48        self.log_file = log_file
49        self.log_file.parent.mkdir(parents=True, exist_ok=True)
50        self.log_file.unlink(missing_ok=True)

Initialize the train logger.

log_file
def log(self, message: LogRecord) -> None:
52    def log(self, message: LogRecord) -> None:
53        """Log a message."""
54        with self.log_file.open("a") as f:
55            f.write(str(message))
56            f.write("\n")
57            f.flush()

Log a message.

class LogStdout(TrainLog):
60class LogStdout(TrainLog):
61    """Train logger that logs to standard output."""
62
63    def log(self, message: LogRecord) -> None:
64        """Log a message."""
65        print(str(message))

Train logger that logs to standard output.

def log(self, message: LogRecord) -> None:
63    def log(self, message: LogRecord) -> None:
64        """Log a message."""
65        print(str(message))

Log a message.

class LogMulti(TrainLog):
68class LogMulti(TrainLog):
69    """Train logger that logs to multiple loggers."""
70
71    def __init__(self, logs: list[TrainLog]) -> None:
72        """Initialize the train logger."""
73        self.logs = logs
74
75    def log(self, message: LogRecord) -> None:
76        """Log a message."""
77        for log in self.logs:
78            log.log(message)

Train logger that logs to multiple loggers.

LogMulti(logs: list[TrainLog])
71    def __init__(self, logs: list[TrainLog]) -> None:
72        """Initialize the train logger."""
73        self.logs = logs

Initialize the train logger.

logs
def log(self, message: LogRecord) -> None:
75    def log(self, message: LogRecord) -> None:
76        """Log a message."""
77        for log in self.logs:
78            log.log(message)

Log a message.

def create_log( log_file: pathlib.Path | None = None, log_stdout: bool = True) -> TrainLog:
81def create_log(
82    log_file: pathlib.Path | None = None, log_stdout: bool = True
83) -> TrainLog:
84    """Create a train logger that logs to a file and standard output."""
85    logs: list[TrainLog] = []
86    if log_file:
87        logs.append(LogFile(log_file))
88    if log_stdout:
89        logs.append(LogStdout())
90    return LogMulti(logs)

Create a train logger that logs to a file and standard output.