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()
class Logger(logging.Logger):
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.

logpath: pathier.pathier.Pathier | None

Return a file handler path whose stem matches self.name, if there is one.

logpaths: list[pathier.pathier.Pathier]

A list of Pathier objects for any file handlers attached to this Logger.

def close(self):
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.

def get_log(self) -> loggi.models.Log | None:
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.

def logprint(self, message: str, level: str | int = 'INFO'):
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
def getLogger( name: str, path: pathier.pathier.Pathier | pathlib.Path | str = WindowsPath('E:/1vsCode/python/loggi')) -> loggi.logger.Logger:
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

def load_log( logpath: pathier.pathier.Pathier | pathlib.Path | str) -> loggi.models.Log:
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.

def get_logpaths( logger: loggi.logger.Logger | logging.Logger) -> list[pathier.pathier.Pathier]:
 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.

def get_logpath( logger: loggi.logger.Logger | logging.Logger) -> pathier.pathier.Pathier | None:
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.

def get_log(logger: loggi.logger.Logger | logging.Logger) -> loggi.models.Log | None:
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.

def close(logger: loggi.logger.Logger | logging.Logger):
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.