|
7 | 7 | import logging |
8 | 8 | import os |
9 | 9 | from datetime import datetime |
| 10 | +from typing import Optional |
10 | 11 |
|
11 | | -def millisecond_timestamp(*args): |
12 | | - return datetime.now().strftime('%m-%d %H:%M:%S.%f')[:-3] |
13 | 12 |
|
14 | | -def setup_logging(name=None, log_level=logging.INFO): |
15 | | - logger = logging.getLogger(name) |
16 | | - logger.setLevel(log_level) |
| 13 | +def millisecond_timestamp(include_year: bool = False) -> str: |
| 14 | + format_string = "%Y-%m-%d %H:%M:%S.%f" if include_year else "%m-%d %H:%M:%S.%f" |
| 15 | + return datetime.now().strftime(format_string)[:-3] |
17 | 16 |
|
18 | | - if not logger.handlers: |
19 | | - console_handler = logging.StreamHandler() |
20 | | - console_handler.setLevel(log_level) |
21 | 17 |
|
22 | | - formatter = logging.Formatter('%(asctime)s - %(name)s:%(lineno)d - %(levelname)s - %(message)s') |
23 | | - formatter.formatTime = millisecond_timestamp |
| 18 | +class CompactFormatter(logging.Formatter): |
| 19 | + def __init__( |
| 20 | + self, |
| 21 | + fmt: Optional[str] = None, |
| 22 | + datefmt: Optional[str] = None, |
| 23 | + style: str = "%", |
| 24 | + validate: bool = True, |
| 25 | + *, |
| 26 | + defaults: Optional[dict] = None, |
| 27 | + show_lower_levels: bool = True, |
| 28 | + ): |
| 29 | + super().__init__(fmt, datefmt, style, validate, defaults=defaults) |
| 30 | + self.show_lower_levels = show_lower_levels |
| 31 | + self.original_fmt = fmt |
24 | 32 |
|
25 | | - console_handler.setFormatter(formatter) |
26 | | - logger.addHandler(console_handler) |
| 33 | + def format(self, record: logging.LogRecord) -> str: |
| 34 | + # Remove .py extension from filename |
| 35 | + record.filename = os.path.splitext(record.filename)[0] |
27 | 36 |
|
28 | | - # suppress verbose torch.profiler logging |
29 | | - os.environ["KINETO_LOG_LEVEL"] = "5" |
| 37 | + if self.show_lower_levels or record.levelno > logging.INFO: |
| 38 | + return super().format(record) |
| 39 | + else: |
| 40 | + # Create a copy of the record and modify it |
| 41 | + new_record = logging.makeLogRecord(record.__dict__) |
| 42 | + new_record.levelname = "" |
| 43 | + # Temporarily change the format string |
| 44 | + temp_fmt = self.original_fmt.replace(" - %(levelname)s", "") |
| 45 | + self._style._fmt = temp_fmt |
| 46 | + formatted_message = super().format(new_record) |
| 47 | + # Restore the original format string |
| 48 | + self._style._fmt = self.original_fmt |
| 49 | + return formatted_message |
30 | 50 |
|
31 | | - return logger |
| 51 | + |
| 52 | +class SingletonLogger: |
| 53 | + """Singleton (global) logger to avoid logging duplication""" |
| 54 | + |
| 55 | + _instance = None |
| 56 | + |
| 57 | + @classmethod |
| 58 | + def get_logger( |
| 59 | + cls, |
| 60 | + name: str = "global_logger", |
| 61 | + level: int = logging.INFO, |
| 62 | + include_year: bool = False, |
| 63 | + show_lower_levels: bool = False, |
| 64 | + ) -> logging.Logger: |
| 65 | + """ |
| 66 | + Get or create a singleton logger instance. |
| 67 | +
|
| 68 | + :param name: Name of the logger |
| 69 | + :param level: Logging level |
| 70 | + :param include_year: Whether to include the year in timestamps |
| 71 | + :param show_lower_levels: Whether to show level names for INFO and DEBUG messages |
| 72 | + :return: Logger instance |
| 73 | + """ |
| 74 | + if cls._instance is None: |
| 75 | + cls._instance = cls._setup_logger( |
| 76 | + name, level, include_year, show_lower_levels |
| 77 | + ) |
| 78 | + return cls._instance |
| 79 | + |
| 80 | + @staticmethod |
| 81 | + def _setup_logger( |
| 82 | + name: str, |
| 83 | + level: int, |
| 84 | + include_year: bool = False, |
| 85 | + show_lower_levels: bool = False, |
| 86 | + ) -> logging.Logger: |
| 87 | + logger = logging.getLogger(name) |
| 88 | + |
| 89 | + if not logger.handlers: |
| 90 | + logger.setLevel(level) |
| 91 | + |
| 92 | + console_handler = logging.StreamHandler() |
| 93 | + console_handler.setLevel(level) |
| 94 | + |
| 95 | + formatter = CompactFormatter( |
| 96 | + "%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s", |
| 97 | + show_lower_levels=show_lower_levels, |
| 98 | + ) |
| 99 | + formatter.formatTime = lambda record, datefmt=None: millisecond_timestamp( |
| 100 | + include_year |
| 101 | + ) |
| 102 | + console_handler.setFormatter(formatter) |
| 103 | + logger.addHandler(console_handler) |
| 104 | + |
| 105 | + # Suppress verbose torch.profiler logging |
| 106 | + os.environ["KINETO_LOG_LEVEL"] = "5" |
| 107 | + |
| 108 | + logger.propagate = False |
| 109 | + return logger |
0 commit comments