loggi.logger
1import logging 2 3from pathier import Pathier, Pathish 4 5from loggi import models 6 7root = Pathier(__file__).parent 8 9 10class Logger(logging.Logger): 11 @property 12 def logpath(self) -> Pathier | None: 13 """Return a file handler path whose stem matches `self.name`, if there is one.""" 14 for path in self.logpaths: 15 if path.stem == self.name: 16 return path 17 18 @property 19 def logpaths(self) -> list[Pathier]: 20 """A list of `Pathier` objects for any file handlers attached to this `Logger`.""" 21 return [ 22 Pathier(handler.baseFilename) 23 for handler in self.handlers 24 if isinstance(handler, logging.FileHandler) 25 ] 26 27 def close(self): 28 """Remove and close this logger's handlers.""" 29 for handler in self.handlers: 30 self.removeHandler(handler) 31 handler.close() 32 33 def get_log(self) -> models.Log | None: 34 """Returns a `models.Log` object populated from this logger's `logpath`.""" 35 if path := self.logpath: 36 return models.Log.load_log(path) 37 38 def logprint(self, message: str, level: str | int = "INFO"): 39 """Log and print `message`. 40 41 Only prints if the logger is enabled for the give `level`.""" 42 getattr( 43 self, 44 (level if isinstance(level, str) else logging.getLevelName(level)).lower(), 45 )(message) 46 if self.isEnabledFor( 47 level if isinstance(level, int) else logging.getLevelName(level) 48 ): 49 print(message) 50 51 52def getLogger(name: str, path: Pathish = Pathier.cwd()) -> Logger: 53 """Get a configured `loggi.Logger` instance for `name` with a file handler. 54 55 The log file will be located in `path` at `path/{name}.log`. 56 57 Default level is `INFO`. 58 59 Logs are in the format: `{levelname}|-|{asctime}|-|{message} 60 61 asctime is formatted as `%x %X`""" 62 path = Pathier(path) 63 path.mkdir() 64 # make sure loggi.Logger is the current logger class 65 logging.setLoggerClass(Logger) 66 logger = logging.Logger.manager.getLogger(name) 67 # TODO: Add option for a stream handler 68 # Add file handler using `logger.name` 69 logpath = path / f"{logger.name}.log" 70 handler = logging.FileHandler(logpath, encoding="utf-8") 71 if handler.baseFilename not in [ 72 existing_handler.baseFilename 73 for existing_handler in logger.handlers 74 if isinstance(existing_handler, logging.FileHandler) 75 ]: 76 handler.setFormatter( 77 logging.Formatter( 78 "{levelname}|-|{asctime}|-|{message}", style="{", datefmt="%x %X" 79 ) 80 ) 81 logger.addHandler(handler) 82 logger.setLevel(logging.INFO) 83 return logger # type: ignore 84 85 86def load_log(logpath: Pathish) -> models.Log: 87 """Return a `loggi.models.Log` object for the log file at `logpath`.""" 88 return models.Log.load_log(Pathier(logpath)) 89 90 91# |===================================================| 92# Backwards compatibility with previous loggi versions 93# |===================================================| 94 95 96def get_logpaths(logger: Logger | logging.Logger) -> list[Pathier]: 97 """Loop through the handlers for `logger` and return a list of paths for any handler of type `FileHandler`.""" 98 return [ 99 Pathier(handler.baseFilename) 100 for handler in logger.handlers 101 if isinstance(handler, logging.FileHandler) 102 ] 103 104 105def get_logpath(logger: Logger | logging.Logger) -> Pathier | None: 106 """Search `logger.handlers` for a `FileHandler` that has a file stem matching `logger.name`. 107 108 Returns `None` if not found.""" 109 for path in get_logpaths(logger): 110 if path.stem == logger.name: 111 return path 112 113 114def get_log(logger: Logger | logging.Logger) -> models.Log | None: 115 """Find the corresponding log file for `logger`, load it into a `models.Log` instance, and then return it. 116 117 Returns `None` if a log file can't be found.""" 118 path = get_logpath(logger) 119 if path: 120 return load_log(path) 121 122 123def close(logger: Logger | logging.Logger): 124 """Removes and closes handlers for `logger`.""" 125 for handler in logger.handlers: 126 logger.removeHandler(handler) 127 handler.close()
11class Logger(logging.Logger): 12 @property 13 def logpath(self) -> Pathier | None: 14 """Return a file handler path whose stem matches `self.name`, if there is one.""" 15 for path in self.logpaths: 16 if path.stem == self.name: 17 return path 18 19 @property 20 def logpaths(self) -> list[Pathier]: 21 """A list of `Pathier` objects for any file handlers attached to this `Logger`.""" 22 return [ 23 Pathier(handler.baseFilename) 24 for handler in self.handlers 25 if isinstance(handler, logging.FileHandler) 26 ] 27 28 def close(self): 29 """Remove and close this logger's handlers.""" 30 for handler in self.handlers: 31 self.removeHandler(handler) 32 handler.close() 33 34 def get_log(self) -> models.Log | None: 35 """Returns a `models.Log` object populated from this logger's `logpath`.""" 36 if path := self.logpath: 37 return models.Log.load_log(path) 38 39 def logprint(self, message: str, level: str | int = "INFO"): 40 """Log and print `message`. 41 42 Only prints if the logger is enabled for the give `level`.""" 43 getattr( 44 self, 45 (level if isinstance(level, str) else logging.getLevelName(level)).lower(), 46 )(message) 47 if self.isEnabledFor( 48 level if isinstance(level, int) else logging.getLevelName(level) 49 ): 50 print(message)
Instances of the Logger class represent a single logging channel. A "logging channel" indicates an area of an application. Exactly how an "area" is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of "input processing" might include sub-areas "read CSV files", "read XLS files" and "read Gnumeric files"). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods, much like the Java or Python package namespace. So in the instance given above, channel names might be "input" for the upper level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. There is no arbitrary limit to the depth of nesting.
Return a file handler path whose stem matches self.name
, if there is one.
A list of Pathier
objects for any file handlers attached to this Logger
.
28 def close(self): 29 """Remove and close this logger's handlers.""" 30 for handler in self.handlers: 31 self.removeHandler(handler) 32 handler.close()
Remove and close this logger's handlers.
34 def get_log(self) -> models.Log | None: 35 """Returns a `models.Log` object populated from this logger's `logpath`.""" 36 if path := self.logpath: 37 return models.Log.load_log(path)
Returns a models.Log
object populated from this logger's logpath
.
39 def logprint(self, message: str, level: str | int = "INFO"): 40 """Log and print `message`. 41 42 Only prints if the logger is enabled for the give `level`.""" 43 getattr( 44 self, 45 (level if isinstance(level, str) else logging.getLevelName(level)).lower(), 46 )(message) 47 if self.isEnabledFor( 48 level if isinstance(level, int) else logging.getLevelName(level) 49 ): 50 print(message)
Log and print message
.
Only prints if the logger is enabled for the give level
.
Inherited Members
- logging.Logger
- Logger
- setLevel
- debug
- info
- warning
- warn
- error
- exception
- critical
- fatal
- log
- findCaller
- makeRecord
- handle
- addHandler
- removeHandler
- hasHandlers
- callHandlers
- getEffectiveLevel
- isEnabledFor
- getChild
- logging.Filterer
- addFilter
- removeFilter
- filter
53def getLogger(name: str, path: Pathish = Pathier.cwd()) -> Logger: 54 """Get a configured `loggi.Logger` instance for `name` with a file handler. 55 56 The log file will be located in `path` at `path/{name}.log`. 57 58 Default level is `INFO`. 59 60 Logs are in the format: `{levelname}|-|{asctime}|-|{message} 61 62 asctime is formatted as `%x %X`""" 63 path = Pathier(path) 64 path.mkdir() 65 # make sure loggi.Logger is the current logger class 66 logging.setLoggerClass(Logger) 67 logger = logging.Logger.manager.getLogger(name) 68 # TODO: Add option for a stream handler 69 # Add file handler using `logger.name` 70 logpath = path / f"{logger.name}.log" 71 handler = logging.FileHandler(logpath, encoding="utf-8") 72 if handler.baseFilename not in [ 73 existing_handler.baseFilename 74 for existing_handler in logger.handlers 75 if isinstance(existing_handler, logging.FileHandler) 76 ]: 77 handler.setFormatter( 78 logging.Formatter( 79 "{levelname}|-|{asctime}|-|{message}", style="{", datefmt="%x %X" 80 ) 81 ) 82 logger.addHandler(handler) 83 logger.setLevel(logging.INFO) 84 return logger # type: ignore
Get a configured loggi.Logger
instance for name
with a file handler.
The log file will be located in path
at path/{name}.log
.
Default level is INFO
.
Logs are in the format: `{levelname}|-|{asctime}|-|{message}
asctime is formatted as %x %X
87def load_log(logpath: Pathish) -> models.Log: 88 """Return a `loggi.models.Log` object for the log file at `logpath`.""" 89 return models.Log.load_log(Pathier(logpath))
Return a loggi.models.Log
object for the log file at logpath
.
97def get_logpaths(logger: Logger | logging.Logger) -> list[Pathier]: 98 """Loop through the handlers for `logger` and return a list of paths for any handler of type `FileHandler`.""" 99 return [ 100 Pathier(handler.baseFilename) 101 for handler in logger.handlers 102 if isinstance(handler, logging.FileHandler) 103 ]
Loop through the handlers for logger
and return a list of paths for any handler of type FileHandler
.
106def get_logpath(logger: Logger | logging.Logger) -> Pathier | None: 107 """Search `logger.handlers` for a `FileHandler` that has a file stem matching `logger.name`. 108 109 Returns `None` if not found.""" 110 for path in get_logpaths(logger): 111 if path.stem == logger.name: 112 return path
Search logger.handlers
for a FileHandler
that has a file stem matching logger.name
.
Returns None
if not found.
115def get_log(logger: Logger | logging.Logger) -> models.Log | None: 116 """Find the corresponding log file for `logger`, load it into a `models.Log` instance, and then return it. 117 118 Returns `None` if a log file can't be found.""" 119 path = get_logpath(logger) 120 if path: 121 return load_log(path)
Find the corresponding log file for logger
, load it into a models.Log
instance, and then return it.
Returns None
if a log file can't be found.
124def close(logger: Logger | logging.Logger): 125 """Removes and closes handlers for `logger`.""" 126 for handler in logger.handlers: 127 logger.removeHandler(handler) 128 handler.close()
Removes and closes handlers for logger
.