Skip to content

Commit ebb38e9

Browse files
authored
Merge pull request #10015 from SomberNight/202507_logging_only_once
logging: add "only_once=False" param to logger.info/warning/etc calls
2 parents 1695948 + 0f1a282 commit ebb38e9

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

electrum/i18n.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,23 @@ def safe_translator(msg: str, **kwargs):
5858
try:
5959
parsed2 = list(sf.parse(translation))
6060
except ValueError: # malformed format string in translation
61-
_logger.info(f"rejected translation string: failed to parse. original={msg!r}. {translation=!r}")
61+
_logger.warning(
62+
f"rejected translation string: failed to parse. original={msg!r}. {translation=!r}",
63+
only_once=True)
6264
return msg
6365
# num of replacement fields must match:
6466
if len(parsed1) != len(parsed2):
65-
_logger.info(f"rejected translation string: num replacement fields mismatch. original={msg!r}. {translation=!r}")
67+
_logger.warning(
68+
f"rejected translation string: num replacement fields mismatch. original={msg!r}. {translation=!r}",
69+
only_once=True)
6670
return msg
6771
# set of "field_name"s must not change. (re-ordering is explicitly allowed):
6872
field_names1 = set(tupl[1] for tupl in parsed1)
6973
field_names2 = set(tupl[1] for tupl in parsed2)
7074
if field_names1 != field_names2:
71-
_logger.info(f"rejected translation string: set of field_names mismatch. original={msg!r}. {translation=!r}")
75+
_logger.warning(
76+
f"rejected translation string: set of field_names mismatch. original={msg!r}. {translation=!r}",
77+
only_once=True)
7278
return msg
7379
# checks done.
7480
return translation

electrum/logging.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
import pathlib
1010
import os
1111
import platform
12-
from typing import Optional, TYPE_CHECKING
12+
from typing import Optional, TYPE_CHECKING, Set
1313
import copy
1414
import subprocess
15+
import hashlib
1516

1617
if TYPE_CHECKING:
1718
from .simple_config import SimpleConfig
@@ -190,6 +191,24 @@ def _process_verbosity_log_levels(verbosity):
190191
raise Exception(f"invalid log filter: {filt}")
191192

192193

194+
class _CustomLogger(logging.getLoggerClass()):
195+
def __init__(self, name, *args, **kwargs):
196+
super().__init__(name, *args, **kwargs)
197+
self.msg_hashes_seen = set() # type: Set[bytes]
198+
# ^ note: size grows without bounds, but only for log lines using "only_once".
199+
200+
def _log(self, level, msg: str, *args, only_once: bool = False, **kwargs) -> None:
201+
"""Overridden to add 'only_once' arg to logger.debug()/logger.info()/logger.warning()/etc."""
202+
if only_once: # if set, this logger will only log this msg a single time during its lifecycle
203+
msg_hash = hashlib.sha256(msg.encode("utf-8")).digest()
204+
if msg_hash in self.msg_hashes_seen:
205+
return
206+
self.msg_hashes_seen.add(msg_hash)
207+
super()._log(level, msg, *args, **kwargs)
208+
209+
logging.setLoggerClass(_CustomLogger)
210+
211+
193212
# enable logs universally (including for other libraries)
194213
root_logger = logging.getLogger()
195214
root_logger.setLevel(logging.WARNING)
@@ -216,7 +235,7 @@ def _process_verbosity_log_levels(verbosity):
216235

217236
# --- External API
218237

219-
def get_logger(name: str) -> logging.Logger:
238+
def get_logger(name: str) -> _CustomLogger:
220239
if name.startswith("electrum."):
221240
name = name[9:]
222241
return electrum_logger.getChild(name)

0 commit comments

Comments
 (0)