logging模块使用场景
print已经无法满足需求
大型项目需要更详细的输出,便于排错
在java中类似log4j
程序开发的基本流程
- 画图, 把程序的基本模块和流程图画清楚
- 写程序前先写注释, 每一步要干的事
- 写代码
最好在调用一个函数或方法时
def fun(*args):
logging("函数名start-功能-传入参数args")
...
logging("函数名end-返回值")
return 返回值
可直接复制的代码
在同一个
Python
进程中
日志输出到同一个文件
下面这个函数可以写在工具模块中, 按需导入即可
def getLogger_file_1(logfile="", mode="w", log_level=20):
"""
将所有日志输出到一个文件
:param logfile: log文件地址, 不传入logfile就是当前'文件名.log'
:param mode: 写入log文件的模式, w 或 a
:param log_level: 默认info:20; debug:10,warning:30,error:40,critical:50
:return: logger
"""
import logging
fmt_str = "%(asctime)s %(filename)s %(funcName)s:%(lineno)d %(levelname)s-->%(message)s"
if logfile == "":
logfile = __file__ + ".log"
logging.basicConfig(level=log_level,
format=fmt_str,
datefmt='%a, %d %b %Y %H:%M:%S',
filename=logfile,
filemode=mode
)
logging.FileHandler(filename=logfile, encoding="utf-8") # 防止乱码
return logging
日志输出到不同的文件
在同一个
Python
进程中
不同模块的logging输出到不同的日志文件
注意: 在每个需要写日志的模块中都需要这样的一个函数
def getLogger_file_2(logfile="", mode="w", log_level=20):
"""
获取日志记录器,注意在每个模块下都要有这个方法
:param logfile: log文件地址,不传入logfile就是当前'文件名.log'
:param mode: 写入log文件的模式, w 或 a
:param log_level: 默认info:20; debug:10,warning:30,error:40,critical:50
:return: logger
"""
import logging
# ####### 1. 创建日志器
# logging使用的是单例模式,同一python进程不同模块import的logging,如果没有特殊标识,调用的都是同一个logger
logger = logging.getLogger(__name__) # 可以使用__name__变量标识
logger.setLevel(level=log_level) # INFO: 20
# ####### 2. 定义处理器, 控制台和文本
if logfile == "":
logfile = __file__ + ".log"
file_handler = logging.FileHandler(logfile, mode=mode) # 输出到文件
logger.addHandler(file_handler)
# ####### 3. 自定义日志输出格式
fmt = logging.Formatter("%(asctime)s %(filename)s %(funcName)s:%(lineno)d %(levelname)s-->%(message)s")
file_handler.setFormatter(fmt)
return logger
logging日志级别
# 基本使用
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(name)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='/tmp/alert_phone_server.log',
filemode='w'
)
logging.info("logging test")
参数
level
告警级别format
输出格式datefmt
时间格式filename
日志文件filemode
打开日志文件的模式 w 或 a
日志同时生成在控制台和文件
# -*- encoding: utf-8 -*-
"""
@Modify Time @Author @Version @Description
------------ ------- -------- -----------
2021/9/4 8:41 xlgui 1.0 logging模块学习,日志同时输出在控制台和文件
"""
import logging
# ####### 1. 创建日志器
logger = logging.getLogger(__name__) # 参数为空, 默认为root; 即 %(name)s 的值
logger.setLevel(20) # INFO: 20
# ####### 2. 定义处理器, 控制台和文本
stream_handler = logging.StreamHandler() # 输出到控制台的流
file_handler = logging.FileHandler("test_logging.log", mode='w') # 输出到文件
# 还可以自定义输出到不同地方的日志格式
fmt1 = logging.Formatter(fmt="%(name)s - %(levelname)s - %(pathname)s --> %(message)s") # 要查看可以自定义的日志格式, 可以ctrl+左击Formatter
fmt2 = logging.Formatter(fmt="%(filename)s -%(module)s - %(lineno)d -%(funcName)s -%(asctime)s -->%(message)s ")
"""
%(name)s 是 logging.getLogger(__name__) 中的__name__
%(levelname)s 日志输出等级
%(pathname)s 脚本的绝对路径
%(filename)s 脚本名
%(module)s 模块名
%(lineno)d 行号
%(funcName)s 函数名
%(message)s 日志
%(asctime)s 时间
%(thread)d Thread ID (if available)
%(threadName)s Thread name (if available)
%(process)d Process ID (if available)
"""
fmt_str = "[%(asctime)s] %(filename)s %(funcName)s:%(lineno)d %(levelname)s-->%(message)s"
fmt = logging.Formatter(fmt_str)
stream_handler.setFormatter(fmt)
file_handler.setFormatter(fmt2)
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
def test_func(t):
logger.info("test_func-start")
print(t)
import time
time.sleep(2)
logger.info("test_func-end, return None")
if __name__ == '__main__':
test_func("tttt")
高级应用
Handlers 处理器
滚动日志
可以使用logging.handlers模块来实现日志滚动。logging.handlers模块提供了一些预定义的日志处理器,包括支持滚动的处理器。
以下是一个示例,演示如何配置基于时间的日志滚动:
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# 配置日志滚动处理器
handler = TimedRotatingFileHandler('app.log', when='midnight', interval=1, backupCount=7)
handler.suffix = "%Y-%m-%d_%H-%M-%S.log" # 自定义日志文件后缀格式
handler.setLevel(logging.INFO)
# 配置日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 添加处理器到日志记录器
logger.addHandler(handler)
# 打印日志
logger.info('这是一条信息日志')
logger.warning('这是一条警告日志')
logger.error('这是一条错误日志')
在上述代码中,我们首先创建了一个名为logger的日志记录器,并将其日志级别设置为logging.INFO以仅记录INFO级别及以上的日志。
然后,我们使用TimedRotatingFileHandler
创建一个基于时间的日志滚动处理器。在这个示例中,我们设置滚动的时间间隔为每天(when=‘midnight’),并保留7个备份文件。我们还使用suffix属性自定义了日志文件的后缀格式,以便按时间戳对日志文件进行命名。
然后,我们配置了日志格式,并将处理器添加到日志记录器中。
运行以上示例代码后,每天午夜时,日志文件将被滚动并备份,按照指定的命名格式创建新的日志文件。
除了基于时间的滚动,logging.handlers模块还提供了其他类型的滚动处理器,
如
- 基于文件大小的滚动(RotatingFileHandler)
- 根据日志条数滚动(MemoryHandler)
多进程下
需要为process设置name
multiprocessing.Process(target=f, name=p1_name)
日志
import logging
import multiprocessing
from logging.handlers import TimedRotatingFileHandler
import os
log_lock = multiprocessing.Lock()
_log_dict = dict()
log_dir = "logs"
def info(msg):
msg = str(msg)
with log_lock:
name = f"{multiprocessing.current_process().name}"
if name in _log_dict:
logger = _log_dict[name]
else:
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
log_file = f'{log_dir}/{name}.info'
handler = TimedRotatingFileHandler(log_file, when='midnight', interval=1, backupCount=30, encoding='utf-8')
handler.suffix = '%Y-%m-%d'
logger.addHandler(handler)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler.setFormatter(formatter)
_log_dict[name] = logger
logger.info(msg)
def log_error(err):
msg = str(err)
with log_lock:
name = f"ERROR.{multiprocessing.current_process().name}"
if name in _log_dict:
logger = _log_dict[name]
else:
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
log_file = f'{log_dir}/{name}.err'
handler = TimedRotatingFileHandler(log_file, when='midnight', interval=1, backupCount=30, encoding='utf-8')
handler.suffix = '%Y-%m-%d'
logger.addHandler(handler)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler.setFormatter(formatter)
_log_dict[name] = logger
logger.info(msg)