logger-36 2025.21__py3-none-any.whl → 2025.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logger_36/catalog/config/optional.py +10 -1
- logger_36/catalog/handler/console.py +1 -1
- logger_36/catalog/handler/console_rich.py +2 -2
- logger_36/catalog/handler/file.py +1 -1
- logger_36/catalog/handler/generic.py +7 -7
- logger_36/catalog/handler/memory.py +98 -0
- logger_36/constant/record.py +3 -1
- logger_36/task/storage.py +52 -20
- logger_36/type/handler.py +7 -7
- logger_36/type/logger.py +89 -108
- logger_36/version.py +1 -1
- {logger_36-2025.21.dist-info → logger_36-2025.23.dist-info}/METADATA +1 -1
- {logger_36-2025.21.dist-info → logger_36-2025.23.dist-info}/RECORD +15 -14
- {logger_36-2025.21.dist-info → logger_36-2025.23.dist-info}/WHEEL +0 -0
- {logger_36-2025.21.dist-info → logger_36-2025.23.dist-info}/top_level.txt +0 -0
@@ -8,11 +8,20 @@ try:
|
|
8
8
|
import rich # noqa
|
9
9
|
except ModuleNotFoundError:
|
10
10
|
RICH_IS_AVAILABLE = False
|
11
|
-
from logger_36.constant.error import MISSING_RICH_MESSAGE
|
11
|
+
from logger_36.constant.error import MISSING_RICH_MESSAGE # noqa
|
12
12
|
else:
|
13
13
|
RICH_IS_AVAILABLE = True
|
14
14
|
MISSING_RICH_MESSAGE = None
|
15
15
|
|
16
|
+
from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
|
17
|
+
|
18
|
+
if CanCheckMemoryUsage():
|
19
|
+
MEMORY_MEASURE_IS_AVAILABLE = True
|
20
|
+
MEMORY_MEASURE_ERROR = None
|
21
|
+
else:
|
22
|
+
MEMORY_MEASURE_IS_AVAILABLE = False
|
23
|
+
from logger_36.constant.error import MEMORY_MEASURE_ERROR # noqa
|
24
|
+
|
16
25
|
"""
|
17
26
|
COPYRIGHT NOTICE
|
18
27
|
|
@@ -96,7 +96,7 @@ class console_rich_handler_t(base_t):
|
|
96
96
|
""""""
|
97
97
|
return cls(name, message_width, level, kwargs)
|
98
98
|
|
99
|
-
def Rule(self, text: str | None
|
99
|
+
def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | rule_t:
|
100
100
|
""""""
|
101
101
|
if text is None:
|
102
102
|
return rule_t(style=color)
|
@@ -116,7 +116,7 @@ class console_rich_handler_t(base_t):
|
|
116
116
|
self.console.print(message, crop=False, overflow="ignore")
|
117
117
|
self._log_parity = not self._log_parity
|
118
118
|
|
119
|
-
def
|
119
|
+
def EmitMessage(self, message: str | renderable_t, /) -> None:
|
120
120
|
""""""
|
121
121
|
self.console.print(message, crop=False, overflow="ignore")
|
122
122
|
|
@@ -32,7 +32,7 @@ class file_handler_t(base_t):
|
|
32
32
|
self.stream.write(output[0] + "\n")
|
33
33
|
self.stream.flush()
|
34
34
|
|
35
|
-
def
|
35
|
+
def EmitMessage(self, message: str, /) -> None:
|
36
36
|
""""""
|
37
37
|
self.stream.write(message + "\n")
|
38
38
|
self.stream.flush()
|
@@ -38,10 +38,10 @@ class generic_handler_t(base_t):
|
|
38
38
|
self, name: str | None, message_width: int, level: int, kwargs
|
39
39
|
) -> None:
|
40
40
|
"""
|
41
|
-
|
41
|
+
EmitMessage: By definition, the generic handler does not know how to output
|
42
42
|
messages. If not passed, it defaults to output-ing messages in the console.
|
43
43
|
"""
|
44
|
-
|
44
|
+
EmitMessage = kwargs.pop(base_t.EmitMessage.__name__, None)
|
45
45
|
alternating_logs = kwargs.pop("alternating_logs", 0)
|
46
46
|
supports_html = kwargs.pop("supports_html", False)
|
47
47
|
|
@@ -49,8 +49,8 @@ class generic_handler_t(base_t):
|
|
49
49
|
|
50
50
|
base_t.__init__(self, name, message_width, None, level, kwargs)
|
51
51
|
|
52
|
-
if
|
53
|
-
self.
|
52
|
+
if EmitMessage is not None:
|
53
|
+
self.EmitMessage = EmitMessage
|
54
54
|
self.is_rich = False
|
55
55
|
self.console = None # console_t | None.
|
56
56
|
self.console_options = None # rich.console.ConsoleOptions | None.
|
@@ -82,14 +82,14 @@ class generic_handler_t(base_t):
|
|
82
82
|
""""""
|
83
83
|
return cls(name, message_width, level, kwargs)
|
84
84
|
|
85
|
-
def Rule(self, text: str | None
|
85
|
+
def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | rule_t:
|
86
86
|
""""""
|
87
87
|
if self.is_rich:
|
88
88
|
if text is None:
|
89
89
|
return rule_t(style=color)
|
90
90
|
return rule_t(title=text_t(text, style=f"bold {color}"), style=color)
|
91
91
|
|
92
|
-
return base_t.Rule(self, text, color=color)
|
92
|
+
return base_t.Rule(self, text=text, color=color)
|
93
93
|
|
94
94
|
def emit(self, record: l.LogRecord, /) -> None:
|
95
95
|
""""""
|
@@ -131,7 +131,7 @@ class generic_handler_t(base_t):
|
|
131
131
|
else:
|
132
132
|
message = self.MessageFromRecord(record)[0]
|
133
133
|
|
134
|
-
self.
|
134
|
+
self.EmitMessage(message)
|
135
135
|
self._log_parity = not self._log_parity
|
136
136
|
|
137
137
|
|
@@ -0,0 +1,98 @@
|
|
1
|
+
"""
|
2
|
+
Copyright CNRS (https://www.cnrs.fr/index.php/en)
|
3
|
+
Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
|
4
|
+
SEE COPYRIGHT NOTICE BELOW
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
import logging as l
|
9
|
+
import typing as h
|
10
|
+
|
11
|
+
from logger_36.type.handler import handler_t as base_t
|
12
|
+
|
13
|
+
formats_h = h.Literal["json", "message", "raw"]
|
14
|
+
|
15
|
+
|
16
|
+
class memory_handler_t(base_t):
|
17
|
+
def __init__(
|
18
|
+
self, name: str | None, message_width: int, level: int, format_: formats_h
|
19
|
+
) -> None:
|
20
|
+
""""""
|
21
|
+
assert format_ in h.get_args(formats_h)
|
22
|
+
|
23
|
+
base_t.__init__(self, name, message_width, None, level)
|
24
|
+
|
25
|
+
self.format_ = format_
|
26
|
+
self.records = []
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def New(
|
30
|
+
cls,
|
31
|
+
/,
|
32
|
+
*,
|
33
|
+
name: str | None = None,
|
34
|
+
message_width: int = -1,
|
35
|
+
level: int = l.NOTSET,
|
36
|
+
format_: formats_h = "message",
|
37
|
+
**_,
|
38
|
+
) -> h.Self:
|
39
|
+
""""""
|
40
|
+
return cls(name, message_width, level, format_)
|
41
|
+
|
42
|
+
def emit(self, record: l.LogRecord, /) -> None:
|
43
|
+
""""""
|
44
|
+
level = record.levelno
|
45
|
+
|
46
|
+
if self.format_ == "raw":
|
47
|
+
is_not_a_rule = True
|
48
|
+
elif self.format_ == "message":
|
49
|
+
record, is_not_a_rule = self.MessageFromRecord(record)
|
50
|
+
else:
|
51
|
+
record = json.dumps(record.__dict__)
|
52
|
+
is_not_a_rule = True
|
53
|
+
|
54
|
+
self.records.append((level, record, is_not_a_rule))
|
55
|
+
|
56
|
+
def EmitMessage(self, message: str, /) -> None:
|
57
|
+
""""""
|
58
|
+
if self.format_ == "message":
|
59
|
+
message_key = "msg"
|
60
|
+
else:
|
61
|
+
message_key = "message"
|
62
|
+
record = l.makeLogRecord(
|
63
|
+
{"name": "<UNKNOWN LOGGER>", "levelno": l.INFO, message_key: message}
|
64
|
+
)
|
65
|
+
self.emit(record)
|
66
|
+
|
67
|
+
|
68
|
+
"""
|
69
|
+
COPYRIGHT NOTICE
|
70
|
+
|
71
|
+
This software is governed by the CeCILL license under French law and
|
72
|
+
abiding by the rules of distribution of free software. You can use,
|
73
|
+
modify and/ or redistribute the software under the terms of the CeCILL
|
74
|
+
license as circulated by CEA, CNRS and INRIA at the following URL
|
75
|
+
"http://www.cecill.info".
|
76
|
+
|
77
|
+
As a counterpart to the access to the source code and rights to copy,
|
78
|
+
modify and redistribute granted by the license, users are provided only
|
79
|
+
with a limited warranty and the software's author, the holder of the
|
80
|
+
economic rights, and the successive licensors have only limited
|
81
|
+
liability.
|
82
|
+
|
83
|
+
In this respect, the user's attention is drawn to the risks associated
|
84
|
+
with loading, using, modifying and/or developing or reproducing the
|
85
|
+
software by the user in light of its specific status of free software,
|
86
|
+
that may mean that it is complicated to manipulate, and that also
|
87
|
+
therefore means that it is reserved for developers and experienced
|
88
|
+
professionals having in-depth computer knowledge. Users are therefore
|
89
|
+
encouraged to load and test the software's suitability as regards their
|
90
|
+
requirements in conditions enabling the security of their systems and/or
|
91
|
+
data to be ensured and, more generally, to use and operate it in the
|
92
|
+
same conditions as regards security.
|
93
|
+
|
94
|
+
The fact that you are presently reading this means that you have had
|
95
|
+
knowledge of the CeCILL license and that you accept its terms.
|
96
|
+
|
97
|
+
SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
|
98
|
+
"""
|
logger_36/constant/record.py
CHANGED
@@ -4,8 +4,10 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
|
|
4
4
|
SEE COPYRIGHT NOTICE BELOW
|
5
5
|
"""
|
6
6
|
|
7
|
-
SHOW_W_RULE_ATTR = "should_show_w_rule"
|
8
7
|
SHOW_WHERE_ATTR = "should_show_where"
|
8
|
+
SHOW_W_RULE_ATTR = "should_show_w_rule"
|
9
|
+
WHEN_OR_ELAPSED_ATTR = "when_or_elapsed"
|
10
|
+
WHERE_ATTR = "where"
|
9
11
|
|
10
12
|
"""
|
11
13
|
COPYRIGHT NOTICE
|
logger_36/task/storage.py
CHANGED
@@ -10,25 +10,28 @@ from pathlib import Path as path_t
|
|
10
10
|
|
11
11
|
from logger_36.constant.html import BODY_PLACEHOLDER, MINIMAL_HTML, TITLE_PLACEHOLDER
|
12
12
|
from logger_36.instance.logger import L
|
13
|
+
from logger_36.type.logger import logger_t
|
13
14
|
|
14
15
|
|
15
|
-
def SaveLOGasHTML(
|
16
|
+
def SaveLOGasHTML(
|
17
|
+
path: str | path_t | io_base_t | None = None, /, *, logger: logger_t = L
|
18
|
+
) -> None:
|
16
19
|
"""
|
17
20
|
From first console handler found.
|
18
21
|
"""
|
19
22
|
cannot_save = "Cannot save logging record as HTML"
|
20
23
|
|
21
24
|
if path is None:
|
22
|
-
for handler in
|
25
|
+
for handler in logger.handlers:
|
23
26
|
if isinstance(handler, l.FileHandler):
|
24
27
|
path = path_t(handler.baseFilename).with_suffix(".htm")
|
25
28
|
break
|
26
29
|
else:
|
27
|
-
|
30
|
+
logger.warning(f"{cannot_save}: No file handler to build a filename from.")
|
28
31
|
return
|
29
32
|
|
30
33
|
if path.exists():
|
31
|
-
|
34
|
+
logger.warning(
|
32
35
|
f'{cannot_save}: Automatically generated path "{path}" already exists.'
|
33
36
|
)
|
34
37
|
return
|
@@ -38,13 +41,38 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
|
|
38
41
|
actual_file = isinstance(path, path_t)
|
39
42
|
|
40
43
|
if actual_file and path.exists():
|
41
|
-
|
44
|
+
logger.warning(f'{cannot_save}: File "{path}" already exists.')
|
42
45
|
return
|
43
46
|
|
44
|
-
|
45
|
-
|
47
|
+
for handler in logger.handlers:
|
48
|
+
records = getattr(handler, "records", None)
|
49
|
+
if isinstance(records, list) and (
|
50
|
+
(records.__len__() == 0)
|
51
|
+
or all(
|
52
|
+
isinstance(_, tuple)
|
53
|
+
and isinstance(_[0], int)
|
54
|
+
and isinstance(_[1], dict | str | l.LogRecord)
|
55
|
+
and isinstance(_[2], bool)
|
56
|
+
for _ in records
|
57
|
+
)
|
58
|
+
):
|
59
|
+
break
|
60
|
+
else:
|
61
|
+
logger.warning(f"{cannot_save}: No handlers with recording capability found.")
|
62
|
+
return
|
63
|
+
|
64
|
+
if records.__len__() == 0:
|
65
|
+
return
|
66
|
+
|
67
|
+
if isinstance(records[0][1], str):
|
68
|
+
records = map(_HighlightedEvent, records)
|
69
|
+
else:
|
70
|
+
records = map(lambda _: str(_[1]), records)
|
71
|
+
body = "\n".join(records)
|
72
|
+
html = MINIMAL_HTML.replace(TITLE_PLACEHOLDER, logger.name).replace(
|
46
73
|
BODY_PLACEHOLDER, body
|
47
74
|
)
|
75
|
+
|
48
76
|
if actual_file:
|
49
77
|
with open(path, "w") as accessor:
|
50
78
|
accessor.write(html)
|
@@ -52,21 +80,25 @@ def SaveLOGasHTML(path: str | path_t | io_base_t | None = None) -> None:
|
|
52
80
|
path.write(html)
|
53
81
|
|
54
82
|
|
55
|
-
def _HighlightedEvent(event: tuple[int, str], /) -> str:
|
83
|
+
def _HighlightedEvent(event: tuple[int, str, bool], /) -> str:
|
56
84
|
""""""
|
57
|
-
level, message = event
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
85
|
+
level, message, is_not_a_rule = event
|
86
|
+
|
87
|
+
if is_not_a_rule:
|
88
|
+
if level == l.DEBUG:
|
89
|
+
color = "BlueViolet"
|
90
|
+
elif level == l.INFO:
|
91
|
+
color = "black"
|
92
|
+
elif level == l.WARNING:
|
93
|
+
color = "gold"
|
94
|
+
elif level == l.ERROR:
|
95
|
+
color = "orange"
|
96
|
+
elif level == l.CRITICAL:
|
97
|
+
color = "red"
|
98
|
+
else:
|
99
|
+
color = "black"
|
68
100
|
else:
|
69
|
-
color = "
|
101
|
+
color = "DarkTurquoise"
|
70
102
|
|
71
103
|
return f'<span style="color:{color}">{message}</span>'
|
72
104
|
|
logger_36/type/handler.py
CHANGED
@@ -16,7 +16,7 @@ from logger_36.config.message import (
|
|
16
16
|
WHERE_SEPARATOR,
|
17
17
|
)
|
18
18
|
from logger_36.constant.message import NEXT_LINE_PROLOGUE
|
19
|
-
from logger_36.constant.record import SHOW_W_RULE_ATTR
|
19
|
+
from logger_36.constant.record import SHOW_W_RULE_ATTR, WHEN_OR_ELAPSED_ATTR, WHERE_ATTR
|
20
20
|
from logger_36.constant.rule import (
|
21
21
|
DEFAULT_RULE,
|
22
22
|
DEFAULT_RULE_LENGTH,
|
@@ -66,7 +66,7 @@ class extension_t:
|
|
66
66
|
message = self.PreProcessedMessage(message)
|
67
67
|
|
68
68
|
if hasattr(record, SHOW_W_RULE_ATTR):
|
69
|
-
return self.Rule(message, color=rule_color), False
|
69
|
+
return self.Rule(text=message, color=rule_color), False
|
70
70
|
|
71
71
|
if (self.message_width <= 0) or (message.__len__() <= self.message_width):
|
72
72
|
if "\n" in message:
|
@@ -78,11 +78,11 @@ class extension_t:
|
|
78
78
|
lines = WrappedLines([message], self.message_width)
|
79
79
|
message = NEXT_LINE_PROLOGUE.join(lines)
|
80
80
|
|
81
|
-
when_or_elapsed = getattr(record,
|
81
|
+
when_or_elapsed = getattr(record, WHEN_OR_ELAPSED_ATTR, None)
|
82
82
|
if when_or_elapsed is None:
|
83
83
|
return message, True
|
84
84
|
|
85
|
-
if (where := getattr(record,
|
85
|
+
if (where := getattr(record, WHERE_ATTR, None)) is None:
|
86
86
|
where = ""
|
87
87
|
else:
|
88
88
|
where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
|
@@ -93,7 +93,7 @@ class extension_t:
|
|
93
93
|
f"{MESSAGE_MARKER} {message}{where}"
|
94
94
|
), True
|
95
95
|
|
96
|
-
def Rule(self, text: str | None
|
96
|
+
def Rule(self, /, *, text: str | None = None, color: str = "black") -> str | h.Any:
|
97
97
|
"""
|
98
98
|
Return type hint h.Any: For Rich, for example.
|
99
99
|
"""
|
@@ -113,13 +113,13 @@ class extension_t:
|
|
113
113
|
|
114
114
|
return f"{half_rule} {text} {half_rule}"
|
115
115
|
|
116
|
-
def
|
116
|
+
def EmitMessage(self, message: str, /) -> None:
|
117
117
|
""""""
|
118
118
|
raise NotImplementedError
|
119
119
|
|
120
120
|
def EmitRule(self, /, *, text: str | None = None, color: str = "black") -> None:
|
121
121
|
""""""
|
122
|
-
self.
|
122
|
+
self.EmitMessage(self.Rule(text=text, color=color))
|
123
123
|
|
124
124
|
|
125
125
|
class handler_t(l.Handler, extension_t):
|
logger_36/type/logger.py
CHANGED
@@ -19,7 +19,12 @@ from os import sep as FOLDER_SEPARATOR
|
|
19
19
|
from pathlib import Path as path_t
|
20
20
|
from traceback import TracebackException as traceback_t
|
21
21
|
|
22
|
-
from logger_36.catalog.config.optional import
|
22
|
+
from logger_36.catalog.config.optional import (
|
23
|
+
MEMORY_MEASURE_ERROR,
|
24
|
+
MEMORY_MEASURE_IS_AVAILABLE,
|
25
|
+
MISSING_RICH_MESSAGE,
|
26
|
+
RICH_IS_AVAILABLE,
|
27
|
+
)
|
23
28
|
from logger_36.catalog.handler.console import console_handler_t
|
24
29
|
from logger_36.catalog.handler.file import file_handler_t
|
25
30
|
from logger_36.config.issue import ISSUE_CONTEXT_END, ISSUE_CONTEXT_SEPARATOR
|
@@ -30,7 +35,6 @@ from logger_36.config.message import (
|
|
30
35
|
TIME_FORMAT,
|
31
36
|
WHERE_SEPARATOR,
|
32
37
|
)
|
33
|
-
from logger_36.constant.error import MEMORY_MEASURE_ERROR
|
34
38
|
from logger_36.constant.generic import NOT_PASSED
|
35
39
|
from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR, ORDER, order_h
|
36
40
|
from logger_36.constant.logger import WARNING_LOGGER_NAME, WARNING_TYPE_COMPILED_PATTERN
|
@@ -40,7 +44,6 @@ from logger_36.constant.path import PROJECT_FILE_RELATIVE, USER_FOLDER
|
|
40
44
|
from logger_36.constant.record import SHOW_W_RULE_ATTR, SHOW_WHERE_ATTR
|
41
45
|
from logger_36.task.format.message import MessageWithActualExpected
|
42
46
|
from logger_36.task.measure.chronos import ElapsedTime
|
43
|
-
from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
|
44
47
|
from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
|
45
48
|
from logger_36.type.handler import any_handler_t as base_handler_t
|
46
49
|
from logger_36.type.handler import extension_t as handler_extension_t
|
@@ -61,7 +64,6 @@ logger_handle_h = logger_handle_raw_h | logger_handle_with_self_h
|
|
61
64
|
|
62
65
|
_DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
|
63
66
|
_DATE_ORIGIN = _DATE_TIME_ORIGIN.date()
|
64
|
-
_MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
|
65
67
|
|
66
68
|
|
67
69
|
@d.dataclass(slots=True, repr=False, eq=False)
|
@@ -73,20 +75,15 @@ class logger_t(base_t):
|
|
73
75
|
_should_activate_log_interceptions: Loggers instantiated after a logger_t logger
|
74
76
|
will be missed by an early call of ToggleLogInterceptions. Therefore, passing True
|
75
77
|
for activate_log_interceptions only sets _should_activate_log_interceptions to True,
|
76
|
-
which is later checked in AddHandler to effectively call ToggleLogInterceptions
|
77
|
-
_should_hold_messages is False (which normally indicates that the handler about to
|
78
|
-
be added is the last one).
|
79
|
-
|
80
|
-
_should_hold_messages: Must not be False until at least one handler has been added.
|
78
|
+
which is later checked in AddHandler to effectively call ToggleLogInterceptions.
|
81
79
|
"""
|
82
80
|
|
83
|
-
should_record_messages: bool = False
|
84
|
-
should_watch_memory_usage: bool = False
|
85
81
|
exit_on_error: bool = False # Implies exit_on_critical.
|
86
82
|
exit_on_critical: bool = False
|
83
|
+
should_monitor_memory_usage: bool = False
|
87
84
|
|
88
|
-
|
89
|
-
|
85
|
+
history: dict[date_time_t, str] = d.field(init=False, default_factory=dict)
|
86
|
+
n_events: dict[int, int] = d.field(init=False, default_factory=dict)
|
90
87
|
|
91
88
|
last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
|
92
89
|
last_message_date: date_t = d.field(init=False, default=_DATE_ORIGIN)
|
@@ -98,10 +95,9 @@ class logger_t(base_t):
|
|
98
95
|
init=False, default_factory=dict
|
99
96
|
)
|
100
97
|
intercepts_exceptions: bool = d.field(init=False, default=False)
|
101
|
-
|
98
|
+
|
99
|
+
# Used only until the last handler is added (see AddHandler).
|
102
100
|
_should_activate_log_interceptions: bool = d.field(init=False, default=False)
|
103
|
-
_on_hold: list[l.LogRecord] = d.field(init=False, default_factory=list)
|
104
|
-
_recording_handler: handler_extension_t | None = d.field(init=False, default=None)
|
105
101
|
|
106
102
|
name_: d.InitVar[str | None] = None
|
107
103
|
level_: d.InitVar[int] = l.NOTSET
|
@@ -156,8 +152,6 @@ class logger_t(base_t):
|
|
156
152
|
activate_exc_interceptions: bool,
|
157
153
|
) -> None:
|
158
154
|
""""""
|
159
|
-
global _MEMORY_MEASURE_ERROR
|
160
|
-
|
161
155
|
if name_ is None:
|
162
156
|
name_ = f"{type(self).__name__}:{hex(id(self))[2:]}"
|
163
157
|
|
@@ -169,12 +163,7 @@ class logger_t(base_t):
|
|
169
163
|
self.exit_on_critical = True
|
170
164
|
|
171
165
|
for level_id in l.getLevelNamesMapping().values():
|
172
|
-
self.
|
173
|
-
|
174
|
-
if self.should_record_messages:
|
175
|
-
self.ActivateMessageRecording()
|
176
|
-
|
177
|
-
self.info(f'New logger "{self.name}" for "{PROJECT_FILE_RELATIVE}"')
|
166
|
+
self.n_events[level_id] = 0
|
178
167
|
|
179
168
|
if activate_wrn_interceptions:
|
180
169
|
self.ToggleWarningInterceptions(True)
|
@@ -183,11 +172,12 @@ class logger_t(base_t):
|
|
183
172
|
if activate_exc_interceptions:
|
184
173
|
self.ToggleExceptionInterceptions(True)
|
185
174
|
|
186
|
-
if self.
|
187
|
-
self.
|
188
|
-
|
189
|
-
|
190
|
-
|
175
|
+
if self.should_monitor_memory_usage:
|
176
|
+
self.ActivateMemoryUsageMonitoring()
|
177
|
+
|
178
|
+
self.history[date_time_t.now()] = (
|
179
|
+
f'Logger "{self.name}" instantiation for "{PROJECT_FILE_RELATIVE}"'
|
180
|
+
)
|
191
181
|
|
192
182
|
def handle(self, record: l.LogRecord, /) -> None:
|
193
183
|
""""""
|
@@ -206,19 +196,8 @@ class logger_t(base_t):
|
|
206
196
|
|
207
197
|
# Where.
|
208
198
|
should_show_where = getattr(record, SHOW_WHERE_ATTR, record.levelno != l.INFO)
|
209
|
-
if should_show_where or self.
|
210
|
-
|
211
|
-
for path in s.path:
|
212
|
-
if module.is_relative_to(path):
|
213
|
-
module = module.relative_to(path).with_suffix("")
|
214
|
-
module = str(module).replace(FOLDER_SEPARATOR, ".")
|
215
|
-
break
|
216
|
-
else:
|
217
|
-
if module.is_relative_to(USER_FOLDER):
|
218
|
-
module = module.relative_to(USER_FOLDER)
|
219
|
-
where = f"{module}:{record.funcName}:{record.lineno}"
|
220
|
-
if should_show_where:
|
221
|
-
record.where = where
|
199
|
+
if should_show_where or self.should_monitor_memory_usage:
|
200
|
+
where = _RecordLocation(record, should_show_where)
|
222
201
|
else:
|
223
202
|
where = None
|
224
203
|
|
@@ -226,13 +205,10 @@ class logger_t(base_t):
|
|
226
205
|
if not isinstance(record.msg, str):
|
227
206
|
record.msg = str(record.msg)
|
228
207
|
|
229
|
-
|
230
|
-
|
231
|
-
else:
|
232
|
-
self._HandleRecord(record)
|
233
|
-
self.events[record.levelno] += 1
|
208
|
+
base_t.handle(self, record)
|
209
|
+
self.n_events[record.levelno] += 1
|
234
210
|
|
235
|
-
if self.
|
211
|
+
if self.should_monitor_memory_usage:
|
236
212
|
self.memory_usages.append((where, CurrentMemoryUsage()))
|
237
213
|
|
238
214
|
if (self.exit_on_critical and (record.levelno is l.CRITICAL)) or (
|
@@ -242,13 +218,6 @@ class logger_t(base_t):
|
|
242
218
|
# __post_init__ set self.exit_on_critical if self.exit_on_error.
|
243
219
|
s.exit(1)
|
244
220
|
|
245
|
-
def _FlushRecordsOnHold(self) -> None:
|
246
|
-
""""""
|
247
|
-
for held in self._on_hold:
|
248
|
-
self._HandleRecord(held)
|
249
|
-
|
250
|
-
self._on_hold.clear()
|
251
|
-
|
252
221
|
def _AcknowledgeDateChange(self, date: date_t, /) -> None:
|
253
222
|
""""""
|
254
223
|
self.last_message_date = date
|
@@ -261,30 +230,12 @@ class logger_t(base_t):
|
|
261
230
|
SHOW_W_RULE_ATTR: True,
|
262
231
|
}
|
263
232
|
)
|
264
|
-
|
265
|
-
if self._should_hold_messages:
|
266
|
-
self._on_hold.append(record)
|
267
|
-
else:
|
268
|
-
self._HandleRecord(record)
|
269
|
-
|
270
|
-
def _HandleRecord(self, record: l.LogRecord, /) -> None:
|
271
|
-
""""""
|
272
|
-
if self.should_record_messages:
|
273
|
-
message = self._recording_handler.MessageFromRecord(record)[0]
|
274
|
-
self.recorded.append((record.levelno, message))
|
275
|
-
|
276
233
|
base_t.handle(self, record)
|
277
234
|
|
278
|
-
def ActivateMessageRecording(self) -> None:
|
279
|
-
""""""
|
280
|
-
self._recording_handler = handler_extension_t("recording_handler", 0, None)
|
281
|
-
self.should_record_messages = True # Useless if called from __post_init__.
|
282
|
-
self.info(f'Message recording activated for logger "{self.name}"')
|
283
|
-
|
284
235
|
def ResetEventCounts(self) -> None:
|
285
236
|
""""""
|
286
|
-
for level_id in self.
|
287
|
-
self.
|
237
|
+
for level_id in self.n_events:
|
238
|
+
self.n_events[level_id] = 0
|
288
239
|
|
289
240
|
def ToggleWarningInterceptions(self, state: bool, /) -> None:
|
290
241
|
""""""
|
@@ -297,7 +248,7 @@ class logger_t(base_t):
|
|
297
248
|
logger.handle = t.MethodType(_HandleForWarnings(self), logger)
|
298
249
|
|
299
250
|
l.captureWarnings(True)
|
300
|
-
self.
|
251
|
+
self.history[date_time_t.now()] = "Warning Interception: ON"
|
301
252
|
else:
|
302
253
|
if not self.intercepts_warnings:
|
303
254
|
return
|
@@ -307,12 +258,12 @@ class logger_t(base_t):
|
|
307
258
|
self.intercepted_wrn_handle = None
|
308
259
|
|
309
260
|
l.captureWarnings(False)
|
310
|
-
self.
|
261
|
+
self.history[date_time_t.now()] = "Warning Interception: OFF"
|
311
262
|
|
312
263
|
def ToggleLogInterceptions(self, state: bool, /) -> None:
|
313
264
|
""""""
|
314
265
|
if state:
|
315
|
-
if self.intercepts_logs:
|
266
|
+
if self._should_activate_log_interceptions or self.intercepts_logs:
|
316
267
|
return
|
317
268
|
|
318
269
|
# Note: Alternative to self.manager is logging.root.manager.
|
@@ -331,8 +282,14 @@ class logger_t(base_t):
|
|
331
282
|
intercepted = sorted(self.intercepted_log_handles.keys())
|
332
283
|
if intercepted.__len__() > 0:
|
333
284
|
as_str = ", ".join(intercepted)
|
334
|
-
self.
|
285
|
+
self.history[date_time_t.now()] = (
|
286
|
+
f"Now Intercepting LOGs from: {as_str}"
|
287
|
+
)
|
335
288
|
else:
|
289
|
+
if self._should_activate_log_interceptions:
|
290
|
+
self._should_activate_log_interceptions = False
|
291
|
+
return
|
292
|
+
|
336
293
|
if not self.intercepts_logs:
|
337
294
|
return
|
338
295
|
|
@@ -340,7 +297,7 @@ class logger_t(base_t):
|
|
340
297
|
logger = l.getLogger(name)
|
341
298
|
logger.handle = handle
|
342
299
|
self.intercepted_log_handles.clear()
|
343
|
-
self.
|
300
|
+
self.history[date_time_t.now()] = "Log Interception: OFF"
|
344
301
|
|
345
302
|
def ToggleExceptionInterceptions(self, state: bool, /) -> None:
|
346
303
|
""""""
|
@@ -351,7 +308,7 @@ class logger_t(base_t):
|
|
351
308
|
s.excepthook = self.DealWithException
|
352
309
|
thrd.excepthook = self.DealWithExceptionInThread
|
353
310
|
self.intercepts_exceptions = True
|
354
|
-
self.
|
311
|
+
self.history[date_time_t.now()] = "Exception Interception: ON"
|
355
312
|
else:
|
356
313
|
if not self.intercepts_exceptions:
|
357
314
|
return
|
@@ -359,7 +316,19 @@ class logger_t(base_t):
|
|
359
316
|
s.excepthook = s.__excepthook__
|
360
317
|
thrd.excepthook = thrd.__excepthook__
|
361
318
|
self.intercepts_exceptions = False
|
362
|
-
self.
|
319
|
+
self.history[date_time_t.now()] = "Exception Interception: OFF"
|
320
|
+
|
321
|
+
def ActivateMemoryUsageMonitoring(self) -> None:
|
322
|
+
""""""
|
323
|
+
if MEMORY_MEASURE_IS_AVAILABLE:
|
324
|
+
# Useless if called from __post_init__.
|
325
|
+
self.should_monitor_memory_usage = True
|
326
|
+
self.history[date_time_t.now()] = (
|
327
|
+
f'Memory usage monitoring activated for logger "{self.name}"'
|
328
|
+
)
|
329
|
+
else:
|
330
|
+
self.should_monitor_memory_usage = False
|
331
|
+
self.error(MEMORY_MEASURE_ERROR)
|
363
332
|
|
364
333
|
def AddHandler(
|
365
334
|
self,
|
@@ -372,22 +341,16 @@ class logger_t(base_t):
|
|
372
341
|
name: str | None = None,
|
373
342
|
level: int = l.INFO,
|
374
343
|
message_width: int = -1,
|
375
|
-
should_still_hold_messages: bool = False,
|
376
344
|
**kwargs,
|
377
345
|
) -> None:
|
378
346
|
"""
|
379
347
|
Silently ignores re-holding request after un-holding.
|
380
348
|
"""
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
self._should_activate_log_interceptions = False
|
387
|
-
|
388
|
-
self._should_hold_messages = False
|
389
|
-
should_flush_on_hold = True
|
390
|
-
new_handler_warning = "\n(Handlers added from now on will miss above logs.)"
|
349
|
+
if self._should_activate_log_interceptions:
|
350
|
+
# Turn _should_activate_log_interceptions off before calling
|
351
|
+
# ToggleLogInterceptions because it checks it.
|
352
|
+
self._should_activate_log_interceptions = False
|
353
|
+
self.ToggleLogInterceptions(True)
|
391
354
|
|
392
355
|
if isinstance(handler_t_or_handler, type):
|
393
356
|
handler = handler_t_or_handler.New(
|
@@ -397,17 +360,12 @@ class logger_t(base_t):
|
|
397
360
|
handler = handler_t_or_handler
|
398
361
|
base_t.addHandler(self, handler)
|
399
362
|
|
400
|
-
# Wait until after the handler has been added to flush messages on hold.
|
401
|
-
if should_flush_on_hold:
|
402
|
-
self._FlushRecordsOnHold()
|
403
|
-
|
404
363
|
path = getattr(handler, "baseFilename", "")
|
405
364
|
if isinstance(path, path_t) or (path.__len__() > 0):
|
406
365
|
path = f"\nPath: {path}"
|
407
|
-
self.
|
366
|
+
self.history[date_time_t.now()] = (
|
408
367
|
f'New handler "{handler.name}" of type "{type(handler).__name__}" and '
|
409
368
|
f"level {handler.level}={l.getLevelName(handler.level)}{path}"
|
410
|
-
f"{new_handler_warning}"
|
411
369
|
)
|
412
370
|
|
413
371
|
def MakeMonochrome(self) -> None:
|
@@ -416,13 +374,12 @@ class logger_t(base_t):
|
|
416
374
|
|
417
375
|
def MakeRich(self, *, alternating_logs: int = 0) -> None:
|
418
376
|
""""""
|
419
|
-
if
|
420
|
-
s.__stderr__.write(MISSING_RICH_MESSAGE + "\n")
|
421
|
-
|
422
|
-
if console_rich_handler_t is console_handler_t:
|
423
|
-
handler_kwargs = {}
|
424
|
-
else:
|
377
|
+
if RICH_IS_AVAILABLE:
|
425
378
|
handler_kwargs = {"alternating_logs": alternating_logs}
|
379
|
+
else:
|
380
|
+
handler_kwargs = {}
|
381
|
+
self.error(MISSING_RICH_MESSAGE)
|
382
|
+
|
426
383
|
self.AddHandler(console_rich_handler_t, **handler_kwargs)
|
427
384
|
|
428
385
|
def MakePermanent(self, path: str | path_t, /) -> None:
|
@@ -509,8 +466,11 @@ class logger_t(base_t):
|
|
509
466
|
message = text.indent(message, LINE_INDENT)
|
510
467
|
|
511
468
|
for handler in self.handlers:
|
512
|
-
|
513
|
-
|
469
|
+
EmitMessage = getattr(
|
470
|
+
handler, handler_extension_t.EmitMessage.__name__, None
|
471
|
+
)
|
472
|
+
if EmitMessage is not None:
|
473
|
+
EmitMessage(message)
|
514
474
|
|
515
475
|
info_raw = LogAsIs # To follow the convention of the logging methods info, error...
|
516
476
|
|
@@ -519,7 +479,8 @@ class logger_t(base_t):
|
|
519
479
|
) -> None:
|
520
480
|
""""""
|
521
481
|
for handler in self.handlers:
|
522
|
-
|
482
|
+
EmitRule = getattr(handler, handler_extension_t.EmitRule.__name__, None)
|
483
|
+
if EmitRule is not None:
|
523
484
|
EmitRule(text=message, color=color)
|
524
485
|
|
525
486
|
def AddContextLevel(self, new_level: str, /) -> None:
|
@@ -647,6 +608,26 @@ class logger_t(base_t):
|
|
647
608
|
return False
|
648
609
|
|
649
610
|
|
611
|
+
def _RecordLocation(record: l.LogRecord, should_also_store: bool, /) -> str:
|
612
|
+
""""""
|
613
|
+
module = path_t(record.pathname)
|
614
|
+
for path in s.path:
|
615
|
+
if module.is_relative_to(path):
|
616
|
+
module = module.relative_to(path).with_suffix("")
|
617
|
+
module = str(module).replace(FOLDER_SEPARATOR, ".")
|
618
|
+
break
|
619
|
+
else:
|
620
|
+
if module.is_relative_to(USER_FOLDER):
|
621
|
+
module = module.relative_to(USER_FOLDER)
|
622
|
+
|
623
|
+
output = f"{module}:{record.funcName}:{record.lineno}"
|
624
|
+
|
625
|
+
if should_also_store:
|
626
|
+
record.where = output
|
627
|
+
|
628
|
+
return output
|
629
|
+
|
630
|
+
|
650
631
|
def _HandleForWarnings(interceptor: base_t, /) -> logger_handle_h:
|
651
632
|
""""""
|
652
633
|
|
logger_36/version.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
logger_36/__init__.py,sha256=mK6AD0eWI2Sk42oxleTvsxzYJ28FbHK5WNkpLgAhnNE,2129
|
2
|
-
logger_36/version.py,sha256=
|
2
|
+
logger_36/version.py,sha256=3y8_8Te0bVUofCcWg9yoGDEx2HlTct92Ol2Pk3NTA8w,1680
|
3
3
|
logger_36/api/content.py,sha256=DuT4UX4r_1DTXzuuRD-tvsTZk5X-Nj11loBKhuWOMw0,1791
|
4
4
|
logger_36/api/gpu.py,sha256=NNs1IvQ7bh8Dppm8O8K2YLWbm4rogc3Ie_-D6xzkX3g,1726
|
5
5
|
logger_36/api/memory.py,sha256=vOY4cTTrC3u7L0OrKXdPNlsCahYjCrY4h7iqpGZv9kU,2217
|
@@ -8,11 +8,12 @@ logger_36/api/system.py,sha256=h-3GfhZPwawv0UKBWKkT1LzxSCZwpA2VIsy03lLYi6w,1725
|
|
8
8
|
logger_36/api/time.py,sha256=JG0vgzPSRZ7UWQyoihnVu4sjPC-okFIKA3ZyNh2GaZo,1798
|
9
9
|
logger_36/api/type.py,sha256=eLZ2yuH-sYeh4Z2KnAwTRJEbmkmgzBPMncdqXfFUTG8,1760
|
10
10
|
logger_36/catalog/config/console_rich.py,sha256=t9p9-AkSgPiLAsm1evAdbz77g7JcVLePhUJ1FzNi3cY,2330
|
11
|
-
logger_36/catalog/config/optional.py,sha256=
|
12
|
-
logger_36/catalog/handler/console.py,sha256=
|
13
|
-
logger_36/catalog/handler/console_rich.py,sha256=
|
14
|
-
logger_36/catalog/handler/file.py,sha256=
|
15
|
-
logger_36/catalog/handler/generic.py,sha256=
|
11
|
+
logger_36/catalog/config/optional.py,sha256=8d8HdpE07gHfsdoL8mVAlRlh9AgLcb4z7I7h6ob7CfU,2174
|
12
|
+
logger_36/catalog/handler/console.py,sha256=YEQN8fw7ra9w-5sJACNC4pktv9einm5ASP9TncU8TQo,2311
|
13
|
+
logger_36/catalog/handler/console_rich.py,sha256=gPpq8ij1ZFTXaLgISaCTXDDwv7uWPivLeq2ttpiuKlk,6190
|
14
|
+
logger_36/catalog/handler/file.py,sha256=yg8GnsV6AmDsx1R5iZlsK-5idqD733gc09Syl02TG7Y,2458
|
15
|
+
logger_36/catalog/handler/generic.py,sha256=L8mLc-iQlBWKBqEN3-wIKy2yk_TVgqIWYn_P1hXDsQA,6361
|
16
|
+
logger_36/catalog/handler/memory.py,sha256=-31hTXgGImC6if4wJF6eegvwYhsYAQsDTkdoXIUxZB8,3224
|
16
17
|
logger_36/catalog/logger/chronos.py,sha256=S4m9TMPQy_Ju500mpE1jNzu2gZG-QKdVuvX9RVRKHR8,1911
|
17
18
|
logger_36/catalog/logger/gpu.py,sha256=Py5YY0nD_pqJzJsEKQYoOGHcPqyNVJ3J2noOS3hDL6g,2890
|
18
19
|
logger_36/catalog/logger/memory.py,sha256=J0ZGKO7j1FZA_aDGxpABtvzDy1RjCDiDmWYh4U98fEI,4253
|
@@ -29,7 +30,7 @@ logger_36/constant/logger.py,sha256=ZQYX9JiPsoivwRgYNtdEqRKCagSKD88lRqvxP8MX1ZE,
|
|
29
30
|
logger_36/constant/memory.py,sha256=Q_E5tTWa-cGaNwrE_xmKa3BxQG6oJO6DHczrxc_M4sE,1817
|
30
31
|
logger_36/constant/message.py,sha256=YJOEzdI0ZjUOdHo3CsiS56FVPhrfNoQYvXuUkprH61g,2312
|
31
32
|
logger_36/constant/path.py,sha256=OfLh70Jyc8po9Ls34nQh_bRr3PXyQ3kF9ciR9QPhiqI,2213
|
32
|
-
logger_36/constant/record.py,sha256=
|
33
|
+
logger_36/constant/record.py,sha256=VMqGxVTbtHpRiDOsYiXEoyYOocYFm8vtcRY-7zXzJrk,1796
|
33
34
|
logger_36/constant/rule.py,sha256=tBKQgPTt6G_p5eInDdWoEEAvQFz4WMSt5THsS5jvk14,1779
|
34
35
|
logger_36/constant/system.py,sha256=pLlLXG5sepQlSUOo3TphaGrHg8xzJBp-GxpL2NPP47k,1904
|
35
36
|
logger_36/extension/html_.py,sha256=W9SyiYsaaYHUrHLGAAN2wiJGXUlwOBJ5gzdjmEcnF18,3342
|
@@ -37,16 +38,16 @@ logger_36/extension/line.py,sha256=joeojQX1bZJM53333mOEU3s-YC5ExGOrN9Cu9Lh5-FU,2
|
|
37
38
|
logger_36/instance/logger.py,sha256=X_U10RYU1h2Aa70D8hBnmFyJZtRILK16KN-GB4xkHMU,1782
|
38
39
|
logger_36/instance/loggers.py,sha256=inBk4KKrQ-z3szaopQ29-qQwh1iSc842sWo5J6zJoiM,1725
|
39
40
|
logger_36/task/inspection.py,sha256=ZgPcrPo3h_kEnM-UpHDLg7PAFfB2fqsLFdfmi6hlPVA,4484
|
40
|
-
logger_36/task/storage.py,sha256=
|
41
|
+
logger_36/task/storage.py,sha256=a50M628062iGEymuj5d9iKo7Ad01OXCZrEQnfNqy0gY,4486
|
41
42
|
logger_36/task/format/memory.py,sha256=J1Oy3jw8wjSp2kuiRUm_VFpzXOHX2FOc7nuRrCyrskw,3723
|
42
43
|
logger_36/task/format/message.py,sha256=Rm6zymVEEGcgKfmxMPXP7q3PtwZJKlXGhqZ5tnvlwxA,3502
|
43
44
|
logger_36/task/measure/chronos.py,sha256=7ijMZgP4EP18HbLV2yCxpNpRS9724Wyk523f-nkbhUM,2529
|
44
45
|
logger_36/task/measure/memory.py,sha256=kkPHEIUTUhkCOLrAt01eLJLnsnkl0nFPNhFZdIB_JAw,1991
|
45
|
-
logger_36/type/handler.py,sha256=
|
46
|
+
logger_36/type/handler.py,sha256=4LCFZzI4OEH6lVBrBQ09ZIvF0nq-_j7OOVnujBDyDWA,6665
|
46
47
|
logger_36/type/issue.py,sha256=QHAYf7QgrjJUtF2D46z6X630qTgeP_0FE5hIwf54RsE,2688
|
47
|
-
logger_36/type/logger.py,sha256
|
48
|
+
logger_36/type/logger.py,sha256=nh7EuJvdkaDrtdHPWIjot2a2hGfAzwufMWm9_2KoDXM,24645
|
48
49
|
logger_36/type/loggers.py,sha256=7EX7Sg_RlduBjdfFlNZmUfNeDloH1xU30Rdkg_-rXh8,3172
|
49
|
-
logger_36-2025.
|
50
|
-
logger_36-2025.
|
51
|
-
logger_36-2025.
|
52
|
-
logger_36-2025.
|
50
|
+
logger_36-2025.23.dist-info/METADATA,sha256=d94bUT9zjDr1Yjny2bsSobExZ1W0MXcIv2hX_DLLGC8,6529
|
51
|
+
logger_36-2025.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
52
|
+
logger_36-2025.23.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
|
53
|
+
logger_36-2025.23.dist-info/RECORD,,
|
File without changes
|
File without changes
|