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        for handler in self.handlers:
 29            self.removeHandler(handler)
 30            handler.close()
 31
 32    def get_log(self) -> models.Log | None:
 33        """Returns a `models.Log` object populated from this logger's `logpath`."""
 34        if path := self.logpath:
 35            return models.Log.load_log(path)
 36
 37    def logprint(self, message: str, level: str | int = "INFO"):
 38        getattr(
 39            self,
 40            (level if isinstance(level, str) else logging.getLevelName(level)).lower(),
 41        )(message)
 42        if self.isEnabledFor(
 43            level if isinstance(level, int) else logging.getLevelName(level)
 44        ):
 45            print(message)
 46
 47
 48def getLogger(name: str, path: Pathish = Pathier.cwd()) -> Logger:
 49    """Get a configured `loggi.Logger` instance for `name` with a file handler.
 50
 51    The log file will be located in `path` at `path/{name}.log`.
 52
 53    Default level is `INFO`.
 54
 55    Logs are in the format: `{levelname}|-|{asctime}|-|{message}
 56
 57    asctime is formatted as `%x %X`"""
 58    path = Pathier(path)
 59    path.mkdir()
 60    # make sure loggi.Logger is the current logger class
 61    logging.setLoggerClass(Logger)
 62    logger = logging.Logger.manager.getLogger(name)
 63    # TODO: Add option for a stream handler
 64    # Add file handler using `logger.name`
 65    logpath = path / f"{logger.name}.log"
 66    handler = logging.FileHandler(logpath, encoding="utf-8")
 67    if handler.baseFilename not in [
 68        existing_handler.baseFilename
 69        for existing_handler in logger.handlers
 70        if isinstance(existing_handler, logging.FileHandler)
 71    ]:
 72        handler.setFormatter(
 73            logging.Formatter(
 74                "{levelname}|-|{asctime}|-|{message}", style="{", datefmt="%x %X"
 75            )
 76        )
 77        logger.addHandler(handler)
 78    logger.setLevel(logging.INFO)
 79    return logger  # type: ignore
 80
 81
 82def load_log(logpath: Pathish) -> models.Log:
 83    """Return a `loggi.models.Log` object for the log file at `logpath`."""
 84    return models.Log.load_log(Pathier(logpath))
 85
 86
 87# |===================================================|
 88# Backwards compatibility with previous loggi versions
 89# |===================================================|
 90
 91
 92def get_logpaths(logger: Logger | logging.Logger) -> list[Pathier]:
 93    """Loop through the handlers for `logger` and return a list of paths for any handler of type `FileHandler`."""
 94    return [
 95        Pathier(handler.baseFilename)
 96        for handler in logger.handlers
 97        if isinstance(handler, logging.FileHandler)
 98    ]
 99
100
101def get_logpath(logger: Logger | logging.Logger) -> Pathier | None:
102    """Search `logger.handlers` for a `FileHandler` that has a file stem matching `logger.name`.
103
104    Returns `None` if not found."""
105    for path in get_logpaths(logger):
106        if path.stem == logger.name:
107            return path
108
109
110def get_log(logger: Logger | logging.Logger) -> models.Log | None:
111    """Find the corresponding log file for `logger`, load it into a `models.Log` instance, and then return it.
112
113    Returns `None` if a log file can't be found."""
114    path = get_logpath(logger)
115    if path:
116        return load_log(path)
117
118
119def close(logger: Logger | logging.Logger):
120    """Removes and closes handlers for `logger`."""
121    for handler in logger.handlers:
122        logger.removeHandler(handler)
123        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        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        getattr(
40            self,
41            (level if isinstance(level, str) else logging.getLevelName(level)).lower(),
42        )(message)
43        if self.isEnabledFor(
44            level if isinstance(level, int) else logging.getLevelName(level)
45        ):
46            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        for handler in self.handlers:
30            self.removeHandler(handler)
31            handler.close()
def get_log(self) -> loggi.models.Log | None:
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)

Returns a models.Log object populated from this logger's logpath.

def logprint(self, message: str, level: str | int = 'INFO'):
38    def logprint(self, message: str, level: str | int = "INFO"):
39        getattr(
40            self,
41            (level if isinstance(level, str) else logging.getLevelName(level)).lower(),
42        )(message)
43        if self.isEnabledFor(
44            level if isinstance(level, int) else logging.getLevelName(level)
45        ):
46            print(message)
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:
49def getLogger(name: str, path: Pathish = Pathier.cwd()) -> Logger:
50    """Get a configured `loggi.Logger` instance for `name` with a file handler.
51
52    The log file will be located in `path` at `path/{name}.log`.
53
54    Default level is `INFO`.
55
56    Logs are in the format: `{levelname}|-|{asctime}|-|{message}
57
58    asctime is formatted as `%x %X`"""
59    path = Pathier(path)
60    path.mkdir()
61    # make sure loggi.Logger is the current logger class
62    logging.setLoggerClass(Logger)
63    logger = logging.Logger.manager.getLogger(name)
64    # TODO: Add option for a stream handler
65    # Add file handler using `logger.name`
66    logpath = path / f"{logger.name}.log"
67    handler = logging.FileHandler(logpath, encoding="utf-8")
68    if handler.baseFilename not in [
69        existing_handler.baseFilename
70        for existing_handler in logger.handlers
71        if isinstance(existing_handler, logging.FileHandler)
72    ]:
73        handler.setFormatter(
74            logging.Formatter(
75                "{levelname}|-|{asctime}|-|{message}", style="{", datefmt="%x %X"
76            )
77        )
78        logger.addHandler(handler)
79    logger.setLevel(logging.INFO)
80    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:
83def load_log(logpath: Pathish) -> models.Log:
84    """Return a `loggi.models.Log` object for the log file at `logpath`."""
85    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]:
93def get_logpaths(logger: Logger | logging.Logger) -> list[Pathier]:
94    """Loop through the handlers for `logger` and return a list of paths for any handler of type `FileHandler`."""
95    return [
96        Pathier(handler.baseFilename)
97        for handler in logger.handlers
98        if isinstance(handler, logging.FileHandler)
99    ]

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:
102def get_logpath(logger: Logger | logging.Logger) -> Pathier | None:
103    """Search `logger.handlers` for a `FileHandler` that has a file stem matching `logger.name`.
104
105    Returns `None` if not found."""
106    for path in get_logpaths(logger):
107        if path.stem == logger.name:
108            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:
111def get_log(logger: Logger | logging.Logger) -> models.Log | None:
112    """Find the corresponding log file for `logger`, load it into a `models.Log` instance, and then return it.
113
114    Returns `None` if a log file can't be found."""
115    path = get_logpath(logger)
116    if path:
117        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):
120def close(logger: Logger | logging.Logger):
121    """Removes and closes handlers for `logger`."""
122    for handler in logger.handlers:
123        logger.removeHandler(handler)
124        handler.close()

Removes and closes handlers for logger.