logger.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import os
  2. import threading
  3. from datetime import datetime, timezone, timedelta
  4. class Logger:
  5. def __init__(self, module_name, log_path="./logs", max_log_size=5*1024*1024, max_log_files=10, log_lvl=1):
  6. """
  7. :param module_name: 模块名,日志文件前缀
  8. :param log_path: 日志目录
  9. :param max_log_size: 单个日志文件最大字节数
  10. :param max_log_files: 保留的最大日志文件数
  11. :param log_lvl: 日志等级(0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
  12. """
  13. self.module_name = module_name
  14. self.log_path = log_path
  15. self.max_log_size = max_log_size
  16. self.max_log_files = max_log_files
  17. self.log_lvl = log_lvl
  18. os.makedirs(log_path, exist_ok=True)
  19. self.log_file = os.path.join(log_path, f"{module_name}.log")
  20. self.lock = threading.Lock()
  21. # 维护文件大小计数器
  22. if os.path.exists(self.log_file):
  23. self.cur_size = os.path.getsize(self.log_file)
  24. else:
  25. self.cur_size = 0
  26. def _get_bj_time_ms(self):
  27. tz = timezone(timedelta(hours=8)) # 北京时间
  28. now = datetime.now(tz)
  29. return now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
  30. def _rotate_log(self):
  31. """日志切分"""
  32. if os.path.exists(self.log_file) and self.cur_size >= self.max_log_size:
  33. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  34. new_log_file = os.path.join(self.log_path, f"{self.module_name}_{timestamp}.log")
  35. os.rename(self.log_file, new_log_file)
  36. self.cur_size = 0
  37. self._manage_log_files()
  38. def _manage_log_files(self):
  39. """控制日志文件数量"""
  40. log_files = sorted(
  41. (f for f in os.listdir(self.log_path) if f.endswith(".log")),
  42. key=lambda x: os.path.getctime(os.path.join(self.log_path, x))
  43. )
  44. while len(log_files) > self.max_log_files:
  45. oldest_file = log_files.pop(0)
  46. os.remove(os.path.join(self.log_path, oldest_file))
  47. def _log(self, lvl_name, text):
  48. with self.lock:
  49. self._rotate_log()
  50. formatted_now = self._get_bj_time_ms()
  51. line = f"[{formatted_now}][{lvl_name}]: {text}\n"
  52. with open(self.log_file, "a", encoding="utf-8") as f:
  53. f.write(line)
  54. self.cur_size += len(line.encode("utf-8"))
  55. # ========== 对外接口 ==========
  56. def LOGDBG(self, text):
  57. if self.log_lvl <= 0:
  58. self._log("DEBUG", text)
  59. def LOGINFO(self, text):
  60. if self.log_lvl <= 1:
  61. self._log("INFO", text)
  62. def LOGWARN(self, text):
  63. if self.log_lvl <= 2:
  64. self._log("WARN", text)
  65. def LOGERR(self, text):
  66. if self.log_lvl <= 3:
  67. self._log("ERROR", text)
  68. # ================== 全局实例 ==================
  69. g_logger = Logger(
  70. module_name="MyModule",
  71. log_path="./logs",
  72. max_log_size=50*1024*1024, # 2 MB
  73. max_log_files=5,
  74. log_lvl=1
  75. )
  76. # ========== 使用示例 ==========
  77. if __name__ == "__main__":
  78. g_logger.LOGINFO("启动服务成功")
  79. g_logger.LOGDBG("这是调试信息")
  80. g_logger.LOGWARN("这是警告")
  81. g_logger.LOGERR("这是错误信息")