refactor(jd): 优化京东服务异常处理与日志格式

- 引入 JDServiceException 统一处理京东相关异常
- 针对风控错误增加自动重试及代理池失效代理清理机制
- 调整请求重试次数从3次改为2次,提升效率
- 将服务端异常返回改为抛出异常,简化调用逻辑
- 优化 app_store.py 中异常捕获及日志输出逻辑
- ctrip.py 中订单提交相关接口改用抛出异常替代返回错误码
- 增加 _delete_proxy 方法用于代理失效处理
- 修改日志格式化器,生产环境使用 JSON,开发环境输出可读性更好的格式
- 统一日志时间使用本地时间替代 UTC 时间,提升时间可读性
- 完善 trace_id 上下文传递,日志中自动带入 trace_id 信息
This commit is contained in:
danial
2025-11-03 23:48:09 +08:00
parent 6c768b6e7b
commit ef4390217f
3 changed files with 93 additions and 150 deletions

View File

@@ -18,37 +18,37 @@ trace_id_var: ContextVar[str] = ContextVar("trace_id", default="")
class TraceContextFormatter(logging.Formatter):
"""
Custom formatter that adds trace context to log records.
Formats logs as JSON in production, human-readable in development.
"""
def __init__(self, use_json: bool = True):
"""
Initialize formatter.
Args:
use_json: Whether to format as JSON (True) or human-readable (False)
"""
super().__init__()
self.use_json = use_json
def format(self, record: logging.LogRecord) -> str:
"""
Format log record with trace context.
Args:
record: Log record
Returns:
str: Formatted log message
"""
# Get trace ID from context
trace_id = trace_id_var.get()
if self.use_json:
# JSON format for production
log_data = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"timestamp": datetime.now(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
@@ -57,61 +57,61 @@ class TraceContextFormatter(logging.Formatter):
"function": record.funcName,
"line": record.lineno,
}
# Add exception info if present
if record.exc_info:
log_data["exception"] = self.formatException(record.exc_info)
# Add extra fields
if hasattr(record, "extra"):
log_data["extra"] = getattr(record, "extra")
return json.dumps(log_data)
else:
# Human-readable format for development
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
trace_part = f" [trace_id={trace_id}]" if trace_id else ""
message = f"{timestamp} - {record.levelname:8s} - {record.name:30s}{trace_part} - {record.getMessage()}"
if record.exc_info:
message += "\n" + self.formatException(record.exc_info)
return message
def setup_logging() -> None:
"""
Configure application logging.
This should be called at application startup.
"""
# Determine if we should use JSON format
use_json = settings.is_production
# Create formatter
formatter = TraceContextFormatter(use_json=use_json)
# Configure root logger
root_logger = logging.getLogger()
root_logger.setLevel(settings.log_level)
# Remove existing handlers
root_logger.handlers.clear()
# Create console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(settings.log_level)
console_handler.setFormatter(formatter)
# Add handler to root logger
root_logger.addHandler(console_handler)
# Set levels for third-party loggers
logging.getLogger("uvicorn").setLevel(logging.INFO)
logging.getLogger("uvicorn.access").setLevel(logging.INFO)
logging.getLogger("uvicorn.error").setLevel(logging.INFO)
logging.getLogger("fastapi").setLevel(logging.INFO)
# Reduce noise from OpenTelemetry
logging.getLogger("opentelemetry").setLevel(logging.WARNING)
@@ -119,7 +119,7 @@ def setup_logging() -> None:
def set_trace_id(trace_id: str) -> None:
"""
Set trace ID in context for current request.
Args:
trace_id: Trace ID to set
"""
@@ -129,7 +129,7 @@ def set_trace_id(trace_id: str) -> None:
def get_trace_id() -> str:
"""
Get trace ID from context.
Returns:
str: Current trace ID or empty string
"""
@@ -139,10 +139,10 @@ def get_trace_id() -> str:
def get_logger(name: str) -> logging.Logger:
"""
Get logger instance.
Args:
name: Logger name (usually __name__)
Returns:
logging.Logger: Logger instance
"""
@@ -153,15 +153,17 @@ class LoggerAdapter(logging.LoggerAdapter):
"""
Logger adapter that automatically includes trace context.
"""
def process(self, msg: str, kwargs: MutableMapping[str, Any]) -> tuple[str, MutableMapping[str, Any]]:
def process(
self, msg: str, kwargs: MutableMapping[str, Any]
) -> tuple[str, MutableMapping[str, Any]]:
"""
Process log message to add trace context.
Args:
msg: Log message
kwargs: Keyword arguments
Returns:
tuple: Processed message and kwargs
"""
@@ -176,10 +178,10 @@ class LoggerAdapter(logging.LoggerAdapter):
def get_logger_with_trace(name: str) -> LoggerAdapter:
"""
Get logger adapter with automatic trace context.
Args:
name: Logger name (usually __name__)
Returns:
LoggerAdapter: Logger adapter instance
"""