logger-36 2023.4__py3-none-any.whl → 2023.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- logger_36/main.py +74 -47
- logger_36/test.py +50 -0
- logger_36/version.py +1 -1
- {logger_36-2023.4.dist-info → logger_36-2023.6.dist-info}/METADATA +1 -1
- logger_36-2023.6.dist-info/RECORD +8 -0
- logger_36-2023.4.dist-info/RECORD +0 -7
- {logger_36-2023.4.dist-info → logger_36-2023.6.dist-info}/WHEEL +0 -0
- {logger_36-2023.4.dist-info → logger_36-2023.6.dist-info}/top_level.txt +0 -0
logger_36/main.py
CHANGED
@@ -46,29 +46,27 @@ try:
|
|
46
46
|
from psutil import Process as process_t
|
47
47
|
|
48
48
|
_PROCESS = process_t()
|
49
|
-
|
50
|
-
|
51
|
-
def UsedMemoryInGb() -> float:
|
52
|
-
""""""
|
53
|
-
return round(_GIGA_SCALING * _PROCESS.memory_info().rss, 1)
|
54
|
-
|
49
|
+
_MEGA_SCALING = 1.0 / (1024.0**2)
|
50
|
+
_GIGA_SCALING = _MEGA_SCALING / 1024.0
|
55
51
|
except ModuleNotFoundError:
|
56
52
|
_PROCESS = None
|
57
|
-
|
58
|
-
def UsedMemoryInGb() -> float:
|
59
|
-
""""""
|
60
|
-
return 0.0
|
53
|
+
_MEGA_SCALING = _GIGA_SCALING = 0.0
|
61
54
|
|
62
55
|
|
63
56
|
# This module is certainly imported early. Therefore, the current time should be close enough to the real start time.
|
64
57
|
_START_TIME = time.time()
|
65
58
|
|
59
|
+
_TERMINAL_WIDTH = 1000
|
60
|
+
_TAB_SIZE = 5
|
66
61
|
|
62
|
+
_WHERE_SEPARATOR = "@"
|
63
|
+
# Tab size is set by console_t so that the dash is aligned for all log levels (might not work for file handlers though)
|
67
64
|
_MESSAGE_FORMAT = (
|
68
|
-
"%(asctime)s[%(levelname)s]\t- "
|
69
|
-
"%(message)s
|
70
|
-
"%(module)s:%(funcName)s:%(lineno)d"
|
65
|
+
f"%(asctime)s[%(levelname)s]\t- "
|
66
|
+
f"%(message)s {_WHERE_SEPARATOR} "
|
67
|
+
f"%(module)s:%(funcName)s:%(lineno)d"
|
71
68
|
)
|
69
|
+
_NEXT_LINE_PROLOGUE = "\n" + (27 + _TAB_SIZE) * " "
|
72
70
|
_DATE_FORMAT = "%Y-%m-%d@%H:%M:%S"
|
73
71
|
|
74
72
|
_SYSTEM_DETAILS = (
|
@@ -93,15 +91,52 @@ _SYSTEM_DETAILS = {_dtl.capitalize(): getattr(pltf, _dtl)() for _dtl in _SYSTEM_
|
|
93
91
|
_MAX_DETAIL_NAME_LENGTH = max(map(len, _SYSTEM_DETAILS.keys()))
|
94
92
|
|
95
93
|
|
96
|
-
#
|
97
|
-
|
94
|
+
# Alternative implementations: using logging.Filter or logging.LoggerAdapter
|
95
|
+
|
96
|
+
|
98
97
|
class _handler_extension:
|
99
|
-
|
100
|
-
|
98
|
+
__slots__ = ("formatter", "show_memory_usage")
|
99
|
+
formatter: lggg.Formatter
|
100
|
+
show_memory_usage: bool
|
101
101
|
|
102
102
|
def __init__(self) -> None:
|
103
103
|
""""""
|
104
104
|
self.formatter = lggg.Formatter(fmt=_MESSAGE_FORMAT, datefmt=_DATE_FORMAT)
|
105
|
+
self.show_memory_usage = False
|
106
|
+
|
107
|
+
def FormattedMessage(
|
108
|
+
self, record: lggg.LogRecord, /
|
109
|
+
) -> tuple[str, list[str] | None]:
|
110
|
+
"""
|
111
|
+
Note: "message" is not yet an attribute of record (it will be set by format()); Use "msg" instead.
|
112
|
+
"""
|
113
|
+
if not isinstance(record.msg, str):
|
114
|
+
record.msg = str(record.msg)
|
115
|
+
if "\n" in record.msg:
|
116
|
+
original_message = record.msg
|
117
|
+
lines = original_message.splitlines()
|
118
|
+
record.msg = lines[0]
|
119
|
+
else:
|
120
|
+
original_message = lines = None
|
121
|
+
|
122
|
+
formatted = self.formatter.format(record)
|
123
|
+
|
124
|
+
# Revert the record message to its original value for subsequent handlers
|
125
|
+
if original_message is not None:
|
126
|
+
record.msg = original_message
|
127
|
+
|
128
|
+
return formatted, lines
|
129
|
+
|
130
|
+
def MemoryUsage(self) -> str:
|
131
|
+
""""""
|
132
|
+
if self.show_memory_usage:
|
133
|
+
usage = _PROCESS.memory_info().rss
|
134
|
+
if usage > _GIGA_SCALING:
|
135
|
+
return f" :{round(_GIGA_SCALING * usage, 1)}GB"
|
136
|
+
else:
|
137
|
+
return f" :{round(_MEGA_SCALING * usage, 1)}MB"
|
138
|
+
|
139
|
+
return ""
|
105
140
|
|
106
141
|
|
107
142
|
class console_handler_t(lggg.Handler, _handler_extension):
|
@@ -116,7 +151,7 @@ class console_handler_t(lggg.Handler, _handler_extension):
|
|
116
151
|
_ACTUAL: ClassVar[str] = r" Actual=[^.]+\."
|
117
152
|
_EXPECTED: ClassVar[str] = r" Expected([!<>]=|: )[^.]+\."
|
118
153
|
|
119
|
-
console: console_t
|
154
|
+
console: console_t
|
120
155
|
|
121
156
|
def __init__(self, /, *, level=lggg.NOTSET) -> None:
|
122
157
|
""""""
|
@@ -125,33 +160,28 @@ class console_handler_t(lggg.Handler, _handler_extension):
|
|
125
160
|
|
126
161
|
self.setFormatter(self.formatter)
|
127
162
|
self.console = console_t(
|
128
|
-
record=True, force_terminal=True, width=
|
163
|
+
record=True, force_terminal=True, width=_TERMINAL_WIDTH, tab_size=_TAB_SIZE
|
129
164
|
)
|
130
165
|
|
131
166
|
def emit(self, record: lggg.LogRecord, /) -> None:
|
132
167
|
""""""
|
133
168
|
cls = self.__class__
|
134
169
|
|
135
|
-
formatted = self.
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
highlighted.append(f" +{ElapsedTime()}{memory_usage}\n", style="green")
|
144
|
-
highlighted.append(" " + "\n ".join(lines[1:]))
|
145
|
-
else:
|
146
|
-
highlighted = text_t(formatted)
|
147
|
-
highlighted.append(f" +{ElapsedTime()}{memory_usage}", style="green")
|
170
|
+
formatted, lines = self.FormattedMessage(record)
|
171
|
+
|
172
|
+
highlighted = text_t(formatted)
|
173
|
+
highlighted.append(f" +{ElapsedTime()}{self.MemoryUsage()}", style="green")
|
174
|
+
if lines is not None:
|
175
|
+
highlighted.append(
|
176
|
+
_NEXT_LINE_PROLOGUE + _NEXT_LINE_PROLOGUE.join(lines[1:])
|
177
|
+
)
|
148
178
|
|
149
179
|
highlighted.stylize("dodger_blue2", end=19)
|
150
180
|
highlighted.highlight_words(
|
151
181
|
(f"[{record.levelname}]",), style=cls._LEVEL_COLOR[record.levelno]
|
152
182
|
)
|
153
183
|
highlighted.highlight_regex(
|
154
|
-
f"
|
184
|
+
f"{_WHERE_SEPARATOR} {record.module}:{record.funcName}:{record.lineno}",
|
155
185
|
style=cls._GRAY_STYLE,
|
156
186
|
)
|
157
187
|
highlighted.highlight_regex(cls._ACTUAL, style="red")
|
@@ -161,26 +191,23 @@ class console_handler_t(lggg.Handler, _handler_extension):
|
|
161
191
|
|
162
192
|
|
163
193
|
class file_handler_t(lggg.FileHandler, _handler_extension):
|
164
|
-
def __init__(self, file: str, *args, **kwargs) -> None:
|
194
|
+
def __init__(self, file: str | path_t, *args, **kwargs) -> None:
|
165
195
|
""""""
|
166
|
-
lggg.FileHandler.__init__(self, file, *args, **kwargs)
|
196
|
+
lggg.FileHandler.__init__(self, str(file), *args, **kwargs)
|
167
197
|
_handler_extension.__init__(self)
|
168
198
|
|
169
199
|
self.setFormatter(self.formatter)
|
170
200
|
|
171
201
|
def emit(self, record: lggg.LogRecord, /) -> None:
|
172
202
|
""""""
|
173
|
-
formatted = self.
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
if "\n" in formatted:
|
179
|
-
lines = formatted.splitlines()
|
180
|
-
next_lines = "\n ".join(lines[1:])
|
181
|
-
message = f"{lines[0]} +{ElapsedTime()}{memory_usage}\n {next_lines}"
|
203
|
+
formatted, lines = self.FormattedMessage(record)
|
204
|
+
formatted = formatted.replace("\t", (9 - record.levelname.__len__()) * " ")
|
205
|
+
|
206
|
+
if lines is None:
|
207
|
+
message = f"{formatted} +{ElapsedTime()}{self.MemoryUsage()}"
|
182
208
|
else:
|
183
|
-
|
209
|
+
next_lines = _NEXT_LINE_PROLOGUE.join(lines[1:])
|
210
|
+
message = f"{formatted} +{ElapsedTime()}{self.MemoryUsage()}{_NEXT_LINE_PROLOGUE}{next_lines}"
|
184
211
|
|
185
212
|
print(message, file=self.stream)
|
186
213
|
self.stream.flush()
|
@@ -213,7 +240,7 @@ def AddFileHandler(file: Union[str, path_t], /) -> None:
|
|
213
240
|
def SaveLOGasHTML(file: Union[str, path_t], /) -> None:
|
214
241
|
""""""
|
215
242
|
if file.exists():
|
216
|
-
|
243
|
+
print(f'Cannot save logging record as HTML: File "{file}" already exists')
|
217
244
|
|
218
245
|
console = None
|
219
246
|
found = False
|
@@ -225,7 +252,7 @@ def SaveLOGasHTML(file: Union[str, path_t], /) -> None:
|
|
225
252
|
with open(file, "w") as accessor:
|
226
253
|
accessor.write(console.export_html())
|
227
254
|
|
228
|
-
|
255
|
+
print("Cannot save logging record as HTML: No handler has a RICH console")
|
229
256
|
|
230
257
|
|
231
258
|
def WhereFunction(function: Any, /) -> str:
|
logger_36/test.py
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright CNRS/Inria/UCA
|
2
|
+
# Contributor(s): Eric Debreuve (since 2023)
|
3
|
+
#
|
4
|
+
# eric.debreuve@cnrs.fr
|
5
|
+
#
|
6
|
+
# This software is governed by the CeCILL license under French law and
|
7
|
+
# abiding by the rules of distribution of free software. You can use,
|
8
|
+
# modify and/ or redistribute the software under the terms of the CeCILL
|
9
|
+
# license as circulated by CEA, CNRS and INRIA at the following URL
|
10
|
+
# "http://www.cecill.info".
|
11
|
+
#
|
12
|
+
# As a counterpart to the access to the source code and rights to copy,
|
13
|
+
# modify and redistribute granted by the license, users are provided only
|
14
|
+
# with a limited warranty and the software's author, the holder of the
|
15
|
+
# economic rights, and the successive licensors have only limited
|
16
|
+
# liability.
|
17
|
+
#
|
18
|
+
# In this respect, the user's attention is drawn to the risks associated
|
19
|
+
# with loading, using, modifying and/or developing or reproducing the
|
20
|
+
# software by the user in light of its specific status of free software,
|
21
|
+
# that may mean that it is complicated to manipulate, and that also
|
22
|
+
# therefore means that it is reserved for developers and experienced
|
23
|
+
# professionals having in-depth computer knowledge. Users are therefore
|
24
|
+
# encouraged to load and test the software's suitability as regards their
|
25
|
+
# requirements in conditions enabling the security of their systems and/or
|
26
|
+
# data to be ensured and, more generally, to use and operate it in the
|
27
|
+
# same conditions as regards security.
|
28
|
+
#
|
29
|
+
# The fact that you are presently reading this means that you have had
|
30
|
+
# knowledge of the CeCILL license and that you accept its terms.
|
31
|
+
|
32
|
+
from pathlib import Path as path_t
|
33
|
+
from tempfile import TemporaryDirectory
|
34
|
+
|
35
|
+
from logger_36 import LOGGER, AddFileHandler
|
36
|
+
|
37
|
+
|
38
|
+
with TemporaryDirectory() as tmp_folder:
|
39
|
+
tmp_folder = path_t(tmp_folder)
|
40
|
+
tmp_file = tmp_folder / "log.txt"
|
41
|
+
|
42
|
+
AddFileHandler(tmp_file)
|
43
|
+
|
44
|
+
for level in ("debug", "info", "warning", "error", "critical"):
|
45
|
+
LogMessage = getattr(LOGGER, level)
|
46
|
+
LogMessage(f"{level.capitalize()} message")
|
47
|
+
LogMessage(f"Multi-line\n{level.capitalize()}\nmessage")
|
48
|
+
|
49
|
+
content = open(tmp_file, "r").read()
|
50
|
+
print(f"\n{content}")
|
logger_36/version.py
CHANGED
@@ -0,0 +1,8 @@
|
|
1
|
+
logger_36/__init__.py,sha256=hoDitcbdJeHgQYTpkAnfYepCxUxkP6lbxPbHkHas3AU,1739
|
2
|
+
logger_36/main.py,sha256=gMML8vC0QmYItGvzrFIQxYohHMTANVdji5Y3FgW2GJQ,10257
|
3
|
+
logger_36/test.py,sha256=8bh0i-HZaZVImf66M0F-0R-T_PI6gYlPxO44KbCjI5s,2118
|
4
|
+
logger_36/version.py,sha256=r36L0uTNUpNnKoBVQqEARtyQO1bzYXBVu9Z2Af_Lnjg,1575
|
5
|
+
logger_36-2023.6.dist-info/METADATA,sha256=aosgF4UGoXIRGGc5XodWc0CKGtciMbpqpIlG0TaviBs,4241
|
6
|
+
logger_36-2023.6.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
7
|
+
logger_36-2023.6.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
|
8
|
+
logger_36-2023.6.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
logger_36/__init__.py,sha256=hoDitcbdJeHgQYTpkAnfYepCxUxkP6lbxPbHkHas3AU,1739
|
2
|
-
logger_36/main.py,sha256=3B9rupYmD9uetFeHzb_dk8l_7sx51-in1-yYbV3abpg,9227
|
3
|
-
logger_36/version.py,sha256=mwuHINlUSknBMxwdd964m2zq-4spq-ceuSw63CAiS0A,1575
|
4
|
-
logger_36-2023.4.dist-info/METADATA,sha256=tUdw-rpD5BJ3194-Uh337d3MccZwVFJ9F3Mf08BaTJU,4241
|
5
|
-
logger_36-2023.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
6
|
-
logger_36-2023.4.dist-info/top_level.txt,sha256=sM95BTMWmslEEgR_1pzwZsOeSp8C_QBiu8ImbFr0XLc,10
|
7
|
-
logger_36-2023.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|