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