python 日志模块logging

日志记录的流程框架

在 Python 中,怎样才能算作一个比较标准的日志记录过程呢?或许很多人会使用 print 语句输出一些运行信息,然后再在控制台观察,运行的时候再将输出重定向到文件输出流保存到文件中,这样其实是非常不规范的,在 Python 中有一个标准的 logging 模块,我们可以使用它来进行标注的日志记录,利用它我们可以更方便地进行日志记录,同时还可以做更方便的级别区分以及一些额外日志信息的记录,如时间、运行模块信息等。

整个日志记录的框架可以分为这么几个部分:

  1. Logger:即 Logger MainClass,是我们进行日志记录时创建的对象,我们可以调用它的方法传入日志模板和信息,来生成一条条日志记录,称作 Log Record。
  2. Log Record:就代指生成的一条条日志记录。
  3. Handler:即用来处理日志记录的类,它可以将 Log Record输出到我们指定的日志位置和存储形式等,如我们可以指定将日志通过 FTP 协议记录到远程的服务器上,Handler就会帮我们完成这些事情。
  4. Formatter:实际上生成的 Log Record也是一个个对象,那么我们想要把它们保存成一条条我们想要的日志文本的话,就需要有一个格式化的过程,那么这个过程就由 Formatter来完成,返回的就是日志字符串,然后传回给 Handler 来处理。
  5. Filter:另外保存日志的时候我们可能不需要全部保存,我们可能只需要保存我们想要的部分就可以了,所以保存前还需要进行一下过滤,留下我们想要的日志,如只保存某个级别的日志,或只保存包含某个关键字的日志等,那么这个过滤过程就交给 Filter 来完成。
  6. Parent Handler:Handler 之间可以存在分层关系,以使得不同 Handler之间共享相同功能的代码。

日志记录的相关用法

下面我们初步来了解下 logging 模块的基本用法,先用一个实例来感受一下:

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')

在这里我们首先引入了 logging 模块,然后进行了一下基本的配置,这里通过 basicConfig 配置了 level 信息和 format 信息,这里 level 配置为 INFO 信息,即只输出 INFO 级别的信息,另外这里指定了 format 格式的字符串,包括 asctime、name、levelname、message 四个内容,分别代表运行时间、模块名称、日志级别、日志内容,这样输出内容便是这四者组合而成的内容了,这就是 logging 的全局配置。

接下来声明了一个 Logger 对象,它就是日志输出的主类,调用对象的 info() 方法就可以输出 INFO 级别的日志信息,调用 debug() 方法就可以输出 DEBUG 级别的日志信息,非常方便。在初始化的时候我们传入了模块的名称,这里直接使用 name 来代替了,就是模块的名称,如果直接运行这个脚本的话就是 main,如果是 import 的模块的话就是被引入模块的名称,这个变量在不同的模块中的名字是不同的,所以一般使用 name 来表示就好了,再接下来输出了四条日志信息,其中有两条 INFO、一条 WARNING、一条 DEBUG 信息,我们看下输出结果:

2018-06-03 13:42:43,526 - __main__ - INFO - This is a log info
2018-06-03 13:42:43,526 - __main__ - WARNING - Warning exists
2018-06-03 13:42:43,526 - __main__ - INFO - Finish

可以看到输出结果一共有三条日志信息,每条日志都是对应了指定的格式化内容,另外我们发现 DEBUG 的信息是没有输出的,这是因为我们在全局配置的时候设置了输出为 INFO 级别,所以 DEBUG 级别的信息就被过滤掉了。

这时如果我们将输出的日志级别设置为 DEBUG,就可以看到 DEBUG 级别的日志输出了:

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

输出结果:

2018-06-03 13:49:22,770 - __main__ - INFO - This is a log info
2018-06-03 13:49:22,770 - __main__ - DEBUG - Debugging
2018-06-03 13:49:22,770 - __main__ - WARNING - Warning exists
2018-06-03 13:49:22,770 - __main__ - INFO - Finish

接下来我们首先来全面了解一下 basicConfig 的参数都有哪些:

logging.basicConfig(filename=None, filemode='a', format=None, datefmt=None, style=None, level=None, steam=None, 
handlers=None)
参数说明:
filename:日志输出文件名称;
filemode:日志文件写入模式,只有两种模式('w','a');默认是'a'即追加模式;
format:指定日志信息输出格式;
datefmt:指定时间输出格式;
style:如果 format 参数指定了,这个参数就可以指定格式化时的占位符风格,如 %{、$ 等;
level:指定日志输出的类别,程序会输出大于等于此级别的信息;
steam:在没有指定 filename 的时候会默认使用 StreamHandler,这时 stream 可以指定初始化的文件流。
handlers:可以指定日志处理时所使用的 Handlers,必须是可迭代的。

其中format参数的部分格式:

格式说明
%(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
%(processName)s打印线程名称
%(module)s打印模块名称
%(message)s打印日志信息

再来个实例:

import logging
 
logging.basicConfig(level=logging.DEBUG,
                    filename='output.log',
                    datefmt='%Y/%m/%d %H:%M:%S',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s')
logger = logging.getLogger(__name__)
 
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')

这里我们指定了输出文件的名称为 output.log,另外指定了日期的输出格式,其中年月日的格式变成了 %Y/%m/%d,另外输出的 format 格式增加了 lineno、module 这两个信息,运行之后便会生成一个 output.log 的文件,内容如下:

2018/06/03 14:43:26 - __main__ - INFO - 9 - demo3 - This is a log info
2018/06/03 14:43:26 - __main__ - DEBUG - 10 - demo3 - Debugging
2018/06/03 14:43:26 - __main__ - WARNING - 11 - demo3 - Warning exists
2018/06/03 14:43:26 - __main__ - INFO - 12 - demo3 - Finish

以上我们通过 basicConfig 来进行了一些全局的配置,我们同样可以使用 Formatter、Handler 进行更灵活的处理,下面我们来了解一下。

Level

等级数值
CRITICAL50
FATAL50
ERROR40
WARNING30
WARN30
INFO20
DEBUG10
NOTSET0

这里最高的等级是 CRITICAL 和 FATAL,两个对应的数值都是 50,另外对于 WARNING 还提供了简写形式 WARN,两个对应的数值都是 30。

我们设置了输出 level,系统便只会输出 level 数值大于或等于该 level 的的日志结果,例如我们设置了输出日志 level 为 INFO,那么输出级别大于等于 INFO 的日志,如 WARNING、ERROR 等,DEBUG 和 NOSET 级别的不会输出。

import logging
 
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.WARN)
 
# Log
logger.debug('Debugging')
logger.critical('Critical Something')
logger.error('Error Occurred')
logger.warning('Warning exists')
logger.info('Finished')

这里我们设置了输出级别为 WARN,然后对应输出了五种不同级别的日志信息,运行结果如下:

Critical Something
Error Occurred
Warning exists

可以看到只有 CRITICAL、ERROR、WARNING 信息输出了,DEBUG、INFO 信息没有输出。

Handler

先来个handler的实例:

import logging
 
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('output.log')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
 
logger.info('This is a log info')
logger.debug('Debugging')
logger.warning('Warning exists')
logger.info('Finish')

这里我们没有再使用 basicConfig 全局配置,而是先声明了一个 Logger 对象,然后指定了其对应的 Handler 为 FileHandler 对象,然后 Handler 对象还单独指定了 Formatter 对象单独配置输出格式,最后给 Logger 对象添加对应的 Handler 即可,最后可以发现日志就会被输出到 output.log 中,内容如下:

2018-06-03 14:53:36,467 - __main__ - INFO - This is a log info
2018-06-03 14:53:36,468 - __main__ - WARNING - Warning exists
2018-06-03 14:53:36,468 - __main__ - INFO - Finish

另外我们还可以使用其他的 Handler 进行日志的输出,logging 模块提供的 Handler 有:

  1. StreamHandler:logging.StreamHandler;日志输出到流,可以是 sys.stderr,sys.stdout 或者文件。
  2. FileHandler:logging.FileHandler;日志输出到文件。
  3. BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式。
  4. RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚。
  5. TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件。
  6. SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets。
  7. DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets。
  8. SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址。
  9. SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog。
  10. NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志。
  11. MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer。
  12. HTTPHandler:logging.handlers.HTTPHandler;通过”GET”或者”POST”远程输出到HTTP服务器。

用法:

  1. 实例化一个logger对象后,
  2. 设置好相关的handler的配置,如日志输出文件名,日志信息输出格式等
  3. 最后使用logger对象的addHandler(handler_name)

示例:使用Handler实现将日志同时输出到控制台和文件

# 实例一个logger对象,将其输出级别设为DEBUG
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 设置控制台handler对象
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(level=logging.DEBUG)
logger.addHandler(stream_handler)

# 设置文件Handler对象
file_handler = logging.FileHandler(filename='log/test_log.txt', mode='a')
file_handler.setLevel(level=logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message0)s')
file_handler.setFormatter(fmt=formatter)  # 传入的fmt需要先使用logging.Formatter()格式化
logger.addHandler(file_handler)

# log
logger.warning('This a warning!!!')
logger.info('This is a info test!!')
logger.critical('This is the most level error!!')
logger.debug('This is a DEBUG test!!')
logger.info('Finish...')

输出结果:
控制台输出:

This a warning!!!
This is a info test!!
This is the most level error!!
This is a DEBUG test!!
Finish...

test_log.txt文件内容:

2019-10-22 18:37:28,788 - __main__ - WARNING - This a warning!!!
2019-10-22 18:37:28,788 - __main__ - INFO - This is a info test!!
2019-10-22 18:37:28,789 - __main__ - CRITICAL - This is the most level error!!
2019-10-22 18:37:28,789 - __main__ - INFO - Finish...

Formatter

在进行日志格式化输出的时候,我们可以不借助于 basicConfig 来全局配置格式化输出内容,可以借助于 Formatter 来完成,下面我们再来单独看下 Formatter 的用法:

import logging
 
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.WARN)
formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
 
# Log
logger.debug('Debugging')
logger.critical('Critical Something')
logger.error('Error Occurred')
logger.warning('Warning exists')
logger.info('Finished')

在这里我们指定了一个 Formatter,并传入了 fmt 和 datefmt 参数,这样就指定了日志结果的输出格式和时间格式,然后 handler 通过 setFormatter() 方法设置此 Formatter 对象即可,输出结果如下:

2018/06/03 15:47:15 - __main__ - CRITICAL - Critical Something
2018/06/03 15:47:15 - __main__ - ERROR - Error Occurred
2018/06/03 15:47:15 - __main__ - WARNING - Warning exists

Formatter的一般使用流程:

  1. formatter = logging.Formatter(‘日志输出格式’, datefmt=‘时间格式(时间模块中的时间格式表)’)
  2. 再使用Handler对象的setFormatter()函数传入formatter,即可设置完成。
  3. 最后将Handler传入logger对象中。

以上内容总结自 :https://cuiqingcai.com/6080.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值