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 Log(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 Log(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 Log( 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 Log([event for event in self.events if event.level in levels], self.path) 92 93 def filter_messages( 94 self, 95 include_patterns: list[str] = ["*"], 96 exclude_patterns: list[str] = [], 97 case_sensitive: bool = True, 98 ): 99 """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`. 100 101 Both lists can contain wildcard patterns.""" 102 return Log( 103 [ 104 event 105 for event in self.events 106 if event.message 107 in younotyou( 108 [event.message], include_patterns, exclude_patterns, case_sensitive 109 ) 110 ], 111 self.path, 112 ) 113 114 @classmethod 115 def load_log(cls, logpath: Pathish) -> Self: 116 """Load a `Log` object from the log file at `logpath`.""" 117 logpath = Pathier(logpath) 118 events = cls._split_log_into_events(logpath.read_text(encoding="utf-8")) 119 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.
@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 Log(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 Log(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 Log( 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 Log([event for event in self.events if event.level in levels], self.path) 93 94 def filter_messages( 95 self, 96 include_patterns: list[str] = ["*"], 97 exclude_patterns: list[str] = [], 98 case_sensitive: bool = True, 99 ): 100 """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`. 101 102 Both lists can contain wildcard patterns.""" 103 return Log( 104 [ 105 event 106 for event in self.events 107 if event.message 108 in younotyou( 109 [event.message], include_patterns, exclude_patterns, case_sensitive 110 ) 111 ], 112 self.path, 113 ) 114 115 @classmethod 116 def load_log(cls, logpath: Pathish) -> Self: 117 """Load a `Log` object from the log file at `logpath`.""" 118 logpath = Pathier(logpath) 119 events = cls._split_log_into_events(logpath.read_text(encoding="utf-8")) 120 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 Log( 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 Log([event for event in self.events if event.level in levels], self.path)
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):
94 def filter_messages( 95 self, 96 include_patterns: list[str] = ["*"], 97 exclude_patterns: list[str] = [], 98 case_sensitive: bool = True, 99 ): 100 """Returns a new `Log` object containing events with messages matching those in `include_patterns`, but not matching `exclude_patterns`. 101 102 Both lists can contain wildcard patterns.""" 103 return Log( 104 [ 105 event 106 for event in self.events 107 if event.message 108 in younotyou( 109 [event.message], include_patterns, exclude_patterns, case_sensitive 110 ) 111 ], 112 self.path, 113 )
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:
115 @classmethod 116 def load_log(cls, logpath: Pathish) -> Self: 117 """Load a `Log` object from the log file at `logpath`.""" 118 logpath = Pathier(logpath) 119 events = cls._split_log_into_events(logpath.read_text(encoding="utf-8")) 120 return cls(cls._parse_events(events), logpath)
Load a Log
object from the log file at logpath
.