当前位置: 首页 > 工具软件 > uvicorn > 使用案例 >

Fastapi使用uvicorn将日志和控制台打印输出到文件 loguru

颛孙凯定
2023-12-01

问题过程

  • 使用Github搜索,没有找到方法。
  • 在官方文档中查找,并进行搜索。没有找到内容。
  • google & 百度,都没有找到任何信息。
  • 在bleepcoder中找到类似的问题,测试ok 问题链接

解决过程

此方法需要用到loguru模块,没有安装的小伙伴请 pip install loguru
废话不多说,直接开始操作

创建配置文件

为了方便配置,创建一个配置文件来储存需要用到的参数,觉得没必要的话可以直接跳过这一步使用参数。
这里的的日志文件会按照创建时的日期生成文件名。

import os
import time


# -----------------------系统调试------------------------------------
DEBUG = True
# -----------------------日志-----------------------------------------
LOG_DIR = os.path.join(os.getcwd(), f'application\log\{time.strftime("%Y-%m-%d")}.log')
LOG_FORMAT = '<level>{level: <8}</level>  <green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> - <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>'

创建拓展文件

原方法为了方便演示将所有代码集成到了一个main之中,我们将部分内容拆开单独形成文件,避免主程序过于臃肿,也便于修改和调试。
此文件定义了日志处理方法

创建一个cusotm_log.py内容如下,这里导入了一个config模块,大家如果没有的话可以直接使用参数

import logging
from loguru import logger
from ..config import config
from loguru._defaults import LOGURU_FORMAT


class InterceptHandler(logging.Handler):
    def emit(self, record):
        # Get corresponding Loguru level if it exists
        try:
            level = logger.level(record.levelname).name
        except ValueError:
            level = record.levelno

        # Find caller from where originated the logged message
        frame, depth = logging.currentframe(), 2
        while frame.f_code.co_filename == logging.__file__:
            frame = frame.f_back
            depth += 1

        logger.opt(depth=depth, exception=record.exc_info).log(
            level, record.getMessage()
        )


def format_record(record: dict) -> str:
    format_string = config.LOG_FORMAT

    if record["extra"].get("payload") is not None:
        record["extra"]["payload"] = pformat(
            record["extra"]["payload"], indent=4, compact=True, width=88
        )
        format_string += "\n<level>{extra[payload]}</level>"

    format_string += "{exception}\n"
    return format_string

初始化logru

我们在Fastapi初始化时对loguru的日志方法进行声明
以下是run.py代码片段, 同理 如果没有创建config文件请直接使用参数进行配置

import uvicorn
from fastapi import FastAPI, Depends, Security
import sys
import logging
from loguru import logger
from .config import config
# 此处是刚才创建的文件,请自行调整目录
from .extend.cusotm_log import InterceptHandler, format_record


def init_app():
    app = FastAPI(title=config.PROJECT_NAME, version=config.VERSION,
                  description=config.DESCRIPTION, debug=config.DEBUG)
    logging.getLogger().handlers = [InterceptHandler()]
    logger.configure(
        handlers=[{"sink": sys.stdout, "level": logging.DEBUG, "format": format_record}])
    logger.add(config.LOG_DIR, encoding='utf-8', rotation="9:46")
    logger.debug('日志系统已加载')
    logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
    return app

app = init_app()

'''
 路由省略
'''
uvicorn.run(
            app='application.main:app',
            host='0.0.0.0',
            port=80,
            reload=True,
            access_log=True
            )

验证结果

OK,看起来一切都就绪了,让我们来验证一下日志输出结果
用控制台输入 python run.py 运行我们的程序把

starting application
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
INFO:     Started reloader process [45972] using statreload
DEBUG     2021-04-13 10:37:48.884 - application.main:init_app - 日志系统已加载
INFO:     Started server process [54160]
INFO      2021-04-13 10:37:48.900 - uvicorn.server:serve - Started server process [54160]
INFO:     Waiting for application startup.
INFO      2021-04-13 10:37:48.903 - uvicorn.lifespan.on:startup - Waiting for application startup.
INFO:     Application startup complete.
INFO      2021-04-13 10:37:48.907 - uvicorn.lifespan.on:startup - Application startup complete.

那么在来看看我们的log文件

2021-04-13 10:37:48.884 | DEBUG    | application.main:init_app:17 - 日志系统已加载
2021-04-13 10:37:48.900 | INFO     | uvicorn.server:serve:64 - Started server process [54160]
2021-04-13 10:37:48.903 | INFO     | uvicorn.lifespan.on:startup:26 - Waiting for application startup.
2021-04-13 10:37:48.907 | INFO     | uvicorn.lifespan.on:startup:38 - Application startup complete.

拓展到路由

让我们再在路由里尝试一下使用日志,下面简单定义一下

@app.get("/", tags=["Home Page"])
async def root():
    logger.info('我被访问啦!!!')
    return {"message": "主页"}

控制台输出

starting application
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
INFO:     Started reloader process [1768] using statreload
DEBUG     2021-04-13 10:34:24.606 - application.main:init_app - 日志系统已加载
INFO:     Started server process [47832]
INFO      2021-04-13 10:34:24.622 - uvicorn.server:serve - Started server process [47832]
INFO:     Waiting for application startup.
INFO      2021-04-13 10:34:24.624 - uvicorn.lifespan.on:startup - Waiting for application startup.
INFO:     Application startup complete.
INFO      2021-04-13 10:34:24.627 - uvicorn.lifespan.on:startup - Application startup complete.
INFO      2021-04-13 10:34:34.100 - application.main:root - 我被访问啦!!!
INFO      2021-04-13 10:34:34.104 - uvicorn.protocols.http.h11_impl:send - 127.0.0.1:7043 - "GET / HTTP/1.1" 200
INFO      2021-04-13 10:34:34.274 - uvicorn.protocols.http.h11_impl:send - 127.0.0.1:7043 - "GET /favicon.ico HTTP/1.1" 404

日志输出

2021-04-13 10:34:24.606 | DEBUG    | application.main:init_app:17 - 日志系统已加载
2021-04-13 10:34:24.622 | INFO     | uvicorn.server:serve:64 - Started server process [47832]
2021-04-13 10:34:24.624 | INFO     | uvicorn.lifespan.on:startup:26 - Waiting for application startup.
2021-04-13 10:34:24.627 | INFO     | uvicorn.lifespan.on:startup:38 - Application startup complete.
2021-04-13 10:34:34.100 | INFO     | application.main:root:30 - 我被访问啦!!!
2021-04-13 10:34:34.104 | INFO     | uvicorn.protocols.http.h11_impl:send:467 - 127.0.0.1:7043 - "GET / HTTP/1.1" 200
2021-04-13 10:34:34.274 | INFO     | uvicorn.protocols.http.h11_impl:send:467 - 127.0.0.1:7043 - "GET /favicon.ico HTTP/1.1" 404

至此,日志功能就实现了

 类似资料: