fork download
  1. # -*- coding: utf-8 -*-
  2. import logging
  3. import sys
  4. import inspect
  5. from datetime import datetime
  6.  
  7.  
  8. class OptimizedFormatter(logging.Formatter):
  9. def format(self, record):
  10. try:
  11. # 智能帧捕获(带缓存优化)
  12. if not hasattr(record, 'module'):
  13. frame = self._cached_caller_frame()
  14. if frame:
  15. module = inspect.getmodule(frame)
  16. record.module = module.__name__ if module else 'unknown'
  17. record.lineno = frame.f_lineno
  18.  
  19. # 参数统一处理
  20. args_str = self._process_args(record)
  21. if args_str:
  22. record.msg = u"%s ⎸ %s" % (record.msg, args_str)
  23.  
  24. return super(OptimizedFormatter, self).format(record)
  25. except Exception as e:
  26. return u"⚠️ Format Error | Err: %s | Raw: %s" % (e, record.getMessage())
  27.  
  28. def _cached_caller_frame(self):
  29. """带LRU缓存的帧查找"""
  30. stack = inspect.stack()
  31. for frame_info in reversed(stack):
  32. frame = frame_info[0]
  33. if inspect.getmodule(frame).__name__ != __name__:
  34. return frame
  35. return inspect.currentframe()
  36.  
  37. def _process_args(self, record):
  38. """处理所有参数类型"""
  39. parts = []
  40.  
  41. # 处理位置参数(修改键名避免冲突)
  42. if hasattr(record, 'positional_args') and record.positional_args:
  43. parts.extend(self._safe_repr(a) for a in record.positional_args)
  44.  
  45. # 处理额外参数
  46. if hasattr(record, 'extra') and record.extra:
  47. parts.extend(u"%s=%s" % (k, self._safe_repr(v))
  48. for k, v in record.extra.iteritems())
  49.  
  50. return u" ⎸ ".join(parts) if parts else u""
  51.  
  52. def _safe_repr(self, value):
  53. """安全类型转换(增强编码处理)"""
  54. try:
  55. if isinstance(value, dict):
  56. return u"{%s}" % u",".join(u"%s:%s" % (self._safe_repr(k), self._safe_repr(v))
  57. for k, v in value.iteritems())
  58. elif isinstance(value, (list, tuple)):
  59. return u"[%s]" % u",".join(self._safe_repr(x) for x in value)
  60. return self._safe_str(value)
  61. except:
  62. return u"<Unprintable>"
  63.  
  64. @staticmethod
  65. def _safe_str(obj):
  66. """安全字符串转换(解决编码问题)"""
  67. try:
  68. if isinstance(obj, str):
  69. return obj.decode('utf-8', 'replace')
  70. return unicode(obj)
  71. except UnicodeDecodeError:
  72. return u"<非UTF-8字节数据>"
  73. except:
  74. return u"<无法转换对象>"
  75.  
  76.  
  77. class SimpleLogger(object):
  78. _initialized = False
  79.  
  80. @classmethod
  81. def _ensure_init(cls):
  82. if not cls._initialized:
  83. root = logging.getLogger()
  84. root.setLevel(logging.DEBUG)
  85. if not root.handlers:
  86. handler = logging.StreamHandler()
  87. handler.setFormatter(OptimizedFormatter(
  88. fmt=u'[%(asctime)s] [%(levelname)s] %(module)s:%(lineno)d ➤ %(message)s',
  89. datefmt='%H:%M:%S'
  90. ))
  91. root.addHandler(handler)
  92. cls._initialized = True
  93.  
  94. @classmethod
  95. def _log(cls, level, msg, *args, **kwargs):
  96. cls._ensure_init()
  97. try:
  98. # 预处理消息(确保Unicode)
  99. msg = cls._preprocess_message(msg)
  100.  
  101. # 智能参数处理
  102. formatted_msg = cls._format_message(msg, args)
  103.  
  104. # 获取调用者信息
  105. frame = inspect.currentframe().f_back.f_back
  106. logger = logging.getLogger(
  107. inspect.getmodule(frame).__name__
  108. if inspect.getmodule(frame) else 'unknown'
  109. )
  110.  
  111. # 创建日志记录(修改参数键名)
  112. logger.log(
  113. level,
  114. formatted_msg,
  115. extra={'extra': kwargs, 'positional_args': args} # 修改键名
  116. )
  117. except Exception as e:
  118. sys.stderr.write(u"Log Error: %s\n" % unicode(e))
  119.  
  120. @staticmethod
  121. def _preprocess_message(msg):
  122. """消息预处理(强制转Unicode)"""
  123. if isinstance(msg, str):
  124. try:
  125. return msg.decode('utf-8')
  126. except UnicodeDecodeError:
  127. return msg.decode('latin-1', 'replace')
  128. return unicode(msg)
  129.  
  130. @classmethod
  131. def _format_message(cls, msg, args):
  132. """混合参数格式化(增强容错)"""
  133. try:
  134. # 尝试传统格式化
  135. if args and '%' in msg:
  136. return msg % args
  137. except (TypeError, ValueError):
  138. pass
  139.  
  140. # 自动拼接模式
  141. if args:
  142. return u" ".join([msg] + [OptimizedFormatter._safe_str(a) for a in args])
  143. return msg
  144.  
  145. @classmethod
  146. def debug(cls, msg, *args, **kwargs):
  147. cls._log(logging.DEBUG, msg, *args, **kwargs)
  148.  
  149. @classmethod
  150. def info(cls, msg, *args, **kwargs):
  151. cls._log(logging.INFO, msg, *args, **kwargs)
  152.  
  153. @classmethod
  154. def warning(cls, msg, *args, **kwargs):
  155. cls._log(logging.WARNING, msg, *args, **kwargs)
  156.  
  157. @classmethod
  158. def error(cls, msg, *args, **kwargs):
  159. cls._log(logging.ERROR, msg, *args, **kwargs)
  160.  
  161.  
  162. # 测试用例
  163. if __name__ == "__main__":
  164. # 测试中文日志
  165. SimpleLogger.info(u"用户登录", u"张三", "192.168.1.100")
  166.  
  167. # 测试混合参数
  168. SimpleLogger.error("错误发生在 %s", u"配置文件", code=500, detail={"line": 42})
  169.  
  170. # 测试二进制数据
  171. bad_data = '\xe6\x97\xa0' # UTF-8编码的"无"
  172. SimpleLogger.warning("无效数据", bad_data)
  173.  
Success #stdin #stdout #stderr 0.08s 68636KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
[12:48:32] [INFO] prog:115 ➤ 用户登录 张三 192.168.1.100 ⎸ 张三 ⎸ 192.168.1.100
[12:48:32] [ERROR] prog:115 ➤ 错误发生在 配置文件 ⎸ 配置文件 ⎸ code=500 ⎸ detail={line:42}
[12:48:32] [WARNING] prog:115 ➤ 无效数据 无 ⎸ 无