3
0

log.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on 2018-12-08 16:50
  4. ---------
  5. @summary:
  6. ---------
  7. @author: Boris
  8. @email: boris_liu@foxmail.com
  9. """
  10. import logging
  11. import os
  12. import sys
  13. import time
  14. from logging.handlers import BaseRotatingHandler
  15. import loguru
  16. import pymongo
  17. from better_exceptions import format_exception
  18. import feapder.setting as setting
  19. LOG_FORMAT = "%(threadName)s|%(asctime)s|%(filename)s|%(funcName)s|line:%(lineno)d|%(levelname)s| %(message)s"
  20. PRINT_EXCEPTION_DETAILS = True
  21. class InterceptHandler(logging.Handler):
  22. def emit(self, record):
  23. # Retrieve context where the logging call occurred, this happens to be in the 6th frame upward
  24. logger_opt = loguru.logger.opt(depth=6, exception=record.exc_info)
  25. logger_opt.log(record.levelname, record.getMessage())
  26. # 重写 RotatingFileHandler 自定义log的文件名
  27. # 原来 xxx.log xxx.log.1 xxx.log.2 xxx.log.3 文件由近及远
  28. # 现在 xxx.log xxx1.log xxx2.log 如果backup_count 是2位数时 则 01 02 03 三位数 001 002 .. 文件由近及远
  29. class RotatingFileHandler(BaseRotatingHandler):
  30. def __init__(
  31. self, filename, mode="a", max_bytes=0, backup_count=0, encoding=None, delay=0
  32. ):
  33. BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
  34. self.max_bytes = max_bytes
  35. self.backup_count = backup_count
  36. self.placeholder = str(len(str(backup_count)))
  37. self._to_db = None
  38. self.filename = filename
  39. @property
  40. def to_db(self):
  41. if not self._to_db:
  42. self._to_db = pymongo.MongoClient(setting.MONGO_IP, setting.MONGO_PORT)
  43. return self._to_db.pyspider
  44. def shouldRollover(self, record):
  45. parmars = {
  46. "spider_name":record.name,
  47. "msg":record.msg,
  48. "Message":str(record.getMessage)
  49. }
  50. if record.levelname == "ERROR":
  51. crawl_type = 'list'
  52. if 'detail' in record.name:
  53. crawl_type = 'detail'
  54. url = ''
  55. item={
  56. "recordname":record.name,
  57. "spidercode":"spidercode",
  58. "author":self.filename,
  59. "account":"",
  60. "crawl_time":time.time(),
  61. "crawl_type": crawl_type,
  62. "status_code":"status_code",
  63. "url":url,
  64. "reason":record.msg,
  65. 'parmars': parmars,
  66. }
  67. # print('<<<<<<<<<<<<<<<<<<<<<<<插入error_info')
  68. # print(item)
  69. # print(self.to_db.error_info)
  70. # self.to_db.error_info.insert_one(item)
  71. def get_logger(
  72. name=None,
  73. path=None,
  74. log_level=None,
  75. is_write_to_console=None,
  76. is_write_to_file=None,
  77. color=None,
  78. mode=None,
  79. max_bytes=None,
  80. backup_count=None,
  81. encoding=None,
  82. ):
  83. """
  84. @summary: 获取log
  85. ---------
  86. @param name: log名
  87. @param path: log文件存储路径 如 D://xxx.log
  88. @param log_level: log等级 CRITICAL/ERROR/WARNING/INFO/DEBUG
  89. @param is_write_to_console: 是否输出到控制台
  90. @param is_write_to_file: 是否写入到文件 默认否
  91. @param color:是否有颜色
  92. @param mode:写文件模式
  93. @param max_bytes: 每个日志文件的最大字节数
  94. @param backup_count:日志文件保留数量
  95. @param encoding:日志文件编码
  96. ---------
  97. @result:
  98. """
  99. # 加载setting里最新的值
  100. name = name or setting.LOG_NAME
  101. path = path or setting.LOG_PATH
  102. log_level = log_level or setting.LOG_LEVEL
  103. is_write_to_console = (
  104. is_write_to_console
  105. if is_write_to_console is not None
  106. else setting.LOG_IS_WRITE_TO_CONSOLE
  107. )
  108. is_write_to_file = (
  109. is_write_to_file
  110. if is_write_to_file is not None
  111. else setting.LOG_IS_WRITE_TO_FILE
  112. )
  113. color = color if color is not None else setting.LOG_COLOR
  114. mode = mode or setting.LOG_MODE
  115. max_bytes = max_bytes or setting.LOG_MAX_BYTES
  116. backup_count = backup_count or setting.LOG_BACKUP_COUNT
  117. encoding = encoding or setting.LOG_ENCODING
  118. # logger 配置
  119. name = name.split(os.sep)[-1].split(".")[0] # 取文件名
  120. logger = logging.getLogger(name)
  121. logger.setLevel(log_level)
  122. formatter = logging.Formatter(LOG_FORMAT)
  123. if PRINT_EXCEPTION_DETAILS:
  124. formatter.formatException = lambda exc_info: format_exception(*exc_info)
  125. # 定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M
  126. if is_write_to_file:
  127. # if path and not os.path.exists(os.path.dirname(path)):
  128. # os.makedirs(os.path.dirname(path))
  129. rf_handler = RotatingFileHandler(
  130. path,
  131. mode=mode,
  132. max_bytes=max_bytes,
  133. backup_count=backup_count,
  134. encoding=encoding,
  135. )
  136. rf_handler.setFormatter(formatter)
  137. logger.addHandler(rf_handler)
  138. if color and is_write_to_console:
  139. loguru_handler = InterceptHandler()
  140. loguru_handler.setFormatter(formatter)
  141. # logging.basicConfig(handlers=[loguru_handler], level=0)
  142. logger.addHandler(loguru_handler)
  143. elif is_write_to_console:
  144. stream_handler = logging.StreamHandler()
  145. stream_handler.stream = sys.stdout
  146. stream_handler.setFormatter(formatter)
  147. logger.addHandler(stream_handler)
  148. _handler_list = []
  149. _handler_name_list = []
  150. # 检查是否存在重复handler
  151. for _handler in logger.handlers:
  152. if str(_handler) not in _handler_name_list:
  153. _handler_name_list.append(str(_handler))
  154. _handler_list.append(_handler)
  155. logger.handlers = _handler_list
  156. return logger
  157. # logging.disable(logging.DEBUG) # 关闭所有log
  158. # 不让打印log的配置
  159. STOP_LOGS = [
  160. # ES
  161. "urllib3.response",
  162. "urllib3.connection",
  163. "elasticsearch.trace",
  164. "requests.packages.urllib3.util",
  165. "requests.packages.urllib3.util.retry",
  166. "urllib3.util",
  167. "requests.packages.urllib3.response",
  168. "requests.packages.urllib3.contrib.pyopenssl",
  169. "requests.packages",
  170. "urllib3.util.retry",
  171. "requests.packages.urllib3.contrib",
  172. "requests.packages.urllib3.connectionpool",
  173. "requests.packages.urllib3.poolmanager",
  174. "urllib3.connectionpool",
  175. "requests.packages.urllib3.connection",
  176. "elasticsearch",
  177. "log_request_fail",
  178. # requests
  179. "requests",
  180. "selenium.webdriver.remote.remote_connection",
  181. "selenium.webdriver.remote",
  182. "selenium.webdriver",
  183. "selenium",
  184. # markdown
  185. "MARKDOWN",
  186. "build_extension",
  187. # newspaper
  188. "calculate_area",
  189. "largest_image_url",
  190. "newspaper.images",
  191. "newspaper",
  192. "Importing",
  193. "PIL",
  194. ]
  195. # 关闭日志打印
  196. for STOP_LOG in STOP_LOGS:
  197. log_level = eval("logging." + setting.OTHERS_LOG_LEVAL)
  198. logging.getLogger(STOP_LOG).setLevel(log_level)
  199. # print(logging.Logger.manager.loggerDict) # 取使用debug模块的name
  200. # 日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG
  201. class Log:
  202. log = None
  203. def __getattr__(self, name):
  204. # 调用log时再初始化,为了加载最新的setting
  205. if self.__class__.log is None:
  206. self.__class__.log = get_logger()
  207. return getattr(self.__class__.log, name)
  208. @property
  209. def debug(self):
  210. return self.__class__.log.debug
  211. @property
  212. def info(self):
  213. return self.__class__.log.info
  214. @property
  215. def warning(self):
  216. return self.__class__.log.warning
  217. @property
  218. def exception(self):
  219. return self.__class__.log.exception
  220. @property
  221. def error(self):
  222. return self.__class__.log.error
  223. @property
  224. def critical(self):
  225. return self.__class__.log.critical
  226. log = Log()