loggi.models

  1import re
  2from dataclasses import dataclass
  3from datetime import datetime
  4
  5from pathier import Pathier, Pathish
  6from typing_extensions import Self
  7from younotyou import younotyou
  8
  9root = Pathier(__file__).parent
 10
 11
 12@dataclass
 13class Event:
 14    """Class representing a logged event."""
 15
 16    level: str
 17    date: datetime
 18    message: str
 19
 20    def __str__(self) -> str:
 21        sep = "|-|"
 22        return sep.join([self.level, str(self.date), self.message])
 23
 24
 25@dataclass
 26class Log:
 27    """Class representing a log file as a list of Events."""
 28
 29    events: list[Event]
 30    path: Pathier | None = None
 31
 32    def __add__(self, log: Self) -> Self:
 33        return self.__class__(self.events + log.events)
 34
 35    def __str__(self) -> str:
 36        return "\n".join(str(event) for event in self.events)
 37
 38    def __getitem__(self, subscript: slice) -> Self:
 39        return self.__class__(self.events[subscript], self.path)
 40
 41    def __len__(self) -> int:
 42        return len(self.events)
 43
 44    @property
 45    def num_events(self) -> int:
 46        return len(self.events)
 47
 48    @staticmethod
 49    def _parse_events(events: list[str]) -> list[Event]:
 50        """Convert a list of loggi event strings into a list of `Event` objects."""
 51        sep = "|-|"
 52        to_datetime = lambda date: datetime.strptime(date, "%x %X")
 53        logs = []
 54        for event in events:
 55            level, date, message = event.split(sep, maxsplit=3)
 56            logs.append(Event(level, to_datetime(date), message))
 57        return logs
 58
 59    @staticmethod
 60    def _split_log_into_events(log: str) -> list[str]:
 61        """Decompose a string of loggi events into a list of events, accounting for multi-line events."""
 62        events = []
 63        event = ""
 64        for line in log.splitlines(True):
 65            if re.findall("[A-Z]+\\|\\-\\|", line):
 66                if event:
 67                    events.append(event.strip("\n"))
 68                event = line
 69            else:
 70                event += line
 71        if event:
 72            events.append(event.strip("\n"))
 73        return events
 74
 75    def chronosort(self):
 76        """Sort this object's events by date."""
 77        self.events = sorted(self.events, key=lambda event: event.date)
 78
 79    def filter_dates(
 80        self, start: datetime = datetime.fromtimestamp(0), stop: datetime | None = None
 81    ) -> Self:
 82        """Returns a new `Log` object containing events between `start` and `stop`, inclusive."""
 83        if not stop:
 84            stop = datetime.now()
 85        return self.__class__(
 86            [event for event in self.events if start <= event.date <= stop], self.path
 87        )
 88
 89    def filter_levels(self, levels: list[str]) -> Self:
 90        """Returns a new `Log` object containing events with the specified levels."""
 91        return self.__class__(
 92            [event for event in self.events if event.level in levels], self.path
 93        )
 94
 95    def filter_messages(
 96        self,
 97        include_patterns: list[str] = ["*"],
 98        exclude_patterns: list[str] = [],
 99        case_sensitive: bool = True,
100    ):
101        """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`.
102
103        Both lists can contain wildcard patterns."""
104        return Log(
105            [
106                event
107                for event in self.events
108                if event.message
109                in younotyou(
110                    [event.message], include_patterns, exclude_patterns, case_sensitive
111                )
112            ],
113            self.path,
114        )
115
116    @classmethod
117    def load_log(cls, logpath: Pathish) -> Self:
118        """Load a `Log` object from the log file at `logpath`."""
119        logpath = Pathier(logpath)
120        events = cls._split_log_into_events(logpath.read_text(encoding="utf-8"))
121        return cls(cls._parse_events(events), logpath)
@dataclass
class Event:
13@dataclass
14class Event:
15    """Class representing a logged event."""
16
17    level: str
18    date: datetime
19    message: str
20
21    def __str__(self) -> str:
22        sep = "|-|"
23        return sep.join([self.level, str(self.date), self.message])

Class representing a logged event.

Event(level: str, date: datetime.datetime, message: str)
@dataclass
class Log:
 26@dataclass
 27class Log:
 28    """Class representing a log file as a list of Events."""
 29
 30    events: list[Event]
 31    path: Pathier | None = None
 32
 33    def __add__(self, log: Self) -> Self:
 34        return self.__class__(self.events + log.events)
 35
 36    def __str__(self) -> str:
 37        return "\n".join(str(event) for event in self.events)
 38
 39    def __getitem__(self, subscript: slice) -> Self:
 40        return self.__class__(self.events[subscript], self.path)
 41
 42    def __len__(self) -> int:
 43        return len(self.events)
 44
 45    @property
 46    def num_events(self) -> int:
 47        return len(self.events)
 48
 49    @staticmethod
 50    def _parse_events(events: list[str]) -> list[Event]:
 51        """Convert a list of loggi event strings into a list of `Event` objects."""
 52        sep = "|-|"
 53        to_datetime = lambda date: datetime.strptime(date, "%x %X")
 54        logs = []
 55        for event in events:
 56            level, date, message = event.split(sep, maxsplit=3)
 57            logs.append(Event(level, to_datetime(date), message))
 58        return logs
 59
 60    @staticmethod
 61    def _split_log_into_events(log: str) -> list[str]:
 62        """Decompose a string of loggi events into a list of events, accounting for multi-line events."""
 63        events = []
 64        event = ""
 65        for line in log.splitlines(True):
 66            if re.findall("[A-Z]+\\|\\-\\|", line):
 67                if event:
 68                    events.append(event.strip("\n"))
 69                event = line
 70            else:
 71                event += line
 72        if event:
 73            events.append(event.strip("\n"))
 74        return events
 75
 76    def chronosort(self):
 77        """Sort this object's events by date."""
 78        self.events = sorted(self.events, key=lambda event: event.date)
 79
 80    def filter_dates(
 81        self, start: datetime = datetime.fromtimestamp(0), stop: datetime | None = None
 82    ) -> Self:
 83        """Returns a new `Log` object containing events between `start` and `stop`, inclusive."""
 84        if not stop:
 85            stop = datetime.now()
 86        return self.__class__(
 87            [event for event in self.events if start <= event.date <= stop], self.path
 88        )
 89
 90    def filter_levels(self, levels: list[str]) -> Self:
 91        """Returns a new `Log` object containing events with the specified levels."""
 92        return self.__class__(
 93            [event for event in self.events if event.level in levels], self.path
 94        )
 95
 96    def filter_messages(
 97        self,
 98        include_patterns: list[str] = ["*"],
 99        exclude_patterns: list[str] = [],
100        case_sensitive: bool = True,
101    ):
102        """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`.
103
104        Both lists can contain wildcard patterns."""
105        return Log(
106            [
107                event
108                for event in self.events
109                if event.message
110                in younotyou(
111                    [event.message], include_patterns, exclude_patterns, case_sensitive
112                )
113            ],
114            self.path,
115        )
116
117    @classmethod
118    def load_log(cls, logpath: Pathish) -> Self:
119        """Load a `Log` object from the log file at `logpath`."""
120        logpath = Pathier(logpath)
121        events = cls._split_log_into_events(logpath.read_text(encoding="utf-8"))
122        return cls(cls._parse_events(events), logpath)

Class representing a log file as a list of Events.

Log( events: list[loggi.models.Event], path: pathier.pathier.Pathier | None = None)
def chronosort(self):
76    def chronosort(self):
77        """Sort this object's events by date."""
78        self.events = sorted(self.events, key=lambda event: event.date)

Sort this object's events by date.

def filter_dates( self, start: datetime.datetime = datetime.datetime(1969, 12, 31, 18, 0), stop: datetime.datetime | None = None) -> Self:
80    def filter_dates(
81        self, start: datetime = datetime.fromtimestamp(0), stop: datetime | None = None
82    ) -> Self:
83        """Returns a new `Log` object containing events between `start` and `stop`, inclusive."""
84        if not stop:
85            stop = datetime.now()
86        return self.__class__(
87            [event for event in self.events if start <= event.date <= stop], self.path
88        )

Returns a new Log object containing events between start and stop, inclusive.

def filter_levels(self, levels: list[str]) -> Self:
90    def filter_levels(self, levels: list[str]) -> Self:
91        """Returns a new `Log` object containing events with the specified levels."""
92        return self.__class__(
93            [event for event in self.events if event.level in levels], self.path
94        )

Returns a new Log object containing events with the specified levels.

def filter_messages( self, include_patterns: list[str] = ['*'], exclude_patterns: list[str] = [], case_sensitive: bool = True):
 96    def filter_messages(
 97        self,
 98        include_patterns: list[str] = ["*"],
 99        exclude_patterns: list[str] = [],
100        case_sensitive: bool = True,
101    ):
102        """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`.
103
104        Both lists can contain wildcard patterns."""
105        return Log(
106            [
107                event
108                for event in self.events
109                if event.message
110                in younotyou(
111                    [event.message], include_patterns, exclude_patterns, case_sensitive
112                )
113            ],
114            self.path,
115        )

Returns a new Log object containing events with messages matching those in include_patterns, but not matching exclude_patterns.

Both lists can contain wildcard patterns.

@classmethod
def load_log(cls, logpath: pathier.pathier.Pathier | pathlib.Path | str) -> Self:
117    @classmethod
118    def load_log(cls, logpath: Pathish) -> Self:
119        """Load a `Log` object from the log file at `logpath`."""
120        logpath = Pathier(logpath)
121        events = cls._split_log_into_events(logpath.read_text(encoding="utf-8"))
122        return cls(cls._parse_events(events), logpath)

Load a Log object from the log file at logpath.