diff --git a/logger/__init__.py b/logger/__init__.py index 8c7b45e..9200563 100644 --- a/logger/__init__.py +++ b/logger/__init__.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- """Logger module for GeoAgent plugin.""" -from .logger import UILogHandler, get_logger, UILogSignal -from .processing_logger import ( - get_processing_logger, - set_processing_ui_log_handler, -) +from .logger import get_logger, configure_logger, attach_ui_handler +from .processing_logger import get_processing_logger __all__ = [ - "UILogHandler", - "get_logger", - "UILogSignal", - "get_processing_logger", - "set_processing_ui_log_handler", + "get_logger", + "configure_logger", + "attach_ui_handler", + "get_processing_logger", ] diff --git a/logger/logger.py b/logger/logger.py index 91c78cf..16b0707 100644 --- a/logger/logger.py +++ b/logger/logger.py @@ -6,6 +6,8 @@ instead of saving to files. """ +import os +from qgis.core import QgsApplication import logging from typing import Optional from qgis.PyQt.QtWidgets import QTextBrowser @@ -40,8 +42,7 @@ class UILogHandler(logging.Handler): def __init__( self, text_browser: Optional[QTextBrowser] = None, - max_lines: int = 1000, - show_debug: bool = False, + max_lines: int = 1000, ): """ Initialize UILogHandler. @@ -54,7 +55,6 @@ def __init__( super().__init__() self.text_browser = text_browser self.max_lines = max_lines - self.show_debug = show_debug self.line_count = 0 # Create signal emitter for thread safety @@ -77,16 +77,7 @@ def set_text_browser(self, text_browser: QTextBrowser) -> None: text_browser: QTextBrowser widget to display logs """ self.text_browser = text_browser - - def set_show_debug(self, show_debug: bool) -> None: - """ - Set whether to show DEBUG level messages. - Args: - show_debug: True to show DEBUG messages, False to hide them - """ - self.show_debug = show_debug - def emit(self, record: logging.LogRecord) -> None: """ Emit a log record to the UI. @@ -97,10 +88,6 @@ def emit(self, record: logging.LogRecord) -> None: Args: record: LogRecord to emit """ - # Filter out DEBUG messages if not in debug mode - if record.levelno == logging.DEBUG and not self.show_debug: - return - try: # Format the message msg = self.format(record) @@ -192,51 +179,48 @@ def _trim_lines(self) -> None: except Exception: pass +def _get_log_file_path() -> str: + base = QgsApplication.qgisSettingsDirPath() + log_dir = os.path.join(base, "GeoAgent") + os.makedirs(log_dir, exist_ok=True) + return os.path.join(log_dir, "geo_agent.log") -def get_logger( - name: str, - text_browser: Optional[QTextBrowser] = None, - show_debug: bool = False, - level: int = logging.INFO, -) -> logging.Logger: - """ - Get a configured logger instance with UI support. - - This function creates or retrieves a logger with both console and UI handlers. - - Args: - name: Logger name (typically __name__) - text_browser: Optional QTextBrowser for UI logging - show_debug: Whether to show DEBUG messages (default: False) - level: Logging level (default: logging.INFO) - - Returns: - Configured logger instance - - Example: - >>> logger = get_logger("GeoAgent.MyModule", text_browser, show_debug=True) - >>> logger.info("This message will appear in both console and UI") - """ - logger = logging.getLogger(name) +def configure_logger(level: int = logging.INFO) -> logging.Logger: + logger = logging.getLogger("geo_agent") logger.setLevel(level) - - # Remove existing handlers to avoid duplicates - logger.handlers.clear() - - # Console handler - console_handler = logging.StreamHandler() - console_handler.setLevel(level) - console_formatter = logging.Formatter( + logger.propagate = False + + # Update existing handlers if present + for h in logger.handlers: + if isinstance(h, logging.FileHandler): + h.setLevel(level) + return logger + + file_handler = logging.FileHandler(_get_log_file_path(), encoding="utf-8") + file_handler.setLevel(level) + + formatter = logging.Formatter( "%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) - console_handler.setFormatter(console_formatter) - logger.addHandler(console_handler) - - # UI handler - if text_browser is not None: - ui_handler = UILogHandler(text_browser, show_debug=show_debug) - ui_handler.setLevel(level) - logger.addHandler(ui_handler) - + file_handler.setFormatter(formatter) + + logger.addHandler(file_handler) return logger + +def get_logger(name: Optional[str] = None) -> logging.Logger: + if name: + return logging.getLogger(f"geo_agent.{name}") + return logging.getLogger("geo_agent") + +def attach_ui_handler(text_browser: QTextBrowser) -> None: + logger = logging.getLogger("geo_agent") + + for h in logger.handlers: + if isinstance(h, UILogHandler): + h.set_text_browser(text_browser) + return + + ui_handler = UILogHandler(text_browser) + ui_handler.setLevel(logger.level) + logger.addHandler(ui_handler) diff --git a/logger/processing_logger.py b/logger/processing_logger.py index 6c52aef..35bcadc 100644 --- a/logger/processing_logger.py +++ b/logger/processing_logger.py @@ -1,43 +1,18 @@ # -*- coding: utf-8 -*- """Processing-specific logger helpers for GeoAgent. -This centralizes the processing logger and its UI handler wiring so that -logging concerns stay within the logger package. +Provides a shared processing logger that inherits configuration +from the main GeoAgent logger. """ import logging -from typing import Optional +from .logger import get_logger -from ..config.settings import SHOW_DEBUG_LOGS -from .logger import get_logger, UILogHandler - -# Create processing logger (no UI handler attached here; UI is wired at runtime) -_processing_logger = get_logger( - "GeoAgent.Processing", - text_browser=None, - show_debug=SHOW_DEBUG_LOGS, - level=logging.DEBUG if SHOW_DEBUG_LOGS else logging.INFO, -) -# Let messages propagate to root; root will carry the UI handler -_processing_logger.propagate = True +# Processing logger inherits handlers and level from core logger +_processing_logger = get_logger("processing") def get_processing_logger() -> logging.Logger: """Return the shared processing logger instance.""" return _processing_logger - - -def set_processing_ui_log_handler(ui_log_handler: Optional[UILogHandler]) -> None: - """Ensure processing logs flow to the shared UI handler via root. - - We avoid attaching the UI handler directly to prevent duplicate delivery; the - handler is expected to be registered on the root logger upstream. - """ - global _processing_logger - if ui_log_handler is None: - return - - # Remove any direct attachment of this UI handler to avoid duplicates - _processing_logger.handlers = [h for h in _processing_logger.handlers if h is not ui_log_handler] - _processing_logger.propagate = True