Source code for jgdv.logging.logger

  1#!/usr/bin/env python3
  2"""
  3
  4"""
  5# Import:
  6from __future__ import annotations
  7
  8# ##-- stdlib imports
  9import datetime
 10import enum
 11import functools as ftz
 12import itertools as itz
 13import logging as logmod
 14import pathlib as pl
 15import re
 16import time
 17import weakref
 18from uuid import UUID, uuid1
 19# ##-- end stdlib imports
 20
 21from ._interface import LogLevel_e
 22
 23# ##-- types
 24# isort: off
 25# General
 26import abc
 27import collections.abc
 28import typing
 29import types
 30from typing import cast, assert_type, assert_never
 31from typing import Generic, NewType, Never
 32from typing import no_type_check, final, override, overload
 33# Protocols and Interfaces:
 34from typing import Protocol, runtime_checkable
 35# isort: on
 36# ##-- end types
 37
 38# ##-- type checking
 39# isort: off
 40if typing.TYPE_CHECKING:
 41    from typing import Final, ClassVar, Any, Self
 42    from typing import Literal, LiteralString
 43    from typing import TypeGuard
 44    from collections.abc import Iterable, Iterator, Callable, Generator
 45    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 46
 47    from jgdv import Maybe
 48## isort: on
 49# ##-- end type checking
 50
 51##-- logging
 52logging = logmod.getLogger(__name__)
 53##-- end logging
 54
 55# Vars:
 56LoggerClass : Final[type] = logmod.getLoggerClass()
 57# Body:
 58
[docs] 59class JGDVLogger(LoggerClass): 60 """ Basic extension of the logger class 61 62 checks the classvar _levels (intEnum) for additional log levels 63 which can be accessed as attributes and items. 64 eg: logger.trace(...) 65 and: logger['trace'](...) 66 67 68 A Logger can add prefixes to a logged messages. 69 eg:_ 70 71 ..code: python 72 73 logger.set_prefixes('[Test]') 74 logger.info('this is a test message') 75 # Result : '[Test] this is a test message' 76 77 """ 78 _prefixes : list[str|Callable] 79 _colour : Maybe[str] 80
[docs] 81 @classmethod 82 def install(cls) -> None: 83 logmod.setLoggerClass(cls)
84 85 def __init__(self, *args:Any, **kwargs:Any) -> None: # noqa: ANN401 86 super().__init__(*args, **kwargs) 87 self._prefixes = [] 88 self._colour = None 89 90 def __getattr__(self, attr:str) -> Callable: 91 try: 92 return ftz.partial(self.log, LogLevel_e[attr]) 93 except KeyError: 94 msg = "Invalid Extension Log Level" 95 raise AttributeError(msg, attr) from None 96 97 def __getitem__(self, key:str) -> Callable: 98 return self.__getattr__(key) 99 100 ##--| public methods
[docs] 101 def set_colour(self, colour:Maybe[str]) -> None: 102 self._colour = self._colour or colour
103
[docs] 104 def set_prefixes(self, *prefixes:str|Callable) -> None: 105 """ 106 Set prefixes for the logger to add to logged messages 107 """ 108 match prefixes: 109 case None | (): 110 pass 111 case [*xs]: 112 self._prefixes = list(xs)
113
[docs] 114 def prefix(self, prefix:str|Callable) -> Self: 115 """ Create a new logger, with a prefix """ 116 match prefix: 117 case str(): 118 child = self.getChild(prefix) 119 case x if callable(x): 120 child = self.getChild(prefix.__name__) 121 case _: 122 raise TypeError(prefix) 123 124 child.set_prefixes(*self._prefixes, prefix) 125 return child
126
[docs] 127 def getChild(self, name:str) -> JGDVLogger: # noqa: N802 128 """ 129 Create a child logger, copying the colour of this logger 130 """ 131 child = cast("JGDVLogger", super().getChild(name)) 132 child.set_colour(self._colour) 133 return child
134
[docs] 135 def makeRecord(self, *args:Any, **kwargs:Any) -> logmod.LogRecord: # noqa: ANN401, N802 136 """ 137 A factory method which can be overridden in subclasses to create 138 specialized LogRecords. 139 args: name, level, fn, lno, msg, args, exc_info, 140 kwargs: func=None, extra=None, sinfo=None 141 """ 142 rv : logmod.LogRecord 143 modified : list = list(args) 144 msg_total = [] 145 for pre in self._prefixes: 146 match pre: 147 case None: 148 pass 149 case str(): 150 msg_total.append(pre) 151 case x if callable(x): 152 msg_total.append(x()) 153 else: 154 match args[4]: 155 case str(): 156 msg_total.append(args[4]) 157 case x: 158 msg_total.append("%s") 159 modified[5] = [args[4], *args[5]] 160 modified[4] = "".join(msg_total) 161 162 rv = super().makeRecord(*modified, **kwargs) 163 if self._colour and "colour" not in rv.__dict__: 164 rv.__dict__["colour"] = self._colour 165 return rv