Skip to content

Commit 1db96a8

Browse files
authored
transform messenger to use python logging (#199)
1 parent b90ab90 commit 1db96a8

File tree

2 files changed

+80
-41
lines changed

2 files changed

+80
-41
lines changed

src/itzi/itzi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ def update_input_arrays(self):
233233
def sim_runner_worker(conf_file):
234234
"""Run one simulation"""
235235
msgr.raise_on_error = True
236+
msgr._itzi_logger.set_verbosity(msgr.verbosity())
236237
try:
237238
# Run the simulation
238239
msgr.message(f"Starting simulation of {os.path.basename(conf_file)}...")
@@ -262,8 +263,6 @@ def itzi_run_one(conf_file):
262263

263264
def itzi_run(cli_args):
264265
"""Run one or multiple simulations from the command line."""
265-
# Do not raise on error when run from CLI
266-
msgr.raise_on_error = False
267266
# set environment variables
268267
if cli_args.o:
269268
os.environ["GRASS_OVERWRITE"] = "1"

src/itzi/messenger.py

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,13 @@
1313
"""
1414

1515
import sys
16+
import logging
1617
import os
1718
from datetime import timedelta, datetime
1819

1920
from itzi.itzi_error import ItziFatal
2021
from itzi.const import VerbosityLevel
2122

22-
OUTPUT = sys.stderr
23-
FATAL = "ERROR: "
24-
WARNING = "WARNING: "
25-
PAD = " " * 20 # Necessary to print a clean line
26-
2723
raise_on_error = True
2824

2925

@@ -35,14 +31,90 @@ def verbosity():
3531
return VerbosityLevel.QUIET
3632

3733

34+
class ItziLogger:
35+
"""Custom logger wrapper maintaining backward compatibility"""
36+
37+
VERBOSE_LEVEL = 15
38+
logging.addLevelName(VERBOSE_LEVEL, "VERBOSE")
39+
40+
def __init__(self):
41+
self.logger = logging.getLogger("itzi")
42+
self.raise_on_error = True
43+
self._setup_handlers()
44+
45+
def _setup_handlers(self):
46+
"""Configure console and optional file handlers"""
47+
# Console handler (stderr)
48+
console_handler = logging.StreamHandler(sys.stderr)
49+
console_handler.setFormatter(logging.Formatter("%(message)s"))
50+
self.logger.addHandler(console_handler)
51+
self.logger.setLevel(logging.DEBUG)
52+
53+
def add_file_handler(self, filepath, level=logging.DEBUG):
54+
"""Add file logging capability"""
55+
file_handler = logging.FileHandler(filepath)
56+
file_handler.setLevel(level)
57+
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
58+
self.logger.addHandler(file_handler)
59+
60+
def set_verbosity(self, verbosity_level):
61+
"""Map verbosity to logging level"""
62+
mapping = {
63+
VerbosityLevel.SUPER_QUIET: logging.ERROR,
64+
VerbosityLevel.QUIET: logging.WARNING,
65+
VerbosityLevel.MESSAGE: logging.INFO,
66+
VerbosityLevel.VERBOSE: self.VERBOSE_LEVEL,
67+
VerbosityLevel.DEBUG: logging.DEBUG,
68+
}
69+
level = mapping.get(verbosity_level, logging.INFO)
70+
self.logger.setLevel(level)
71+
for handler in self.logger.handlers:
72+
if isinstance(handler, logging.StreamHandler) and not isinstance(
73+
handler, logging.FileHandler
74+
):
75+
handler.setLevel(level)
76+
77+
def fatal(self, msg):
78+
"""Log fatal error and raise or exit"""
79+
self.logger.error(f"ERROR: {msg}")
80+
if raise_on_error:
81+
raise ItziFatal(msg)
82+
else:
83+
sys.exit(f"ERROR: {msg}")
84+
85+
def warning(self, msg):
86+
self.logger.warning(f"WARNING: {msg}")
87+
88+
def message(self, msg):
89+
self.logger.info(msg)
90+
91+
def verbose(self, msg):
92+
self.logger.log(self.VERBOSE_LEVEL, msg)
93+
94+
def debug(self, msg):
95+
self.logger.debug(msg)
96+
97+
98+
# Global instance
99+
_itzi_logger = ItziLogger()
100+
101+
# Backward-compatible module-level interface
102+
raise_on_error = _itzi_logger.raise_on_error
103+
fatal = _itzi_logger.fatal
104+
warning = _itzi_logger.warning
105+
message = _itzi_logger.message
106+
verbose = _itzi_logger.verbose
107+
debug = _itzi_logger.debug
108+
109+
38110
def percent(start_time, end_time, sim_time, sim_start_time):
39111
"""Display progress of the simulation"""
40112
sim_time_s = (sim_time - start_time).total_seconds()
41113
duration_s = (end_time - start_time).total_seconds()
42114
advance_perc = sim_time_s / duration_s
43115

44116
if verbosity() == VerbosityLevel.QUIET:
45-
print("{:.1%}".format(advance_perc), file=OUTPUT, end="\r")
117+
print(f"{advance_perc:.1%}", file=sys.stderr, end="\r")
46118

47119
elif verbosity() >= VerbosityLevel.MESSAGE:
48120
elapsed_s = (datetime.now() - sim_start_time).total_seconds()
@@ -59,36 +131,4 @@ def percent(start_time, end_time, sim_time, sim_start_time):
59131
eta=eta,
60132
pad=" " * 10,
61133
)
62-
print(disp, file=OUTPUT, end="\r")
63-
64-
65-
def message(msg):
66-
"""Display a normal message"""
67-
if verbosity() >= VerbosityLevel.MESSAGE:
68-
print(msg + PAD, file=OUTPUT)
69-
70-
71-
def verbose(msg):
72-
"""Display a verbose message"""
73-
if verbosity() >= VerbosityLevel.VERBOSE:
74-
print(msg + PAD, file=OUTPUT)
75-
76-
77-
def debug(msg):
78-
"""Display a debug message"""
79-
if verbosity() >= VerbosityLevel.DEBUG:
80-
print(msg + PAD, file=OUTPUT)
81-
82-
83-
def warning(msg):
84-
"""Display a warning message"""
85-
if verbosity() >= VerbosityLevel.SUPER_QUIET:
86-
print(WARNING + msg + PAD, file=OUTPUT)
87-
88-
89-
def fatal(msg):
90-
"""Display a fatal error and (exit or raise)"""
91-
if raise_on_error:
92-
raise ItziFatal(msg)
93-
else:
94-
sys.exit(FATAL + msg + PAD)
134+
print(disp, file=sys.stderr, end="\r")

0 commit comments

Comments
 (0)