123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- # -*- coding: utf-8 -*-
- """
- Created on 2018-12-08 16:50
- ---------
- @summary:
- ---------
- @author: Boris
- @email: boris_liu@foxmail.com
- """
- import logging
- import os
- import sys
- from logging.handlers import BaseRotatingHandler
- import logstash
- import loguru
- from better_exceptions import format_exception
- import feapder.setting as setting
- LOG_FORMAT = "%(threadName)s|%(asctime)s|%(filename)s|%(funcName)s|line:%(lineno)d|%(levelname)s| %(message)s"
- PRINT_EXCEPTION_DETAILS = True
- class InterceptHandler(logging.Handler):
- def emit(self, record):
- # Retrieve context where the logging call occurred, this happens to be in the 6th frame upward
- logger_opt = loguru.logger.opt(depth=6, exception=record.exc_info)
- logger_opt.log(record.levelname, record.getMessage())
- # 重写 RotatingFileHandler 自定义log的文件名
- # 原来 xxx.log xxx.log.1 xxx.log.2 xxx.log.3 文件由近及远
- # 现在 xxx.log xxx1.log xxx2.log 如果backup_count 是2位数时 则 01 02 03 三位数 001 002 .. 文件由近及远
- class RotatingFileHandler(BaseRotatingHandler):
- def __init__(
- self,
- filename,
- mode="a",
- max_bytes=0,
- backup_count=0,
- encoding=None,
- delay=0
- ):
- BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
- self.max_bytes = max_bytes
- self.backup_count = backup_count
- self.placeholder = str(len(str(backup_count)))
- def doRollover(self):
- if self.stream:
- self.stream.close()
- self.stream = None
- if self.backup_count > 0:
- for i in range(self.backup_count - 1, 0, -1):
- # sfn = "%d_%s" % (i, self.baseFilename)
- # dfn = "%d_%s" % (i + 1, self.baseFilename)
- sfn = ("%0" + self.placeholder + "d.") % i # '%2d.'%i -> 02
- sfn = sfn.join(self.baseFilename.split("."))
- dfn = ("%0" + self.placeholder + "d.") % (i + 1)
- dfn = dfn.join(self.baseFilename.split("."))
- if os.path.exists(sfn):
- # print "%s -> %s" % (sfn, dfn)
- if os.path.exists(dfn):
- os.remove(dfn)
- os.rename(sfn, dfn)
- dfn = (("%0" + self.placeholder + "d.") % 1).join(self.baseFilename.split("."))
- if os.path.exists(dfn):
- os.remove(dfn)
- # Issue 18940: A file may not have been created if delay is True.
- if os.path.exists(self.baseFilename):
- os.rename(self.baseFilename, dfn)
- if not self.delay:
- self.stream = self._open()
- def shouldRollover(self, record):
- if self.stream is None: # delay was set...
- self.stream = self._open()
- if self.max_bytes > 0: # are we rolling over?
- # print('record >>>> ', record)
- msg = "%s\n" % self.format(record)
- self.stream.seek(0, 2) # due to non-posix-compliant Windows feature
- if self.stream.tell() + len(msg) >= self.max_bytes:
- return 1
- return 0
- def get_logger(
- name=None,
- path=None,
- log_level=None,
- is_write_to_console=None,
- is_write_to_file=None,
- is_send_to_logstash = None,
- color=None,
- mode=None,
- max_bytes=None,
- backup_count=None,
- encoding=None,
- ):
- """
- @summary: 获取log
- ---------
- @param name: log名
- @param path: log文件存储路径 如 D://xxx.log
- @param log_level: log等级 CRITICAL/ERROR/WARNING/INFO/DEBUG
- @param is_write_to_console: 是否输出到控制台
- @param is_write_to_file: 是否写入到文件 默认否
- @param color:是否有颜色
- @param mode:写文件模式
- @param max_bytes: 每个日志文件的最大字节数
- @param backup_count:日志文件保留数量
- @param encoding:日志文件编码
- ---------
- @result:
- """
- # 加载setting里最新的值
- name = name or setting.LOG_NAME
- path = path or setting.LOG_PATH
- log_level = log_level or setting.LOG_LEVEL
- is_write_to_console = (
- is_write_to_console
- if is_write_to_console is not None
- else setting.LOG_IS_WRITE_TO_CONSOLE
- )
- is_write_to_file = (
- is_write_to_file
- if is_write_to_file is not None
- else setting.LOG_IS_WRITE_TO_FILE
- )
- is_send_to_logstash = (
- is_send_to_logstash
- if is_send_to_logstash is not None
- else setting.LOG_IS_SEND_TO_LOGSTASH
- )
- color = color if color is not None else setting.LOG_COLOR
- mode = mode or setting.LOG_MODE
- max_bytes = max_bytes or setting.LOG_MAX_BYTES
- backup_count = backup_count or setting.LOG_BACKUP_COUNT
- encoding = encoding or setting.LOG_ENCODING
- # logger 配置
- name = name.split(os.sep)[-1].split(".")[0] # 取文件名
- logger = logging.getLogger(name)
- logger.setLevel(log_level)
- formatter = logging.Formatter(LOG_FORMAT)
- if PRINT_EXCEPTION_DETAILS:
- formatter.formatException = lambda exc_info: format_exception(*exc_info)
- # 定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M
- if is_write_to_file:
- if path and not os.path.exists(os.path.dirname(path)):
- os.makedirs(os.path.dirname(path), exist_ok=True)
- rf_handler = RotatingFileHandler(
- path,
- mode=mode,
- max_bytes=max_bytes,
- backup_count=backup_count,
- encoding=encoding,
- )
- rf_handler.setFormatter(formatter)
- logger.addHandler(rf_handler)
- if is_send_to_logstash:
- stash_handler = logstash.TCPLogstashHandler(
- setting.LOG_STASH_IP, setting.LOG_STASH_PORT, version=1)
- logger.addHandler(stash_handler)
- if color and is_write_to_console:
- loguru_handler = InterceptHandler()
- loguru_handler.setFormatter(formatter)
- # logging.basicConfig(handlers=[loguru_handler], level=0)
- logger.addHandler(loguru_handler)
- elif is_write_to_console:
- stream_handler = logging.StreamHandler()
- stream_handler.stream = sys.stdout
- stream_handler.setFormatter(formatter)
- logger.addHandler(stream_handler)
- _handler_list = []
- _handler_name_list = []
- # 检查是否存在重复handler
- for _handler in logger.handlers:
- if str(_handler) not in _handler_name_list:
- _handler_name_list.append(str(_handler))
- _handler_list.append(_handler)
- logger.handlers = _handler_list
- return logger
- # logging.disable(logging.DEBUG) # 关闭所有log
- # 不让打印log的配置
- STOP_LOGS = [
- # ES
- "urllib3.response",
- "urllib3.connection",
- "elasticsearch.trace",
- "requests.packages.urllib3.util",
- "requests.packages.urllib3.util.retry",
- "urllib3.util",
- "requests.packages.urllib3.response",
- "requests.packages.urllib3.contrib.pyopenssl",
- "requests.packages",
- "urllib3.util.retry",
- "requests.packages.urllib3.contrib",
- "requests.packages.urllib3.connectionpool",
- "requests.packages.urllib3.poolmanager",
- "urllib3.connectionpool",
- "requests.packages.urllib3.connection",
- "elasticsearch",
- "log_request_fail",
- # requests
- "requests",
- "selenium.webdriver.remote.remote_connection",
- "selenium.webdriver.remote",
- "selenium.webdriver",
- "selenium",
- # markdown
- "MARKDOWN",
- "build_extension",
- # newspaper
- "calculate_area",
- "largest_image_url",
- "newspaper.images",
- "newspaper",
- "Importing",
- "PIL",
- ]
- # 关闭日志打印
- for STOP_LOG in STOP_LOGS:
- log_level = eval("logging." + setting.OTHERS_LOG_LEVAL)
- logging.getLogger(STOP_LOG).setLevel(log_level)
- # print(logging.Logger.manager.loggerDict) # 取使用debug模块的name
- # 日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG
- class Log:
- log = None
- def __getattr__(self, name):
- # 调用log时再初始化,为了加载最新的setting
- if self.__class__.log is None:
- self.__class__.log = get_logger()
- return getattr(self.__class__.log, name)
- @property
- def debug(self):
- return self.__class__.log.debug
- @property
- def info(self):
- return self.__class__.log.info
- @property
- def warning(self):
- return self.__class__.log.warning
- @property
- def exception(self):
- return self.__class__.log.exception
- @property
- def error(self):
- return self.__class__.log.error
- @property
- def critical(self):
- return self.__class__.log.critical
- log = Log()
|