FastAPI+Azure Functions日志最佳实践:如何规范实现日志?
一、Azure Functions内置日志的访问与本地调试支持
- 线上访问:在Azure Portal进入你的Function App,点击左侧「监控」->「日志」,可用Kusto查询语句过滤、检索所有内置日志;也可通过Azure CLI执行
az functionapp log tail --name <你的应用名> --resource-group <资源组名>实时查看日志流。 - 本地调试:azure-functions-core-tools完全支持内置日志输出,启动本地函数后,控制台会直接打印函数执行、路由匹配、RPC通信等日志(即你提供的本地日志内容:
Request successfully matched the route with name 'orders' and template '/api/orders/{route}'
[2023-09-15T07:21:32.414Z] Executing 'Functions.orders' (Reason='This function was programmatically called via the host APIs.', Id=168d2tee-4cff-4886-99ed-fa494c140f4f)
[2023-09-15T07:21:32.419Z] [channel] received 91d88tcd-da6e-47f3-b9f5-69572d1e6ab3: RpcLog
[2023-09-15T07:21:32.419Z] Received FunctionInvocationRequest, request ID: batt1a3-bb47-4c81-b456-43da2t8fed033, function ID: ff09tt12-67e9-49e9-985e-180e3d352a44, function name: orders, invocation ID: 168d2tee-4cff-4886-99ed-fa494c140f4f, function type: sync, sync threadpool max workers: 1000
[2023-09-15T07:21:32.420Z] [channel] received 91dt8ecd-da6e-47f3-b9f5-69572a1e6ab3: RpcLog
[2023-09-15T07:21:32.431Z] handle() is deprecated. Pleaseawait .handle_async()instead.
[2023-09-15T07:21:32.634Z] [channel] received 91dt8ecd-da6e-47f3-b9f5-69572d1e6ab3: RpcLog
[2023-09-15T07:21:32.635Z] query: [Select Count() AS TOTAL FROM ( SELECT "OM"."ORDER_NUMBER", "OM"."TRANSACTION_RE...]
[2023-09-15T07:21:33.775Z] [channel] received 91d88ecd-da6e-47f3-b9f5-69t72d1e6ab3: RpcLog
[2023-09-15T07:21:33.776Z] query execution done
[2023-09-15T07:21:33.781Z] [channel] received 91d88ecd-da6e-47f3-b9f5-697td1e6ab3: RpcLog
[2023-09-15T07:21:33.782Z] query: [SELECT DISTINCT "MAIN"."ORDER_NUMBER", "MAIN"."TRANSACTION_REF", "MAIN"."TRANSPO...]
[2023-09-15T07:21:34.397Z] [channel] received 91d88ecd-da6e-47f3-b9f5-69572d1e6ab3: RpcLog
Executed 'Functions.orders' (Succeeded, Id=a69bb19e-0e9d-4892-af5a-ac8bcc26b31d, Duration=1587ms)
)
二、自定义日志的必要性与性能争议
- 自定义日志是生产级应用必备最佳实践:内置日志仅覆盖框架层面运行状态(如函数启动、路由匹配、执行耗时),但业务流程中的关键节点(如用户提交订单、数据更新、异常触发)只有自定义日志能记录。排查生产问题时,没有这些业务日志,根本无法定位具体逻辑错误。
- 性能问题无需过度担心:只要遵循规范(不循环打日志、仅记录关键信息、按级别过滤),Python的
logging模块性能损耗可忽略。Azure Functions日志系统为异步处理,不会阻塞业务代码执行。资深开发的顾虑多针对不规范日志写法,而非自定义日志本身。 - 最优方案是两者结合:保留内置日志监控函数运行健康状态,补充自定义日志记录业务关键流程,两者一同持久化,排查问题时既能看到框架执行轨迹,也能追溯业务逻辑完整流程。
三、日志存储方案选择
直接排除两个选项:
- 本地文本文件:Azure Functions为无服务器环境,实例销毁后本地文件会丢失,完全无法持久化,直接pass。
- Redis:适合实时日志缓存,但长期归档成本高,查询复杂日志效率低,仅适合特定实时监控场景,不推荐作为主要存储。
推荐的存储方案:
1. Azure Monitor Logs(Log Analytics工作区)
这是Azure生态中最适配的日志存储方案:
- 和Azure Functions深度集成,自动收集内置日志,自定义日志也能通过
logging模块直接输出到这里 - 支持Kusto查询语言,可快速过滤、聚合、分析日志
- 支持长期归档,存储成本远低于数据库
- 自带告警功能,可根据日志内容(如错误日志)触发告警
2. Azure Blob Storage(备选)
若需存储大量原始日志用于归档或离线分析,可将日志输出到Blob Storage,配合Azure Monitor或其他工具做后续处理,适合长期保存原始日志的场景。
不推荐数据库存储
关系型数据库写入性能有限,查询大量日志效率低,还会占用业务数据库资源;单独搭建日志数据库的维护成本和存储成本都很高,没必要。
四、FastAPI + Azure Functions自定义日志配置示例
直接使用Azure Functions提供的logger即可,它会自动把日志同步到Azure Monitor:
import logging import azure.functions as func from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware # 获取Azure Functions内置logger logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) # 初始化FastAPI应用 fastapi_app = FastAPI() fastapi_app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # 定义FastAPI路由 @fastapi_app.get("/api/orders/{route:path}") async def get_orders(request: Request, route: str): logger.info(f"订单接口被调用,请求路径:{route},客户端IP:{request.client.host}") try: # 业务逻辑示例:查询订单数据 logger.info("订单数据查询完成") return {"status": "success", "data": []} except Exception as e: logger.error(f"订单查询失败:{str(e)}", exc_info=True) return {"status": "error", "message": str(e)} # 包装成Azure Functions的HTTP触发器 app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) @app.route(route="orders/{*route}") def orders(req: func.HttpRequest) -> func.HttpResponse: return func.AsgiMiddleware(fastapi_app).handle(req)
内容的提问来源于stack exchange,提问作者etred-eps

