)
Uvicorn日志双输出实战5分钟搞定终端文件记录FastAPI项目必备在FastAPI项目的开发过程中日志记录是调试和监控的关键环节。想象一下这样的场景你正在本地调试一个API接口既需要实时查看终端输出的日志以便快速定位问题又希望这些日志能够持久化保存用于后续分析。这就是Uvicorn日志双输出技术大显身手的地方。1. 快速搭建基础日志双输出环境首先让我们创建一个最简单的双输出配置。在FastAPI项目的入口文件通常是main.py中添加以下代码import uvicorn from fastapi import FastAPI import logging app FastAPI() # 基础路由用于测试 app.get(/) async def root(): logging.info(访问了根路由) return {message: Hello World} if __name__ __main__: # 配置基础日志 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(app.log), logging.StreamHandler() ] ) uvicorn.run(app, host0.0.0.0, port8000)这个配置实现了终端输出通过StreamHandler实现文件记录通过FileHandler将日志写入app.log文件统一格式包含时间戳、日志级别和消息内容提示这种基础配置虽然简单但已经能满足大多数开发场景的需求。日志文件默认会保存在项目根目录下。2. 进阶Uvicorn专属日志配置当我们需要更精细地控制Uvicorn的日志行为时可以直接修改其LOGGING_CONFIG。以下是更专业的配置方式import uvicorn from fastapi import FastAPI app FastAPI() # 自定义Uvicorn日志配置 LOGGING_CONFIG { version: 1, disable_existing_loggers: False, formatters: { default: { (): uvicorn.logging.DefaultFormatter, fmt: %(asctime)s - %(levelprefix)s %(message)s, use_colors: True, }, access: { (): uvicorn.logging.AccessFormatter, fmt: %(asctime)s - %(levelprefix)s %(client_addr)s - %(request_line)s %(status_code)s, }, }, handlers: { console: { formatter: default, class: logging.StreamHandler, stream: ext://sys.stdout, }, file_default: { formatter: default, class: logging.FileHandler, filename: uvicorn.log, }, file_access: { formatter: access, class: logging.FileHandler, filename: uvicorn_access.log, }, }, loggers: { uvicorn: {handlers: [console, file_default], level: INFO}, uvicorn.error: {level: INFO}, uvicorn.access: {handlers: [console, file_access], level: INFO, propagate: False}, }, } if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000, log_configLOGGING_CONFIG)这个配置的特点分离日志类型普通日志输出到uvicorn.log访问日志单独记录到uvicorn_access.log不同格式化默认日志包含时间、级别和消息访问日志额外记录客户端地址、请求行和状态码保留彩色输出终端仍然显示彩色日志文件日志则保持简洁格式3. 生产环境推荐配置对于生产环境我们需要考虑日志轮转、性能优化等问题。以下是经过实战检验的配置方案import uvicorn from fastapi import FastAPI from logging.handlers import RotatingFileHandler import logging app FastAPI() # 生产环境日志配置 def setup_logging(): # 创建日志目录 import os os.makedirs(logs, exist_okTrue) # 应用日志配置 logging.config.dictConfig({ version: 1, disable_existing_loggers: False, formatters: { default: { format: %(asctime)s - %(name)s - %(levelname)s - %(message)s }, access: { format: %(asctime)s - %(name)s - %(levelname)s - %(client_addr)s - %(request_line)s - %(status_code)s }, }, handlers: { console: { class: logging.StreamHandler, formatter: default, stream: ext://sys.stdout, }, file_app: { (): RotatingFileHandler, formatter: default, filename: logs/app.log, maxBytes: 1024 * 1024 * 10, # 10MB backupCount: 5, }, file_access: { (): RotatingFileHandler, formatter: access, filename: logs/access.log, maxBytes: 1024 * 1024 * 50, # 50MB backupCount: 10, }, }, loggers: { : {handlers: [console, file_app], level: INFO}, uvicorn.error: {level: INFO}, uvicorn.access: {handlers: [console, file_access], level: INFO, propagate: False}, }, }) if __name__ __main__: setup_logging() uvicorn.run( app, host0.0.0.0, port8000, log_configNone, # 使用我们自定义的配置 access_logTrue )关键优化点日志轮转使用RotatingFileHandler防止单个日志文件过大应用日志限制为10MB保留5个备份访问日志限制为50MB保留10个备份目录组织所有日志文件存放在logs子目录中性能考虑访问日志单独处理避免影响主日志性能合理设置日志级别避免过度记录4. 常见问题与调试技巧在实际使用中你可能会遇到以下典型问题4.1 日志不显示或记录不全检查清单确认日志级别设置正确DEBUG/INFO/WARNING等检查handler是否正确添加到logger确保没有在其他地方覆盖了日志配置4.2 日志文件权限问题当遇到无法写入日志文件时# 检查文件权限 ls -la /path/to/logfile # 更改文件所有者假设使用www-data用户 sudo chown www-data:www-data /path/to/logfile4.3 性能优化建议对于高流量应用考虑使用异步日志处理器如concurrent-log-handler将访问日志与应用日志分离到不同磁盘定期归档和清理旧日志4.4 集成第三方日志服务如果需要将日志发送到ELK等系统可以添加如下handlerhandlers: { logstash: { class: logstash.LogstashHandler, host: logstash.example.com, port: 5959, version: 1, tags: [python], }, }记得在loggers配置中添加这个handler。5. 实际项目中的最佳实践经过多个FastAPI项目的实践我总结了以下经验开发环境使用简单的双输出配置方便调试测试环境增加日志详细程度可以考虑添加文件行号format: %(asctime)s - %(name)s - %(levelname)s - %(pathname)s:%(lineno)d - %(message)s生产环境使用RotatingFileHandler确保日志不会无限增长访问日志和应用日志分离设置合理的日志级别通常INFO足够容器化部署将日志输出到stdout由容器平台收集handlers: { console: { class: logging.StreamHandler, formatter: default, stream: ext://sys.stdout, }, }调试技巧临时提升日志级别logging.getLogger(uvicorn.error).setLevel(logging.DEBUG)