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