Merge pull request #10159 from SomberNight/202508_max_logfile_size
logging: add config.LOGS_MAX_TOTAL_SIZE_BYTES: to limit size on disk
This commit is contained in:
@@ -116,24 +116,52 @@ class TruncatingMemoryHandler(logging.handlers.MemoryHandler):
|
|||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
|
|
||||||
def _delete_old_logs(path, *, num_files_keep: int):
|
def _delete_old_logs(path, *, num_files_keep: int, max_total_size: int):
|
||||||
files = sorted(list(pathlib.Path(path).glob("electrum_log_*.log")), reverse=True)
|
"""Delete old logfiles, only keeping the latest few."""
|
||||||
for f in files[num_files_keep:]:
|
def sortkey_oldest_first(p: pathlib.PurePath):
|
||||||
|
fname = p.name
|
||||||
|
basename, ext, counter = str(fname).partition(".log")
|
||||||
|
# - each time electrum is launched, there will be a new basename, ordered by date
|
||||||
|
# - for any given basename, there might be multiple log files, differing by counter
|
||||||
|
# - empty counter is newest, then .1 is older, .2 is even older, etc
|
||||||
try:
|
try:
|
||||||
os.remove(str(f))
|
counter = int(counter[1:]) if counter else 0 # convert ".2" -> 2
|
||||||
|
except ValueError:
|
||||||
|
_logger.warning(f"failed to parse log file name: {fname}")
|
||||||
|
counter = 0
|
||||||
|
return basename, -counter
|
||||||
|
files = sorted(
|
||||||
|
list(pathlib.Path(path).glob("electrum_log_*.log*")),
|
||||||
|
key=sortkey_oldest_first,
|
||||||
|
)
|
||||||
|
total_size = sum(os.stat(f).st_size for f in files) # in bytes
|
||||||
|
num_files_remaining = len(files)
|
||||||
|
for f in files:
|
||||||
|
fsize = os.stat(f).st_size
|
||||||
|
if total_size < max_total_size and num_files_remaining <= num_files_keep:
|
||||||
|
break
|
||||||
|
total_size -= fsize
|
||||||
|
num_files_remaining -= 1
|
||||||
|
try:
|
||||||
|
os.remove(f)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
_logger.warning(f"cannot delete old logfile: {e}")
|
_logger.warning(f"cannot delete old logfile: {e}")
|
||||||
|
|
||||||
|
|
||||||
_logfile_path = None
|
_logfile_path = None
|
||||||
def _configure_file_logging(log_directory: pathlib.Path, *, num_files_keep: int):
|
def _configure_file_logging(
|
||||||
|
log_directory: pathlib.Path,
|
||||||
|
*,
|
||||||
|
num_files_keep: int,
|
||||||
|
max_total_size: int,
|
||||||
|
):
|
||||||
from .util import os_chmod
|
from .util import os_chmod
|
||||||
|
|
||||||
global _logfile_path
|
global _logfile_path
|
||||||
assert _logfile_path is None, 'file logging already initialized'
|
assert _logfile_path is None, 'file logging already initialized'
|
||||||
log_directory.mkdir(exist_ok=True, mode=0o700)
|
log_directory.mkdir(exist_ok=True, mode=0o700)
|
||||||
|
|
||||||
_delete_old_logs(log_directory, num_files_keep=num_files_keep)
|
_delete_old_logs(log_directory, num_files_keep=num_files_keep, max_total_size=max_total_size)
|
||||||
|
|
||||||
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
||||||
PID = os.getpid()
|
PID = os.getpid()
|
||||||
@@ -142,7 +170,12 @@ def _configure_file_logging(log_directory: pathlib.Path, *, num_files_keep: int)
|
|||||||
with open(_logfile_path, "w+") as f:
|
with open(_logfile_path, "w+") as f:
|
||||||
os_chmod(_logfile_path, 0o600)
|
os_chmod(_logfile_path, 0o600)
|
||||||
|
|
||||||
file_handler = logging.FileHandler(_logfile_path, encoding='utf-8')
|
logfile_backupcount = 4
|
||||||
|
file_handler = logging.handlers.RotatingFileHandler(
|
||||||
|
_logfile_path,
|
||||||
|
maxBytes=max_total_size // (logfile_backupcount+1),
|
||||||
|
backupCount=logfile_backupcount,
|
||||||
|
encoding='utf-8')
|
||||||
file_handler.setFormatter(file_formatter)
|
file_handler.setFormatter(file_formatter)
|
||||||
file_handler.setLevel(logging.DEBUG)
|
file_handler.setLevel(logging.DEBUG)
|
||||||
root_logger.addHandler(file_handler)
|
root_logger.addHandler(file_handler)
|
||||||
@@ -236,8 +269,9 @@ electrum_logger.setLevel(logging.DEBUG)
|
|||||||
# --- External API
|
# --- External API
|
||||||
|
|
||||||
def get_logger(name: str) -> _CustomLogger:
|
def get_logger(name: str) -> _CustomLogger:
|
||||||
if name.startswith("electrum."):
|
prefix = "electrum."
|
||||||
name = name[9:]
|
if name.startswith(prefix):
|
||||||
|
name = name[len(prefix):]
|
||||||
return electrum_logger.getChild(name)
|
return electrum_logger.getChild(name)
|
||||||
|
|
||||||
|
|
||||||
@@ -283,7 +317,8 @@ def configure_logging(config: 'SimpleConfig', *, log_to_file: Optional[bool] = N
|
|||||||
if log_to_file:
|
if log_to_file:
|
||||||
log_directory = pathlib.Path(config.path) / "logs"
|
log_directory = pathlib.Path(config.path) / "logs"
|
||||||
num_files_keep = config.LOGS_NUM_FILES_KEEP
|
num_files_keep = config.LOGS_NUM_FILES_KEEP
|
||||||
_configure_file_logging(log_directory, num_files_keep=num_files_keep)
|
max_total_size = config.LOGS_MAX_TOTAL_SIZE_BYTES
|
||||||
|
_configure_file_logging(log_directory, num_files_keep=num_files_keep, max_total_size=max_total_size)
|
||||||
|
|
||||||
# clean up and delete in-memory logs
|
# clean up and delete in-memory logs
|
||||||
global _inmemory_startup_logs
|
global _inmemory_startup_logs
|
||||||
|
|||||||
@@ -892,7 +892,18 @@ Warning: setting this to too low will result in lots of payment failures."""),
|
|||||||
short_desc=lambda: _("Write logs to file"),
|
short_desc=lambda: _("Write logs to file"),
|
||||||
long_desc=lambda: _('Debug logs can be persisted to disk. These are useful for troubleshooting.'),
|
long_desc=lambda: _('Debug logs can be persisted to disk. These are useful for troubleshooting.'),
|
||||||
)
|
)
|
||||||
LOGS_NUM_FILES_KEEP = ConfigVar('logs_num_files_keep', default=30, type_=int)
|
LOGS_NUM_FILES_KEEP = ConfigVar(
|
||||||
|
'logs_num_files_keep', default=30, type_=int,
|
||||||
|
long_desc=lambda: _("Old log files get deleted on startup, with only the newest few being kept."),
|
||||||
|
)
|
||||||
|
LOGS_MAX_TOTAL_SIZE_BYTES = ConfigVar(
|
||||||
|
'logs_max_total_size', default=200_000_000, type_=int,
|
||||||
|
long_desc=lambda: _(
|
||||||
|
"Old log files get deleted on startup. "
|
||||||
|
"This value limits the max total size of the old log files kept, "
|
||||||
|
"and also separately the max size of the current log file. "
|
||||||
|
"Hence, the max disk usage will be twice this value."),
|
||||||
|
)
|
||||||
GUI_ENABLE_DEBUG_LOGS = ConfigVar('gui_enable_debug_logs', default=False, type_=bool)
|
GUI_ENABLE_DEBUG_LOGS = ConfigVar('gui_enable_debug_logs', default=False, type_=bool)
|
||||||
LOCALIZATION_LANGUAGE = ConfigVar(
|
LOCALIZATION_LANGUAGE = ConfigVar(
|
||||||
'language', default="", type_=str,
|
'language', default="", type_=str,
|
||||||
|
|||||||
Reference in New Issue
Block a user