一.什么是 logging 模块
- logging 模块是 Python 自带的标准模块
二.logging 模块有什么作用
- 主要用于输出运行日志
- 可以控制输出日志的等级, 过滤一些重要信息, 不显示大量无关要紧的调试信息
- 日志保存的路径, 可以是输出到终端, 也可以是输出到文件
- 以及文件轮转等等, 日志文件轮转指的是设置保存日志文件个数, 当超过最大日志文件个数, 最早的那个日志文件会被删除
三.logging 模块的使用
1.直接导入 logging 模块
import logging
先进行日志的基本配置
logging.basicConfig(
# filename='access.log', # 日志名字 (不指定默认输出到终端)
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s', # 日志格式
datefmt='%Y-%m-%d %H:%M:%S %p', # 时间格式
level=30, # 日志等级
)
进行日志输出
logging.debug('在大楼使用电子设备') # 10 调试信息
logging.info('大楼里面使用打火机') # 20 正常运行信息
logging.warning('大楼里抽烟') # 30 警告 可能出错
logging.error('正在大楼里玩火') # 40 出错
logging.critical('拿着手榴弹在大楼里溜达') # 50 出错长时间不管会崩溃
''' 输出结果
2020-12-11 19:50:30 PM - root - WARNING - test: 大楼里抽烟
2020-12-11 19:50:30 PM - root - ERROR - test: 正在大楼里玩火
2020-12-11 19:50:30 PM - root - CRITICAL - test: 拿着手榴弹在大楼里溜达
'''
通过日志等级过滤掉了"debug"以及"info"的日志信息 (大于以及等于你设置的那个等级才会输出)
四.logging 模块的四种对象
1.logger : 负责生产日志
logger1 = logging.getLogger('[日志名]')
2.fitter : 过滤日志 (不常用)
玉炉香 红蜡泪 偏照画堂秋思
3.handler : 控制日志输出的位置 (文件or终端)
fh1 = logging.FileHandler(filename='a1.log', encoding='utf-8') # 文件a1
fh2 = logging.FileHandler(filename='a2.log', encoding='utf-8') # 文件a2
sh = logging.StreamHandler() # 终端
4.formatter : 控制日志的格式
formatter1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(massage)s', # 日志输出格式
datefmt='%Y-%m-%d %H:%M:%S %p', # 时间格式
)
-
日志输出格式
属性名称 | 使用格式 | 作用 |
---|---|---|
asctime | %(asctime)s | 日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896 |
created | %(created)f | 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值 |
relativeCreated | %(relativeCreated)d | 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的) |
msecs | %(msecs)d | 日志事件发生事件的毫秒部分 |
levelname | %(levelname)s | 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') |
levelno | %(levelno)s | 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) |
name | %(name)s | 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger |
message | %(message)s | 日志记录的文本内容,通过 msg % args 计算得到的 |
pathname | %(pathname)s | 调用日志记录函数的源码文件的全路径 |
filename | %(filename)s | pathname的文件名部分,包含文件后缀 |
module | %(module)s | filename的名称部分,不包含后缀 |
lineno | %(lineno)d | 调用日志记录函数的源代码所在的行号 |
funcName | %(funcName)s | 调用日志记录函数的函数名 |
process | %(process)d | 进程ID |
processName | %(processName)s | 进程名称,Python 3.1新增 |
thread | %(thread)d | 线程ID |
threadName | %(thread)s | 线程名称 |
五.日志字典配置
#自定义日志的输出格式
formatter1_format = '%(asctime)s %(name)s %(filename)s:%(lineno)d %(levelname)s: %(message)s'
formatter2_format = '%(asctime)s %(name)s : %(message)s'
formatter3_format = '%(asctime)s : %(message)s'
#通过变量的方式存放路径,也可以使用"os.path"来规范路径
logfile_path1 = r'F:\Pycharm File\PycharmProjects\python正课\day18\a1.log' # log文件名
logfile_path2 = r'F:\Pycharm File\PycharmProjects\python正课\day18\a2.log' # log文件名
#log配置字典, 里面就是上面提到的四种对象
LOGGING_DIC = {
'version': 1, # 指定版本信息
'disable_existing_loggers': False, # 关闭已存在日志。默认False
# 控制日志的格式
'formatters': { # 固定格式不能修改
"formatter1": { # 开头自定义的日志输出格式名
'format': formatter1_format # "format" 固定格式不能修改
},
'formatter2': {
'format': formatter2_format
},
'formatter3': {
'format': formatter3_format
},
},
# 过滤日志 (不常用)
'filters': {},
# 控制日志输出的位置
'handlers': {
'file1_hanlder': { # 自定义"handlers"名字,可以改
'level': 'DEBUG', # 日志过滤等级
'class': 'logging.FileHandler', # 保存到文件里面去(日志保存的形式)
'formatter': 'formatter1', # 绑定的日志输出格式
'filename': logfile_path1, # 制定日志文件路径
'encoding': 'utf-8', # 日志文件的编码,不再担心乱码问题
},
'file2_hanlder': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'formatter': 'formatter2',
'filename': logfile_path2,
'encoding': 'utf-8',
},
'terminal': { # 自定义的"handlers"名字(终端)
'level': 'DEBUG', # 日志过滤等级
'class': 'logging.StreamHandler',# 打印到屏幕
'formatter': 'formatter3' # 日志输出格式
},
},
# 负责生产日志
'loggers': {
# '' 代表默认的,在执行'logging.getLogger("key")'时,在"loggers"里面没有找到这个"key"时就使用这个
'': {
# 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'handlers': ['file1_hanlder', 'file2_hanlder','terminal'],
'level': 'DEBUG',
'propagate': False, # 向上(更高level的logger)传递,默认True, 通常设置为False
},
# 在执行'logging.getLogger("key")'时,在"loggers"里面找到这个"key"时就使用这个
'自定义日志名1': {
'handlers': ['terminal'],
'level': 'DEBUG',
'propagate': False,
},
'自定义日志名2': {
'handlers': ['file2_hanlder','terminal'],
'level': 'INFO',
'propagate': False,
},
},
}
六.导入字典模块来进行使用
先导入"setting.py"以及"logging.config"
import logging.config
from conf import setting
加载配置字典
logging.config.dictConfig(setting.LOGGING_DIC)
测试一:
logger1 = logging.getLogger('自定义日志名1') # 执行后回去日志字典的"logger"里面找这个'自定义日志名1'(key)
logger1.info('派大星向海绵宝宝转账 : $1000000万')
测试二:
logger2 = logging.getLogger('自定义日志名2')
logger2.error('派大星向章鱼哥转账 : $1000000万')
测试三:
logger3 = logging.getLogger('找不到就默认')
logger3.info('派大星向蟹老板转账 : $1000000万')
''' 日志输出结果
2020-12-11 21:13:10,758 : 派大星向海绵宝宝转账 : $1000000万
2020-12-11 21:13:10,758 : 派大星向章鱼哥转账 : $1000000万
2020-12-11 21:13:10,758 : 派大星向蟹老板转账 : $1000000万
'''
七.日志级别的两层关卡
-
第一层是 logger 中设置的日志级别
-
第二层是 handler 中设置的日志级别
当使用"logger_obj.info("XXXX")"/"logger_obj.error("XXXX")"等等这样的功能输入内容的时候
"logger"中的日志等级会进行判断/过滤如果日志级别满足,那么就会被收取到
满足以后会交给"handlers"中你自定义的"handler"日志等级来进行第二次过滤
如果又满足,那么就会被你相应的"handler"功能进行处理
八.日志功能在实际应用中的简单示例
- 以下只是截取了程序的一小段来做演示(倒数第二行为记录日志)
from db import db_handler
from lib import common
def register_interface(name, password, balance=15000):
'''
注册接口.
:param name:用户名
:param password: 密码
:param balance: 确认密码
:return:True,False
'''
user_dic = db_handler.select(name)
if user_dic:
return False, '用户已存在'
else:
user_dic = {'name': name, 'password': password, 'balance': balance,
'locked': False, 'bankflow': [], 'shoppingcart': {}}
db_handler.save(user_dic)
user_logger.info('用户%s注册成功' % name) # 如果注册成功就会记录这个用户的注册信息日志
return True, '注册成功'
完整代码可参考 ATM 简单项目实现