File size: 4,986 Bytes
7763bf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"""
Centralized logging module for AGI Multi-Model API.

Provides structured logging with:
- Colored console output
- File logging with rotation
- Configurable log levels
- Timestamp and module name tracking
"""

import logging
import sys
from pathlib import Path
from logging.handlers import RotatingFileHandler
from typing import Optional


class ColoredFormatter(logging.Formatter):
    """Custom formatter with color support for console output."""

    # ANSI color codes
    COLORS = {
        'DEBUG': '\033[36m',      # Cyan
        'INFO': '\033[32m',       # Green
        'WARNING': '\033[33m',    # Yellow
        'ERROR': '\033[31m',      # Red
        'CRITICAL': '\033[35m',   # Magenta
    }
    RESET = '\033[0m'
    BOLD = '\033[1m'

    def format(self, record):
        """Format log record with colors."""
        # Add color to level name
        levelname = record.levelname
        if levelname in self.COLORS:
            record.levelname = f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}"

        # Format the message
        result = super().format(record)

        # Reset levelname for other handlers
        record.levelname = levelname

        return result


class Logger:
    """
    Singleton logger class for the entire application.

    Usage:
        from logger import get_logger
        logger = get_logger(__name__)
        logger.info("Application started")
    """

    _instance: Optional[logging.Logger] = None
    _initialized: bool = False

    @classmethod
    def get_logger(
        cls,
        name: str = "AGI",
        level: int = logging.INFO,
        log_file: Optional[str] = "agi.log",
        max_bytes: int = 10 * 1024 * 1024,  # 10MB
        backup_count: int = 5
    ) -> logging.Logger:
        """
        Get or create the application logger.

        Args:
            name: Logger name (typically module name)
            level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
            log_file: Path to log file (None to disable file logging)
            max_bytes: Maximum size of log file before rotation
            backup_count: Number of backup files to keep

        Returns:
            Configured logger instance
        """
        # Create or get logger
        logger = logging.getLogger(name)

        # Only configure handlers once for the root logger
        if not cls._initialized and name == "AGI":
            logger.setLevel(level)

            # Console handler with colors
            console_handler = logging.StreamHandler(sys.stdout)
            console_handler.setLevel(level)
            console_formatter = ColoredFormatter(
                fmt='%(asctime)s | %(levelname)s | %(name)s | %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S'
            )
            console_handler.setFormatter(console_formatter)
            logger.addHandler(console_handler)

            # File handler with rotation (if enabled)
            if log_file:
                log_path = Path(log_file)
                log_path.parent.mkdir(parents=True, exist_ok=True)

                file_handler = RotatingFileHandler(
                    log_file,
                    maxBytes=max_bytes,
                    backupCount=backup_count
                )
                file_handler.setLevel(level)
                file_formatter = logging.Formatter(
                    fmt='%(asctime)s | %(levelname)-8s | %(name)s | %(funcName)s:%(lineno)d | %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S'
                )
                file_handler.setFormatter(file_formatter)
                logger.addHandler(file_handler)

            # Prevent propagation to avoid duplicate logs
            logger.propagate = False
            cls._initialized = True

        return logger


# Convenience function for easy import
def get_logger(name: str = "AGI", level: int = logging.INFO) -> logging.Logger:
    """
    Get a logger instance for the specified module.

    Args:
        name: Logger name (use __name__ for automatic module naming)
        level: Logging level (default: INFO)

    Returns:
        Configured logger instance

    Example:
        from logger import get_logger
        logger = get_logger(__name__)
        logger.info("Starting application")
    """
    return Logger.get_logger(name, level)


# Initialize the root logger on module import
_root_logger = Logger.get_logger("AGI", level=logging.INFO)


if __name__ == "__main__":
    # Test the logger
    logger = get_logger("test_module")

    logger.debug("This is a debug message")
    logger.info("This is an info message")
    logger.warning("This is a warning message")
    logger.error("This is an error message")
    logger.critical("This is a critical message")

    print("\nTesting with different module names:")
    api_logger = get_logger("api")
    api_logger.info("API logger initialized")

    client_logger = get_logger("client")
    client_logger.info("Client logger initialized")