|
26 | 26 | # 4. Handles status updates |
27 | 27 |
|
28 | 28 | import argparse |
| 29 | +import copy |
29 | 30 | import logging |
30 | 31 | import sys |
31 | 32 | import time |
|
36 | 37 |
|
37 | 38 | import yaml |
38 | 39 |
|
39 | | -from console_color import init_windows_console |
| 40 | +from console_color import init_windows_console, color_segment, RED, YELLOW |
40 | 41 | from config_manager import ConfigManager |
41 | 42 | from api_client import APIClient, resolve_scope_id_from_identifier |
42 | 43 | from task_manager import TaskManager |
|
49 | 50 | from cmd_doctor import cmd_doctor |
50 | 51 | from cmd_projects import cmd_projects |
51 | 52 |
|
| 53 | +_LEVEL_ABBREV = { |
| 54 | + "DEBUG": "DEBG", |
| 55 | + "INFO": "INFO", |
| 56 | + "WARNING": "WARN", |
| 57 | + "ERROR": "ERRO", |
| 58 | + "CRITICAL": "CRIT", |
| 59 | +} |
| 60 | + |
| 61 | +_LEVEL_COLOR = { |
| 62 | + "WARNING": YELLOW, |
| 63 | + "ERROR": RED, |
| 64 | + "CRITICAL": RED, |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +class _ShortLevelFormatter(logging.Formatter): |
| 69 | + """Formats the log level as a fixed 4-letter abbreviation.""" |
| 70 | + |
| 71 | + def format(self, record: logging.LogRecord) -> str: |
| 72 | + record = copy.copy(record) |
| 73 | + record.levelname = _LEVEL_ABBREV.get(record.levelname, record.levelname[:4].upper()) |
| 74 | + return super().format(record) |
| 75 | + |
| 76 | + |
| 77 | +class _ColoredShortLevelFormatter(logging.Formatter): |
| 78 | + """Like _ShortLevelFormatter but colorizes WARN/ERRO/CRIT on color-capable streams.""" |
| 79 | + |
| 80 | + def __init__(self, fmt: str, stream) -> None: |
| 81 | + super().__init__(fmt) |
| 82 | + self._stream = stream |
| 83 | + |
| 84 | + def format(self, record: logging.LogRecord) -> str: |
| 85 | + record = copy.copy(record) |
| 86 | + color = _LEVEL_COLOR.get(record.levelname) |
| 87 | + abbrev = _LEVEL_ABBREV.get(record.levelname, record.levelname[:4].upper()) |
| 88 | + record.levelname = color_segment(color, abbrev, self._stream) if color else abbrev |
| 89 | + return super().format(record) |
| 90 | + |
| 91 | + |
52 | 92 | def setup_logging(): |
53 | 93 | """Configure logging for the application.""" |
54 | 94 | log_dir = Path("logs") |
55 | 95 | log_dir.mkdir(exist_ok=True) |
56 | 96 |
|
57 | | - # Full logging: format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
58 | | - logging.basicConfig( |
59 | | - level=logging.INFO, |
60 | | - format='%(levelname)s - %(message)s', |
61 | | - handlers=[ |
62 | | - RotatingFileHandler( |
63 | | - log_dir / 'observatory.log', |
64 | | - maxBytes=1024*1024, |
65 | | - backupCount=5 |
66 | | - ), |
67 | | - logging.StreamHandler(sys.stdout) |
68 | | - ] |
| 97 | + fmt = "%(levelname)s %(message)s" |
| 98 | + stdout_handler = logging.StreamHandler(sys.stdout) |
| 99 | + stdout_handler.setFormatter(_ColoredShortLevelFormatter(fmt, sys.stdout)) |
| 100 | + |
| 101 | + file_handler = RotatingFileHandler( |
| 102 | + log_dir / 'observatory.log', |
| 103 | + maxBytes=1024*1024, |
| 104 | + backupCount=5, |
69 | 105 | ) |
| 106 | + file_handler.setFormatter(_ShortLevelFormatter(fmt)) |
| 107 | + |
| 108 | + logging.basicConfig(level=logging.INFO, handlers=[file_handler, stdout_handler]) |
70 | 109 |
|
71 | 110 |
|
72 | 111 | class ObservatoryAutomation: |
|
0 commit comments