|
8 | 8 | import inspect
|
9 | 9 |
|
10 | 10 | import numpy as np
|
| 11 | +from rich.logging import RichHandler |
| 12 | +from rich.console import Console |
11 | 13 |
|
12 | 14 | from . import utils
|
13 | 15 |
|
@@ -286,57 +288,95 @@ def as_effect_dict_with_ts(name_of_param: str,
|
286 | 288 | return effect_ts_dict
|
287 | 289 |
|
288 | 290 |
|
289 |
| -# TODO: Move logging to utils.py |
290 |
| -class CustomFormatter(logging.Formatter): |
| 291 | +class MultilineFormater(logging.Formatter): |
| 292 | + |
| 293 | + def format(self, record): |
| 294 | + message_lines = record.getMessage().split('\n') |
| 295 | + |
| 296 | + # Prepare the log prefix (timestamp + log level) |
| 297 | + timestamp = self.formatTime(record, self.datefmt) |
| 298 | + log_level = record.levelname.ljust(8) # Align log levels for consistency |
| 299 | + log_prefix = f"{timestamp} | {log_level} |" |
| 300 | + |
| 301 | + # Format all lines |
| 302 | + first_line = [f'{log_prefix} {message_lines[0]}'] |
| 303 | + if len(message_lines) > 1: |
| 304 | + lines = first_line + [f"{log_prefix} {line}" for line in message_lines[1:]] |
| 305 | + else: |
| 306 | + lines = first_line |
| 307 | + |
| 308 | + return '\n'.join(lines) |
| 309 | + |
| 310 | + |
| 311 | +class ColoredMultilineFormater(MultilineFormater): |
291 | 312 | # ANSI escape codes for colors
|
292 | 313 | COLORS = {
|
293 |
| - 'DEBUG': '\033[96m', # Cyan |
294 |
| - 'INFO': '\033[92m', # Green |
295 |
| - 'WARNING': '\033[93m', # Yellow |
296 |
| - 'ERROR': '\033[91m', # Red |
297 |
| - 'CRITICAL': '\033[91m\033[1m', # Bold Red |
| 314 | + 'DEBUG': '\033[32m', # Green |
| 315 | + 'INFO': '\033[34m', # Blue |
| 316 | + 'WARNING': '\033[33m', # Yellow |
| 317 | + 'ERROR': '\033[31m', # Red |
| 318 | + 'CRITICAL': '\033[1m\033[31m', # Bold Red |
298 | 319 | }
|
299 | 320 | RESET = '\033[0m'
|
300 | 321 |
|
301 | 322 | def format(self, record):
|
| 323 | + lines = super().format(record).splitlines() |
302 | 324 | log_color = self.COLORS.get(record.levelname, self.RESET)
|
303 |
| - original_message = record.getMessage() |
304 |
| - message_lines = original_message.split('\n') |
305 | 325 |
|
306 | 326 | # Create a formatted message for each line separately
|
307 | 327 | formatted_lines = []
|
308 |
| - for line in message_lines: |
309 |
| - temp_record = logging.LogRecord( |
310 |
| - record.name, record.levelno, record.pathname, record.lineno, |
311 |
| - line, record.args, record.exc_info, record.funcName, record.stack_info |
312 |
| - ) |
313 |
| - formatted_line = super().format(temp_record) |
314 |
| - formatted_lines.append(f"{log_color}{formatted_line}{self.RESET}") |
315 |
| - |
316 |
| - formatted_message = '\n'.join(formatted_lines) |
317 |
| - return formatted_message |
318 |
| - |
319 |
| - |
320 |
| -def setup_logging(level_name: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']): |
| 328 | + for line in lines: |
| 329 | + formatted_lines.append(f"{log_color}{line}{self.RESET}") |
| 330 | + |
| 331 | + return '\n'.join(formatted_lines) |
| 332 | + |
| 333 | + |
| 334 | +def _get_logging_handler(log_file: Optional[str] = None, |
| 335 | + use_rich_handler: bool = False) -> logging.Handler: |
| 336 | + """Returns a logging handler for the given log file.""" |
| 337 | + if use_rich_handler and log_file is None: |
| 338 | + # RichHandler for console output |
| 339 | + console = Console(width=120) |
| 340 | + rich_handler = RichHandler( |
| 341 | + console=console, |
| 342 | + rich_tracebacks=True, |
| 343 | + omit_repeated_times=True, |
| 344 | + show_path=False, |
| 345 | + log_time_format="%Y-%m-%d %H:%M:%S", |
| 346 | + ) |
| 347 | + rich_handler.setFormatter(logging.Formatter("%(message)s")) # Simplified formatting |
| 348 | + |
| 349 | + return rich_handler |
| 350 | + elif log_file is None: |
| 351 | + # Regular Logger with custom formating enabled |
| 352 | + file_handler = logging.StreamHandler() |
| 353 | + file_handler.setFormatter(ColoredMultilineFormater( |
| 354 | + fmt="%(message)s", |
| 355 | + datefmt="%Y-%m-%d %H:%M:%S", |
| 356 | + )) |
| 357 | + return file_handler |
| 358 | + else: |
| 359 | + # FileHandler for file output |
| 360 | + file_handler = logging.FileHandler(log_file) |
| 361 | + file_handler.setFormatter(MultilineFormater( |
| 362 | + fmt="%(message)s", |
| 363 | + datefmt="%Y-%m-%d %H:%M:%S", |
| 364 | + )) |
| 365 | + return file_handler |
| 366 | + |
| 367 | +def setup_logging(default_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO', |
| 368 | + log_file: Optional[str] = 'flixOpt.log', |
| 369 | + use_rich_handler: bool = False): |
321 | 370 | """Setup logging configuration"""
|
322 | 371 | logger = logging.getLogger('flixOpt') # Use a specific logger name for your package
|
323 |
| - logging_level = get_logging_level_by_name(level_name) |
324 |
| - logger.setLevel(logging_level) |
325 |
| - |
| 372 | + logger.setLevel(get_logging_level_by_name(default_level)) |
326 | 373 | # Clear existing handlers
|
327 | 374 | if logger.hasHandlers():
|
328 | 375 | logger.handlers.clear()
|
329 | 376 |
|
330 |
| - # Create console handler |
331 |
| - c_handler = logging.StreamHandler() |
332 |
| - c_handler.setLevel(logging_level) |
333 |
| - |
334 |
| - # Create a clean and aligned formatter |
335 |
| - log_format = '%(asctime)s - %(levelname)-8s : %(message)s' |
336 |
| - date_format = '%Y-%m-%d %H:%M:%S' |
337 |
| - c_format = CustomFormatter(log_format, datefmt=date_format) |
338 |
| - c_handler.setFormatter(c_format) |
339 |
| - logger.addHandler(c_handler) |
| 377 | + logger.addHandler(_get_logging_handler(use_rich_handler=use_rich_handler)) |
| 378 | + if log_file is not None: |
| 379 | + logger.addHandler(_get_logging_handler(log_file, use_rich_handler=False)) |
340 | 380 |
|
341 | 381 | return logger
|
342 | 382 |
|
|
0 commit comments