|
6 | 6 |
|
7 | 7 | import logging |
8 | 8 | import sys |
9 | | -from typing import Optional |
| 9 | +from logging.handlers import TimedRotatingFileHandler |
| 10 | +from pathlib import Path |
| 11 | +from typing import Any, Optional, Tuple |
| 12 | + |
| 13 | + |
| 14 | +def _load_log_config() -> Tuple[str, str, str, Optional[Any]]: |
| 15 | + config = None |
| 16 | + try: |
| 17 | + from openviking_cli.utils.config import get_openviking_config |
| 18 | + |
| 19 | + config = get_openviking_config() |
| 20 | + log_level_str = config.log.level.upper() |
| 21 | + log_format = config.log.format |
| 22 | + log_output = config.log.output |
| 23 | + |
| 24 | + if log_output == "file": |
| 25 | + workspace_path = Path(config.storage.workspace).resolve() |
| 26 | + log_dir = workspace_path / "log" |
| 27 | + log_dir.mkdir(parents=True, exist_ok=True) |
| 28 | + log_output = str(log_dir / "openviking.log") |
| 29 | + except Exception: |
| 30 | + log_level_str = "INFO" |
| 31 | + log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" |
| 32 | + log_output = "stdout" |
| 33 | + |
| 34 | + return log_level_str, log_format, log_output, config |
| 35 | + |
| 36 | + |
| 37 | +def _create_log_handler(log_output: str, config: Optional[Any]) -> logging.Handler: |
| 38 | + # Prevent creating a file literally named "file" |
| 39 | + if log_output == "file": |
| 40 | + log_output = "stdout" |
| 41 | + |
| 42 | + if log_output == "stdout": |
| 43 | + return logging.StreamHandler(sys.stdout) |
| 44 | + elif log_output == "stderr": |
| 45 | + return logging.StreamHandler(sys.stderr) |
| 46 | + else: |
| 47 | + if config is not None: |
| 48 | + try: |
| 49 | + log_rotation = config.log.rotation |
| 50 | + if log_rotation: |
| 51 | + log_rotation_days = config.log.rotation_days |
| 52 | + log_rotation_interval = config.log.rotation_interval |
| 53 | + |
| 54 | + if log_rotation_interval == "midnight": |
| 55 | + when = "midnight" |
| 56 | + interval = 1 |
| 57 | + else: |
| 58 | + when = log_rotation_interval |
| 59 | + interval = 1 |
| 60 | + |
| 61 | + return TimedRotatingFileHandler( |
| 62 | + log_output, |
| 63 | + when=when, |
| 64 | + interval=interval, |
| 65 | + backupCount=log_rotation_days, |
| 66 | + encoding="utf-8", |
| 67 | + ) |
| 68 | + else: |
| 69 | + return logging.FileHandler(log_output, encoding="utf-8") |
| 70 | + except Exception: |
| 71 | + return logging.FileHandler(log_output, encoding="utf-8") |
| 72 | + else: |
| 73 | + return logging.FileHandler(log_output, encoding="utf-8") |
10 | 74 |
|
11 | 75 |
|
12 | 76 | def get_logger( |
13 | 77 | name: str = "openviking", |
14 | 78 | format_string: Optional[str] = None, |
15 | 79 | ) -> logging.Logger: |
16 | | - """ |
17 | | - Get a configured logger. |
18 | | -
|
19 | | - Args: |
20 | | - name: Logger name |
21 | | - format_string: Custom format string (overrides config) |
22 | | -
|
23 | | - Returns: |
24 | | - Configured logger |
25 | | - """ |
26 | 80 | logger = logging.getLogger(name) |
27 | 81 |
|
28 | 82 | if not logger.handlers: |
29 | | - try: |
30 | | - from openviking_cli.utils.config import get_openviking_config |
31 | | - |
32 | | - config = get_openviking_config() |
33 | | - log_level_str = config.log_level.upper() |
34 | | - log_format = config.log_format |
35 | | - log_output = config.log_output |
36 | | - except Exception: |
37 | | - log_level_str = "INFO" |
38 | | - log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" |
39 | | - log_output = "stdout" |
40 | | - |
| 83 | + log_level_str, log_format, log_output, config = _load_log_config() |
41 | 84 | level = getattr(logging, log_level_str, logging.INFO) |
42 | | - |
43 | | - if log_output == "stdout": |
44 | | - handler = logging.StreamHandler(sys.stdout) |
45 | | - elif log_output == "stderr": |
46 | | - handler = logging.StreamHandler(sys.stderr) |
47 | | - else: |
48 | | - handler = logging.FileHandler(log_output) |
| 85 | + handler = _create_log_handler(log_output, config) |
49 | 86 |
|
50 | 87 | if format_string is None: |
51 | 88 | format_string = log_format |
52 | | - |
53 | 89 | formatter = logging.Formatter(format_string) |
54 | 90 | handler.setFormatter(formatter) |
55 | 91 | logger.addHandler(handler) |
56 | 92 | logger.propagate = False |
57 | | - |
58 | 93 | logger.setLevel(level) |
59 | 94 |
|
60 | 95 | return logger |
61 | 96 |
|
62 | 97 |
|
63 | 98 | # Default logger instance |
64 | 99 | default_logger = get_logger() |
| 100 | + |
| 101 | + |
| 102 | +def configure_uvicorn_logging() -> None: |
| 103 | + """Configure Uvicorn loggers to use OpenViking's logging configuration. |
| 104 | +
|
| 105 | + This function configures the 'uvicorn', 'uvicorn.error', and 'uvicorn.access' |
| 106 | + loggers to use the same handlers and format as our openviking loggers. |
| 107 | + """ |
| 108 | + log_level_str, log_format, log_output, config = _load_log_config() |
| 109 | + level = getattr(logging, log_level_str, logging.INFO) |
| 110 | + handler = _create_log_handler(log_output, config) |
| 111 | + formatter = logging.Formatter(log_format) |
| 112 | + handler.setFormatter(formatter) |
| 113 | + |
| 114 | + # Configure all Uvicorn loggers |
| 115 | + uvicorn_logger_names = ["uvicorn", "uvicorn.error", "uvicorn.access"] |
| 116 | + for logger_name in uvicorn_logger_names: |
| 117 | + logger = logging.getLogger(logger_name) |
| 118 | + logger.handlers.clear() |
| 119 | + logger.addHandler(handler) |
| 120 | + logger.setLevel(level) |
| 121 | + logger.propagate = False |
0 commit comments