logger-36 2024.28__py3-none-any.whl → 2024.30__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.
@@ -5,7 +5,7 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  from logger_36.constant.memory import STORAGE_UNITS, storage_units_h
8
- from logger_36.task.format.message import FormattedMessage
8
+ from logger_36.task.format.message import MessageWithActualExpected
9
9
 
10
10
  _KILO_UNIT = 1000.0
11
11
  _MEGA_UNIT = _KILO_UNIT * 1000.0
@@ -41,7 +41,7 @@ def FormattedUsage(
41
41
  value, unit = FormattedUsageWithAutoUnit(usage, decimals)
42
42
  else:
43
43
  raise ValueError(
44
- FormattedMessage(
44
+ MessageWithActualExpected(
45
45
  "Invalid unit", actual=unit, expected=str(STORAGE_UNITS)[1:-1]
46
46
  )
47
47
  )
@@ -7,35 +7,11 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import difflib as diff
8
8
  import typing as h
9
9
 
10
- from logger_36.config.message import (
11
- ELAPSED_TIME_FORMAT,
12
- LEVEL_CLOSING,
13
- LEVEL_OPENING,
14
- MEMORY_FORMAT,
15
- MESSAGE_MARKER,
16
- )
17
10
  from logger_36.constant.generic import NOT_PASSED
18
11
  from logger_36.constant.message import expected_op_h
19
12
 
20
13
 
21
- def MessageFormat(with_where: bool, with_memory_usage: bool, /) -> str:
22
- """"""
23
- output = [
24
- f"%(asctime)s"
25
- f"{LEVEL_OPENING}%(level_first_letter)s{LEVEL_CLOSING}\t"
26
- f"{MESSAGE_MARKER}%(message)s"
27
- ]
28
-
29
- if with_where:
30
- output.append("%(where)s")
31
- output.append(ELAPSED_TIME_FORMAT)
32
- if with_memory_usage:
33
- output.append(MEMORY_FORMAT)
34
-
35
- return "".join(output)
36
-
37
-
38
- def FormattedMessage(
14
+ def MessageWithActualExpected(
39
15
  message: str,
40
16
  /,
41
17
  *,
@@ -14,8 +14,8 @@ def RuleAsText(text: str | None, /) -> str:
14
14
 
15
15
 
16
16
  try:
17
- from rich.rule import Rule as rule_t
18
- from rich.text import Text as text_t
17
+ from rich.rule import Rule as rule_t # noqa
18
+ from rich.text import Text as text_t # noqa
19
19
 
20
20
  def Rule(text: str | None, color: str, /) -> rule_t | str:
21
21
  """"""
@@ -5,25 +5,35 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import time
8
- from datetime import datetime as dttm
8
+ from datetime import datetime as date_time_t
9
9
 
10
10
  # This module is imported early. Therefore, the current date and time should be close
11
11
  # enough to the real start time of the main script.
12
- _START_DATE_AND_TIME = dttm.now()
12
+ _START_DATE_AND_TIME = date_time_t.now()
13
13
 
14
14
 
15
15
  def TimeStamp(*, precision: str = "microseconds") -> str:
16
16
  """"""
17
- return dttm.now().isoformat(timespec=precision).replace(".", "-").replace(":", "-")
17
+ return (
18
+ date_time_t.now()
19
+ .isoformat(timespec=precision)
20
+ .replace(".", "-")
21
+ .replace(":", "-")
22
+ )
18
23
 
19
24
 
20
- def ElapsedTime() -> str:
25
+ def ElapsedTime(
26
+ *, should_return_now: bool = False
27
+ ) -> str | tuple[str, date_time_t.date]:
21
28
  """"""
22
- elapsed_seconds = (dttm.now() - _START_DATE_AND_TIME).total_seconds()
23
- output = time.strftime("%Hh %Mm %Ss", time.gmtime(elapsed_seconds))
24
- while output.startswith("00") and (" " in output):
25
- output = output.split(maxsplit=1)[-1]
26
-
29
+ now = date_time_t.now()
30
+ elapsed_seconds = (now - _START_DATE_AND_TIME).total_seconds()
31
+ output = time.strftime("%H:%M:%S", time.gmtime(elapsed_seconds))
32
+ while output.startswith("00:"):
33
+ output = output.split(sep=":", maxsplit=1)[-1]
34
+
35
+ if should_return_now:
36
+ return output, now
27
37
  return output
28
38
 
29
39
 
@@ -5,7 +5,7 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  try:
8
- from psutil import Process as process_t
8
+ from psutil import Process as process_t # noqa
9
9
 
10
10
  _PROCESS = process_t()
11
11
  except ModuleNotFoundError:
logger_36/task/storage.py CHANGED
@@ -12,7 +12,7 @@ from html.parser import HTMLParser as html_parser_t
12
12
  from pathlib import Path as path_t
13
13
 
14
14
  try:
15
- from rich.console import Console as console_t
15
+ from rich.console import Console as console_t # noqa
16
16
  except ModuleNotFoundError:
17
17
  console_t = None
18
18
 
logger_36/type/handler.py CHANGED
@@ -8,15 +8,17 @@ import dataclasses as d
8
8
  import logging as lggg
9
9
  import sys as sstm
10
10
  import typing as h
11
- from os import sep as FOLDER_SEPARATOR
12
- from pathlib import Path as path_t
13
11
 
14
- from logger_36.config.message import TIME_FORMAT, WHERE_FORMAT
12
+ from logger_36.config.message import (
13
+ LEVEL_CLOSING,
14
+ LEVEL_OPENING,
15
+ MESSAGE_MARKER,
16
+ WHERE_SEPARATOR,
17
+ )
15
18
  from logger_36.constant.error import MEMORY_MEASURE_ERROR
16
19
  from logger_36.constant.handler import HANDLER_CODES
17
20
  from logger_36.constant.message import NEXT_LINE_PROLOGUE
18
- from logger_36.constant.record import HIDE_WHERE_ATTR, SHOW_WHERE_ATTR
19
- from logger_36.task.format.message import FormattedMessage, MessageFormat
21
+ from logger_36.task.format.message import MessageWithActualExpected
20
22
  from logger_36.task.measure.chronos import TimeStamp
21
23
  from logger_36.task.measure.memory import CanCheckUsage as CanCheckMemoryUsage
22
24
 
@@ -26,10 +28,9 @@ _MEMORY_MEASURE_ERROR = MEMORY_MEASURE_ERROR
26
28
  @d.dataclass(slots=True, repr=False, eq=False)
27
29
  class handler_extension_t:
28
30
  name: str | None = None
29
- show_where: bool = True
30
- show_memory_usage: bool = False
31
+ should_store_memory_usage: bool = False
31
32
  message_width: int = -1
32
- FormattedRecord: h.Callable[[lggg.LogRecord], str] = d.field(init=False)
33
+ MessageFromRecord: h.Callable[..., str] = d.field(init=False)
33
34
 
34
35
  handler: d.InitVar[lggg.Handler | None] = None
35
36
  level: d.InitVar[int] = lggg.NOTSET
@@ -43,7 +44,7 @@ class handler_extension_t:
43
44
 
44
45
  if self.name in HANDLER_CODES:
45
46
  raise ValueError(
46
- FormattedMessage(
47
+ MessageWithActualExpected(
47
48
  "Invalid handler name",
48
49
  actual=self.name,
49
50
  expected=f"a name not in {str(HANDLER_CODES)[1:-1]}",
@@ -53,8 +54,8 @@ class handler_extension_t:
53
54
  if self.name is None:
54
55
  self.name = TimeStamp()
55
56
 
56
- if self.show_memory_usage and not CanCheckMemoryUsage():
57
- self.show_memory_usage = False
57
+ if self.should_store_memory_usage and not CanCheckMemoryUsage():
58
+ self.should_store_memory_usage = False
58
59
  if _MEMORY_MEASURE_ERROR is not None:
59
60
  print(_MEMORY_MEASURE_ERROR, file=sstm.stderr)
60
61
  _MEMORY_MEASURE_ERROR = None
@@ -64,85 +65,45 @@ class handler_extension_t:
64
65
  if 0 < self.message_width < 5:
65
66
  self.message_width = 5
66
67
  if formatter is None:
67
- message_format = MessageFormat(self.show_where, self.show_memory_usage)
68
- formatter = lggg.Formatter(fmt=message_format, datefmt=TIME_FORMAT)
69
- handler.setFormatter(formatter)
70
- self.FormattedRecord = handler.formatter.format
68
+ self.MessageFromRecord = self._MessageFromRecord
69
+ else:
70
+ handler.setFormatter(formatter)
71
+ self.MessageFromRecord = handler.formatter.format
71
72
 
72
- def FormattedLines(
73
+ def _MessageFromRecord(
73
74
  self,
74
75
  record: lggg.LogRecord,
75
76
  /,
76
77
  *,
77
78
  PreProcessed: h.Callable[[str], str] | None = None,
78
- should_join_lines: bool = False,
79
- ) -> tuple[str, str | None]:
79
+ ) -> str:
80
80
  """
81
81
  See logger_36.catalog.handler.README.txt.
82
82
  """
83
- record.level_first_letter = record.levelname[0]
84
-
85
83
  message = record.msg
86
- if not isinstance(message, str):
87
- message = str(message)
88
- original_message = message
89
84
 
90
85
  if PreProcessed is not None:
91
86
  message = PreProcessed(message)
92
- if (has_newlines := ("\n" in message)) or (
93
- (self.message_width > 0) and (message.__len__() > self.message_width)
94
- ):
95
- if has_newlines:
96
- lines = message.splitlines()
97
- if self.message_width > 0:
98
- lines = _WrappedLines(lines, self.message_width)
99
- else:
100
- lines = _WrappedLines([message], self.message_width)
101
- next_lines = NEXT_LINE_PROLOGUE.join(lines[1:])
102
- next_lines = f"{NEXT_LINE_PROLOGUE}{next_lines}"
103
- message = lines[0]
87
+ if (self.message_width <= 0) or (message.__len__() <= self.message_width):
88
+ if "\n" in message:
89
+ message = NEXT_LINE_PROLOGUE.join(message.splitlines())
104
90
  else:
105
- next_lines = None
106
- if self.message_width > 0:
107
- n_missing_s = self.message_width - message.__len__()
108
- if n_missing_s > 3:
109
- message += " " + (n_missing_s - 1) * "."
110
- elif n_missing_s > 0:
111
- message += n_missing_s * " "
112
-
113
- record.msg = message
114
- if self.show_where and not hasattr(record, SHOW_WHERE_ATTR):
115
- hide_where = getattr(record, HIDE_WHERE_ATTR, False)
116
- if hide_where:
117
- record.where = ""
91
+ if "\n" in message:
92
+ lines = _WrappedLines(message.splitlines(), self.message_width)
118
93
  else:
119
- module = path_t(record.pathname)
120
- path_was_found = False
121
- for path in sstm.path:
122
- if module.is_relative_to(path):
123
- module = module.relative_to(path)
124
- path_was_found = True
125
- break
126
- if path_was_found:
127
- module = str(module.parent / module.stem)
128
- module = module.replace(FOLDER_SEPARATOR, ".")
129
- else:
130
- module = record.module
131
- record.where = WHERE_FORMAT.format(
132
- module=module, funcName=record.funcName, lineno=record.lineno
133
- )
134
- first_line = self.FormattedRecord(record).replace("\t", " ")
135
-
136
- # Revert the record message to its original value for subsequent handlers.
137
- record.msg = original_message
94
+ lines = _WrappedLines([message], self.message_width)
95
+ message = NEXT_LINE_PROLOGUE.join(lines)
138
96
 
139
- if should_join_lines:
140
- if next_lines is None:
141
- return first_line, None
142
- else:
143
- return f"{first_line}{next_lines}", None
97
+ if (where := getattr(record, "where", None)) is None:
98
+ where = ""
144
99
  else:
145
- return first_line, next_lines
100
+ where = f"{NEXT_LINE_PROLOGUE}{WHERE_SEPARATOR} {where}"
101
+
102
+ return (
103
+ f"{record.when_or_elapsed}"
104
+ f"{LEVEL_OPENING}{record.level_first_letter}{LEVEL_CLOSING} "
105
+ f"{MESSAGE_MARKER} {message}{where}"
106
+ )
146
107
 
147
108
 
148
109
  def _WrappedLines(lines: list[str], message_width: int, /) -> list[str]:
logger_36/type/issue.py CHANGED
@@ -11,7 +11,7 @@ from logger_36.config.issue import ISSUE_BASE_CONTEXT
11
11
  from logger_36.constant.generic import NOT_PASSED
12
12
  from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR
13
13
  from logger_36.constant.message import expected_op_h
14
- from logger_36.task.format.message import FormattedMessage
14
+ from logger_36.task.format.message import MessageWithActualExpected
15
15
 
16
16
  issue_t = str
17
17
 
@@ -32,7 +32,7 @@ def NewIssue(
32
32
  """"""
33
33
  if context.__len__() == 0:
34
34
  context = ISSUE_BASE_CONTEXT
35
- message = FormattedMessage(
35
+ message = MessageWithActualExpected(
36
36
  message,
37
37
  actual=actual,
38
38
  expected=expected,
logger_36/type/logger.py CHANGED
@@ -10,35 +10,48 @@ import sys as sstm
10
10
  import traceback as tcbk
11
11
  import types as t
12
12
  import typing as h
13
- from datetime import datetime as dttm
13
+ from datetime import date as date_t
14
+ from datetime import datetime as date_time_t
15
+ from os import sep as FOLDER_SEPARATOR
14
16
  from pathlib import Path as path_t
15
17
  from traceback import TracebackException as traceback_t
16
18
 
17
19
  from logger_36.config.issue import ISSUE_CONTEXT_END, ISSUE_CONTEXT_SEPARATOR
18
- from logger_36.config.message import DATE_FORMAT
20
+ from logger_36.config.message import (
21
+ DATE_FORMAT,
22
+ ELAPSED_TIME_SEPARATOR,
23
+ LONG_ENOUGH,
24
+ TIME_FORMAT,
25
+ )
19
26
  from logger_36.constant.generic import NOT_PASSED
27
+ from logger_36.constant.handler import ANONYMOUS
20
28
  from logger_36.constant.issue import ISSUE_LEVEL_SEPARATOR, ORDER, order_h
21
29
  from logger_36.constant.logger import (
22
- HIDE_WHERE_KWARG,
23
30
  LOGGER_NAME,
24
31
  WARNING_LOGGER_NAME,
25
32
  WARNING_TYPE_COMPILED_PATTERN,
26
33
  logger_handle_h,
27
34
  )
28
35
  from logger_36.constant.memory import UNKNOWN_MEMORY_USAGE
29
- from logger_36.constant.message import expected_op_h
30
- from logger_36.constant.record import SHOW_MEMORY_ATTR, SHOW_W_RULE_ATTR
31
- from logger_36.task.format.memory import (
32
- FormattedUsageWithAutoUnit as FormattedMemoryUsage,
36
+ from logger_36.constant.message import TIME_LENGTH_m_1, expected_op_h
37
+ from logger_36.constant.record import (
38
+ HIDE_WHERE_ATTR,
39
+ SHOW_W_RULE_ATTR,
40
+ STORE_MEMORY_ATTR,
33
41
  )
34
- from logger_36.task.format.message import FormattedMessage
42
+ from logger_36.task.format.message import MessageWithActualExpected
35
43
  from logger_36.task.measure.chronos import ElapsedTime
36
44
  from logger_36.task.measure.memory import CurrentUsage as CurrentMemoryUsage
37
45
  from logger_36.type.issue import NewIssue, issue_t
38
46
 
47
+ logger_base_t = lggg.Logger
48
+
49
+ _DATE_TIME_ORIGIN = date_time_t.fromtimestamp(1970, None)
50
+ _DATE_ORIGIN = _DATE_TIME_ORIGIN.date()
51
+
39
52
 
40
53
  @d.dataclass(slots=True, repr=False, eq=False)
41
- class logger_t(lggg.Logger):
54
+ class logger_t(logger_base_t):
42
55
  name_: d.InitVar[str] = LOGGER_NAME
43
56
  level_: d.InitVar[int] = lggg.NOTSET
44
57
  activate_wrn_interceptions: d.InitVar[bool] = True
@@ -50,8 +63,9 @@ class logger_t(lggg.Logger):
50
63
 
51
64
  on_hold: list[lggg.LogRecord] = d.field(init=False, default_factory=list)
52
65
  events: dict[int, int] = d.field(init=False, default_factory=dict)
53
- last_message_date: str = d.field(init=False, default="")
54
- any_handler_shows_memory: bool = d.field(init=False, default=False)
66
+ last_message_now: date_time_t = d.field(init=False, default=_DATE_TIME_ORIGIN)
67
+ last_message_date: date_t = d.field(init=False, default=_DATE_ORIGIN)
68
+ any_handler_stores_memory: bool = d.field(init=False, default=False)
55
69
  memory_usages: list[tuple[str, int]] = d.field(init=False, default_factory=list)
56
70
  context_levels: list[str] = d.field(init=False, default_factory=list)
57
71
  staged_issues: list[issue_t] = d.field(init=False, default_factory=list)
@@ -64,9 +78,9 @@ class logger_t(lggg.Logger):
64
78
  self, name_: str, level_: int, activate_wrn_interceptions: bool
65
79
  ) -> None:
66
80
  """"""
67
- lggg.Logger.__init__(self, name_)
81
+ logger_base_t.__init__(self, name_)
68
82
  self.setLevel(level_)
69
- self.propagate = False # Part of lggg.Logger.
83
+ self.propagate = False # Part of logger_base_t.
70
84
 
71
85
  for level in lggg.getLevelNamesMapping().values():
72
86
  self.events[level] = 0
@@ -92,7 +106,7 @@ class logger_t(lggg.Logger):
92
106
  logger.handle = t.MethodType(_HandleForWarnings(self), logger)
93
107
 
94
108
  lggg.captureWarnings(True)
95
- self.info("Warning Interception: ON", **HIDE_WHERE_KWARG)
109
+ self.info("Warning Interception: ON")
96
110
 
97
111
  def _DeactivateWarningInterceptions(self) -> None:
98
112
  """"""
@@ -102,7 +116,7 @@ class logger_t(lggg.Logger):
102
116
  self.intercepted_wrn_handle = None
103
117
 
104
118
  lggg.captureWarnings(False)
105
- self.info("Warning Interception: OFF", **HIDE_WHERE_KWARG)
119
+ self.info("Warning Interception: OFF")
106
120
 
107
121
  def ToggleWarningInterceptions(self, state: bool, /) -> None:
108
122
  """"""
@@ -130,16 +144,13 @@ class logger_t(lggg.Logger):
130
144
  intercepted = sorted(self.intercepted_log_handles.keys())
131
145
  if intercepted.__len__() > 0:
132
146
  as_str = ", ".join(intercepted)
133
- self.info(
134
- f"Now Intercepting LOGs from: {as_str}",
135
- **HIDE_WHERE_KWARG,
136
- )
147
+ self.info(f"Now Intercepting LOGs from: {as_str}")
137
148
  elif self.intercepted_log_handles.__len__() > 0:
138
149
  for name, handle in self.intercepted_log_handles.items():
139
150
  logger = lggg.getLogger(name)
140
151
  logger.handle = handle
141
152
  self.intercepted_log_handles.clear()
142
- self.info("Log Interception: OFF", **HIDE_WHERE_KWARG)
153
+ self.info("Log Interception: OFF")
143
154
 
144
155
  @property
145
156
  def max_memory_usage(self) -> int:
@@ -162,69 +173,94 @@ class logger_t(lggg.Logger):
162
173
  def AddHandler(self, handler: lggg.Handler, should_hold_messages: bool, /) -> None:
163
174
  """"""
164
175
  self.should_hold_messages = should_hold_messages
165
- lggg.Logger.addHandler(self, handler)
176
+ logger_base_t.addHandler(self, handler)
166
177
 
167
178
  extension = getattr(handler, "extension", None)
168
179
  if extension is None:
169
- show_memory_usage = False
180
+ name = handler.name
181
+ if (name is None) or (name.__len__() == 0):
182
+ name = ANONYMOUS
170
183
  else:
171
- show_memory_usage = getattr(extension, SHOW_MEMORY_ATTR, False)
172
- if show_memory_usage:
173
- self.any_handler_shows_memory = True
184
+ name = getattr(extension, "name", ANONYMOUS)
185
+ if getattr(extension, STORE_MEMORY_ATTR, False):
186
+ self.any_handler_stores_memory = True
187
+
188
+ path = getattr(handler, "baseFilename", "")
189
+ if isinstance(path, path_t) or (path.__len__() > 0):
190
+ path = f"\nPath: {path}"
174
191
 
175
- extension = getattr(handler, "extension", handler.name)
176
- if isinstance(extension, str):
177
- name = extension
178
- else:
179
- name = getattr(extension, "name", "Anonymous")
180
192
  self.info(
181
- f'New handler "{name}" with class "{type(handler).__name__}" and '
182
- f"level {lggg.getLevelName(handler.level)}",
183
- **HIDE_WHERE_KWARG,
193
+ f'New handler "{name}" of type "{type(handler).__name__}" and '
194
+ f"level {handler.level}={lggg.getLevelName(handler.level)}{path}",
184
195
  )
185
196
 
186
197
  def handle(self, record: lggg.LogRecord, /) -> None:
187
198
  """"""
188
- if (not self.should_hold_messages) and (self.on_hold.__len__() > 0):
189
- for hold in self.on_hold:
190
- lggg.Logger.handle(self, hold)
191
- self.on_hold.clear()
192
-
193
- record.elapsed_time = ElapsedTime()
194
-
195
- if self.any_handler_shows_memory or not self.hasHandlers():
196
- # Memory usage is also added if there are no handlers yet, just in case.
197
- usage = CurrentMemoryUsage()
198
- self.memory_usages.append(
199
- (f"{record.module}.{record.funcName}.{record.lineno}", usage)
200
- )
199
+ elapsed_time, now = ElapsedTime(should_return_now=True)
201
200
 
202
- value, unit = FormattedMemoryUsage(usage, 1)
203
- record.memory_usage = f"{value}{unit}"
201
+ if (self.on_hold.__len__() > 0) and not self.should_hold_messages:
202
+ for held in self.on_hold:
203
+ logger_base_t.handle(self, held)
204
+ self.on_hold.clear()
204
205
 
205
- date = dttm.now().strftime(DATE_FORMAT)
206
- if date != self.last_message_date:
206
+ if (date := now.date()) != self.last_message_date:
207
207
  self.last_message_date = date
208
208
  # levelno: Added for management by logging.Logger.handle.
209
209
  date_record = lggg.makeLogRecord(
210
210
  {
211
211
  "name": self.name,
212
212
  "levelno": lggg.INFO,
213
- "msg": f"DATE: {date}",
213
+ "msg": f"DATE: {date.strftime(DATE_FORMAT)}",
214
214
  SHOW_W_RULE_ATTR: True,
215
215
  }
216
216
  )
217
217
  if self.should_hold_messages:
218
218
  self.on_hold.append(date_record)
219
219
  else:
220
- lggg.Logger.handle(self, date_record)
220
+ logger_base_t.handle(self, date_record)
221
+
222
+ # When.
223
+ if now - self.last_message_now > LONG_ENOUGH:
224
+ record.when_or_elapsed = now.strftime(TIME_FORMAT)
225
+ else:
226
+ record.when_or_elapsed = (
227
+ f"{ELAPSED_TIME_SEPARATOR}{elapsed_time:.<{TIME_LENGTH_m_1}}"
228
+ )
229
+ self.last_message_now = now
230
+
231
+ # Where.
232
+ # Memory usage is also stored if there are no handlers yet, just in case.
233
+ should_store_where = self.any_handler_stores_memory or not self.hasHandlers()
234
+ should_show_where = (record.levelno != lggg.INFO) and not hasattr(
235
+ record, HIDE_WHERE_ATTR
236
+ )
237
+ if should_store_where or should_show_where:
238
+ module = path_t(record.pathname)
239
+ for path in sstm.path:
240
+ if module.is_relative_to(path):
241
+ module = module.relative_to(path).with_suffix("")
242
+ module = str(module).replace(FOLDER_SEPARATOR, ".")
243
+ break
244
+ else:
245
+ module = record.module
246
+ where = f"{module}:{record.funcName}:{record.lineno}"
247
+ if should_show_where:
248
+ record.where = where
249
+ else:
250
+ where = None
251
+
252
+ # How.
253
+ record.level_first_letter = record.levelname[0]
254
+
255
+ # What.
256
+ if not isinstance(record.msg, str):
257
+ record.msg = str(record.msg)
221
258
 
222
259
  if self.should_hold_messages:
223
260
  self.on_hold.append(record)
224
261
  else:
225
- lggg.Logger.handle(self, record)
262
+ logger_base_t.handle(self, record)
226
263
 
227
- self.events[record.levelno] += 1
228
264
  if (self.exit_on_critical and (record.levelno is lggg.CRITICAL)) or (
229
265
  self.exit_on_error and (record.levelno is lggg.ERROR)
230
266
  ):
@@ -232,6 +268,11 @@ class logger_t(lggg.Logger):
232
268
  # __post_init__ set self.exit_on_critical if self.exit_on_error.
233
269
  sstm.exit(1)
234
270
 
271
+ self.events[record.levelno] += 1
272
+
273
+ if should_store_where:
274
+ self.memory_usages.append((where, CurrentMemoryUsage()))
275
+
235
276
  def Log(
236
277
  self,
237
278
  message: str,
@@ -247,7 +288,7 @@ class logger_t(lggg.Logger):
247
288
  """"""
248
289
  if isinstance(level, str):
249
290
  level = lggg.getLevelNamesMapping()[level.upper()]
250
- message = FormattedMessage(
291
+ message = MessageWithActualExpected(
251
292
  message,
252
293
  actual=actual,
253
294
  expected=expected,
@@ -357,7 +398,7 @@ class logger_t(lggg.Logger):
357
398
 
358
399
  if order not in ORDER:
359
400
  raise ValueError(
360
- FormattedMessage(
401
+ MessageWithActualExpected(
361
402
  "Invalid commit order",
362
403
  actual=order,
363
404
  expected=f"One of {str(ORDER)[1:-1]}",
@@ -379,17 +420,18 @@ class logger_t(lggg.Logger):
379
420
  formatted = "\n".join(lines)
380
421
  """
381
422
 
423
+ hide_where = {HIDE_WHERE_ATTR: None}
382
424
  if unified:
383
425
  level, _ = issues[0].split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
384
426
  wo_level = []
385
427
  for issue in issues:
386
428
  _, issue = issue.split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
387
429
  wo_level.append(issue)
388
- self.log(int(level), "\n".join(wo_level), stacklevel=2)
430
+ self.log(int(level), "\n".join(wo_level), stacklevel=2, extra=hide_where)
389
431
  else:
390
432
  for issue in issues:
391
433
  level, issue = issue.split(ISSUE_LEVEL_SEPARATOR, maxsplit=1)
392
- self.log(int(level), issue, stacklevel=2)
434
+ self.log(int(level), issue, stacklevel=2, extra=hide_where)
393
435
  self.staged_issues.clear()
394
436
 
395
437
  def __enter__(self) -> None:
@@ -408,10 +450,10 @@ class logger_t(lggg.Logger):
408
450
  return False
409
451
 
410
452
 
411
- def _HandleForWarnings(interceptor: lggg.Logger, /) -> logger_handle_h:
453
+ def _HandleForWarnings(interceptor: logger_base_t, /) -> logger_handle_h:
412
454
  """"""
413
455
 
414
- def handle_p(_: lggg.Logger, record: lggg.LogRecord, /) -> None:
456
+ def handle_p(_: logger_base_t, record: lggg.LogRecord, /) -> None:
415
457
  pieces = WARNING_TYPE_COMPILED_PATTERN.match(record.msg)
416
458
  if pieces is None:
417
459
  # The warning message does not follow the default format.
@@ -437,11 +479,11 @@ def _HandleForWarnings(interceptor: lggg.Logger, /) -> logger_handle_h:
437
479
 
438
480
 
439
481
  def _HandleForInterceptions(
440
- intercepted: lggg.Logger, interceptor: lggg.Logger, /
482
+ intercepted: logger_base_t, interceptor: logger_base_t, /
441
483
  ) -> logger_handle_h:
442
484
  """"""
443
485
 
444
- def handle_p(_: lggg.Logger, record: lggg.LogRecord, /) -> None:
486
+ def handle_p(_: logger_base_t, record: lggg.LogRecord, /) -> None:
445
487
  duplicate = lggg.makeLogRecord(record.__dict__)
446
488
  duplicate.msg = f"{record.msg} :{intercepted.name}:"
447
489
  interceptor.handle(duplicate)
logger_36/version.py CHANGED
@@ -4,7 +4,7 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2023
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- __version__ = "2024.28"
7
+ __version__ = "2024.30"
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: logger-36
3
- Version: 2024.28
3
+ Version: 2024.30
4
4
  Summary: Simple logger with a catalog of handlers
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/logger-36/
6
6
  Author: Eric Debreuve