最近一直想自己的批量框架,参考了POC-T框架和sqlmap的框架结构,发现logging模块被大量用来处理控制台输出以及日志记录,鉴于我自己也要写框架,那么本文就记录下我的logging模块学习记录。
为什么需要logging
在开发过程中,如果程序出现了问题,我们可以使用编辑器的Debug模式来检查bug,但是在发布之后,我们的程序相当于在一个黑盒状态去运行,我们只能看到运行效果,可是程序难免出错,这种情况的话我们就需要日志模块来记录程序当前状态、时间状态、错误状态、标准输出等,这样不论是正常运行还是出现报错,都有记录,我们可以针对性的快速排查问题。
因此,日志记录对于程序的运行状态以及debug都起到了很高效的作用。如果一个程序没有标准的日志记录,就不能算作一个合格的开发者。
logging和print的对比
- logging对输出进行了分级,print没有
- logging具有更灵活的格式化功能,比如运行时间、模块信息
- print输出都在控制台上,logging可以输出到任何位置,比如文件甚至是远程服务器
logging的结构拆分
模块 | 用途 |
---|---|
Logger | 记录日志时创建的对象,调用其方法来传入日志模板和信息生成日志记录 |
Log Record | Logger对象生成的一条条记录 |
Handler | 处理日志记录,输出或者存储日志记录 |
Formatter | 格式化日志记录 |
Filter | 日志过滤器 |
Parent Handler | Handler之间存在分层关系 |
简单的实例
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
模块中自带了几个日志级别
等级 | 数值 | 对应方法 |
---|---|---|
CRITICAL | 50 | logger.critical(“msg”) |
FATAL | 50 | logger.fatal(“msg”) |
ERROR | 40 | logger.error(“msg”) |
WARNING | 30 | logger.warning(“msg”) |
WARN | 30 | ~~logger.warn(“msg”)~~废弃 |
INFO | 20 | logger.info(“msg”) |
DEBUG | 10 | logger.debug(“msg”) |
NOTSET | 0 | 无 |
在我们的实例中我们设置了输出级别为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有很多,我简单列举几种
种类 | 位置 | 用途 |
---|---|---|
StreamHandler | logging.StreamHandler | 日志输出到流,可以是 sys.stderr,sys.stdout 或者文件 |
FileHandler | logging.FileHandler | 日志输出到文件 |
SMTPHandler | logging.handlers.SMTPHandler | 远程输出日志到邮件地址 |
SysLogHandler | logging.handlers.SysLogHandler | 日志输出到syslog |
HTTPHandler | logging.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
了!
参考链接
评论