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