logger.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import logging
  2. import os
  3. import sys
  4. from datetime import datetime
  5. from typing import Optional
  6. def setup_logging(verbose: bool = False, log_file: Optional[str] = None, log_dir: Optional[str] = None):
  7. """
  8. 设置日志系统
  9. Args:
  10. verbose: 是否启用详细日志
  11. log_file: 日志文件名
  12. log_dir: 日志目录
  13. """
  14. # 确定日志级别
  15. log_level = logging.DEBUG if verbose else logging.INFO
  16. # 创建根logger
  17. root_logger = logging.getLogger()
  18. root_logger.setLevel(log_level)
  19. # 清除已有的处理器
  20. root_logger.handlers.clear()
  21. # 设置日志格式
  22. console_format = "%(asctime)s [%(levelname)s] %(message)s"
  23. file_format = "%(asctime)s [%(levelname)s] [%(name)s] %(message)s"
  24. date_format = "%Y-%m-%d %H:%M:%S"
  25. # 控制台处理器
  26. console_handler = logging.StreamHandler(sys.stdout)
  27. console_handler.setLevel(log_level)
  28. console_formatter = logging.Formatter(console_format, datefmt=date_format)
  29. console_handler.setFormatter(console_formatter)
  30. root_logger.addHandler(console_handler)
  31. # 文件处理器(如果指定)
  32. if log_file:
  33. # 确定日志文件路径
  34. if log_dir:
  35. os.makedirs(log_dir, exist_ok=True)
  36. log_path = os.path.join(log_dir, log_file)
  37. else:
  38. log_path = log_file
  39. # 添加时间戳到日志文件名
  40. base_name, ext = os.path.splitext(log_path)
  41. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  42. log_path = f"{base_name}_{timestamp}{ext}"
  43. file_handler = logging.FileHandler(log_path, encoding='utf-8')
  44. file_handler.setLevel(log_level)
  45. file_formatter = logging.Formatter(file_format, datefmt=date_format)
  46. file_handler.setFormatter(file_formatter)
  47. root_logger.addHandler(file_handler)
  48. # 记录日志文件位置
  49. root_logger.info(f"日志文件: {os.path.abspath(log_path)}")
  50. # 设置schema_tools模块的日志级别
  51. schema_tools_logger = logging.getLogger("schema_tools")
  52. schema_tools_logger.setLevel(log_level)
  53. # 设置第三方库的日志级别(避免过多输出)
  54. logging.getLogger("asyncio").setLevel(logging.WARNING)
  55. logging.getLogger("asyncpg").setLevel(logging.WARNING)
  56. logging.getLogger("openai").setLevel(logging.WARNING)
  57. logging.getLogger("httpx").setLevel(logging.WARNING)
  58. logging.getLogger("urllib3").setLevel(logging.WARNING)
  59. # 返回schema_tools的logger
  60. return schema_tools_logger
  61. class ColoredFormatter(logging.Formatter):
  62. """带颜色的日志格式化器(用于控制台)"""
  63. # ANSI颜色代码
  64. COLORS = {
  65. 'DEBUG': '\033[36m', # 青色
  66. 'INFO': '\033[32m', # 绿色
  67. 'WARNING': '\033[33m', # 黄色
  68. 'ERROR': '\033[31m', # 红色
  69. 'CRITICAL': '\033[35m', # 紫色
  70. }
  71. RESET = '\033[0m'
  72. def format(self, record):
  73. # 保存原始级别名
  74. levelname = record.levelname
  75. # 添加颜色
  76. if levelname in self.COLORS:
  77. record.levelname = f"{self.COLORS[levelname]}{levelname}{self.RESET}"
  78. # 格式化消息
  79. formatted = super().format(record)
  80. # 恢复原始级别名
  81. record.levelname = levelname
  82. return formatted
  83. def get_colored_console_handler(level=logging.INFO):
  84. """获取带颜色的控制台处理器"""
  85. handler = logging.StreamHandler(sys.stdout)
  86. handler.setLevel(level)
  87. # 检查是否支持颜色(Windows需要特殊处理)
  88. if sys.platform == "win32":
  89. try:
  90. import colorama
  91. colorama.init()
  92. use_color = True
  93. except ImportError:
  94. use_color = False
  95. else:
  96. # Unix/Linux/Mac通常支持ANSI颜色
  97. use_color = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
  98. if use_color:
  99. formatter = ColoredFormatter(
  100. "%(asctime)s [%(levelname)s] %(message)s",
  101. datefmt="%Y-%m-%d %H:%M:%S"
  102. )
  103. else:
  104. formatter = logging.Formatter(
  105. "%(asctime)s [%(levelname)s] %(message)s",
  106. datefmt="%Y-%m-%d %H:%M:%S"
  107. )
  108. handler.setFormatter(formatter)
  109. return handler
  110. class TableProcessingLogger:
  111. """表处理专用日志器"""
  112. def __init__(self, logger_name: str = "schema_tools.TableProcessor"):
  113. self.logger = logging.getLogger(logger_name)
  114. self.current_table = None
  115. self.start_time = None
  116. def start_table(self, table_name: str):
  117. """开始处理表"""
  118. self.current_table = table_name
  119. self.start_time = datetime.now()
  120. self.logger.info(f"{'='*60}")
  121. self.logger.info(f"开始处理表: {table_name}")
  122. self.logger.info(f"开始时间: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}")
  123. def end_table(self, success: bool = True):
  124. """结束处理表"""
  125. if self.start_time:
  126. duration = (datetime.now() - self.start_time).total_seconds()
  127. status = "成功" if success else "失败"
  128. self.logger.info(f"处理{status},耗时: {duration:.2f}秒")
  129. self.logger.info(f"{'='*60}\n")
  130. self.current_table = None
  131. self.start_time = None
  132. def log_step(self, step_name: str, message: str = None):
  133. """记录处理步骤"""
  134. if message:
  135. self.logger.info(f" [{step_name}] {message}")
  136. else:
  137. self.logger.info(f" [{step_name}]")
  138. def log_warning(self, message: str):
  139. """记录警告"""
  140. self.logger.warning(f" ⚠ {message}")
  141. def log_error(self, message: str):
  142. """记录错误"""
  143. self.logger.error(f" ✗ {message}")