Python 日志系统设计:构建可观测的应用
Python 日志系统设计构建可观测的应用引言大家好我是一名正在从Rust转向Python的后端开发者。在构建大型应用程序时日志系统是不可或缺的组成部分。良好的日志系统不仅能帮助我们追踪问题还能提供应用运行时的关键洞察。作为从Rust过来的开发者我发现Python的logging模块功能非常强大但配置起来也相对复杂。今天我想和大家分享一下我在设计Python日志系统方面的一些经验。日志系统的重要性为什么需要日志系统问题排查快速定位和解决生产环境中的问题性能监控追踪应用性能指标安全审计记录关键操作和访问记录业务分析通过日志数据了解用户行为日志级别Python的logging模块定义了以下日志级别级别数值用途DEBUG10详细的调试信息INFO20一般信息WARNING30警告信息ERROR40错误信息CRITICAL50严重错误信息基础配置简单配置import logging # 基础配置 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s ) logger logging.getLogger(__name__) logger.debug(这是一条调试信息) logger.info(这是一条普通信息) logger.warning(这是一条警告信息) logger.error(这是一条错误信息) logger.critical(这是一条严重错误信息)输出到文件import logging logging.basicConfig( levellogging.DEBUG, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, filenameapp.log, filemodew # w 覆盖模式a 追加模式 ) logger logging.getLogger(__name__) logger.info(应用启动)高级配置使用配置字典import logging from logging.config import dictConfig logging_config { version: 1, disable_existing_loggers: False, formatters: { standard: { format: %(asctime)s - %(name)s - %(levelname)s - %(message)s }, detailed: { format: %(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s } }, handlers: { console: { class: logging.StreamHandler, formatter: standard, level: INFO }, file: { class: logging.FileHandler, filename: app.log, formatter: detailed, level: DEBUG }, error_file: { class: logging.FileHandler, filename: error.log, formatter: detailed, level: ERROR } }, loggers: { : { # root logger handlers: [console, file, error_file], level: DEBUG, propagate: True } } } dictConfig(logging_config) logger logging.getLogger(__name__) logger.debug(调试信息) logger.info(普通信息) logger.error(错误信息)自定义日志格式import logging # 创建logger logger logging.getLogger(my_app) logger.setLevel(logging.DEBUG) # 创建formatter formatter logging.Formatter( %(asctime)s - %(name)s - %(levelname)s - %(process)d - %(thread)d - %(message)s ) # 创建handler console_handler logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(formatter) file_handler logging.FileHandler(app.log) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(formatter) # 添加handler到logger logger.addHandler(console_handler) logger.addHandler(file_handler) logger.info(应用启动)实际应用场景场景1多模块日志管理在大型项目中每个模块应该有自己的logger# module_a.py import logging logger logging.getLogger(__name__) def do_something(): logger.debug(正在执行do_something) try: # 业务逻辑 logger.info(操作成功) except Exception as e: logger.error(f操作失败: {e}, exc_infoTrue)# main.py import logging from logging.config import dictConfig import module_a logging_config { version: 1, disable_existing_loggers: False, formatters: { standard: { format: %(asctime)s - %(name)s - %(levelname)s - %(message)s } }, handlers: { console: { class: logging.StreamHandler, formatter: standard, level: INFO } }, loggers: { module_a: { handlers: [console], level: DEBUG, propagate: False } } } dictConfig(logging_config) if __name__ __main__: logger logging.getLogger(__name__) logger.info(应用启动) module_a.do_something()场景2日志轮转对于长期运行的应用日志文件会不断增大需要进行轮转import logging from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler logger logging.getLogger(my_app) logger.setLevel(logging.DEBUG) # 按文件大小轮转 rotating_handler RotatingFileHandler( app.log, maxBytes1024 * 1024 * 5, # 5MB backupCount5 # 保留5个备份 ) # 按时间轮转 timed_handler TimedRotatingFileHandler( app.log, whenmidnight, # 每天午夜轮转 interval1, backupCount7 # 保留7天的日志 ) formatter logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s) rotating_handler.setFormatter(formatter) timed_handler.setFormatter(formatter) logger.addHandler(rotating_handler) logger.addHandler(timed_handler)场景3结构化日志在现代应用中结构化日志JSON格式越来越流行import logging import json from datetime import datetime class JsonFormatter(logging.Formatter): def format(self, record): log_record { timestamp: datetime.now().isoformat(), logger: record.name, level: record.levelname, message: record.getMessage(), module: record.module, line: record.lineno } if record.exc_info: log_record[exception] self.formatException(record.exc_info) return json.dumps(log_record) logger logging.getLogger(structured_logger) logger.setLevel(logging.DEBUG) handler logging.StreamHandler() handler.setFormatter(JsonFormatter()) logger.addHandler(handler) logger.info(用户登录, extra{user_id: 123, ip: 192.168.1.1}) logger.error(数据库连接失败, extra{db_host: localhost, db_port: 5432})场景4日志过滤根据条件过滤日志import logging class ErrorFilter(logging.Filter): def filter(self, record): # 只允许ERROR级别及以上的日志通过 return record.levelno logging.ERROR logger logging.getLogger(filtered_logger) logger.setLevel(logging.DEBUG) console_handler logging.StreamHandler() console_handler.setLevel(logging.DEBUG) console_handler.addFilter(ErrorFilter()) formatter logging.Formatter(%(asctime)s - %(levelname)s - %(message)s) console_handler.setFormatter(formatter) logger.addHandler(console_handler) logger.debug(调试信息) # 不会输出 logger.info(普通信息) # 不会输出 logger.error(错误信息) # 会输出最佳实践1. 使用结构化日志结构化日志便于日志分析和查询import logging logger logging.getLogger(__name__) # 使用extra参数添加额外信息 logger.info(用户登录成功, extra{ user_id: 123, username: john_doe, ip_address: 192.168.1.100, user_agent: Mozilla/5.0 })2. 记录异常信息try: # 可能出错的代码 result risky_operation() except Exception as e: # 记录完整的异常堆栈 logger.error(f操作失败: {e}, exc_infoTrue) # 或者使用logger.exception自动记录堆栈 logger.exception(操作失败)3. 使用日志包装器import logging from functools import wraps def log_function_call(func): wraps(func) def wrapper(*args, **kwargs): logger logging.getLogger(func.__module__) logger.debug(f调用 {func.__name__}, 参数: args{args}, kwargs{kwargs}) try: result func(*args, **kwargs) logger.debug(f{func.__name__} 执行成功, 返回: {result}) return result except Exception as e: logger.error(f{func.__name__} 执行失败: {e}, exc_infoTrue) raise return wrapper log_function_call def process_data(data): # 处理数据 return data4. 配置分离将日志配置放在单独的配置文件中# logging_config.py LOGGING_CONFIG { version: 1, disable_existing_loggers: False, formatters: { standard: { format: %(asctime)s - %(name)s - %(levelname)s - %(message)s } }, handlers: { console: { class: logging.StreamHandler, formatter: standard, level: INFO } }, loggers: { : { handlers: [console], level: DEBUG, propagate: True } } }# main.py from logging.config import dictConfig from logging_config import LOGGING_CONFIG dictConfig(LOGGING_CONFIG)与Rust日志系统的对比特性PythonloggingRustlogcrate配置方式代码配置或配置文件编译时配置日志级别运行时可调整编译时确定结构化日志需要自定义formatter原生支持性能一般非常快生态丰富的第三方库简洁的核心库实战项目完整的日志系统import logging from logging.config import dictConfig from logging.handlers import RotatingFileHandler import json from datetime import datetime class JsonFormatter(logging.Formatter): def format(self, record): log_record { timestamp: datetime.now().isoformat(), logger: record.name, level: record.levelname, message: record.getMessage(), module: record.module, line: record.lineno, process: record.process, thread: record.thread } if record.exc_info: log_record[exception] self.formatException(record.exc_info) if hasattr(record, extra): log_record.update(record.extra) return json.dumps(log_record) def setup_logging(): logging_config { version: 1, disable_existing_loggers: False, formatters: { json: { (): JsonFormatter }, standard: { format: %(asctime)s - %(name)s - %(levelname)s - %(message)s } }, handlers: { console: { class: logging.StreamHandler, formatter: standard, level: INFO }, file: { class: logging.handlers.RotatingFileHandler, filename: app.log, formatter: json, level: DEBUG, maxBytes: 1024 * 1024 * 5, backupCount: 5 }, error_file: { class: logging.handlers.RotatingFileHandler, filename: error.log, formatter: json, level: ERROR, maxBytes: 1024 * 1024 * 5, backupCount: 5 } }, loggers: { app: { handlers: [console, file, error_file], level: DEBUG, propagate: False }, app.database: { handlers: [file], level: DEBUG, propagate: False }, app.api: { handlers: [console, file], level: INFO, propagate: False } } } dictConfig(logging_config) if __name__ __main__: setup_logging() logger logging.getLogger(app) db_logger logging.getLogger(app.database) api_logger logging.getLogger(app.api) logger.info(应用启动) db_logger.debug(连接数据库) api_logger.info(处理API请求)总结构建一个良好的日志系统是构建可观测应用的关键。通过合理配置日志级别、输出格式和存储策略我们可以实现问题快速定位通过详细的日志信息快速定位问题性能监控通过日志分析应用性能安全审计记录关键操作业务分析通过日志数据了解系统运行状态作为从Rust转向Python的开发者我发现Python的日志系统虽然配置复杂但灵活性很高。通过合理使用可以构建出满足各种需求的日志系统。延伸阅读Python官方文档 - logging模块structlog - 结构化日志库日志最佳实践指南