Logging System¶
Overview¶
ZooUI implements a centralized logging system that provides consistent, configurable logging across all modules. The system is designed to facilitate debugging, track application behavior, and diagnose issues effectively.
Architecture¶
The logging system is built around the LoggerConfig class in zooui/logger.py, which provides:
Centralized configuration - Single initialization point for all logging behavior
Multiple output targets - Console and/or file logging with independent level control
Color-coded console output - Visual identification of log levels (optional)
Rotating log files - Automatic file rotation to manage disk space (10 MB max, 5 backups)
Runtime control - Dynamic log level adjustment during execution
Per-module loggers - Granular control over individual module logging
Log Levels¶
The system uses Python’s standard logging levels:
Level |
Value |
Usage |
|---|---|---|
DEBUG |
10 |
Detailed diagnostic information for development |
INFO |
20 |
General informational messages about normal flow |
WARNING |
30 |
Potentially harmful situations (default level) |
ERROR |
40 |
Serious problems that need attention |
CRITICAL |
50 |
Critical failures that may halt the application |
Level Behavior by Mode¶
The effective log level depends on the execution mode:
- Normal Mode (default)
Console: WARNING and above
File: INFO and above
- Verbose Mode (
--verbose) Console: INFO and above
File: DEBUG and above
- Debug Mode (
--debug) Console: DEBUG and above
File: DEBUG and above
Configuration¶
Command-Line Interface¶
The primary way to configure logging is through command-line arguments:
# Enable debug mode (maximum detail)
python main.py --debug
# Enable verbose mode (informational messages)
python main.py --verbose
# Specify custom log directory
python main.py --log-dir /tmp/zooui-logs
# Disable console output (file logging only)
python main.py --no-console
# Disable file logging (console only)
python main.py --no-file
# Disable colored output
python main.py --no-color
Configuration File¶
Logging can also be configured via JSON configuration file:
{
"logging": {
"debug": false,
"verbose": true,
"log_to_file": true,
"log_to_console": true,
"colored_output": true,
"log_dir": "~/.local/state/zooui/logs"
}
}
Load the configuration with:
python main.py --config zooui_config_example.json
Programmatic Configuration¶
Initialize logging programmatically using the LoggerConfig class:
from zooui.logger import LoggerConfig
LoggerConfig.initialize(
debug=True, # Enable debug mode
log_to_file=True, # Enable file logging
log_to_console=True, # Enable console logging
log_dir='custom_logs', # Custom log directory
colored_output=True, # Enable colored console output
verbose=False # Verbose mode
)
Usage¶
Basic Usage in Modules¶
To use logging in your code, import the get_logger function:
from zooui.logger import get_logger
class MyClass:
def __init__(self):
self.logger = get_logger('MyClass')
self.logger.info('MyClass initialized')
def process_data(self, data):
self.logger.debug(f'Processing data: {data}')
try:
result = self._compute(data)
self.logger.info(f'Processing successful: {result}')
return result
except Exception as e:
self.logger.error(f'Processing failed: {e}')
raise
Loggers are named with a zooui. prefix internally (e.g., zooui.MyClass)
and are cached — repeated calls to get_logger('MyClass') return the same
instance. If logging hasn’t been initialized yet, the first call to
get_logger() auto-initializes the system with default settings
(console: WARNING, file: INFO).
Logging Exceptions¶
Use logger.exception() to automatically include traceback information:
try:
risky_operation()
except Exception as e:
# Logs the exception with full traceback
self.logger.exception('Operation failed')
# Alternative with custom message
self.logger.exception(f'Failed to process {item}: {e}')
Runtime Control¶
Adjust logging behavior during execution:
from zooui.logger import LoggerConfig
import logging
# Enable debug mode at runtime
LoggerConfig.enable_debug()
# Disable debug mode
LoggerConfig.disable_debug()
# Set specific log level globally
LoggerConfig.set_level(logging.INFO)
# Set log level for specific module
LoggerConfig.set_level(logging.DEBUG, module='TileManager')
# Get log file path
log_path = LoggerConfig.get_log_file_path()
print(f'Logs are being written to: {log_path}')
Effective Debugging Strategies¶
Progressive Debugging¶
Start with higher log levels and progressively increase detail:
Start with ERROR/WARNING - Identify obvious problems
Enable INFO - Understand application flow
Enable DEBUG - Examine detailed behavior
Enable per-module DEBUG - Focus on specific components
Example workflow:
# Step 1: Check for errors
python main.py | grep ERROR
# Step 2: Add context with verbose mode
python main.py --verbose
# Step 3: Full debugging for specific issue
python main.py --debug
# Step 4: Review detailed logs in file
tail -f ~/.local/state/zooui/logs/zooui.log | grep TileManager
Debugging Specific Components¶
For targeted debugging, enable debug logging only for specific modules:
from zooui.logger import LoggerConfig
import logging
# Initialize with normal logging
LoggerConfig.initialize()
# Enable debug only for TileManager
LoggerConfig.set_level(logging.DEBUG, module='TileManager')
# Enable debug for multiple specific modules
for module in ['TileManager', 'TileCache', 'TileStore']:
LoggerConfig.set_level(logging.DEBUG, module=module)
Strategic Log Placement¶
Place log statements strategically for maximum debugging value:
- At Entry Points
Log function entry with key parameters:
def load_tile(self, tile_id, level): self.logger.debug(f'load_tile called: tile_id={tile_id}, level={level}')
- At Decision Points
Log conditional branches:
if tile in self.cache: self.logger.debug(f'Tile {tile_id} found in cache') else: self.logger.debug(f'Tile {tile_id} not in cache, loading from disk')
- At State Changes
Log significant state transitions:
self.logger.info(f'Scene state changed: {old_state} -> {new_state}')
- Before Expensive Operations
Log before time-consuming operations:
self.logger.debug(f'Starting conversion of {image_path} ({size} MB)') result = self.converter.convert(image_path) self.logger.debug(f'Conversion completed in {elapsed}s')
- At Error Boundaries
Log at exception handling points:
try: result = operation() except SpecificError as e: self.logger.error(f'Specific error occurred: {e}') # Handle error except Exception as e: self.logger.exception(f'Unexpected error: {e}') raise
Performance Considerations¶
Logging can impact performance. Follow these guidelines:
- Avoid Logging in Tight Loops
Don’t log every iteration:
# Bad: logs millions of times for i in range(1000000): self.logger.debug(f'Processing item {i}') # Good: logs periodically total = len(items) self.logger.info(f'Processing {total} items') for i, item in enumerate(items): # Log every 10,000 items if i % 10000 == 0: self.logger.debug(f'Progress: {i}/{total} ({i*100//total}%)')
- Use Lazy Formatting
Let the logger handle string formatting:
# Good: formatting only happens if message is logged self.logger.debug('Processing tile %s at level %d', tile_id, level) # Also good: f-strings are acceptable self.logger.debug(f'Processing tile {tile_id} at level {level}')
- Disable File Logging for Performance Testing
When measuring performance, disable file I/O:
python main.py --no-file
Log Output Formats¶
Console Format¶
The console output uses a compact format for readability:
[LEVEL ] ModuleName | Message
Example output:
[INFO ] main | Starting ZooUI application
[DEBUG ] TileManager | Initializing tile cache with 100 MB
[WARNING ] Scene | Object 'text1' outside visible bounds
[ERROR ] PDFConverter | Failed to convert document.pdf: File not found
When colored output is enabled (default), each level has a distinct color:
DEBUG: Cyan
INFO: Green
WARNING: Yellow
ERROR: Red
CRITICAL: Magenta
File Format¶
Log files include additional information for post-mortem analysis:
YYYY-MM-DD HH:MM:SS | [LEVEL ] | ModuleName | FunctionName | Message
Example output:
2025-12-09 14:23:45 | [INFO ] | main | main | Starting ZooUI application
2025-12-09 14:23:45 | [DEBUG ] | TileManager | __init__ | Initializing tile cache with 100 MB
2025-12-09 14:23:46 | [WARNING ] | Scene | add_object | Object 'text1' outside visible bounds
2025-12-09 14:23:47 | [ERROR ] | PDFConverter | convert | Failed to convert document.pdf: File not found
File Management¶
Log File Location¶
By default, logs are written to:
~/.local/state/zooui/logs/zooui.log
Specify a custom location with:
python main.py --log-dir /var/log/zooui
Rotation Strategy¶
Log files automatically rotate when they reach 10 MB. The system maintains:
zooui.log- Current log filezooui.log.1- Previous log filezooui.log.2- Second previous log filezooui.log.3- Third previous log filezooui.log.4- Fourth previous log filezooui.log.5- Fifth previous log file (oldest retained)
When zooui.log reaches 10 MB:
1. zooui.log.5 is deleted
2. zooui.log.4 → zooui.log.5
3. zooui.log.3 → zooui.log.4
4. zooui.log.2 → zooui.log.3
5. zooui.log.1 → zooui.log.2
6. zooui.log → zooui.log.1
7. New zooui.log is created
Viewing Logs¶
Common commands for log analysis:
# View real-time logs
tail -f ~/.local/state/zooui/logs/zooui.log
# Search for errors
grep ERROR ~/.local/state/zooui/logs/zooui.log
# View recent errors
grep ERROR ~/.local/state/zooui/logs/zooui.log | tail -20
# Find logs for specific module
grep TileManager ~/.local/state/zooui/logs/zooui.log
# View logs with context (5 lines before/after)
grep -C 5 "Exception" ~/.local/state/zooui/logs/zooui.log
# Count errors by type
grep ERROR ~/.local/state/zooui/logs/zooui.log | cut -d'|' -f4 | sort | uniq -c
API Reference¶
LoggerConfig Class¶
- class LoggerConfig¶
Centralized logger configuration for ZooUI.
- initialize(debug=False, log_to_file=True, log_to_console=True, log_dir=None, colored_output=True, verbose=False)¶
Initialize the logging system.
- Parameters:
debug (bool) – Enable debug mode (sets console level to DEBUG)
log_to_file (bool) – Enable logging to file
log_to_console (bool) – Enable logging to console
log_dir (str) – Directory for log files (default: ~/.local/state/zooui/logs)
colored_output (bool) – Enable colored console output
verbose (bool) – Enable verbose mode (shows more detailed info)
- get_logger(name)¶
Get a logger instance for the specified module.
- Parameters:
name (str) – Name of the module/class requesting the logger
- Returns:
Configured logger instance
- Return type:
logging.Logger
- set_level(level, module=None)¶
Change the logging level at runtime.
- Parameters:
level (int) – Logging level (e.g., logging.DEBUG, logging.INFO)
module (str) – Specific module name, or None for all modules
- enable_debug()¶
Enable debug mode at runtime.
- disable_debug()¶
Disable debug mode at runtime.
- get_log_file_path()¶
Get the path to the current log file.
- Returns:
Path to log file, or None if file logging is disabled
- Return type:
Path
Convenience Functions¶
- get_logger(name)¶
Convenience function to get a logger instance. Calls
LoggerConfig.get_logger(name), auto-initializing logging with defaults if it hasn’t been set up yet.- Parameters:
name (str) – Name of the module/class requesting the logger
- Returns:
Configured logger instance
- Return type:
logging.Logger
Example:
from zooui.logger import get_logger logger = get_logger('MyModule') logger.info('Module initialized')
ColoredFormatter¶
- class ColoredFormatter¶
Custom formatter that adds ANSI color codes to console output based on the log level. Used internally by
LoggerConfig.initialize()whencolored_output=True.- Color mapping:
DEBUG: Cyan
INFO: Green
WARNING: Yellow
ERROR: Red
CRITICAL: Magenta
- format(record)¶
Format the log record with color codes added to the
colorandresetattributes on the record.- Parameters:
record (logging.LogRecord) – The log record to format
- Returns:
Formatted log string with ANSI color codes
- Return type:
str
Best Practices¶
Log Level Selection¶
Choose appropriate log levels based on the significance of the message:
- DEBUG
Internal state changes
Function entry/exit points
Detailed parameter values
Iteration progress
Cache hits/misses
- INFO
Significant state transitions
Successful completion of major operations
Configuration values on startup
User-initiated actions
- WARNING
Deprecated features in use
Recoverable errors
Unexpected but handled conditions
Performance degradation
Resource limits approaching
- ERROR
Failed operations that require intervention
Unhandled but caught exceptions
Data corruption or loss
Configuration errors
- CRITICAL
Application cannot continue
Severe system failures
Data integrity compromised
Message Content¶
Write clear, actionable log messages:
- Good Messages
logger.error(f'Failed to load tile {tile_id} at level {level}: {error}') logger.debug(f'Cache hit for tile {tile_id} (size: {size} bytes)') logger.warning(f'Tile generation took {elapsed}s, expected <1s')
- Poor Messages
logger.error('Error') # No context logger.debug('Here') # Unclear location logger.warning('Something wrong') # No specifics
Include Context¶
Provide sufficient context for debugging:
# Include relevant identifiers
self.logger.error(f'Failed to process tile={tile_id}, level={level}, coord=({x},{y})')
# Include state information
self.logger.debug(f'Cache status: {len(self.cache)}/{self.max_cache} tiles')
# Include timing information
self.logger.info(f'Operation completed in {elapsed:.2f}s')
# Include error details
self.logger.exception(f'Conversion failed for {path}: {e}')
Avoid Sensitive Data¶
Don’t log sensitive information:
# Bad: logs password
logger.debug(f'Connecting to {host} with password {password}')
# Good: omits password
logger.debug(f'Connecting to {host}')
# Bad: logs entire user object (may contain sensitive data)
logger.debug(f'User data: {user}')
# Good: logs only necessary information
logger.debug(f'Processing request for user_id={user.id}')
Troubleshooting¶
Common Issues¶
- No logs appearing
Verify logging is initialized (should be automatic in zooui.app)
Check log level is appropriate for the messages
Ensure handlers are configured:
from zooui.logger import LoggerConfig LoggerConfig.initialize(debug=True)
- Too much console output
Run without
--debugor--verboseflagsDisable console logging:
python main.py --no-consoleUse file logging for detailed logs:
python main.py --verbose --no-console
- Cannot find log files
Check log file path:
from zooui.logger import LoggerConfig print(LoggerConfig.get_log_file_path())
Verify log directory exists and is writable
Check if file logging is enabled (not using
--no-file)
- Colors not showing in console
Ensure terminal supports ANSI color codes
Try disabling and re-enabling:
python main.py --no-colorthen run normallyCheck if running in environment that strips color codes
- Log file not rotating
Check file permissions on log directory
Verify files aren’t being held open by other processes
Check disk space availability
Migration from Old System¶
If you have old code using the standard logging module directly:
Old Code
import logging
class MyClass:
def __init__(self):
self.__logger = logging.getLogger("MyClass")
def my_method(self):
self.__logger.debug("Processing")
New Code
from zooui.logger import get_logger
class MyClass:
def __init__(self):
self.logger = get_logger("MyClass")
def my_method(self):
self.logger.debug("Processing")
All existing logger method calls (.debug(), .info(), .warning(), .error(), .critical(), .exception()) work identically.
Examples¶
Complete Debugging Session¶
Here’s a complete example of debugging a tile loading issue:
Step 1: Identify the problem
$ python main.py
[ERROR ] TileManager | Failed to load tile 12345
Step 2: Enable verbose logging
$ python main.py --verbose
[INFO ] TileManager | Loading tile 12345 from cache
[INFO ] TileCache | Cache miss for tile 12345
[INFO ] TileManager | Loading tile 12345 from disk
[ERROR ] TileManager | Failed to load tile 12345
Step 3: Enable full debug logging
$ python main.py --debug
[DEBUG ] TileManager | Searching for tile 12345 in cache
[DEBUG ] TileCache | Cache stats: 245/1000 tiles, 45 MB/100 MB
[DEBUG ] TileCache | Tile 12345 not in cache
[DEBUG ] TileManager | Computing tile path: level=3, x=456, y=789
[DEBUG ] TileManager | Tile path: /data/tiles/3/456/789.png
[DEBUG ] TileManager | Checking if file exists: /data/tiles/3/456/789.png
[ERROR ] TileManager | File not found: /data/tiles/3/456/789.png
Step 4: Review detailed logs
$ grep "tile 12345" ~/.local/state/zooui/logs/zooui.log
2025-12-09 14:23:45 | [DEBUG ] | TileManager | load_tile | Loading tile 12345
2025-12-09 14:23:45 | [ERROR ] | TileManager | load_tile | Failed to load tile 12345
Now we know the issue: the tile file doesn’t exist at the expected path.
Conditional Debugging¶
Enable debugging conditionally based on runtime conditions:
from zooui.logger import LoggerConfig, get_logger
import logging
class TileManager:
def __init__(self):
self.logger = get_logger('TileManager')
self.debug_tile_ids = set() # Tiles to debug
def enable_tile_debugging(self, tile_id):
"""Enable detailed logging for specific tile."""
self.debug_tile_ids.add(tile_id)
def load_tile(self, tile_id):
# Temporarily enable debug for this tile if marked
if tile_id in self.debug_tile_ids:
old_level = logging.getLogger(f'zooui.{self.logger.name}').level
LoggerConfig.set_level(logging.DEBUG, module='TileManager')
self.logger.debug(f'Loading tile {tile_id}')
# ... tile loading logic ...
# Restore old level
if tile_id in self.debug_tile_ids:
LoggerConfig.set_level(old_level, module='TileManager')
Performance Profiling with Logging¶
Use logging to identify performance bottlenecks:
import time
from zooui.logger import get_logger
class Converter:
def __init__(self):
self.logger = get_logger('Converter')
def convert(self, input_path, output_path):
start = time.time()
self.logger.debug(f'Starting conversion: {input_path}')
# Step 1
step_start = time.time()
self._load_image(input_path)
self.logger.debug(f'Image loaded in {time.time()-step_start:.3f}s')
# Step 2
step_start = time.time()
self._process_image()
self.logger.debug(f'Image processed in {time.time()-step_start:.3f}s')
# Step 3
step_start = time.time()
self._save_image(output_path)
self.logger.debug(f'Image saved in {time.time()-step_start:.3f}s')
total = time.time() - start
self.logger.info(f'Conversion completed in {total:.3f}s')
if total > 5.0:
self.logger.warning(f'Slow conversion detected: {total:.3f}s for {input_path}')
See Also¶
Contribution Guidelines - Guidelines for contributing to ZooUI
ZooUI Installation - Installation instructions
<no title> - Main documentation