File size: 3,985 Bytes
48ae4e0 |
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
"""
Structured JSON Logging Configuration
Provides consistent logging across the application
"""
import logging
import json
import sys
from datetime import datetime
from typing import Any, Dict, Optional
class JSONFormatter(logging.Formatter):
"""Custom JSON formatter for structured logging"""
def format(self, record: logging.LogRecord) -> str:
"""Format log record as JSON"""
log_data = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
# Add extra fields if present
if hasattr(record, 'provider'):
log_data['provider'] = record.provider
if hasattr(record, 'endpoint'):
log_data['endpoint'] = record.endpoint
if hasattr(record, 'duration'):
log_data['duration_ms'] = record.duration
if hasattr(record, 'status'):
log_data['status'] = record.status
if hasattr(record, 'http_code'):
log_data['http_code'] = record.http_code
# Add exception info if present
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
# Add stack trace if present
if record.stack_info:
log_data['stack_trace'] = self.formatStack(record.stack_info)
return json.dumps(log_data)
def setup_logger(name: str, level: str = "INFO") -> logging.Logger:
"""
Setup a logger with JSON formatting
Args:
name: Logger name
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
Returns:
Configured logger instance
"""
logger = logging.getLogger(name)
# Clear any existing handlers
logger.handlers = []
# Set level
logger.setLevel(getattr(logging, level.upper()))
# Create console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(getattr(logging, level.upper()))
# Set JSON formatter
json_formatter = JSONFormatter()
console_handler.setFormatter(json_formatter)
# Add handler to logger
logger.addHandler(console_handler)
# Prevent propagation to root logger
logger.propagate = False
return logger
def log_api_request(
logger: logging.Logger,
provider: str,
endpoint: str,
duration_ms: float,
status: str,
http_code: Optional[int] = None,
level: str = "INFO"
):
"""
Log an API request with structured data
Args:
logger: Logger instance
provider: Provider name
endpoint: API endpoint
duration_ms: Request duration in milliseconds
status: Request status (success/error)
http_code: HTTP status code
level: Log level
"""
log_level = getattr(logging, level.upper())
extra = {
'provider': provider,
'endpoint': endpoint,
'duration': duration_ms,
'status': status,
}
if http_code:
extra['http_code'] = http_code
message = f"{provider} - {endpoint} - {status} - {duration_ms}ms"
logger.log(log_level, message, extra=extra)
def log_error(
logger: logging.Logger,
provider: str,
error_type: str,
error_message: str,
endpoint: Optional[str] = None,
exc_info: bool = False
):
"""
Log an error with structured data
Args:
logger: Logger instance
provider: Provider name
error_type: Type of error
error_message: Error message
endpoint: API endpoint (optional)
exc_info: Include exception info
"""
extra = {
'provider': provider,
'error_type': error_type,
}
if endpoint:
extra['endpoint'] = endpoint
message = f"{provider} - {error_type}: {error_message}"
logger.error(message, extra=extra, exc_info=exc_info)
# Global application logger
app_logger = setup_logger("crypto_monitor", level="INFO")
|