ChatGPT
Logger是Python标准库中logging模块提供的一种日志记录工具。它允许你在应用程序中记录不同级别的日志消息,以便于调试、追踪和分析应用程序的运行。使用Logger有助于管理和组织日志输出,以及提供可配置的日志记录。
以下是Logger的主要组成部分:
Logger(记录器):是主要的接口,负责发送日志消息。应用程序通常会创建一个或多个Logger对象,每个Logger对象负责一组相关的功能或模块。
Handler(处理器):决定将日志消息发送到何处。Handler可以将日志消息输出到控制台、文件、网络等不同的目标。
Formatter(格式化器):定义日志消息的输出格式。Formatter可以格式化消息的时间、级别、模块名等信息,使日志信息更易于阅读和分析。
Filter(过滤器):允许更细粒度地控制哪些日志消息应该被处理。通过使用过滤器,可以根据需要选择性地记录或忽略某些消息。
下面是一个简单的例子,演示如何使用Logger记录日志:
import logging
# 创建Logger对象 名字为 my_logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
# 创建文件处理器,并设置级别为DEBUG 输出文件名字为:my_log.log
file_handler = logging.FileHandler('my_log.log')
file_handler.setLevel(logging.DEBUG)
# 创建控制台处理器,并设置级别为INFO
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 创建日志消息的格式化器
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 将格式化器添加到处理器
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 记录不同级别的日志消息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')
运行效果
同时还可以发现 控制台终端输出的消息 比文件中 少了一条DEBUG消息 ,这是因为 终端中设置的是INFO级别(日志消息只有>=INFO才记录),文件时DEBUG级别(日志消息只有>=DEBUG才记录), 而日志的级别从低到高分别是:DEBUG->INFO->WARNING->ERROR->CRITICAL。
对应的解释如下:
DEBUG: 用于详细的调试信息。在开发和调试阶段使用,通常不应在生产环境中启用。
INFO: 提供程序正常运行时的信息。适用于生产环境,用于确认程序是否按照预期工作。
WARNING: 表示可能的问题,但不会影响程序的正常运行。需要注意并进行检查,以确保程序的稳定性。
ERROR: 指示更严重的问题,可能导致某些功能的失败。需要及时关注和修复。
CRITICAL: 表示严重的错误,可能导致程序无法继续运行。通常需要立即解决。
在给定的日志配置中,如果设置了级别为INFO,那么 INFO、WARNING、ERROR、CRITICAL级别的日志消息都会被记录,而DEBUG级别的消息将被忽略。
同时还有一些没用的小知识:
属性名称 | 格式 | 说明 |
---|---|---|
filename | 指定日志文件名 | |
filemode | 指定文件的打开模式,'w'或者'a'; | |
format | 格式器 | 指定输出的格式和内容 |
datefmt | 指定时间格式,同time.strftime() | |
level | 设置日志级别,默认为logging.WARNNING | |
stream | 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略 |
可能会用到的一些其他参数名字: 用法如上
console_handler = logging.StreamHandler() #创建日志处理器 logger.addHandler(console_handler) #添加日志处理器
还有一些其他的没用的小知识 :其他日志处理器如下
- StreamHandler:logging.StreamHandler;日志输出到流,可以是sys.stderr,sys.stdout或者文件
- FileHandler:logging.FileHandler;日志输出到文件
- BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式
- RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚
- TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件
- SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets
- DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets
- SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址
- SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog
- NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志
- MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer
- HTTPHandler:logging.handlers.HTTPHandler;通过"GET"或者"POST"远程输出到HTTP服务器
举个栗子:
import logging
from logging.handlers import RotatingFileHandler
# 配置日志
logger = logging.getLogger("example_logger")
logger.setLevel(logging.DEBUG)
# 创建 RotatingFileHandler 处理器,设置日志文件名和最大文件大小 backupCout指定要保留的备份日
#志文件的数量
handler = RotatingFileHandler("example.log", maxBytes=1, backupCount=3)
# 配置日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 将处理器添加到 logger
logger.addHandler(handler)
def simulate_logging():
# 模拟写入日志
for i in range(10):
logger.debug(f"This is log entry {i}")
if __name__ == "__main__":
simulate_logging()
结果很明显了:
在上面的例子中,我们使用了 RotatingFileHandler
处理器,并设置了最大文件大小为 1 字节,备份文件的数量为 3。这意味着当日志文件大小达到 1 字节时,会将当前日志文件切分为一个备份文件,并保留最多 3 个备份文件。
你可以根据实际需求调整 maxBytes
和 backupCount
参数。当日志文件大小达到 maxBytes
时,会触发日志回滚。
还有篇文章不错一定要看:https://www.cnblogs.com/yxh168/articles/11855581.html#:~:text=backupCount,%E6%98%AF%E4%BF%9D%E7%95%99%E6%97%A5%E5%BF%97%E4%B8%AA%E6%95%B0.%E9%BB%98%E8%AE%A4%E7%9A%840%E6%98%AF%E4%B8%8D%E4%BC%9A%E8%87%AA%E5%8A%A8%E5%88%A0%E9%99%A4%E6%8E%89%E6%97%A5%E5%BF%97%2C%E8%8B%A5%E8%AE%BE10%2C%E5%88%99%E5%9C%A8%E6%96%87%E4%BB%B6%E7%9A%84%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%BA%93%E4%BC%9A%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E6%9C%89%E8%B6%85%E8%BF%87%E8%BF%99%E4%B8%AA10%2C%E8%8B%A5%E8%B6%85%E8%BF%87%2C%E5%88%99%E4%BC%9A%E4%BB%8E%E6%9C%80%E5%85%88%E5%88%9B%E5%BB%BA%E7%9A%84%E5%BC%80%E5%A7%8B%E5%88%A0%E9%99%A4https://www.cnblogs.com/yxh168/articles/11855581.html#:~:text=backupCount,%E6%98%AF%E4%BF%9D%E7%95%99%E6%97%A5%E5%BF%97%E4%B8%AA%E6%95%B0.%E9%BB%98%E8%AE%A4%E7%9A%840%E6%98%AF%E4%B8%8D%E4%BC%9A%E8%87%AA%E5%8A%A8%E5%88%A0%E9%99%A4%E6%8E%89%E6%97%A5%E5%BF%97%2C%E8%8B%A5%E8%AE%BE10%2C%E5%88%99%E5%9C%A8%E6%96%87%E4%BB%B6%E7%9A%84%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%BA%93%E4%BC%9A%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E6%9C%89%E8%B6%85%E8%BF%87%E8%BF%99%E4%B8%AA10%2C%E8%8B%A5%E8%B6%85%E8%BF%87%2C%E5%88%99%E4%BC%9A%E4%BB%8E%E6%9C%80%E5%85%88%E5%88%9B%E5%BB%BA%E7%9A%84%E5%BC%80%E5%A7%8B%E5%88%A0%E9%99%A4
import logging
import traceback
# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s-%(levelname)s-%(message)s')
def divide_numbers(a, b):
try:
result = a / b
return result
except Exception as e:
# 捕获异常并记录 traceback 到日志
logging.error("An error occurred: %s", e, exc_info=True) # 错误信息捕获
return None
def main():
logging.info("logger开始")
# 模拟除法操作,可能引发异常
result = divide_numbers(10, 0)
# 如果异常被捕获,程序可以继续执行
if result is None:
logging.warning("The result is not available due to a previous error.")
# 模拟正常情况
result = divide_numbers(10, 2)
logging.info("The result is: %s", result)
if __name__ == "__main__":
main()
结果一目了然了:
这是更加常见的 因为一个完成的项目应该是包含不同的模块的,我又想针对不同的模块来设置不同的logging,就需要这个了
module.py
import logging
# 为模块创建一个日志记录器
logger = logging.getLogger(__name__)
# 设置模块的日志级别为DEBUG
logger.setLevel(logging.DEBUG)
def print_message():
logger.debug("这是来自模块的调试消息。")
logger.info("这是来自模块的信息消息。")
main.py
import logging
import module # 配置根日志记录器
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
) # 为主模块创建一个日志记录器
logger = logging.getLogger(__name__)
# logger = logging.getLogger("mainxxxx") #你也可以自己指定
def main():
logger.info("这是来自主模块的信息消息。")
module.print_message()
if __name__ == "__main__":
main()
在这个例子中,主模块main.py
的根日志级别设置为INFO
,而module.py
的日志级别被设置为DEBUG
。这意味着,module.py
中的DEBUG
级别的日志消息将被输出,而在main.py
中只有INFO
级别及以上的消息才会被输出。
这样,当你运行
main.py
时,你将看到类似以下的输出:
2023-12-06 00:00:00,000 - INFO - 这是来自主模块的信息消息。
2023-12-06 00:00:00,000 - DEBUG - 这是来自模块的调试消息。
2023-12-06 00:00:00,000 - INFO - 这是来自模块的信息消息。
注意:
在Python的logging
模块中,basicConfig
函数是用于配置默认的根日志记录器(root logger)的函数,而不是为特定的logger配置参数。当你在一个模块中使用basicConfig
时,它会配置根日志记录器,而不是模块专属的logger。
如果你想为特定模块配置独立的logger,通常会使用getLogger
来创建一个logger实例,然后对该实例进行配置。这样做的好处是,你可以独立地控制每个logger的配置,而不影响其他模块或根记录器。
有一个知识点:
当你在不同的模块中使用getLogger(__name__)
时,每个模块都会得到一个独立的logger实例,其名称基于模块的名称。这样,你可以轻松地在日志中识别消息来自哪个模块。
例如,如果你有两个模块main
和module
,它们都使用getLogger(__name__)
创建logger,那么它们的logger名称分别是main
和module
。当你在日志中看到来自main
模块的消息时,你知道这些消息来自main
模块。
config.yml
Log:
log_level: 1
path_to_log: ./log
log.py
import logging
import os
import sys
from datetime import datetime
from logging import handlers
import yaml
class Logger(object):
# 日志级别关系映射
level_relations = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL
}
def __init__(self, log_file_path, level='info', when='midnight', backCount=30,
fmt='%(asctime)s - %(module)s.cpp[line:%(lineno)d] - %(levelname)s: %(message)s'):
"""
:param log_file_path: 日志文件路径
:param level: 日志级别
:param when: 切割时间间隔:S: 秒, M: 分, H: 小时, D: 天: W0~W6: 星期几, midnight: 每天零点
:param backCount: 保留的日志文件数量
:param fmt: 日志格式
"""
self.logger = logging.getLogger(log_file_path)
# self.logger.handlers.clear()
# 是否已创建日志记录程序
if not self.logger.handlers:
self.logger.setLevel(self.level_relations.get(level))
log_formatter = logging.Formatter(fmt)
# 日志流处理器,将日志输出到屏幕上
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(log_formatter)
self.logger.addHandler(stream_handler)
# 刷新标准输出缓冲区,确保之前的日志消息被立即输出到屏幕
sys.stdout.flush()
# 日志定时循环文件处理器,将日志写入到文件
file_handler = handlers.TimedRotatingFileHandler(filename=log_file_path, when=when, backupCount=backCount,
encoding='utf-8')
file_handler.setFormatter(log_formatter)
self.logger.addHandler(file_handler)
def __getattr__(self, name):
# log.xxx 时,如果在日志级别中,返回对应的日志级别方法
if name in self.level_relations:
return getattr(self.logger, name)
# 返回日志对象的指定属性名的值
return self.logger.__getattribute__(name)
# 加载配置文件
def get_config(file_name):
# 读取配置文件
if len(file_name) == 1:
file_name = 'config.yml'
else:
file_name = file_name[1] # 自定义配置文件名称
try:
with open(file_name, 'r', encoding='utf-8') as config_file:
cfg = yaml.load(config_file, Loader=yaml.FullLoader)
except:
with open(file_name, 'r', encoding='ansi') as config_file:
cfg = yaml.load(config_file, Loader=yaml.FullLoader)
# log保存路径,以年月日命名log文件
log_file_dir = cfg['Log']['path_to_log']
log_file = log_file_dir + "/" + datetime.now().strftime('%m_%d_%Y.log')
# 判断log文件夹是否存在,若不存在,创建该目录文件夹
if not os.path.exists(log_file_dir):
os.makedirs(log_file_dir)
# 日志级别
dict = {"0": "debug", "1": "info", "2": "warning", "3": "error", "4": "critical"}
# 读取log路径及设置log等级
log = Logger(log_file, level=dict[str(cfg['Log']['log_level'])])
return log, cfg
log, cfg = get_config(sys.argv)
resources/logging.yml
version: 1 # 配置文件版本,当前为1
formatters:
simple: # 定义一个名为 "simple" 的日志格式
format: '%(asctime)s - %(module)s.cpp[line:%(lineno)d] - %(levelname)s: %(message)s'
# 详细定义日志记录的格式,包括时间、模块、行号、日志级别和消息
handlers:
timed_rotating:
class: logging.handlers.TimedRotatingFileHandler
# 使用 TimedRotatingFileHandler 类来处理日志
level: DEBUG # 设置日志处理器的级别为 DEBUG
formatter: simple # 使用上面定义的 "simple" 格式
when: midnight # 每天的午夜切分日志文件
interval: 1 # 间隔为1天
backupCount: 30 # 保留最多30个备份文件
filename: ./log/snap-video-service.log # 指定日志文件路径
encoding: utf-8 # 设置日志文件的编码为 utf-8
console_handler:
class: logging.StreamHandler
# 使用 StreamHandler 类来将日志输出到控制台
level: DEBUG # 设置日志处理器的级别为 DEBUG
formatter: simple # 使用上面定义的 "simple" 格式
stream: ext://sys.stdout # 输出到标准输出流
loggers:
app:
level: DEBUG # 设置 "app" logger 的级别为 DEBUG
handlers: [ timed_rotating ] # 将 "timed_rotating" 处理器添加到 "app" logger
propagate: no # 防止日志传播给父 logger
root:
level: INFO # 设置根 logger 的级别为 INFO
handlers: [ console_handler, timed_rotating ] # 将两个处理器添加到根 logger
config.yml
Log:
log_level: 0
path_to_log: ./log #保存本地配置
#其他的配置xxxx
videoSave:
#推送视频画面的宽,与输入视频流保持一致
# videoW: 1920
videoW: 1280
#推送视频画面的高,与输入视频流保持一致
# videoH: 1080
videoH: 720
#推送rtmp视频流和保存本地视频的帧率
videoFps: 15
#是否保存处理后的视频文件,1保存,0不保存
isSaveVideo: 0
# 推送rtmp视频流或者保存本地 1:rtmp 0:本地
rtmpOrLocal: 1
# url地址
videoPushUrl: rtmp://localhost:1935/myapp
#若保存视频文件,填写保存路径
videoSavePath: ./data
#若保存视频文件,填写单个视频的最大占存,单位为兆(M)
videoMaxSize: 500
config.py
# 导入必要的模块
import logging
import logging.config as log_config
import os
import yaml
# 配置全局日志
def global_config(logging_cnf='resources/logging.yml'):
# 创建 'log' 目录,如果目录已存在则忽略
os.makedirs('log', exist_ok=True)
# 读取日志配置文件
with open(logging_cnf, encoding='utf-8') as f:
# 解析配置文件内容
log_cnf = yaml.safe_load(f.read())
# 配置 logging 模块
log_config.dictConfig(log_cnf)
# 获取根 logger
logger = logging.getLogger()
# 尝试读取应用程序配置文件
try:
with open('./config.yml', 'r', encoding='utf-8') as config_file:
# 解析配置文件内容
cfg = yaml.load(config_file, Loader=yaml.FullLoader)
except (Exception,) as e:
# 记录错误日志,指示配置文件读取失败
logger.error("config.yml配置文件读取失败,请核查")
main.py
import sys
import config
from utils import VideoSave
if __name__ == '__main__':
if len(sys.argv) < 2:
config.global_config()
else:
config.global_config(sys.argv[1])
https://www.jb51.net/article/192489.htmhttps://www.jb51.net/article/192489.htm