目录
1.使用Python的 logging 模块记录日志
2. Python log 的日志级别
3.logging 日志格式
4.logging 模块:Logger 、Handler、Filter、Formatter
5.自动分割日志文件-日志文件按照时间划分或者按照大小划分
6总结与扩展
7 Linux 下的 rsyslog 日志服务器
8.参考
官方文档:
[1]https://docs.python.org/2/library/logging.html
0.程序日志的重要性
程序日志的重要性不言而喻,定位问题就会用到日志信息;另外,通过跟踪日志,也可以快速了解他人写的代码的执行情况。
下面给出一个例子,使用 logging 模块以一定的格式向控制台和本地日志文件 输出 程序执行过程中的日志信息:
#-*- coding: UTF-8 -*-
#!/usr/bin/env python
import logging
def MyLogTest():
logger = logging.getLogger('MyLogTest')
logger.setLevel(level=logging.INFO)
LogFormat=logging.Formatter('%(asctime)s - %(pathname)s - %(filename)s-%(funcName)s-[line:%(lineno)d] - %(levelname)s: %(message)s')
file_handler = logging.FileHandler('MyLogTest.log')
file_handler.setLevel(level=logging.INFO)
file_handler.setFormatter(LogFormat)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(LogFormat)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
logger.debug('debug级别,一般用来打印一些调试信息,级别最低')
logger.info('info级别,一般用来打印一些正常的操作信息')
logger.warning('waring级别,一般用来打印警告信息')
logger.error('error级别,一般用来打印一些错误信息')
logger.critical('critical级别,一般用来打印一些致命的错误信息,等级最高')
if __name__ == '__main__':
MyLogTest()
控制台输出:
2019-12-29 21:01:43,566 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:25] - INFO: info级别,一般用来打印一些正常的操作信息
2019-12-29 21:01:43,589 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:26] - WARNING: waring级别,一般用来打印警告信息
2019-12-29 21:01:43,608 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:27] - ERROR: error级别,一般用来打印一些错误信息
2019-12-29 21:01:43,623 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:28] - CRITICAL: critical级别,一般用来打印一些致命的错误信息,等级最高
MyLogTest.log文件输出:
2019-12-29 21:03:52,020 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:25] - INFO: info级别,一般用来打印一些正常的操作信息
2019-12-29 21:03:52,043 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:26] - WARNING: waring级别,一般用来打印警告信息
2019-12-29 21:03:52,051 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:27] - ERROR: error级别,一般用来打印一些错误信息
2019-12-29 21:03:52,056 - E:\programming_languages\Python\logging\MyLogTest2.py - MyLogTest2.py-MyLogTest-[line:28] - CRITICAL: critical级别,一般用来打印一些致命的错误信息,等级最高
如果想知道 logging 模块所在路径,Linux 下同样适用,适合于查看源代码,继承(改造)logging 模块
>>> import logging
>>> logging
Python 标准库 logging 用作记录日志,默认分为六种日志级别(括号为级别对应的数值):
1)NOTSET(0)
2)DEBUG(10)
3)INFO(20)
4)WARNING(30)
5)ERROR(40)
6)CRITICAL(50)
我们自定义日志级别时注意不要和默认的日志级别数值相同,logging 执行时输出大于等于设置的日志级别的日志信息,如设置日志级别是 INFO,则 INFO、WARNING、ERROR、CRITICAL 级别的日志都会输出。
logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.DEBUG)
2019-07-19 15:54:26,625 - log_test.py[line:11] - DEBUG: debug级别,一般用来打印一些调试信息,级别最低
format 可以指定输出的内容和格式,其内置的参数如下:
%(name)s:Logger的名字
%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(message)s:打印日志信息
logging 库采取了模块化的设计,提供了许多组件:记录器、处理器、过滤器和格式化器
简单地说,其中 logger 是负责记录日志消息的,然后我们要把这些日志消息放到哪里,交给 Handler 处理,Filter 则帮我们过滤信息(不限于通过级别过滤,意思是还有其他过滤方式),Formatter 就是跟上面的 format 一个意思,用来设置日志内容和格式。
Logger 负责记录日志
Handler 负责日志的处理(怎么输出,输出到哪里)
Formatter 控制日志的输出格式
Filter 日志的过滤控制
logger = logging.getLogger('test')
logger.setLevel(level=logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
...
我们发现Formatter是给handler设置的,这很好理解,因为handler是负责把日志输出到哪里,所以是给它设置格式,而不是给logger;那为什么level需要设置两次呢?给logger设置是告诉它要记录哪些级别的日志,给handler设是告诉它要输出哪些级别的日志,相当于进行了两次过滤。这样的好处在于,当我们有多个日志去向时,比如既保存到文件,又输出到控制台,就可以分别给他们设置不同的级别;logger 的级别是先过滤的,所以被 logger 过滤的日志 handler 也是无法记录的,这样就可以只改 logger 的级别而影响所有输出。两者结合可以更方便地管理日志记录的级别。
有时候我们需要对日志文件进行分割,以方便我们的管理。python 提供了两个处理器,方便我们分割文件:
logging.handlers.RotatingFileHandler -> 按照大小自动分割日志文件,一旦达到指定的大小重新生成文件
logging.handlers.TimedRotatingFileHandler -> 按照时间自动分割日志文件
如果将日志保存在一个文件中,那么时间一长,或者日志一多,单个日志文件就会很大,既不利于备份,也不利于查看。我们会想到能不能按照时间或者大小对日志文件进行划分呢?答案肯定是可以的,并且还很简单,logging 考虑到了我们这个需求。logging.handlers 文件中提供了 TimedRotatingFileHandler 和 RotatingFileHandler 类分别可以实现按时间和大小划分。打开这个 handles 文件,可以看到还有其他功能的 Handler 类,它们都继承自基类 BaseRotatingHandler。
# TimedRotatingFileHandler 类构造函数
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
# RotatingFileHandler 类的构造函数
def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
示例代码:
1)按照日志文件的大小进行划分:
from logging import handlers
# 每隔 1000 Byte 划分一个日志文件,备份文件为 3 个
file_handler = logging.handlers.RotatingFileHandler("test.log", mode="w", maxBytes=1000, backupCount=3, encoding="utf-8")
2)按照日志的记录时间进行划分:
# 每隔 1小时 划分一个日志文件,interval 是时间间隔,备份文件为 10 个
handler2 = logging.handlers.TimedRotatingFileHandler("test.log", when="H", interval=1, backupCount=10)
若改为when='S'
,则以秒为周期进行切割,运行几次后会生成文件,文件名以时间为后缀进行区分,其中没有后缀的为最新日志文件。
Python logging 库设计的真的非常灵活,如果有特殊的需要还可以在这个基础的 logging 库上进行改进,创建新的 Handler 类解决实际开发中的问题。
Python 官网虽然说 logging 库是线程安全的,但在多进程、多线程、多进程多线程环境中仍然还有值得考虑的问题,比如,如何将日志按照进程(或线程)划分为不同的日志文件,也即一个进程(或线程)对应一个文件。
[1]https://blog.csdn.net/paopaohll/article/details/84388313
[1]https://blog.csdn.net/Runner1st/article/details/96481954
[2]https://cloud.tencent.com/developer/article/1354396