SXXXXXXX_GitUtility/logger_config.py
2025-04-07 14:01:15 +02:00

146 lines
5.8 KiB
Python

# logger_config.py
import logging
import tkinter as tk
from tkinter import scrolledtext
# This is the single source of truth for the log file name
LOG_FILE = "git_svn_sync.log"
class TextHandler(logging.Handler):
"""
A handler class to redirect log messages to a Tkinter Text widget.
Ensures thread safety for GUI updates from different threads if necessary,
although current usage seems single-threaded for logging.
"""
def __init__(self, text_widget):
"""
Initializes the TextHandler with a Tkinter ScrolledText widget.
Args:
text_widget (tkinter.scrolledtext.ScrolledText): The Tkinter widget.
"""
super().__init__()
# Ensure we store the actual widget
if not isinstance(text_widget, tk.Text) and not isinstance(
text_widget, scrolledtext.ScrolledText
):
raise ValueError(
"TextHandler requires a valid Tkinter Text or ScrolledText widget."
)
self.text_widget = text_widget
def emit(self, record):
"""
Formats and directs the log record to the Tkinter text widget.
This method should be thread-safe if logging can occur from multiple threads.
Tkinter GUI updates must happen in the main thread.
"""
msg = self.format(record) + "\n"
# Check if the widget exists and is valid before attempting to update it
if self.text_widget and self.text_widget.winfo_exists():
try:
# Schedule the GUI update in the main Tkinter thread
# Using after(0, ...) ensures it runs in the main event loop
self.text_widget.after(0, self._insert_text, msg)
except Exception as e:
# Fallback or logging for errors during scheduling
print(f"Error scheduling log message update in Tkinter: {e}")
# else:
# Optional: Log to console if the widget is gone
# print(f"Log Widget gone: {msg.strip()}")
def _insert_text(self, msg):
"""Helper method to insert text, intended to be called via 'after'."""
# Double-check widget existence right before insertion
if self.text_widget and self.text_widget.winfo_exists():
try:
# Store current state, modify, insert, restore state
current_state = self.text_widget["state"]
self.text_widget.config(state=tk.NORMAL)
self.text_widget.insert(tk.END, msg)
self.text_widget.config(state=current_state)
# Autoscroll to the end
self.text_widget.see(tk.END)
except tk.TclError as e:
# Handle cases where the widget might be destroyed between checks
print(f"TclError inserting log into Text widget: {e}")
except Exception as e:
print(f"Unexpected error inserting log into Text widget: {e}")
def setup_logger(log_text_widget=None):
"""
Sets up the application logger with optional file and text handlers.
Args:
log_text_widget (tkinter.scrolledtext.ScrolledText, optional):
The Tkinter ScrolledText widget for live log display. Defaults to None.
Returns:
logging.Logger: The configured logger instance.
"""
# Use a specific, consistent name for the application logger
logger_name = "GitSvnSyncApp"
logger = logging.getLogger(logger_name)
# Set the desired logging level (e.g., INFO, DEBUG)
logger.setLevel(logging.INFO)
# Prevent adding duplicate handlers if called multiple times
if not logger.handlers:
# Define a standard log format
log_formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - [%(funcName)s] - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
# --- File Handler ---
try:
# Use the centrally defined LOG_FILE name
file_handler = logging.FileHandler(
LOG_FILE, mode="a", encoding="utf-8"
) # Append mode
file_handler.setLevel(logging.INFO) # Log INFO level and above to file
file_handler.setFormatter(log_formatter)
logger.addHandler(file_handler)
print(
f"Logging to file: {os.path.abspath(LOG_FILE)}"
) # Print log file path on setup
except Exception as e:
# Fallback to stderr if file logging setup fails
logging.basicConfig(level=logging.INFO) # Basic config to console
logger.error(
f"Critical: Failed to set up file handler for '{LOG_FILE}': {e}. Logging to console."
)
# --- Text Widget Handler (Optional) ---
if log_text_widget:
try:
text_handler = TextHandler(log_text_widget)
# Log DEBUG level and above to the GUI for more detail during runtime
text_handler.setLevel(logging.DEBUG)
text_handler.setFormatter(log_formatter)
logger.addHandler(text_handler)
logger.info("GUI logging handler configured.")
except ValueError as e:
logger.error(f"Error setting up GUI TextHandler: {e}")
except Exception as e:
logger.error(f"Unexpected error setting up GUI TextHandler: {e}")
else:
logger.info("No GUI log widget provided. Skipping TextHandler setup.")
# Initial log message indicating setup is complete
logger.info("Logger setup complete.")
# Example debug message (will show in GUI if configured, not in file unless file level is DEBUG)
logger.debug("Debug logging is active for handlers that support it.")
else:
logger.info("Logger already configured. Skipping setup.")
return logger
# --- Import os for abspath in setup_logger ---
import os