-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogger.py
More file actions
124 lines (109 loc) · 4.04 KB
/
logger.py
File metadata and controls
124 lines (109 loc) · 4.04 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
123
124
import sys
from pathlib import Path
from loguru import logger
from typing import Dict, Union, Optional
import json
from datetime import datetime
import traceback
class LogSetupError(Exception):
"""Raised when log setup fails"""
pass
def format_error_context(error: Exception, request_id: Optional[str] = None, **kwargs) -> str:
"""Format error context as JSON for structured logging.
Args:
error: The exception to format
request_id: Optional request identifier for tracing
**kwargs: Additional context to include in the output
Returns:
JSON string containing error details and context
"""
context = {
'error_type': error.__class__.__name__,
'error_message': str(error),
'timestamp': datetime.utcnow().isoformat(),
'request_id': request_id,
'traceback': getattr(error, '__traceback__', None) and ''.join(traceback.format_tb(error.__traceback__)),
**kwargs
}
return json.dumps({k: v for k, v in context.items() if v is not None})
def setup_logging(config: Dict[str, Union[str, bool, "development" | "production"]]) -> None:
"""Configure logging based on environment settings.
Args:
config: Dictionary containing logging configuration with keys:
- log_level: Logging level (default: "INFO")
- debug: Enable debug mode (default: False)
- env: Environment name (default: "development")
Raises:
LogSetupError: If log directory creation or setup fails
"""
log_level = config.get("log_level", "INFO")
debug = config.get("debug", False)
env = config.get("env", "development")
# Remove default handler
logger.remove()
# Configure format based on environment
log_format = (
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
"<level>{message}</level>"
)
if env == "development":
# Development: Log to console with debug info if enabled
logger.add(
sys.stderr,
format=log_format,
level="DEBUG" if debug else log_level,
backtrace=True,
diagnose=True,
)
else:
# Production: Log to file with rotation
try:
log_path = Path("logs")
log_path.mkdir(exist_ok=True)
except Exception as e:
raise LogSetupError(f"Failed to create log directory: {e}")
logger.add(
log_path / "app.log",
format=log_format,
level=log_level,
rotation="500 MB",
retention="10 days",
compression="zip",
backtrace=False,
diagnose=False,
)
# Add separate error log file
try:
# Add enhanced error logging with structured format
error_format = (
"<red>{time:YYYY-MM-DD HH:mm:ss}</red> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
"<level>{message}</level> | "
"Context: {extra}"
)
logger.add(
log_path / "error.log",
format=error_format,
level="ERROR",
rotation="100 MB",
retention="30 days",
compression="zip",
backtrace=True,
diagnose=True,
catch=True, # Catch any exceptions during logging
)
except Exception as e:
raise LogSetupError(f"Failed to setup error logging: {e}")
if debug:
# Add debug log file if debug is enabled in production
logger.add(
log_path / "debug.log",
format=log_format,
level="DEBUG",
rotation="1 GB",
retention="3 days",
compression="zip",
)