日志模块
- 日志介绍
- 1. logging使用场景
- 设置级别
- 2. 实际logging使用 - 学习版
- 2.1 终端输出StreamHandler
- 2.2 日志文件中输出FileHandler
- 2.3 同时写入终端和文件
- 2.4 .Formatter参数语句
- 3. 封装logging模块 - 实战版 ⭐
- 3.1 配置config文件夹下project_config.py文件
- time模块
- 3.2 封装util文件夹下logging_until.py文件
- 3.3 其他模块下的mycode文件
- 注意事项
- 解决
日志介绍
一个合格的程序,日志是必须要输出的。 我们借助Python的logging库,来完成日志输出
。先编写基础的日志模块代码,可供业务逻辑代码使用。
所说的日志就是程序在运行时,程序当前处于什么状态,通过对外输出信息来确定。对外输出的这个信息,就是程序的运行日志。`
- 级别
CRITICAL =50
FATAL= CRITICAL
ERROR= 40
WARNING =30
WARN= WARNING
INFO= 20
DEBUG=10
NOTSET =0
1. logging使用场景
# 注意使用场景 每个类型的级别不同
logging.debug('debug 日志输出') # 罗嗦模式
logging.info("info日志输出") # 代码关键点输出
logging.warning("warning日志输出") # 可能出问题地方 警告输出
logging.error("error日志输出") # 出问题
logging.fatal("fatal中志输出") # 致命的 程序无法执行时
默认是warnging级别以上的输出信息
设置级别
import logging
logging.getLogger().setLevel(10) # 只会输出 大于等于该数值的 logging
# 注意使用场景
logging.debug('debug 日志输出') # 罗嗦模式
logging.info("info日志输出") # 代码关键点输出
logging.warning("warning日志输出") # 可能出问题地方 警告输出
logging.error("error日志输出") # 出问题
logging.fatal("fatal中志输出") # 致命的 程序无法执行时
默认的warning级别是30,当设置为10的时候,所有的日志都可以看到 方便我们开发时使用
设置为30 则有些日志 不需要让用看到 产品简介 同时关键信息 是让开发人员获取
2. 实际logging使用 - 学习版
- Pycharm目录结构
2.1 终端输出StreamHandler
核心理解:
- 导入模块 (自己的文件名不要和内置模块名称重名,否则内置模块无法使用)
- 日志管理对象: 负责日志的收集工作
- 日志处理器: 负责日志的输出形式管理(终端/文件) 日志格式: 负责日志的输出格式管理
- 日志格式:
'%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s'
- 设置logger对象 假设是logger.py
# 导包
import logging
##################### 日志器对象的创建和配置 #####################
# 1. 日志器对象的创建
logger = logging.getLogger()
# 2. 日志处理器的创建 输出到终端
stream_handler = logging.StreamHandler()
# 3. 将日志处理器绑定到日志器对象上
logger.addHandler(stream_handler)
# TODO: 4. 创建一个格式对象
fmt = logging.Formatter('%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s')
# TODO: 5. 将日志格式对象绑定到日志处理器上
stream_handler.setFormatter(fmt)
- 在模块文件中引入 mycode.py
######################### 日志的输出 #######################
# 1. 设置日志输出级别
logger.setLevel(logging.INFO) # INFO级别以上的输出
# 2. 输出不同级别的日志信息
logger.debug('这是一个debug级别的日志信息')
logger.info('这是一个info级别的日志信息')
logger.warning('这是一个warning级别的日志信息')
logger.error('这是一个error级别的日志信息')
logger.critical('这是一个critical级别的日志信息')
2.2 日志文件中输出FileHandler
- logger.py 输出到文件
# 导入logging模块
import logging
#################### 日志器对象的创建和配置 ####################
# 1. 日志器对象的创建
logger = logging.getLogger()
# TODO: 2. 创建换一个文件类型的日志处理器
# 注意: filename要传入一个log文件的路径,可以使用绝对路径也可以使用相对路径,但是文件目录一定要存在,文件可以不存在
# 举例: ../logs/test.log 路径中 logs目录必须存在, test.log可以不存在
file_handler = logging.FileHandler(
filename='../logs/test.log',
mode='a',
encoding='utf8'
)
# 3. 将日志处理器绑定到日志器对象上
logger.addHandler(file_handler)
# 4. 创建一个格式对象
fmt = logging.Formatter('%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s')
# 5. 将日志格式对象绑定到日志处理器上
file_handler.setFormatter(fmt)
- mycode.py
######################### 日志的输出 #######################
# 1. 设置日志输出级别
logger.setLevel(logging.INFO)
# 2. 输出不同级别的日志信息
logger.debug('这是一个debug级别的日志信息')
logger.info('这是一个info级别的日志信息')
logger.warning('这是一个warning级别的日志信息')
logger.error('这是一个error级别的日志信息')
logger.critical('这是一个critical级别的日志信息')
提前创建好该文件filename=‘…/logs/test.log’
2.3 同时写入终端和文件
file_handler logging.FileHandler('../logs/test.log')
file_handler1 =logging.FileHandler('../logs/test.log')
stream_handler logging.StreamHandler()
fmt = logging.Formatter('%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s')
file_handler.setFormatter(fmt)
file_handler1.setFormatter(fmt)
stream_handler.setFormatter(fmt)
2.4 .Formatter参数语句
‘%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s’
3. 封装logging模块 - 实战版 ⭐
3.1 配置config文件夹下project_config.py文件
测试和发布的时候通过更改配置文件可以更快的帮助我们提高效率
我们的需求是每个整点生成log日志文件,因此要结合time模块
import time
log_root_path = 'E:\\pythonProject\\pythonetl\\logs\\'
log_filename = f'pytel-{time.strftime("%Y%m%d-%H",time.localtime())}.log'
# print(log_filename) pytel-20240902-16.logs
level = 10
time模块
import time
print(time.time()) # 距离1970年过了多少秒 (以秒为单位)
print(time.localtime()) # time.struct_time(tm_year=2024, tm_mon=9, tm_mday=2, tm_hour=16, tm_min=10, tm_sec=57, tm_wday=0, tm_yday=246, tm_isdst=0)
print(time.strftime("%Y %m %d",time.localtime())) # 2024 09 02
print(time.strptime("2022-10-20 16:18:30","%Y-%m-%d %H:%M:%S"))
'''
常见的时间格式化的格式:
%Y:4位数字的年份:2024
%m:2位数字的月份:09
%d:2位数字的日期:02
%H:24小时制的小时
%M:2位数字的分钟
%S:2位数字的秒
如果要格式化为:2022-05-1510:05:55
'''
3.2 封装util文件夹下logging_until.py文件
这里不仅在终端进行了输出日志信息,同时在文件中保存了日志信息
import logging
import os
from config.project_config import log_root_path,log_filename,level
class Logging():
def __init__(self,level=20):
self.logger = logging.getLogger()
self.logger.setLevel(level)
def init_logger():
logger = Logging(level).logger
# 缓存机制 避免日志重复输出 ⭐⭐
if logger.handlers:
return logger
path = log_root_path+log_filename
# 创建并打开文件
with open(path, 'w') as file:
# 文件被创建,但这里不写入任何内容,所以它是空的
pass
# 构造handler
# 优化日志存储文件 StreamHandler
stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler(
filename=path,
mode='a',
encoding='utf-8'
)
fmt = logging.Formatter('%(asctime)s - [%(levelname)s] - %(filename)s[%(lineno)d]:%(message)s')
stream_handler.setFormatter(fmt)
file_handler.setFormatter(fmt)
# 组合
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
return logger
3.3 其他模块下的mycode文件
- 测试1
from util.logging_util import init_logger
logger = init_logger()
logger.info('This is a test!!')
logger.info('This is aaaaaaaaaaaaaaaaa test!!')
- 测试2
from util.logging_util import init_logger
logger = init_logger()
logger.info('This is a test!!')
logger.info('This is aaaaaaaaaaaaaaaaa test!!')
logger.info('This is bbbbbbbbbbbbbbbbbbb test!!')
注意事项
若是在模块中这样运行代码
from util.logging_util import init_logger
#错误用法
#是因为python的缓存机制,第一次调用init_logger函数
#这时会添加filehandler streamhandle
init_logger().info("测试info1")
#第二次调用时,因为履存了rootlogger,所以再次会添加filehandler streamhandler
init_logger().info("测试info2")
#第三次就变成了三个hand1er
init_logger().info("测试info3")
解决
在logging_until.py的init_logger()中加入是否存在句柄的语句
def init_logger():
logger = Logging(level).logger
# 缓存机制 避免日志重复输出
if logger.handlers:
return logger
...