Python的logging模块通过日志器、处理器、格式化器和过滤器实现灵活的日志管理,支持多级别、多目的地输出,相比print()具有可配置性强、格式丰富、线程安全等优势,适用于复杂项目的日志需求。

Python的
logging
在我看来,掌握
logging
logging.basicConfig()
StreamHandler
import logging
# 最简单的配置,日志会输出到控制台
# 默认级别是WARNING,所以INFO和DEBUG不会显示
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug("这是一条调试信息,通常在开发阶段有用。")
logging.info("程序正常运行,记录一些关键步骤。")
logging.warning("出现了一些潜在问题,但程序可能还能继续。")
logging.error("程序执行出错,但可能不致命。")
logging.critical("严重错误,程序可能无法继续运行。")
# 如果想输出到文件,可以这样配置
# logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')但真实项目往往需要更精细的控制。这时,我们就需要手动创建和配置日志器、处理器和格式化器。
获取日志器(Logger):这是你与日志系统交互的主要入口。通常会按模块或功能划分日志器,例如
logging.getLogger(__name__)
立即学习“Python免费学习笔记(深入)”;
import logging
my_logger = logging.getLogger('my_app') # 获取一个名为'my_app'的日志器
my_logger.setLevel(logging.DEBUG) # 设置该日志器的最低记录级别创建处理器(Handler):决定日志的输出目的地。
StreamHandler
sys.stderr
FileHandler
RotatingFileHandler
TimedRotatingFileHandler
SMTPHandler
HTTPHandler
# 创建一个输出到控制台的处理器 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) # 该处理器只处理INFO级别及以上的日志
file_handler = logging.FileHandler('app.log') file_handler.setLevel(logging.DEBUG) # 该处理器处理DEBUG级别及以上的日志
定义格式化器(Formatter):控制日志消息的显示格式。
# 定义控制台日志的格式
console_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
# 定义文件日志的格式,包含时间戳和文件名
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')将格式化器绑定到处理器,再将处理器绑定到日志器:
console_handler.setFormatter(console_formatter)
file_handler.setFormatter(file_formatter)
my_logger.addHandler(console_handler)
my_logger.addHandler(file_handler)
# 实际使用
my_logger.debug("这条调试信息会写入文件,但不会显示在控制台。")
my_logger.info("这条信息会同时写入文件和控制台。")
my_logger.error("程序中发生了一个错误!")这里需要注意,一个日志器可以有多个处理器,每个处理器可以有不同的级别和格式,这提供了极大的灵活性。例如,你可能希望控制台只显示警告和错误,而文件则记录所有详细信息。
我个人觉得,很多人在项目初期,或者在调试一些小脚本时,都会习惯性地用
print()
print()
logging
首先是日志级别(Levels)。
print()
logging
DEBUG
INFO
WARNING
ERROR
CRITICAL
WARNING
DEBUG
print()
其次是输出目的地(Destinations)的灵活性。
print()
logging
Handler
SMTPHandler
print()
再来是格式化(Formatting)。
logging.Formatter
模块化与可配置性也是一个大亮点。在大型项目中,不同的模块可能有不同的日志需求。
logging
Logger
logging.config.fileConfig
logging.config.dictConfig
最后,不得不提的是性能和并发安全。
logging
print()
logging
在复杂项目中,日志管理往往是个让人头疼的问题,如果处理不好,日志本身就会成为新的“噪音源”。我通常会采取以下几种策略来有效管理多个日志文件和配置,这能让日志系统既灵活又易于维护。
一个核心的思路是利用日志器的层级结构。
logging
my_app.module_a
my_app
propagate
False
db
api
# app.py
import logging
logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO) # 父日志器级别
# db.py
import logging
db_logger = logging.getLogger('my_app.db') # 继承父日志器,但可以有自己的配置
db_logger.debug("数据库连接尝试...") # 如果父日志器级别是INFO,这里不会显示
# api.py
import logging
api_logger = logging.getLogger('my_app.api')
api_logger.warning("API请求参数异常!")这样,我就可以通过
my_app.db
更进一步,使用配置文件进行日志配置是复杂项目的标准做法。手动在代码中配置所有的
Logger
Handler
Formatter
logging.config
fileConfig()
dictConfig()
dictConfig()
# logging_config.yaml (示例,实际应用中可以更复杂)
version: 1
disable_existing_loggers: False # 保持现有日志器不变
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
detailed:
format: '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: INFO
formatter: simple
stream: ext://sys.stdout
file_app:
class: logging.handlers.RotatingFileHandler
level: DEBUG
formatter: detailed
filename: logs/app.log
maxBytes: 10485760 # 10MB
backupCount: 5
file_errors:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: detailed
filename: logs/errors.log
maxBytes: 10485760
backupCount: 3
loggers:
my_app:
level: INFO
handlers: [console, file_app]
propagate: False # 不将日志传递给父日志器(这里是root)
my_app.db:
level: DEBUG
handlers: [file_app] # 数据库日志只写入文件
propagate: False
'': # root logger
handlers: [console, file_errors]
level: WARNING然后在代码中加载:
import logging.config
import yaml # 或者json
with open('logging_config.yaml', 'rt') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)
logger = logging.getLogger('my_app')
db_logger = logging.getLogger('my_app.db')
logger.info("应用启动...")
db_logger.debug("尝试连接数据库...")
logger.error("一个严重的应用程序错误!")通过这种方式,我可以清晰地定义不同的日志输出策略:比如所有日志都写到
app.log
errors.log
INFO
处理日志中的异常和错误,这块儿我踩过不少坑,也总结了一些经验。关键在于如何让错误信息既全面又易于理解,同时避免日志系统本身成为性能瓶颈。
首先,使用logger.exception()
except
logger.exception("发生了一个意料之外的错误!")sys.exc_info()
logger.error()
logger.error()
import logging
logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
def divide(a, b):
try:
result = a / b
logger.info(f"计算结果: {result}")
return result
except ZeroDivisionError:
logger.exception("尝试进行除零操作!") # 会自动包含堆栈信息
except Exception as e:
logger.error(f"发生未知错误: {e}", exc_info=True) # 也可以手动传入exc_info=True
divide(10, 2)
divide(10, 0)注意,
logger.exception()
ERROR
ERROR
其次,提供足够的上下文信息。一个错误日志如果只有“发生错误”,那几乎是没用的。在记录错误时,尽可能地包含导致错误发生的上下文数据,比如用户ID、请求参数、操作对象等。
logging
extra
user_id = 123
request_data = {'item_id': 456, 'quantity': 0}
try:
# 模拟一个业务逻辑错误
if request_data['quantity'] <= 0:
raise ValueError("购买数量必须大于0")
except ValueError as e:
logger.error("处理订单失败", extra={'user_id': user_id, 'request_data': request_data})虽然默认的Formatter不会自动显示
extra
extra
提到结构化日志,这在现代微服务架构和日志分析系统中变得越来越重要。将日志输出为JSON格式,可以方便地被ELK Stack(Elasticsearch, Logstash, Kibana)或Splunk等工具解析和查询。你可以自定义一个
json.Formatter
python-json-logger
# 假设使用一个简单的JSON formatter
import json
class JsonFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": self.formatTime(record, self.datefmt),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"filename": record.filename,
"lineno": record.lineno,
}
if record.exc_info:
log_entry["exc_info"] = self.formatException(record.exc_info)
# 合并extra数据
for key, value in record.__dict__.items():
if key not in log_entry and not key.startswith('_'): # 避免内部属性
log_entry[key] = value
return json.dumps(log_entry, ensure_ascii=False)
json_handler = logging.StreamHandler()
json_handler.setFormatter(JsonFormatter())
logger.addHandler(json_handler)
logger.info("这是一个普通事件", extra={'user_agent': 'Mozilla/5.0'})
try:
1 / 0
except ZeroDivisionError:
logger.exception("除零错误发生!")这样,每条日志都是一个独立的JSON对象,机器可读性极高,便于后续的日志聚合、搜索和分析。
最后,要考虑日志的量级和告警机制。不是所有的错误都需要立即人工干预。对于一些可预期的、低频率的错误,记录下来以备分析即可。但对于关键业务流程的错误或高频率的异常,可能需要集成告警系统(如通过邮件、短信、钉钉等)。
logging
Handler
logging.handlers.MemoryHandler
以上就是Python中的日志模块(logging)如何配置和使用?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号