搞掂python日志的应用,同事面前装个13

发布时间 2023-06-09 12:05:42作者: 测试玩家勇哥

Python日志处理

日志是在软件开发中记录程序运行情况的一种重要方式,它对于错误排查和系统运维非常有帮助。Python标准库自带了强大的logging日志模块,被广泛应用于各种Python模块中.

1. 小试牛刀

1.1 简单使用

import logging

# 默认的warning级别,只输出warning以上的
# 使用basicConfig()来指定日志级别和相关信息
logging.basicConfig(
    level=logging.DEBUG,  # 设置级别,根据等级显示
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:%(message)s',  # 设置输出格式
    datefmt="%Y-%m-%d %H:%M:%S"  # 时间输出的格式
)

logging.debug("This is a DEBUG message")
logging.info("This is an INFO message")
logging.warning("This is a WARNING message")
logging.error("This is an ERROR message")
logging.critical("This is a CRITICAL message")

输出结果:

2023-06-09 10:22:39-[ts.py-->line:11]-DEBUG:DEBUG
2023-06-09 10:22:39-[ts.py-->line:12]-INFO:INFO
2023-06-09 10:22:39-[ts.py-->line:13]-WARNING:WARNING
2023-06-09 10:22:39-[ts.py-->line:14]-ERROR:TERROR
2023-06-09 10:22:39-[ts.py-->line:15]-CRITICAL:CRITICAL

1.2 日志级别

五种日志等级,不同情况输出不同等级的日志。

日志等级(level) 描述
DEBUG 调试信息,通常在诊断问题的时候用得着
INFO 普通信息,确认程序按照预期运行
WARNING 警告信息,表示发生意想不到的事情,或者指示接下来可能会出现一些问题,但是程序还是继续运行
ERROR 错误信息,程序运行中出现了一些问题,程序某些功能不能执行
CRITICAL 危险信息,一个严重的错误,导致程序无法继续运行

过滤掉低于自定义设置的日志等级。

import logging

# 默认的warning级别,只输出warning以上的
# 使用basicConfig()来指定日志级别和相关信息
logging.basicConfig(
    level=logging.ERROR,  # 设置级别,根据等级显示
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:%(message)s',  # 设置输出格式
    datefmt="%Y-%m-%d %H:%M:%S"  # 时间输出的格式
)
logging.debug("调试等级不输出")
logging.info("普通信息等级不会输出")
logging.warning("警告不会输出)
logging.error("输出错误等级")
logging.critical("输出崩溃等级")

输出结果:

2023-06-09 10:27:16-[ts.py-->line:13]-ERROR:输出错误等级
2023-06-09 10:27:16-[ts.py-->line:14]-CRITICAL:输出崩溃等级

在上面的示例中,我们将日志级别设置为ERROR,因此只有ERRORCRITICAL级别的日志被输出,DEBUGINFOWARNING级别的日志被过滤掉。

1.3 日志格式

通过format参数可以自定义日志输出的格式。在格式字符串中,可以使用以下占位符来表示不同的日志信息:

  • • %(asctime)s:日志记录的时间(默认格式为YYYY-MM-DD HH:MM:SS

  • • %(levelname)s:日志级别

  • • %(message)s:日志消息

  • • %(name)s:日志器的名称

  • • %(filename)s:包含当前日志记录的源文件的文件名

  • • %(lineno)d:当前日志记录所在的行号

设置自定义的日志格式:

import logging

logging.basicConfig(
    level=logging.DEBUG, # 设置最低过滤级别
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S' # 使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。
)

logging.debug('勇哥 debug ')
logging.info('勇哥 info')
logging.warning('勇哥 warning ')
logging.error('勇哥 error ')
logging.critical('勇哥 critical ')

输出结果:

2023-06-09 10:33:08 - DEBUG - 勇哥 debug 
2023-06-09 10:33:08 - INFO - 勇哥 info
2023-06-09 10:33:08 - WARNING - 勇哥 warning 
2023-06-09 10:33:08 - ERROR - 勇哥 error 
2023-06-09 10:33:08 - CRITICAL - 勇哥 critical

2. 磨磨牛刀

日志处理器(Log Handlers)用于指定日志的输出位置,例如控制台、文件或网络等。

2.1 控制台处理器

StreamHandler是一个将日志输出到控制台的处理器。它可以将日志消息打印到标准输出或标准错误。

将日志输出到控制台:

import logging

logging.basicConfig(level=logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(console_handler)

logger.debug('勇哥 debug ')
logger.info('勇哥 info ')
logger.warning('勇哥 warning')
logger.error('勇哥 error')
logger.critical('勇哥 critical')

输出结果:

2023-06-09 10:52:56,493 - DEBUG - 勇哥 debug 
DEBUG:__main__:勇哥 debug 
2023-06-09 10:52:56,493 - INFO - 勇哥 info 
INFO:__main__:勇哥 info 
2023-06-09 10:52:56,493 - WARNING - 勇哥 warning
WARNING:__main__:勇哥 warning
2023-06-09 10:52:56,494 - ERROR - 勇哥 error
ERROR:__main__:勇哥 error
2023-06-09 10:52:56,494 - CRITICAL - 勇哥 critical
CRITICAL:__main__:勇哥 critical

StreamHandler对象添加到日志器处理器中。设置处理器的级别和格式,控制输出的日志级别和格式。

2.2 文件处理器

FileHandler将日志输出到文件的处理器,将日志消息写入指定的文件。

将日志输出到文件:

import logging

logging.basicConfig(level=logging.DEBUG)

file_handler = logging.FileHandler('app.log', encoding='utf8') # 不带编码会出现中文乱码
file_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(file_handler)

logger.debug('勇哥 debug ')
logger.info('勇哥 info ')
logger.warning('勇哥 warning')
logger.error('勇哥 error')
logger.critical('勇哥 critical')

FileHandler对象添加到日志器中。日志消息写入名为app.log的文件中。

2.3 其他处理器

除了控制台处理器和文件处理器,logging模块还提供了其他处理器,例如:

  • • SMTPHandler:将日志消息通过电子邮件发送。

  • • SocketHandler:将日志消息发送到网络套接字。

  • • SysLogHandler:将日志消息发送到系统日志。

  • • 等等。

你可以根据需要选择适合的处理器,并根据需要配置其级别、格式和其他属性。

3. 牛刀贴膜

日志过滤器(Log Filters)用于对日志进行过滤,只输出满足特定条件的日志消息。通过添加日志过滤器,我们可以进一步控制哪些日志消息应该被处理器处理。

使用过滤器将日志消息限制在特定级别的范围内:

import logging

class LevelFilter(logging.Filter):
    def __init__(self, level):
        super().__init__()
        self.level = level

    def filter(self, record):
        return record.levelno >= self.level

logging.basicConfig(level=logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(console_handler)

level_filter = LevelFilter(logging.WARNING)
logger.addFilter(level_filter)

logger.debug('勇哥 debug')
logger.info('勇哥 info')
logger.warning('勇哥 warning')
logger.error('勇哥 error')
logger.critical('勇哥 critical')

LevelFilter自定义过滤器。该过滤器接受一个日志级别作为参数,在filter方法中根据日志记录的级别判断是否应该处理该日志记录。然后,我们将过滤器添加到控制台处理器中,从而限制了只输出WARNING级别及以上的日志消息。

输出结果:

2023-06-09 11:17:16,546 - WARNING - 勇哥 warning
WARNING:__main__:勇哥 warning
2023-06-09 11:17:16,546 - ERROR - 勇哥 error
ERROR:__main__:勇哥 error
2023-06-09 11:17:16,546 - CRITICAL - 勇哥 critical
CRITICAL:__main__:勇哥 critical

DEBUGINFO级别的日志消息被过滤掉了。

牛刀装鞘

封装日志功能来提供更方便和可复用的日志记录接口

# -*- coding: utf-8 -*-
# @Time : 2019/11/18 10:17
# @Author : 勇哥
# @Email : 262667641@qq.com
# @File : logger.py.py
# @Project : risk_api_project


import logging
import time
from logging import handlers

from common.base_datas import BaseDates


class MyLog:
    level_relations = {
        "debug": logging.DEBUG,
        "info": logging.INFO,
        "warning": logging.WARNING,
        "error": logging.ERROR,
        "critic": logging.CRITICAL
    }  # 日志级别关系映射

    def my_log(self, msg, level="error", when="D", back_count=10):
        """
        实例化 TimeRotatingFileHandler
        interval 是时间间隔, backupCount 是备份文件的个数,如果超过这个个数,就会自动删除,when 是间隔的时间单位,单位有以下几种
        S 秒
        M 分
        H 小时
        D 天
        每星期(interval == 0 时代表星期一
        midnight 每天凌晨
        """
        file_name = BaseDates.log_path

        my_logger = logging.getLogger()  # 定义日志收集器 my_logger
        my_logger.setLevel(self.level_relations.get(level))  # 设置日志级别

        format_str = logging.Formatter(
            "%(asctime)s-%(levelname)s-%(filename)s-[ line:%(lineno)d ] - 日志信息:%(message)s")  # 设置日志格式
        # 创建输出渠道
        sh = logging.StreamHandler()  # 往屏幕输出
        sh.setFormatter(format_str)  # 设置屏幕上显示的格式
        current = time.strftime("%Y-%m-%d", time.localtime())  # 设置当前日期
        if level == "error":
            th = handlers.TimedRotatingFileHandler(filename=f'{file_name}/{current}_{level}.log', when=when,
                                                   backupCount=back_count, encoding="utf-8")
        else:
            th = handlers.TimedRotatingFileHandler(filename=file_name + "/{}_info.log".format(current), when=when,
                                                   backupCount=back_count,
                                                   encoding="utf-8")  # 往文件里写日志

        th.setFormatter(format_str)  # 设置文件里写入的格式
        my_logger.addHandler(sh)  # 将对象加入logger里
        my_logger.addHandler(th)

        if level == "debug":
            my_logger.debug(msg)
        elif level == "error":
            my_logger.error(msg)
        elif level == "info":
            my_logger.info(msg)
        elif level == "warning":
            my_logger.warning(msg)
        else:
            my_logger.critical(msg)

        my_logger.removeHandler(sh)
        my_logger.removeHandler(th)
        logging.shutdown()

    def decorator_log(self, msg=None):
        def warp(fun):
            def inner(*args, **kwargs):
                try:
                    return fun(*args, **kwargs)
                except Exception as e:
                    self.my_log(f"{msg}: {e}", "error")

            return inner

        return warp


if __name__ == '__main__':
    # for i in range(2):
    #     MyLog().my_log("hhhh{}".format(i), "info")
    #     time.sleep(0.04)
    @MyLog().decorator_log(“”知错了嘛?)
    def add():
        print("试一下")
        raise "不好使,异常了。"

输出:

总结

以上就是勇哥今天为各位小伙伴准备的内容,如果你想了解更多关于Python自动化测试的知识和技巧,欢迎关注我:公众号\博客\CSDN\B站:测试玩家勇哥;我会不定期地分享更多的精彩内容。感谢你的阅读和支持!

最后

我相信学习不是一朝一夕形成的,学习是一生的事业,坚持不懈,持续进步,为自己创造更美好的未来。

以上,共勉!


题外话,勇哥打算把新建的技术交流群,打造成一个活跃的高质量技术群。工作中遇到的技术问题,都可以在里面咨询大家,还有工作内推的机会。

有兴趣的小伙伴,欢迎加我(记得备注是进群还是报名学习)。???****

?****?********?长按上方二维码2秒,关注我


勇哥,10年落魄测试老司机,技术栈偏python,目前在一家超大型房产公司担任自动化测试主管,日常工作比较繁杂,主要负责自动化测试,性能测试、软件质量管理及人员管理。工作之余专注于为粉丝进行简历修改、面试辅导、模拟面试、资料分享、一对一自动化测试教学辅导等副业发展。目前已服务十多位小伙伴,取得高薪offer。

关注公众号,测试干货及时送达

往期精选文章?:

python-Threading多线程之线程锁
Pytest 快速入门
pytest 前后置操作详谈
接口自动化之测试数据动态生成并替换
requests模块该如何封装?
最通俗易懂python操作数据库
python正则一篇搞掂
接口自动化如何封装mysql操作
性能测试之必备知识

性能分析思

Python + ChatGPT来实现一个智能对话的钉钉机器人
一文看懂python如何执行cmd命令