forked from Alishahryar1/free-claude-code
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlogging_config.py
More file actions
88 lines (69 loc) · 2.64 KB
/
logging_config.py
File metadata and controls
88 lines (69 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Loguru-based structured logging configuration.
All logs are written to server.log as JSON lines for full traceability.
Stdlib logging is intercepted and funneled to loguru.
Context vars (request_id, node_id, chat_id) from contextualize() are
included at top level for easy grep/filter.
"""
import json
import logging
from loguru import logger
_configured = False
# Context keys we promote to top-level JSON for traceability
_CONTEXT_KEYS = ("request_id", "node_id", "chat_id")
def _serialize_with_context(record) -> str:
"""Format record as JSON with context vars at top level.
Returns a format template; we inject _json into record for output.
"""
extra = record.get("extra", {})
out = {
"time": str(record["time"]),
"level": record["level"].name,
"message": record["message"],
"module": record["name"],
"function": record["function"],
"line": record["line"],
}
for key in _CONTEXT_KEYS:
if key in extra and extra[key] is not None:
out[key] = extra[key]
record["_json"] = json.dumps(out, default=str)
return "{_json}\n"
class InterceptHandler(logging.Handler):
"""Redirect stdlib logging to loguru."""
def emit(self, record: logging.LogRecord) -> None:
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
frame, depth = logging.currentframe(), 2
while frame is not None and frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(
level, record.getMessage()
)
def configure_logging(log_file: str, *, force: bool = False) -> None:
"""Configure loguru with JSON output to log_file and intercept stdlib logging.
Idempotent: skips if already configured (e.g. hot reload).
Use force=True to reconfigure (e.g. in tests with a different log path).
"""
global _configured
if _configured and not force:
return
_configured = True
# Remove default loguru handler (writes to stderr)
logger.remove()
# Truncate log file on fresh start for clean debugging
open(log_file, "w", encoding="utf-8").close()
# Add file sink: JSON lines, DEBUG level, context vars at top level
logger.add(
log_file,
level="DEBUG",
format=_serialize_with_context,
encoding="utf-8",
mode="a",
)
# Intercept stdlib logging: route all root logger output to loguru
intercept = InterceptHandler()
logging.root.handlers = [intercept]
logging.root.setLevel(logging.DEBUG)