--- a +++ b/src/utils/config.py @@ -0,0 +1,135 @@ +# utils/config.py +""" +Configuration Module for Clinical Trial Analysis + +This module manages configuration settings and logging setup for the clinical trial analysis +application. It defines project paths, logging formats, and provides utility functions for +configuring logging behavior. + +The module handles: +1. Project directory and file path definitions +2. Colored console logging configuration +3. File logging setup with timestamped log files +4. Log level and format configurations + +Classes: + ColoredFormatter: Custom formatter that adds color to console logs based on severity. + +Functions: + setup_logging: Configure application-wide logging. + setup_file_logging: Set up logging to a file with appropriate formatting. +""" + +import logging +import os +from datetime import datetime + +# Get the project root directory (one level up from Code) +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) + +# Define output directory relative to project root +# This ensures consistent output paths regardless of execution directory +DEFAULT_OUTPUT_DIR = os.path.join(PROJECT_ROOT, "output") + +TIMEOUT = 30 +TEMPERATURE = 0.0 + + +class ColoredFormatter(logging.Formatter): + """ + Custom formatter that adds color to console logs based on severity. + """ + + # ANSI escape codes for colors + COLORS = { + "DEBUG": "\033[92m", # Green + "INFO": "\033[94m", # Blue + "WARNING": "\033[93m", # Yellow + "ERROR": "\033[91m", # Red + "CRITICAL": "\033[91m", # Red + } + RESET = "\033[0m" # Reset color code + + def format(self, record): + """ + Format the log record with appropriate colors. + + Args: + record: The log record to format. + + Returns: + str: The formatted, color-coded log message. + """ + log_color = self.COLORS.get(record.levelname, self.RESET) + formatted_message = super().format(record) + return f"{log_color}{formatted_message}{self.RESET}" + + +def setup_logging(log_level=logging.INFO, log_to_file=False, log_dir="logs"): + """ + Set up application-wide logging configuration. + + Args: + log_level (int): The logging level (e.g., logging.DEBUG, logging.INFO) + log_to_file (bool): Whether to save logs to a file + log_dir (str): Directory to store log files if log_to_file is True + + Returns: + logging.Logger: The configured root logger. + """ + # Create formatters + console_formatter = ColoredFormatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + file_formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + # Create console handler + console_handler = logging.StreamHandler() + console_handler.setFormatter(console_formatter) + + # Configure root logger + root_logger = logging.getLogger() + root_logger.setLevel(log_level) + + # Remove existing handlers to prevent duplicate logs + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + + # Add console handler + root_logger.addHandler(console_handler) + + # Add file handler if requested + if log_to_file: + setup_file_logging(log_dir, logging, file_formatter, root_logger) + + return root_logger + + +def setup_file_logging(log_dir, logging, file_formatter, root_logger): + """ + Set up logging to a file with timestamped filename. + + Args: + log_dir (str): Directory to store log files + logging: The logging module + file_formatter: The formatter to use for file logs + root_logger: The root logger to attach the file handler to + """ + # Create logs directory if it doesn't exist + os.makedirs(log_dir, exist_ok=True) + + # Create log file with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + log_file = os.path.join(log_dir, f"clinical_trial_analysis_{timestamp}.log") + + # Set up file handler with the configured formatter + file_handler = logging.FileHandler(log_file) + file_handler.setFormatter(file_formatter) + root_logger.addHandler(file_handler) + + root_logger.info(f"Logging to file: {log_file}")