Coverage for src/cosmic_toolbox/logger.py: 87%
52 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-10 10:31 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-10 10:31 +0000
1# Copyright (C) 2017 ETH Zurich
2# Cosmology Research Group
3# Author: Joerg Herbel, Silvan Fischbacher
5"""
6Logging utilities with colored output and progress bars.
8Provides a customized logger with:
9- Color-coded log levels (debug=violet, warning=orange, error=red)
10- Progress bar integration via tqdm
11- Environment variable control (PYTHON_LOGGER_LEVEL)
13"""
15import logging
16import os
17import sys
19logging_levels = {
20 "critical": logging.CRITICAL,
21 "error": logging.ERROR,
22 "warning": logging.WARNING,
23 "info": logging.INFO,
24 "debug": logging.DEBUG,
25}
28def get_logger(filepath, logging_level=None):
29 """
30 Get a configured logger with colored output and progress bar support.
32 If logging_level is unspecified, uses the PYTHON_LOGGER_LEVEL
33 environment variable, defaulting to 'info'.
35 :param filepath: name of the file calling the logger (used for logger name)
36 :param logging_level: logging level
37 ('debug', 'info', 'warning', 'error', 'critical')
38 :return: configured logger object with progressbar attribute
39 """
41 if logging_level is None:
42 logging_level = os.environ.get("PYTHON_LOGGER_LEVEL", "info")
44 logger = logging.getLogger(os.path.basename(filepath)[:10])
46 if len(logger.handlers) == 0:
47 stream_handler = logging.StreamHandler(sys.stdout)
48 stream_handler.setFormatter(ColorFormatter())
49 logger.addHandler(stream_handler)
50 logger.propagate = False
51 set_logger_level(logger, logging_level)
53 logger.progressbar = Progressbar(logger)
55 return logger
58class ColorFormatter(logging.Formatter):
59 """
60 Custom log formatter with color-coded output based on log level.
62 Colors:
63 - DEBUG: violet
64 - INFO: default
65 - WARNING: bold orange
66 - ERROR/CRITICAL: bold red
67 """
69 RED = "\033[91m"
70 VIOLET = "\033[95m"
71 YELLOW = "\033[93m"
72 ORANGE = "\033[33m"
73 BOLD = "\033[1m"
74 UNDERLINE = "\033[4m"
75 reset = "\033[0m"
76 format = "%(asctime)s %(name)10s %(levelname).3s %(message)s "
78 FORMATS = {
79 logging.DEBUG: VIOLET + format + reset,
80 logging.INFO: format,
81 logging.WARNING: BOLD + ORANGE + format + reset,
82 logging.ERROR: BOLD + RED + format + reset,
83 logging.CRITICAL: BOLD + RED + format + reset,
84 }
86 def format(self, record):
87 """
88 Format the log record with appropriate colors.
90 :param record: log record to format
91 :return: formatted string with ANSI color codes
92 """
93 log_fmt = self.FORMATS.get(record.levelno)
94 formatter = logging.Formatter(log_fmt, datefmt="%y-%m-%d %H:%M:%S")
95 return formatter.format(record)
98class Progressbar:
99 """
100 Progress bar wrapper for use with logger.
102 Wraps tqdm with sensible defaults and respects the logger's level.
103 """
105 def __init__(self, logger=None):
106 """
107 Initialize the progress bar.
109 :param logger: logger instance to check level against
110 """
111 self.logger = logger
113 def __call__(self, collection, at_level="info", **kw):
114 """
115 Create a progress bar for iterating over a collection.
117 :param collection: iterable to wrap with progress bar
118 :param at_level: only show progress bar if logger is at this level
119 :param kw: additional arguments passed to tqdm
120 :return: tqdm progress bar iterator
121 """
122 kw.setdefault("bar_format", "{percentage:3.0f}%|{bar:28}| {r_bar:<40} {desc}")
123 kw.setdefault("disable", self.logger.level != logging_levels[at_level])
124 kw.setdefault("colour", "blue")
125 kw.setdefault("mininterval", 1)
126 kw.setdefault("file", sys.stdout)
127 from tqdm import tqdm
129 return tqdm(collection, **kw)
132def set_logger_level(logger, level):
133 """
134 Set the logging level for a logger.
136 :param logger: logger instance
137 :param level: level string ('debug', 'info', 'warning', 'error', 'critical')
138 """
139 logger.setLevel(logging_levels[level])
142def set_all_loggers_level(level):
143 """
144 Set the logging level for all loggers and future loggers.
146 Sets the PYTHON_LOGGER_LEVEL environment variable and updates
147 all existing loggers.
149 :param level: level string ('debug', 'info', 'warning', 'error', 'critical')
150 """
151 os.environ["PYTHON_LOGGER_LEVEL"] = level
153 loggerDict = logging.root.manager.loggerDict
154 for key in loggerDict:
155 try:
156 set_logger_level(logger=loggerDict[key], level=level)
157 except Exception as err:
158 LOGGER.debug(err)
161LOGGER = get_logger(__file__)