|
1 | 1 | import dataclasses |
2 | 2 | import functools |
3 | 3 | import json |
| 4 | +import logging |
4 | 5 | import math |
5 | 6 | import os |
6 | 7 | import re |
|
12 | 13 | from pathlib import Path |
13 | 14 | from typing import Any |
14 | 15 | from typing import Optional |
| 16 | +from typing import Union |
15 | 17 |
|
16 | 18 | from ktoolbox import common |
17 | 19 | from ktoolbox import host |
|
36 | 38 | ENV_TFT_EXTERNAL_URL = "TFT_EXTERNAL_URL" |
37 | 39 | ENV_TFT_EXTERNAL_SERVER_STRING = "TFT_EXTERNAL_SERVER_STRING" |
38 | 40 |
|
| 41 | +ENV_TFT_LOG_PREAMBLE = "TFT_LOG_PREAMBLE" |
| 42 | + |
39 | 43 |
|
40 | 44 | def get_environ(name: str) -> Optional[str]: |
41 | 45 | # Some environment variables are honored as configuration. |
42 | 46 | # Which ones? Run `git grep -w get_environ`! |
43 | 47 | return common.getenv_config(name) |
44 | 48 |
|
45 | 49 |
|
| 50 | +def _get_log_preamble() -> bool: |
| 51 | + return common.str_to_bool( |
| 52 | + get_environ(ENV_TFT_LOG_PREAMBLE), on_default=True, on_error=True |
| 53 | + ) |
| 54 | + |
| 55 | + |
| 56 | +def configure_logging( |
| 57 | + level: Optional[Union[int, bool, str]], |
| 58 | + *loggers: Union[str, logging.Logger], |
| 59 | +) -> None: |
| 60 | + # Wraps `common.log_config_logger` so the timestamp/thread preamble that |
| 61 | + # ktoolbox always prepends (e.g. "2026-01-01 07:39:38.957 INFO [th:123]:") |
| 62 | + # can be stripped via the TFT_LOG_PREAMBLE env var. |
| 63 | + # Default (true) preserves ktoolbox's built-in formatter. |
| 64 | + common.log_config_logger(level, *loggers) |
| 65 | + |
| 66 | + if _get_log_preamble(): |
| 67 | + return |
| 68 | + |
| 69 | + formatter = logging.Formatter("%(levelname)-7s: %(message)s") |
| 70 | + |
| 71 | + seen: set[int] = set() |
| 72 | + for lg in loggers: |
| 73 | + real_logger = common.ExtendedLogger.unwrap(lg) |
| 74 | + cur: Optional[logging.Logger] = real_logger |
| 75 | + while cur is not None: |
| 76 | + for h in cur.handlers: |
| 77 | + if id(h) in seen: |
| 78 | + continue |
| 79 | + seen.add(id(h)) |
| 80 | + h.setFormatter(formatter) |
| 81 | + if not cur.propagate: |
| 82 | + break |
| 83 | + cur = cur.parent |
| 84 | + |
| 85 | + |
46 | 86 | def _is_ib_test_type(test_type: "TestType") -> bool: |
47 | 87 | """Check if the test type requires RDMA/IB tools.""" |
48 | 88 | return test_type in (TestType.IB_WRITE_BW, TestType.IB_READ_BW, TestType.IB_SEND_BW) |
|
0 commit comments