|
1 | 1 | """Logging related items.""" |
2 | 2 |
|
3 | 3 | import logging |
4 | | -from typing import cast |
| 4 | +import os |
| 5 | +import sys |
| 6 | +from typing import Any, Optional, cast |
5 | 7 |
|
6 | | -import coloredlogs |
7 | | -from colorama import Fore |
| 8 | +from rich.console import Console |
| 9 | +from rich.highlighter import NullHighlighter |
| 10 | +from rich.logging import RichHandler |
8 | 11 |
|
9 | 12 | from dfetch import __version__ |
10 | 13 |
|
11 | 14 |
|
| 15 | +def make_console(no_color: bool = False) -> Console: |
| 16 | + """Create a Rich Console with proper color handling.""" |
| 17 | + return Console( |
| 18 | + no_color=no_color |
| 19 | + or os.getenv("NO_COLOR") is not None |
| 20 | + or not sys.stdout.isatty() |
| 21 | + ) |
| 22 | + |
| 23 | + |
| 24 | +def configure_root_logger(console: Optional[Console] = None) -> None: |
| 25 | + """Configure the root logger with RichHandler using the provided Console.""" |
| 26 | + console = console or make_console() |
| 27 | + |
| 28 | + handler = RichHandler( |
| 29 | + console=console, |
| 30 | + show_time=False, |
| 31 | + show_path=False, |
| 32 | + show_level=False, |
| 33 | + markup=True, |
| 34 | + rich_tracebacks=True, |
| 35 | + highlighter=NullHighlighter(), |
| 36 | + ) |
| 37 | + |
| 38 | + logging.basicConfig( |
| 39 | + level=logging.INFO, |
| 40 | + format="%(message)s", |
| 41 | + handlers=[handler], |
| 42 | + force=True, |
| 43 | + ) |
| 44 | + |
| 45 | + |
12 | 46 | class DLogger(logging.Logger): |
13 | 47 | """Logging class extended with specific log items for dfetch.""" |
14 | 48 |
|
15 | 49 | def print_info_line(self, name: str, info: str) -> None: |
16 | 50 | """Print a line of info.""" |
17 | | - self.info(f" {Fore.GREEN}{name:20s}:{Fore.BLUE} {info}") |
| 51 | + self.info( |
| 52 | + f" [bold][bright_green]{name:20s}:[/bright_green][blue] {info}[/blue][/bold]" |
| 53 | + ) |
18 | 54 |
|
19 | 55 | def print_warning_line(self, name: str, info: str) -> None: |
20 | | - """Print a line of info.""" |
21 | | - self.info(f" {Fore.GREEN}{name:20s}:{Fore.YELLOW} {info}") |
| 56 | + """Print a warning line: green name, yellow value.""" |
| 57 | + self.warning( |
| 58 | + f" [bold][bright_green]{name:20s}:[/bright_green][bright_yellow] {info}[/bright_yellow][/bold]" |
| 59 | + ) |
22 | 60 |
|
23 | 61 | def print_title(self) -> None: |
24 | 62 | """Print the DFetch tool title and version.""" |
25 | | - self.info(f"{Fore.BLUE}Dfetch ({__version__})") |
| 63 | + self.info(f"[bold blue]Dfetch ({__version__})[/bold blue]") |
26 | 64 |
|
27 | 65 | def print_info_field(self, field_name: str, field: str) -> None: |
28 | 66 | """Print a field with corresponding value.""" |
29 | 67 | self.print_info_line(field_name, field if field else "<none>") |
30 | 68 |
|
| 69 | + def warning(self, msg: object, *args: Any, **kwargs: Any) -> None: |
| 70 | + """Log warning.""" |
| 71 | + super().warning( |
| 72 | + f"[bold bright_yellow]{msg}[/bold bright_yellow]", *args, **kwargs |
| 73 | + ) |
31 | 74 |
|
32 | | -def setup_root(name: str) -> DLogger: |
33 | | - """Create the root logger.""" |
34 | | - logger = get_logger(name) |
35 | | - |
36 | | - msg_format = "%(message)s" |
| 75 | + def error(self, msg: object, *args: Any, **kwargs: Any) -> None: |
| 76 | + """Log error.""" |
| 77 | + super().error(f"[red]{msg}[/red]", *args, **kwargs) |
37 | 78 |
|
38 | | - level_style = { |
39 | | - "critical": {"color": "magenta", "bright": True, "bold": True}, |
40 | | - "debug": {"color": "green", "bright": True, "bold": True}, |
41 | | - "error": {"color": "red", "bright": True, "bold": True}, |
42 | | - "info": {"color": 4, "bright": True, "bold": True}, |
43 | | - "notice": {"color": "magenta", "bright": True, "bold": True}, |
44 | | - "spam": {"color": "green", "faint": True}, |
45 | | - "success": {"color": "green", "bright": True, "bold": True}, |
46 | | - "verbose": {"color": "blue", "bright": True, "bold": True}, |
47 | | - "warning": {"color": "yellow", "bright": True, "bold": True}, |
48 | | - } |
49 | 79 |
|
50 | | - coloredlogs.install(fmt=msg_format, level_styles=level_style, level="INFO") |
51 | | - |
52 | | - return logger |
| 80 | +def setup_root(name: str, console: Optional[Console] = None) -> DLogger: |
| 81 | + """Create and return the root logger.""" |
| 82 | + logging.setLoggerClass(DLogger) |
| 83 | + configure_root_logger(console) |
| 84 | + logger = logging.getLogger(name) |
| 85 | + return cast(DLogger, logger) |
53 | 86 |
|
54 | 87 |
|
55 | 88 | def increase_verbosity() -> None: |
56 | | - """Increase the verbosity of the logger.""" |
57 | | - coloredlogs.increase_verbosity() |
58 | | - |
59 | | - |
60 | | -def get_logger(name: str) -> DLogger: |
61 | | - """Get logger for a module.""" |
| 89 | + """Increase verbosity of the root logger.""" |
| 90 | + levels = [ |
| 91 | + logging.CRITICAL, |
| 92 | + logging.ERROR, |
| 93 | + logging.WARNING, |
| 94 | + logging.INFO, |
| 95 | + logging.DEBUG, |
| 96 | + ] |
| 97 | + logger_ = logging.getLogger() |
| 98 | + current_level = logger_.getEffectiveLevel() |
| 99 | + try: |
| 100 | + idx = levels.index(current_level) |
| 101 | + if idx < len(levels) - 1: |
| 102 | + new_level = levels[idx + 1] |
| 103 | + else: |
| 104 | + new_level = levels[-1] |
| 105 | + except ValueError: |
| 106 | + new_level = logging.DEBUG |
| 107 | + logger_.setLevel(new_level) |
| 108 | + |
| 109 | + |
| 110 | +def get_logger(name: str, console: Optional[Console] = None) -> DLogger: |
| 111 | + """Get logger for a module, optionally configuring console colors.""" |
62 | 112 | logging.setLoggerClass(DLogger) |
63 | | - return cast(DLogger, logging.getLogger(name)) |
| 113 | + logger = logging.getLogger(name) |
| 114 | + logger.propagate = True |
| 115 | + if console: |
| 116 | + configure_root_logger(console) |
| 117 | + return cast(DLogger, logger) |
64 | 118 |
|
65 | 119 |
|
66 | 120 | def configure_external_logger(name: str, level: int = logging.INFO) -> None: |
67 | 121 | """Configure an external logger from a third party package.""" |
68 | 122 | logger = logging.getLogger(name) |
69 | 123 | logger.setLevel(level) |
70 | 124 | logger.propagate = True |
| 125 | + logger.handlers.clear() |
0 commit comments