|
1 | 1 | import logging |
2 | 2 | import os |
| 3 | +from datetime import datetime, timezone |
3 | 4 | from typing import Any, Dict, Union |
4 | 5 |
|
5 | 6 | from rich.console import Console |
|
9 | 10 |
|
10 | 11 | from dstack._internal.cli.utils.rich import DstackRichHandler |
11 | 12 | from dstack._internal.core.errors import CLIError, DstackError |
| 13 | +from dstack._internal.utils.common import get_dstack_dir |
12 | 14 |
|
13 | 15 | _colors = { |
14 | 16 | "secondary": "grey58", |
@@ -37,10 +39,51 @@ def cli_error(e: DstackError) -> CLIError: |
37 | 39 |
|
38 | 40 | def configure_logging(): |
39 | 41 | dstack_logger = logging.getLogger("dstack") |
40 | | - dstack_logger.setLevel(os.getenv("DSTACK_CLI_LOG_LEVEL", "INFO").upper()) |
41 | | - handler = DstackRichHandler(console=console) |
42 | | - handler.setFormatter(logging.Formatter(fmt="%(message)s", datefmt="[%X]")) |
43 | | - dstack_logger.addHandler(handler) |
| 42 | + dstack_logger.handlers.clear() |
| 43 | + |
| 44 | + log_dir = get_dstack_dir() / "logs" / "cli" |
| 45 | + log_file = log_dir / "latest.log" |
| 46 | + |
| 47 | + if log_file.exists(): |
| 48 | + file_mtime = datetime.fromtimestamp(log_file.stat().st_mtime, tz=timezone.utc) |
| 49 | + current_date = datetime.now(timezone.utc).date() |
| 50 | + |
| 51 | + if file_mtime.date() < current_date: |
| 52 | + date_str = file_mtime.strftime("%Y-%m-%d") |
| 53 | + rotated_file = log_dir / f"{date_str}.log" |
| 54 | + |
| 55 | + counter = 1 |
| 56 | + while rotated_file.exists(): |
| 57 | + rotated_file = log_dir / f"{date_str}-{counter}.log" |
| 58 | + counter += 1 |
| 59 | + |
| 60 | + log_file.rename(rotated_file) |
| 61 | + |
| 62 | + level_names = logging.getLevelNamesMapping() |
| 63 | + stdout_level_name = os.getenv("DSTACK_CLI_LOG_LEVEL", "INFO").upper() |
| 64 | + stdout_level = level_names[stdout_level_name] |
| 65 | + dstack_logger.setLevel(stdout_level) |
| 66 | + |
| 67 | + stdout_handler = DstackRichHandler(console=console) |
| 68 | + stdout_handler.setFormatter(logging.Formatter(fmt="%(message)s", datefmt="[%X]")) |
| 69 | + stdout_handler.setLevel(stdout_level) |
| 70 | + dstack_logger.addHandler(stdout_handler) |
| 71 | + |
| 72 | + file_level_name = os.getenv("DSTACK_CLI_FILE_LOG_LEVEL", "DEBUG").upper() |
| 73 | + file_level = level_names[file_level_name] |
| 74 | + |
| 75 | + log_dir.mkdir(parents=True, exist_ok=True) |
| 76 | + |
| 77 | + file_handler = logging.FileHandler(log_file) |
| 78 | + file_handler.setFormatter( |
| 79 | + logging.Formatter( |
| 80 | + fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S" |
| 81 | + ) |
| 82 | + ) |
| 83 | + file_handler.setLevel(file_level) |
| 84 | + dstack_logger.addHandler(file_handler) |
| 85 | + |
| 86 | + dstack_logger.setLevel(min(stdout_level, file_level)) |
44 | 87 |
|
45 | 88 |
|
46 | 89 | def confirm_ask(prompt, **kwargs) -> bool: |
|
0 commit comments