如何掌握Python logging的基础和进阶使用
Admin 2022-08-23 群英技术资讯 377 次浏览
日常开发中,定位程序异常,追溯事件发生场景都需要通过日志记录的方式。可以说一个好的开发日志设计可以让开发人员在后续项目维护的过程中节省时间成本,提升解决问题的效率。
目前在网上已经有许多关于Python日志操作的文章,部分文章总结的非常到位,Python官方也有日志常用的手册。自己写这篇文章是主要围绕Python官方的logging模块展开,结合自己学习过程以及项目开发中应用场景,总结归纳下Python日志使用,方便自己梳理相关知识,更好的理解;
对于开发日志,很多程序员误区可能就是停留在直接print打印到后台日志中,好的地方方便快捷,但是坏的地方就是日志输出的内容十分混乱,不方便排查。面对不同级别的事件,以及需要执行的任务时,采取的日志操作动作是不一样的。
对此结合Python官方文档总结以下执行任务对应的工具:
需要执行的任务 |
任务对应的工具 |
直接打印程序结果 |
|
记录程序普通操作(比如请求记录,状态监控) |
logging.info() |
程序发生特殊事件引发的警告信息 |
logging.warning() |
程序发生特殊事件引发错误 |
直接抛出异常(raise Exception) |
报告错误而不引发异常 |
logging.error()、logging.exception()、logging.critical() 分别使用特定错误 |
日志功能事件级别对应应用场景(以严重性递增)
级别 |
应用场景 |
DEBUG |
细节信息,仅当诊断问题适用 |
INFO |
确认程序预期运行,记录程序正常运行状态 |
WARNING |
表明有已经或即将发生的意外 |
ERROR |
由于严重的问题,程序某些功能不能使用 |
CRTICAL |
严重的错误,程序已不能继续执行 |
logging模块默认级别是WARNING,意味着只会追踪该级别以上的事件,除非更改日志配置;
日志记录保存到文件
import logging logging.basicConfig(filename="example.log", level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S", encoding='utf-8') # 记录日志信息 logging.debug("test DEBUG") logging.info("test Info") logging.warning("test Warning") logging.warning('%s before you %s', 'Look', 'leap!') logging.error("test Error")
代码注解:
(上述脚本如果连续多次运行,连续运行的消息将追缴到指定的example.log日志文件,如果想每次都是重新开始,即example.log日志不保存之前的日志信息,则修改filemode参数为'w';)
结合Python官方文档,日志库采用模块化的方法,并提供几类组件:记录器、处理器、过滤器和格式器。
官方文档中记录器和处理在日志信息记录流程:
解析:
关于记录器,主要的任务总结有三个:
关于记录器方法总结为两类,配置和消息发送.
记录器配置方法:
记录器常用创建信息方法:
关于处理器,简单的可以理解为将特定严重级别的日志信息发送到特定的位置,常用的处理类型主要有两个:
由于内置处理对象常用的配置方法:
格式器配置日志消息的最终顺序、结构和内容,格式器类的构造函数有三个可选参数:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
备注:
关于style:
fm = Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"%Y-%m-%d %H:%M:%S", style='{')
fm = Formatter("{asctime} - {name} - {levelname} - {message}",
"%Y-%m-%d %H:%M:%S", style='{')
fm = Formatter("$asctime - $name - $levelname - $message",
"%Y-%m-%d %H:%M:%S", style='$')
(这三种style使用方式,效果都一样)
开发人员可以通过三种方式配置日志记录:
关于fileConfig()读取的配置文件(官方示例):
[loggers] keys=root,simpleExample [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
(关于读取的配置文件格式类似ini格式)
关于logging模块,这里介绍一下我目前最常用的业务场景:调用方请求一个后端的rest api接口,我需要记录调用方请求的时间,地址,请求参数,处理请求后的结果,以及我需要将报错的信息保存到指定的文件里,方便排查。
为了后期使用方便,在不更改原有处理函数的基础下增加日志记录的功能,我会选择将日志记录操作封装在一个装饰器函数。
所以我只需将这部分功能分成两部分:生成记录器、请求处理的装饰器函数
# -*- coding: utf-8 -*- from logging import handlers from datetime import date import logging def init_logger(): """ 生成记录器 :return: """ app_logger = logging.getLogger(APP_NAME) app_logger.setLevel(logging.INFO) fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s", "%Y-%m-%d %H:%M:%S") # 正常日志打印到控制台 console = logging.StreamHandler() console.setFormatter(fmt) console.setLevel(logging.INFO) # 异常日志记录到log文件 today = date.today() file_name = "logs/exceptions_" + str(today) + ".log" fh = handlers.TimedRotatingFileHandler(filename=file_name, when='D', backupCount=30, encoding='utf-8') fh.setLevel("ERROR") fh.setFormatter(fmt) app_logger.addHandler(console) app_logger.addHandler(fh) return app_logger
代码解析:
from functools import wraps from flask import request app_logger = init_logger() def rest_log(return_type="dict"): def decorator(func): @wraps(func) def inner(*args, **kwargs): # 组装打印的Message消息日志格式(请求URL,目标主机,请求方法,请求参数,响应内容) log_params = { "request": request.base_url, "host": request.host, "method": request.method } req_data = {} if request.method == "POST": req_data = dict(request.json) elif request.method == "GET": req_data = dict(request.args) log_params.update({"params": req_data}) # 请求处理函数 try: result = func(*args, **kwargs) except Exception as e: # 异常信息处理 err_msg = str(e) result = {"ret_code": 500, "ret_info": err_msg} app_logger.error(log_params, exc_info=True) if return_type == "tuple": result = (result, 500) if return_type == "tuple": log_params['result'] = result[0].data else: log_params['result'] = result app_logger.info(log_params) return result return inner return decorator
代码解析:
简单使用示例:
# -*- coding: utf-8 -*- from flask import request, Blueprint from common.LogUtils import rest_log test_api = Blueprint("TestApi", __name__) @test_api.route("/log/test", methods=["GET"]) @rest_log() def test_log(): name = request.args.get("name", "") number = request.args.get('number', "") if not name or not number: raise Exception("number和name参数都不能为空") response = { "data": { "name": f"Hello, {name}", "number": number }, "ret_code": 200, "ret_info": "success" } return response
备注:
控制台日志打印效果:
2022-05-22 12:01:01 INFO: {'request': 'http://127.0.0.1:23102/log/test', 'host': '127.0.0.1:23102', 'method': 'GET', 'params': {'name': 'zhangsn', 'number': '22'}, 'result': {'data': {'name': 'Hello, zhangsn', 'number': '22'}, 'ret_code': 200, 'ret_info': 'success'}}
异常日志打印:
2022-05-22 11:47:38 ERROR: {'request': 'http://127.0.0.1:23102/log/test', 'host': '127.0.0.1:23102', 'method': 'GET', 'params': {}}
Traceback (most recent call last):
File "C:\Users\admin\TestLogging\common\LogUtils.py", line 63, in inner
result = func(*args, **kwargs)
File "C:\Users\admin\TestLogging\controller\TestLogging.py", line 18, in test_log
raise Exception("number和name参数都不能为空")
Exception: number和name参数都不能为空
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
集合(set)是一个无序的不重复元素序列。因此在每次运行的时候集合的运行结果的内容都是相同的,但元素的排列顺序却不是固定的,所以本章中部分案例的运行结果会出现与给出结果不同的情况(运行结果不唯一)可以使用大括号{}或者set()函数创建集合,注意:创建一个空集合必须用set()而不是{},因为{}是用来创建一个空字典
这篇文章主要介绍了Python基础知识方法重写+文件处理+异常处理,这是基础知识分享的第四篇,看到这里了相信大家前几篇都学得还不错吧,下面我们继续巩固Python基础知识,需要的朋友也可以参考一下
这篇文章主要介绍了Python字符串对齐方法使用(ljust()、rjust()和center()),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
JSON用来存储和交换文本信息,比xml更小/更快/更易解析,下面这篇文章主要给大家介绍了关于python向json中追加数据的两种方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
我们在Python中经常会遇到给数值取整的问题,Python中有不同的取整方法,对应解决不同的取整问题。本文将向大家介绍Python中的取整方法:向上取整math.ceil(x)、向下取整math.floor(x)、四舍五入round()、向零取整int()。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008