-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathlogging_config.py
More file actions
122 lines (99 loc) · 3.78 KB
/
logging_config.py
File metadata and controls
122 lines (99 loc) · 3.78 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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
Centralized logging configuration for the Agentic RAG application.
Provides structured JSON logging with context fields for debugging and audit trails.
"""
import logging
import json
import sys
from datetime import datetime
from typing import Any, Dict
class StructuredFormatter(logging.Formatter):
"""
Custom JSON formatter for structured logging.
Outputs logs in JSON format with timestamp, level, logger name, message,
and any extra context fields passed via the 'extra' parameter.
"""
def format(self, record: logging.LogRecord) -> str:
"""Format log record as JSON."""
log_data: Dict[str, Any] = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
# Add extra context fields from record attributes
# Skip private/internal attributes
for key, value in record.__dict__.items():
if not key.startswith("_") and key not in [
"name",
"msg",
"args",
"created",
"filename",
"funcName",
"levelname",
"levelno",
"lineno",
"module",
"msecs",
"message",
"pathname",
"process",
"processName",
"relativeCreated",
"thread",
"threadName",
"exc_info",
"exc_text",
"stack_info",
]:
# Only add simple JSON-serializable types
if isinstance(value, (str, int, float, bool, list, dict, type(None))):
log_data[key] = value
# Add exception info if present
if record.exc_info:
log_data["exception"] = self.formatException(record.exc_info)
return json.dumps(log_data)
class ContextAdapter(logging.LoggerAdapter):
"""
Logger adapter that handles extra context fields.
Wraps the standard logger to properly handle the 'extra' parameter
and pass it to the StructuredFormatter.
"""
def process(self, msg: str, kwargs: Dict[str, Any]) -> tuple:
"""Process log call to add extra fields to record."""
# Extra fields are already handled by the formatter
# Just pass them through
return msg, kwargs
def setup_logging(level: str = "INFO") -> None:
"""
Initialize structured logging configuration.
Args:
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
"""
# Convert string level to logging constant
numeric_level = getattr(logging, level.upper(), logging.INFO)
# Create console handler with JSON formatter
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(StructuredFormatter())
# Configure root logger
root_logger = logging.getLogger()
root_logger.setLevel(numeric_level)
root_logger.handlers.clear() # Remove existing handlers
root_logger.addHandler(handler)
# Reduce noise from third-party libraries
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)
logging.getLogger("pymilvus").setLevel(logging.WARNING)
logging.getLogger("grpc").setLevel(logging.WARNING)
def get_logger(name: str) -> logging.LoggerAdapter:
"""
Get a module-specific logger with context support.
Args:
name: Logger name (typically module path like 'nodes.authorization')
Returns:
Logger adapter that supports extra context fields
"""
base_logger = logging.getLogger(f"agentic_rag.{name}")
return ContextAdapter(base_logger, {})