File size: 3,485 Bytes
995af0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import logging
import sys
import os
from datetime import datetime
from typing import Optional

class CustomFormatter(logging.Formatter):
    """Custom formatter with color coding and detailed formatting"""
    
    grey = "\x1b[38;21m"
    blue = "\x1b[38;5;39m"
    yellow = "\x1b[38;5;226m"
    red = "\x1b[38;5;196m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"

    def __init__(self, include_path: bool = False):
        date_fmt = "%Y-%m-%d %H:%M:%S.%f"
        if include_path:
            fmt = ("%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s "
                  "[%(pathname)s:%(lineno)d] - %(message)s")
        else:
            fmt = "%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s"
        
        super().__init__(fmt=fmt, datefmt=date_fmt)

    def format(self, record):
        # Save original format
        format_orig = self._style._fmt

        # Apply color based on log level
        if record.levelno == logging.DEBUG:
            self._style._fmt = self.grey + format_orig + self.reset
        elif record.levelno == logging.INFO:
            self._style._fmt = self.blue + format_orig + self.reset
        elif record.levelno == logging.WARNING:
            self._style._fmt = self.yellow + format_orig + self.reset
        elif record.levelno == logging.ERROR:
            self._style._fmt = self.red + format_orig + self.reset
        elif record.levelno == logging.CRITICAL:
            self._style._fmt = self.bold_red + format_orig + self.reset

        # Call original format
        result = logging.Formatter.format(self, record)

        # Restore original format
        self._style._fmt = format_orig

        return result

def setup_logger(
    name: str = __name__,
    log_level: int = logging.DEBUG,
    log_file: Optional[str] = None
) -> logging.Logger:
    """
    Sets up a configured logger with both console and file handlers.
    
    Args:
        name: Logger name
        log_level: Logging level
        log_file: Optional specific log file path
    
    Returns:
        Configured logger instance
    """
    logger = logging.getLogger(name)
    
    # Only add handlers if logger doesn't have any
    if not logger.handlers:
        logger.setLevel(log_level)
        
        # Ensure logs directory exists
        os.makedirs('logs', exist_ok=True)
        
        # Console handler with minimal formatting
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(logging.INFO)
        console_formatter = CustomFormatter(include_path=False)
        console_handler.setFormatter(console_formatter)
        
        # File handler with detailed formatting
        if not log_file:
            log_file = f'logs/sse_stream_{datetime.now():%Y%m%d}.log'
        
        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(logging.DEBUG)
        file_formatter = CustomFormatter(include_path=True)
        file_handler.setFormatter(file_formatter)
        
        # Debug file handler for trace-level logging
        debug_file = f'logs/debug_{datetime.now():%Y%m%d}.log'
        debug_handler = logging.FileHandler(debug_file)
        debug_handler.setLevel(logging.DEBUG)
        debug_formatter = CustomFormatter(include_path=True)
        debug_handler.setFormatter(debug_formatter)
        
        logger.addHandler(console_handler)
        logger.addHandler(file_handler)
        logger.addHandler(debug_handler)
    
    return logger