Logging Processes in the Toolbox
The TI-Toolbox logging system (tit/logger.py) is intentionally minimal. It configures the tit logger hierarchy but adds no handlers by default – no console output, no file output – until you explicitly attach one. This keeps terminal output clean and gives each entry point full control over where logs go.
Architecture
The logging module exposes three public functions:
| Function | Purpose |
|---|---|
setup_logging(level) |
Set log level on the tit logger; adds NO handlers |
add_file_handler(log_file) |
Attach a FileHandler to a named logger |
get_file_only_logger(name, log_file) |
Return a standalone logger that writes only to a file |
setup_logging(level)
from tit.logger import setup_logging
setup_logging("DEBUG")
This does three things:
- Clears any existing handlers on the
titlogger - Sets the log level (defaults to
INFO) - Sets
propagate = Falseso messages never bubble to the root logger or terminal
Third-party loggers (matplotlib, PIL) are silenced to ERROR level.
add_file_handler(log_file, level, logger_name)
from tit.logger import add_file_handler
fh = add_file_handler("/path/to/run.log", level="DEBUG", logger_name="tit")
- Creates the parent directory if needed
- Opens the file in append mode
- Returns the
FileHandlerso callers can remove it when finished
get_file_only_logger(name, log_file)
from tit.logger import get_file_only_logger
logger = get_file_only_logger("tit.analyzer.roi", "/path/to/roi.log")
Returns a logger with propagate = False that writes exclusively to the given file. Useful for per-ROI or per-subject log isolation.
Log Format
All file handlers use the same format:
2025-06-05 23:10:26 | INFO | tit.analyzer | Mesh analyzer initialized successfully
Fields: timestamp, level, logger name, message.
Log Levels
- DEBUG: Detailed diagnostic information (file handlers default to this)
- INFO: General progress information
- WARNING: Potentially problematic situations
- ERROR: Serious problems
- CRITICAL: Fatal errors
How Each Entry Point Uses Logging
GUI (tit/gui/)
The GUI uses a custom _QtHandler(logging.Handler) that bridges log records to a Qt signal. This feeds log messages into the console widget of each tab without any terminal output.
__main__.py Subprocess Entry Points
Modules invoked as subprocesses (simnibs_python -m tit.analyzer config.json) use print() for progress output. This is intentional: BaseProcessThread in the GUI captures stdout from subprocesses and displays it in the console widget. A stdlib logger with a StreamHandler(sys.stdout) is used in some entry points (e.g., tit.sim.__main__) for structured output that still reaches the GUI.
Library Usage
When using tit as a library, call setup_logging() at your entry point and attach handlers as needed:
from tit.logger import setup_logging, add_file_handler
setup_logging("INFO")
fh = add_file_handler("my_analysis.log")
# ... run analysis ...
# Clean up when done
import logging
logging.getLogger("tit").removeHandler(fh)
What Was Removed
Previous versions (~v2.2.3 and earlier) had ~750 lines of custom logging infrastructure including:
get_logger()factory function (deleted)configure_external_loggers()for SimNIBS integration- Dual console + file output by default
TI_LOG_FILE,PROJECT_DIR,SUBJECT_IDenvironment variables- Debug toggles in the GUI
All of this has been replaced by the three functions described above. There are no environment variables and no debug toggles.
Best Practices
- Call
setup_logging()once at your entry point – not in library code - Use
add_file_handler()to direct logs to a file when you need a record - Use
print()in__main__.pymodules where output must reach subprocess capture - Use
logging.getLogger("tit.your_module")in library modules – the hierarchy propagates to whatever handlers are attached to thetitlogger - Clean up handlers when a run completes to avoid leaking file descriptors