Python模块学习之Logging日志模块

Share on:

最近一直想自己的批量框架,参考了POC-T框架和sqlmap的框架结构,发现logging模块被大量用来处理控制台输出以及日志记录,鉴于我自己也要写框架,那么本文就记录下我的logging模块学习记录。

为什么需要logging

在开发过程中,如果程序出现了问题,我们可以使用编辑器的Debug模式来检查bug,但是在发布之后,我们的程序相当于在一个黑盒状态去运行,我们只能看到运行效果,可是程序难免出错,这种情况的话我们就需要日志模块来记录程序当前状态、时间状态、错误状态、标准输出等,这样不论是正常运行还是出现报错,都有记录,我们可以针对性的快速排查问题。

因此,日志记录对于程序的运行状态以及debug都起到了很高效的作用。如果一个程序没有标准的日志记录,就不能算作一个合格的开发者。

logging和print的对比

  • logging对输出进行了分级,print没有
  • logging具有更灵活的格式化功能,比如运行时间、模块信息
  • print输出都在控制台上,logging可以输出到任何位置,比如文件甚至是远程服务器

logging的结构拆分

模块用途
Logger记录日志时创建的对象,调用其方法来传入日志模板和信息生成日志记录
Log RecordLogger对象生成的一条条记录
Handler处理日志记录,输出或者存储日志记录
Formatter格式化日志记录
Filter日志过滤器
Parent HandlerHandler之间存在分层关系

简单的实例

 1import logging
 2
 3logger = logging.getLogger("Your Logger")
 4logger.setLevel(logging.DEBUG)
 5handler = logging.StreamHandler()
 6formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
 7handler.setFormatter(formatter)
 8logger.addHandler(handler)
 9
10logger.info("this is info msg")
11logger.debug("this is debug msg")
12logger.warning("this is warn msg")
13logger.error("this is error msg")

输出

12019/01/13 13:21:17 - Your Logger - INFO - this is info msg
22019/01/13 13:21:17 - Your Logger - DEBUG - this is debug msg
32019/01/13 13:21:17 - Your Logger - WARNING - this is warn msg
42019/01/13 13:21:17 - Your Logger - ERROR - this is error msg

我们来理解下这个实例。

首先创建了一个logger对象作为生成日志记录的对象,然后设置输出级别为DEBUG,然后创建了一个StreamHandler对象handler,来处理日志,随后创建了一个formatter对象来格式化输出日志记录,然后把formatter赋给handler,最后把handler处理器添加到我们的logger对象中,完成了整个处理流程。

知道整个流程之后我们来看一些细的东西。

Level

logging模块中自带了几个日志级别

等级数值对应方法
CRITICAL50logger.critical("msg")
FATAL50logger.fatal("msg")
ERROR40logger.error("msg")
WARNING30logger.warning("msg")
WARN30~~logger.warn("msg")~~废弃
INFO20logger.info("msg")
DEBUG10logger.debug("msg")
NOTSET0

在我们的实例中我们设置了输出级别为DEBUG

1logger.setLevel(logging.DEBUG)

那么在DEBUG级别之下的也就是NOTSET级别的不会被输出。

如果我们把级别设置为INFO,那么我们实例的输出应该是

12019/01/13 13:21:17 - Your Logger - INFO - this is info msg
22019/01/13 13:21:17 - Your Logger - WARNING - this is warn msg
32019/01/13 13:21:17 - Your Logger - ERROR - this is error msg

只会输出比INFO级别高的日志。

Handler

logging提供的Handler有很多,我简单列举几种

种类位置用途
StreamHandlerlogging.StreamHandler日志输出到流,可以是 sys.stderr,sys.stdout 或者文件
FileHandlerlogging.FileHandler日志输出到文件
SMTPHandlerlogging.handlers.SMTPHandler远程输出日志到邮件地址
SysLogHandlerlogging.handlers.SysLogHandler日志输出到syslog
HTTPHandlerlogging.handlers.HTTPHandler通过”GET”或者”POST”远程输出到HTTP服务器

Formatter

fmt参数和datefmt两个参数分别对应日志记录的格式化和时间的格式化。

fmt可用的占位符简单列举几种,更多请参考这里

占位符作用
%(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打印日志信息。

捕获Traceback

1try:
2    result = 10 / 0
3except Exception:
4    logger.error('Faild to get result', exc_info=True)
5    # 或者用下面这个
6    # logging.exception('Error')
7logger.info('Finished')

输出

12019/01/13 14:11:18 - Your Logger - ERROR - Faild to get result
2Traceback (most recent call last):
3  File "E:/Python/test.py", line 22, in <module>
4    result = 10 / 0
5ZeroDivisionError: division by zero
62019/01/13 14:11:18 - Your Logger - INFO - Finished

这样会更合理的捕获异常信息。

自定义日志级别

 1import logging
 2
 3INFO, WARN, ERROR, SUCCESS = range(1, 5)
 4# print(SYSINFO, WARN, ERROR, SUCCESS)
 5logging.addLevelName(INFO, '*')
 6logging.addLevelName(WARN, '!')
 7logging.addLevelName(ERROR, 'x')
 8logging.addLevelName(SUCCESS, '+')
 9
10logger = logging.getLogger('LOGGER')
11handler = logging.StreamHandler()
12formatter = logging.Formatter(fmt='%(asctime)s [%(levelname)s] %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
13handler.setFormatter(formatter)
14logger.addHandler(handler)
15logger.setLevel(INFO)
16
17logger.log(INFO, "INFO")
18logger.log(WARN, "WARN")
19logger.log(ERROR, "ERROR")
20logger.log(SUCCESS, "SUCCESS")

输出

12019/01/13 14:17:59 [*] INFO
22019/01/13 14:17:59 [!] WARN
32019/01/13 14:17:59 [x] ERROR
42019/01/13 14:17:59 [+] SUCCESS

先定义级别和数值,然后调用addLevelName(级别名,'输出名')。记得数值不能小于等于0,注意输出日志的级别。

给输出加上颜色

用到了一个第三方的脚本ansistrm.py,下载地址https://gist.github.com/Y4er/6300ccff3a6628ea7bda24e514013476 原作者脚本不支持win10,我修复了一下。

将这个脚本ansistrm.py和你的log.py放到同一目录,然后log.py如下内容

 1#!/usr/bin/env python3 
 2# -*- coding: utf-8 -*- 
 3# @Time : 2019/1/12 21:01 
 4# @Author : Y4er 
 5# @Site : http://Y4er.com
 6# @File : log.py 
 7
 8
 9import logging
10
11INFO, WARN, ERROR, SUCCESS = range(1, 5)
12# print(SYSINFO, WARN, ERROR, SUCCESS)
13logging.addLevelName(INFO, '*')
14logging.addLevelName(WARN, '!')
15logging.addLevelName(ERROR, 'x')
16logging.addLevelName(SUCCESS, '+')
17
18logger = logging.getLogger('YOUR LOGGER')
19try:
20    from ansistrm import ColorizingStreamHandler
21
22    handle = ColorizingStreamHandler()
23    handle.level_map[logging.getLevelName('*')] = (None, 'cyan', False)
24    handle.level_map[logging.getLevelName('+')] = (None, 'green', False)
25    handle.level_map[logging.getLevelName('x')] = (None, 'red', False)
26    handle.level_map[logging.getLevelName('!')] = (None, 'yellow', False)
27except Exception as e:
28    print(e)
29    handle = logging.StreamHandler()
30
31formatter = logging.Formatter('%(asctime)s - [%(levelname)s]  %(message)s', '%Y/%m/%d %H:%M:%S')
32handle.setFormatter(formatter)
33logger.addHandler(handle)
34logger.setLevel(INFO)
35
36
37class LOGGER:
38    @staticmethod
39    def info(msg):
40        return logger.log(INFO, msg)
41
42    @staticmethod
43    def warning(msg):
44        return logger.log(WARN, msg)
45
46    @staticmethod
47    def error(msg):
48        return logger.log(ERROR, msg)
49
50    @staticmethod
51    def success(msg):
52        return logger.log(SUCCESS, msg)
53
54
55LOGGER.info("INFO msg")
56LOGGER.warning("warning msg")
57LOGGER.error("error msg")
58LOGGER.success("success msg")

在这个脚本中,我写了一个类以及其下的四个静态方法,那么可以这么调用

1LOGGER.info("INFO msg")
2LOGGER.warning("warning msg")
3LOGGER.error("error msg")
4LOGGER.success("success msg")

运行效果:

写在文后

是时候抛弃print了!

参考链接

Python中logging模块的基本用法 - 崔庆才老师

原版ansistrm.py - vsajip

修复ansistrm.py以支持win10