ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

HTMLTestRunner_loguru下html没有显示日志内容问题

2021-09-05 13:32:22  阅读:176  来源: 互联网

标签:log loguru HTMLTestRunner self html msg logger 日志 def


  • 前置条件

    Windows 10

    loguru

    HTMLTestRunner

  • 最近将原有logging日志系统替换成了loguru,loguru的好处不用多说,简单好用。配置起来也比lgging方便多了。封装代码如下:

    import time, os
    from loguru import logger
    from settings import LOG_DIR # 日志保存路径
    
    class Log:
        """输出日志到文件和控制台"""
        def __init__(self):
            # 文件的命名
            log_name = f"test_{time.strftime('%Y-%m-%d', time.localtime()).replace('-','_')}.log"
            log_path = os.path.join(LOG_DIR, log_name)
            # 判断日志文件夹是否存在,不存则创建
            if not os.path.exists(LOG_DIR): 
                os.mkdir(LOG_DIR)
            # 日志输出格式
            formatter = "{time:YYYY-MM-DD HH:mm:ss} | {level}: {message}"
            # 日志写入文件
            logger.add(log_path,   # 写入目录指定文件
                   format=formatter,
                   encoding='utf-8',   
                   retention='10 days',  # 设置历史保留时长
                   backtrace=True,  # 回溯
                   diagnose=True,   # 诊断
                   enqueue=True,   # 异步写入
                   # rotation="5kb",  # 切割,设置文件大小,rotation="12:00",rotation="1 week"
                   # filter="my_module"  # 过滤模块
                   # compression="zip"   # 文件压缩
                  ) 
        
        def debug(self, msg):
            logger.debug(msg)
    
        def info(self, msg):
            logger.info(msg)
    
        def warning(self, msg):
            logger.warning(msg)
    
        def error(self, msg):
            logger.error(msg) 
    
    log = Log()
    
  • 将上述封装代码引入,生成的html仍没有日志。点击通过无法查看日志。

    image-20210905131613628

    原来使用HTMLTestRunner生成html测试报告时,报告中只有console输出,logging的输出无法保存,如果要在报告中加入每一个测试用例执行的日志信息,则需要改HTMLTestRunner源码。

  • 修改_TestResult类,同时别忘了在文件最上面import logging

    import logging
    
    class _TestResult(TestResult):
        # note: _TestResult is a pure representation of results.
        # It lacks the output and reporting ability compares to unittest._TextTestResult.
    
        def __init__(self, verbosity=1, retry=0,save_last_try=False):
            TestResult.__init__(self)
    
            self.stdout0 = None
            self.stderr0 = None
            self.success_count = 0
            self.failure_count = 0
            self.error_count = 0
            self.skip_count = 0
            self.verbosity = verbosity
            # result is a list of result in 4 tuple
            # (
            #   result code (0: success; 1: fail; 2: error;3:skip),
            #   TestCase object,
            #   Test output (byte string),
            #   stack trace,
            # )
            self.result = []
            self.retry = retry
            self.trys = 0
            self.status = 0
    
            self.save_last_try = save_last_try
            self.outputBuffer = StringIO.StringIO()
            self.logger = logging.getLogger() # 新增这一行
    
  • startTest函数中初始化logging.Handler

    def startTest(self, test):
            # test.imgs = []
            test.imgs = getattr(test, "imgs", [])
            # TestResult.startTest(self, test)
            self.outputBuffer.seek(0)
            self.outputBuffer.truncate()
            stdout_redirector.fp = self.outputBuffer
            stderr_redirector.fp = self.outputBuffer
            self.stdout0 = sys.stdout
            self.stderr0 = sys.stderr
            sys.stdout = stdout_redirector
            sys.stderr = stderr_redirector
    	    
            # 新增如下代码
            self.log_cap = StringIO.StringIO()
            self.ch = logging.StreamHandler(self.log_cap)
            self.ch.setLevel(logging.DEBUG)
            formatter = logging.Formatter('[%(levelname)s][%(asctime)s] [%(filename)s]->[%(funcName)s] line:%(lineno)d ---> %(message)s')
            self.ch.setFormatter(formatter)
            self.logger.addHandler(self.ch)
    
  • complete_output函数的返回值中加入logging存在内存中的输出,用换行符隔开

    def complete_output(self):
            """
            Disconnect output redirection and return buffer.
            Safe to call multiple times.
            """
            if self.stdout0:
                sys.stdout = self.stdout0
                sys.stderr = self.stderr0
                self.stdout0 = None
                self.stderr0 = None
            # return self.outputBuffer.getvalue()
            return self.outputBuffer.getvalue()+'\n'+self.log_cap.getvalue() # 新增这一行
    
  • 每个用例执行完后,清除handler,修改stopTest函数

    def stopTest(self, test):
            # Usually one of addSuccess, addError or addFailure would have been called.
            # But there are some path in unittest that would bypass this.
            # We must disconnect stdout in stopTest(), which is guaranteed to be called.
            if self.retry and self.retry>=1:
                if self.status == 1:
                    self.trys += 1
                    if self.trys <= self.retry:
                        if self.save_last_try:
                            t = self.result.pop(-1)
                            if t[0]==1:
                                self.failure_count -=1
                            else:
                                self.error_count -= 1
                        test=copy.copy(test)
                        sys.stderr.write("Retesting... ")
                        sys.stderr.write(str(test))
                        sys.stderr.write('..%d \n' % self.trys)
                        doc = getattr(test,'_testMethodDoc',u"") or u''
                        if doc.find('_retry')!=-1:
                            doc = doc[:doc.find('_retry')]
                        desc ="%s_retry:%d" %(doc, self.trys)
                        if not PY3K:
                            if isinstance(desc, str):
                                desc = desc.decode("utf-8")
                        test._testMethodDoc = desc
                        test(self)
                    else:
                        self.status = 0
                        self.trys = 0
            #self.complete_output()
            a = self.complete_output()
            # 清除log的handle,新增如下代码
            self.logger.removeHandler(self.ch)
            return a
    
  • 可能各自用的HTMLTestRunner版本内容不一样,均只需按照上述修改即可。

  • 按照上述修改,再次运行,生成的html报告还是没有日志内容,会不会是loguru和logging不兼容?

    image-20210905131253600

  • 上网苦苦找寻,终于找到了解决办法,再次修改loguru封装代码如下:

    import time, os, logging
    from loguru import logger
    from settings import LOG_DIR # 日志保存路径
    
    # 新增如下三行代码
    class PropogateHandler(logging.Handler):
        def emit(self, record):
            logging.getLogger(record.name).handle(record)
            
    class Log:
        """输出日志到文件和控制台"""
        def __init__(self):
            # 文件的命名
            log_name = f"test_{time.strftime('%Y-%m-%d', time.localtime()).replace('-','_')}.log"
            log_path = os.path.join(LOG_DIR, log_name)
            # 判断日志文件夹是否存在,不存则创建
            if not os.path.exists(LOG_DIR): 
                os.mkdir(LOG_DIR)
            # 日志输出格式
            formatter = "{time:YYYY-MM-DD HH:mm:ss} | {level}: {message}"
            # 日志写入文件
            logger.add(log_path,   # 写入目录指定文件
                   format=formatter,
                   encoding='utf-8',   
                   retention='10 days',  # 设置历史保留时长
                   backtrace=True,  # 回溯
                   diagnose=True,   # 诊断
                   enqueue=True,   # 异步写入
                   # rotation="5kb",  # 切割,设置文件大小,rotation="12:00",rotation="1 week"
                   # filter="my_module"  # 过滤模块
                   # compression="zip"   # 文件压缩
                  ) 
            # 新增代码
            logger.add(PropogateHandler(), format=formatter)
    
        def debug(self, msg):
            logger.debug(msg)
    
        def info(self, msg):
            logger.info(msg)
    
        def warning(self, msg):
            logger.warning(msg)
    
        def error(self, msg):
            logger.error(msg) 
    
    log = Log()
    

    image-20210905131537538

标签:log,loguru,HTMLTestRunner,self,html,msg,logger,日志,def
来源: https://www.cnblogs.com/tahitimoon/p/15229214.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有