FastAPI与Uvicorn日志配置
问题描述
许多开发者在本地使用uvicorn api:app --reload
命令运行FastAPI应用时,发现通过Python标准logging
模块创建的日志(如启动信息、路由处理中的日志)无法输出。控制台仅显示Uvicorn的内置日志,而应用自身的LOG.info("API is starting up")
和端点中的LOG.info("GET /")
等信息完全缺失。
问题表现为:
- 应用启动时自定义日志不显示
- 请求处理过程中的日志信息不可见
- 需要统一的日志配置方案,兼容本地开发和生产环境
python
# 典型的问题代码示例
from fastapi import FastAPI
import logging
import uvicorn
app = FastAPI(title="api")
LOG = logging.getLogger(__name__) # 此日志器不会输出
@app.get("/")
async def get_index():
LOG.info("GET /") # 此日志不会显示
return {"Hello": "Api"}
解决方案
方法1: 快速启用日志(推荐)
直接使用Uvicorn的内置日志器uvicorn.error
,无需额外配置:
python
from fastapi import FastAPI
import uvicorn
import logging
app = FastAPI(title='api')
logger = logging.getLogger('uvicorn.error') # 使用Uvicorn日志器
@app.get('/')
async def main():
logger.info('GET /') # 正常输出日志
return 'ok'
启动命令设置日志级别:
bash
uvicorn main:app --log-level debug
方法2: 配置Uvicorn日志级别
通过命令行参数控制日志显示:
bash
# 显示所有日志(包含自定义内容)
uvicorn main:app --log-level trace
# 关闭访问日志(保留错误和应用日志)
uvicorn main:app --no-access-log
或在Python代码中配置:
python
if __name__ == '__main__':
uvicorn.run(app, log_level="trace", access_log=False)
方法3: 完整日志系统配置
创建专业日志系统(包含控制台输出、文件轮转和JSON格式):
推荐在生产环境中使用
settings.py
——日志配置文件:
python
# settings.py
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
'handlers': {
'console': {
'formatter': 'standard',
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'app.log',
'maxBytes': 1024 * 1024 * 10, # 10MB
'backupCount': 3,
'formatter': 'standard'
},
},
'loggers': {
'uvicorn': {'handlers': ['console', 'file'], 'level': 'INFO'},
'uvicorn.error': {'handlers': ['console', 'file'], 'level': 'INFO'},
}
}
主程序main.py
:
python
from fastapi import FastAPI
import uvicorn
import settings
app = FastAPI()
logger = logging.getLogger("uvicorn.error")
@app.get("/")
async def read_root():
logger.info("处理根路径请求")
return {"message": "OK"}
if __name__ == "__main__":
uvicorn.run(app, log_config=settings.LOGGING_CONFIG)
方法4: 自定义独立日志器
当需要与Uvicorn日志完全分离时:
python
from fastapi import FastAPI
import logging
import sys
app = FastAPI()
# 创建独立日志器
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)
# 配置控制台输出
console_handler = logging.StreamHandler(sys.stdout)
console_format = logging.Formatter(
"%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)
console_handler.setFormatter(console_format)
# 配置文件输出
file_handler = logging.FileHandler("app.log")
file_format = logging.Formatter(
"%(asctime)s [%(process)d] [%(levelname)s] %(message)s"
)
file_handler.setFormatter(file_format)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
@app.get("/")
async def main():
logger.info("处理根路径请求")
return {"status": "OK"}
JSON日志格式化(高级)
适合ELK等日志分析系统:
python
# settings.py
import json
import logging
class JSONFormatter(logging.Formatter):
def format(self, record):
log_entry = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"process": record.process,
"thread": record.thread
}
return json.dumps(log_entry)
LOGGING_CONFIG = {
# ... handlers配置同上
'formatters': {
'json': {
'()': JSONFormatter
}
},
'handlers': {
'console': {
'formatter': 'json',
'class': 'logging.StreamHandler'
}
}
}
常见问题解决方案
问题:日志在容器中不显示
在Docker等容器环境中运行时:
- 确保设置环境变量
PYTHONUNBUFFERED=1
- 启动命令添加
--proxy-headers
和--forwarded-allow-ips='*'
- 使用
--log-config
指定配置文件路径
问题:双重重载导致日志重复
关闭Uvicorn自动重载,使用外部监视工具如watchfiles
:
bash
uvicorn main:app --reload --reload-include *.py --reload-exclude venv
问题:获取请求信息
在日志中包含请求元数据:
python
async def endpoint(request: Request):
extra_info = {
"path": request.url.path,
"method": request.method,
"client": request.client.host
}
logger.info("处理请求", extra={"context": extra_info})
最佳实践总结
- 开发环境:直接使用
uvicorn.error
日志器 +--log-level debug
- 生产环境:
- 使用
RotatingFileHandler
防止日志膨胀 - 配置
log_config
统一管理日志格式 - JSON格式便于日志分析
- 使用
- 注意事项:
- 避免使用根日志器(root logger)
- 对于长时间运行的服务,设置日志轮转
- 敏感信息不要记录在日志中
重要提示
Uvicorn内部使用多日志器:
uvicorn.error
:核心错误和应用日志uvicorn.access
:访问日志uvicorn.asgi
:ASGI协议日志
自定义日志应首选uvicorn.error
日志器