Files
YG_FT_Platform/request/src/utils/logger.py
DESKTOP-72TV0V4\caoxiaozhu bda8f13446 1. 增加了请求框架
2. 增加了删除虚拟环境的脚本
2026-01-12 14:20:44 +08:00

228 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import structlog
import logging
import sys
import json
from datetime import datetime
from typing import Any, Dict, Optional
from pathlib import Path
import colorama
from colorama import Fore, Back, Style
# 尝试导入pythonjsonlogger如果没有安装则使用备用方案
try:
from pythonjsonlogger import jsonlogger
HAS_JSON_LOGGER = True
except ImportError:
HAS_JSON_LOGGER = False
jsonlogger = None
# 初始化colorama
colorama.init()
class ColoredConsoleRenderer:
"""带颜色的控制台日志渲染器"""
def __call__(self, logger, method_name: str, event_dict: Dict[str, Any]) -> str:
"""渲染日志事件"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
level = event_dict.get("level", "INFO").upper()
message = event_dict.get("event", "")
# 根据日志级别选择颜色
level_colors = {
"DEBUG": Fore.CYAN,
"INFO": Fore.GREEN,
"WARNING": Fore.YELLOW,
"ERROR": Fore.RED,
"CRITICAL": Fore.RED + Back.WHITE + Style.BRIGHT,
}
color = level_colors.get(level, "")
reset = Style.RESET_ALL
# 基础信息
log_line = f"{color}[{timestamp}] {level}{reset} {message}"
# 添加额外的上下文信息
if "request_id" in event_dict:
log_line += f" {Fore.BLUE}[req:{event_dict['request_id']}]{reset}"
if "function" in event_dict:
log_line += f" {Fore.MAGIC}{event_dict['function']}(){reset}"
# 添加其他字段
for key, value in event_dict.items():
if key not in ["level", "event", "timestamp", "request_id", "function"]:
log_line += f" {Fore.CYAN}{key}={value}{reset}"
return log_line
class JSONRenderer:
"""JSON格式的日志渲染器"""
def __call__(self, logger, method_name: str, event_dict: Dict[str, Any]) -> str:
"""渲染日志事件为JSON格式"""
log_data = {
"timestamp": datetime.now().isoformat(),
"level": event_dict.get("level", "INFO"),
"message": event_dict.get("event", ""),
}
# 添加其他字段
for key, value in event_dict.items():
if key not in ["level", "event"]:
log_data[key] = value
return json.dumps(log_data, ensure_ascii=False, default=str)
class LoggerManager:
"""日志管理器"""
def __init__(self):
self._processors = []
self._configured = False
def configure(self,
log_level: str = "INFO",
log_format: str = "console",
log_file: Optional[str] = None,
log_to_console: bool = True):
"""配置日志系统"""
# 配置structlog处理器
processors = [
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
]
# 添加自定义处理器
processors.extend(self._processors)
# 选择渲染器
if log_format == "json":
renderer = JSONRenderer()
else:
renderer = ColoredConsoleRenderer()
processors.append(renderer)
# 配置structlog
structlog.configure(
processors=processors,
wrapper_class=structlog.stdlib.BoundLogger,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)
# 配置标准库logging
# 防止重复配置
root_logger = logging.getLogger()
if not root_logger.handlers:
level = getattr(logging, log_level.upper())
# 如果指定了日志文件,配置文件日志
if log_file:
self._setup_file_handler(log_file, log_level)
# 如果允许控制台输出,配置控制台日志
if log_to_console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(logging.Formatter("%(message)s"))
root_logger.addHandler(console_handler)
else:
# 如果不输出到控制台,至少设置日志级别
root_logger.setLevel(level)
self._configured = True
def _setup_file_handler(self, log_file: str, log_level: str = "INFO"):
"""设置文件日志处理器"""
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file, encoding='utf-8')
level = getattr(logging, log_level.upper())
file_handler.setLevel(level)
if HAS_JSON_LOGGER:
file_handler.setFormatter(jsonlogger.JsonFormatter(
'%(asctime)s %(name)s %(levelname)s %(message)s'
))
else:
# 使用标准库的JSON格式化器
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
# 获取根logger并添加文件处理器
root_logger = logging.getLogger()
root_logger.addHandler(file_handler)
root_logger.setLevel(level)
def add_processor(self, processor):
"""添加自定义处理器"""
self._processors.append(processor)
def get_logger(self, name: Optional[str] = None) -> structlog.stdlib.BoundLogger:
"""获取logger实例"""
if not self._configured:
self.configure()
return structlog.get_logger(name)
# 全局日志管理器实例
logger_manager = LoggerManager()
# 获取logger的便捷函数
def get_logger(name: Optional[str] = None) -> structlog.stdlib.BoundLogger:
"""获取logger实例"""
return logger_manager.get_logger(name)
# 简单的日志打印函数
def log(message: str, level: str = "info", **kwargs):
"""简单的日志打印函数
Args:
message: 日志消息
level: 日志级别 (debug, info, warning, error, critical)
**kwargs: 额外的上下文信息
"""
logger = get_logger()
log_method = getattr(logger, level.lower(), logger.info)
log_method(message, **kwargs)
# 带上下文的日志函数
def log_debug(message: str, **kwargs):
"""打印debug级别日志"""
log(message, "debug", **kwargs)
def log_info(message: str, **kwargs):
"""打印info级别日志"""
log(message, "info", **kwargs)
def log_warning(message: str, **kwargs):
"""打印warning级别日志"""
log(message, "warning", **kwargs)
def log_error(message: str, **kwargs):
"""打印error级别日志"""
log(message, "error", **kwargs)
def log_critical(message: str, **kwargs):
"""打印critical级别日志"""
log(message, "critical", **kwargs)