fastapi的简单实战,且用uvicorn将日志同时输出到控制台和日志文件中

简单描述

  • fastapi的简单实战,且用uvicorn将日志同时输出到控制台和日志文件中

main.py

import signal
import sys
from contextlib import asynccontextmanager

from fastapi import FastAPI
import uvicorn
from fastapi.staticfiles import StaticFiles

from settings import settings
from routers.xxx import xxx_router
from common.logging import logger


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Code to run on startup
    logger.info("Starting up...")
    # You can initialize resources here (e.g., database connections)

    yield

    # Code to run on shutdown
    logger.info("Shutting down...")
    # Clean up resources here

app = FastAPI(lifespan=lifespan,
              debug=settings.debug,
              title="xxx平台",
              description='xxx',
              version='1.0.0',
              docs_url='/docs',
              redoc_url='/redoc',
              )

app.mount("/static", StaticFiles(directory=settings.static_dir), name="static")

app.include_router(xxx_router, prefix="/api/xxx", tags=["xxx"])


@app.get("/")
def read_root():
    return {"data": "welcome to xxx center"}


def signal_handler(sig, frame):
    print(f'Received Ctrl+C! {sig} exiting...')
    # 在这里执行任何必要的清理工作
    sys.exit()


def run_http():
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    uvicorn.run(app="main:app", host=settings.server_ip, port=settings.server_port,
                log_config="uvicorn_config.json",
                log_level=settings.log_level, workers=settings.workers)


if __name__ == "__main__":
    run_http()

settings/settings.py

import json
import os
import sys


# 获取可执行文件所在的目录路径
if getattr(sys, 'frozen', False):
    # 如果程序是被打包成了单一文件,这个条件是True
    BASE_DIR = os.path.dirname(sys.executable)
else:
    # 如果程序是直接运行的.py文件,这个条件是True
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


class BaseConfig(object):
    def __init__(self):
        self.config_path = self.get_config_path()
        self.config = self.get_config()

    @staticmethod
    def get_config_path():
        config_path = os.path.join(BASE_DIR, 'config.json')
        if os.path.exists(config_path):
            return config_path
        print(f'config file {config_path} not exists')

    def get_config(self):
        try:
            with open(self.config_path, 'r', encoding="utf-8") as f:
                return json.load(f)
        except FileNotFoundError:
            print(f'config file {self.config_path} not exists')
        except json.decoder.JSONDecodeError:
            print(f'config file {self.config_path} format error')


class ProjectConfig(BaseConfig):
    """项目中的配置信息"""
    def get_server(self):
        """后台服务配置信息"""
        try:
            return self.config["server"]["ip"], self.config["server"]["port"]
        except Exception as e:
            print(f'http server section not exists: {e}')

    def get_debug_mode(self):
        """是否开启debug模式"""
        try:
            return True if self.config.get("debug_mode") == "true" else False
        except Exception as e:
            print(f'debug_mode section not exists: {e}')

    def get_workers(self):
        """开启进程数"""
        try:
            return self.config["workers"]
        except Exception as e:
            print(f'process workers section not exists: {e}')

    def get_log(self):
        """日志配置信息"""
        try:
            return self.config["logfile"]["level"], self.config["logfile"]["dir"], self.config["logfile"]["max_age"]
        except Exception as e:
            print(f'log file section not exists: {e}')

    def get_mysql(self):
        """mysql数据库配置信息"""
        try:
            return self.config["mysql"]["default"]
        except Exception as e:
            print(f'mysql section not exists: {e}')

    def get_identify(self):
        """识别服务配置信息"""
        try:
            return self.config["identify"]["ip"], self.config["identify"]["port"], self.config["identify"]["api_path"]
        except Exception as e:
            print(f'identify server section not exists: {e}')

    def get_white_list(self):
        """请求白名单列表"""
        try:
            return self.config["white_list"]
        except Exception as e:
            print(f'white list section not exists: {e}')


project_config = ProjectConfig()

# 后台服务
server_ip, server_port = project_config.get_server()

# 是否开启debug
debug = project_config.get_debug_mode()

# 启动进程数
workers = project_config.get_workers()

# 日志
log_level, log_dir, log_max_age = project_config.get_log()
log_dir = os.path.join(BASE_DIR, log_dir)
if not os.path.exists(log_dir):
    os.makedirs(log_dir)

# mysql数据库
mysql_url = project_config.get_mysql()

# 识别服务
identify_ip, identify_port, identify_api_path = project_config.get_identify()

# 访问白名单列表
white_list = project_config.get_white_list()

# 访问静态文件系统目录
static_dir = os.path.join(BASE_DIR, 'myfiles')
if not os.path.exists(static_dir):
    os.makedirs(static_dir)

common/logging.py

import os
import logging
from logging.handlers import RotatingFileHandler

from uvicorn.config import LOG_LEVELS

from settings import settings

# 创建一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大5M
file_handler = RotatingFileHandler(os.path.join(settings.log_dir, "uvicorn.log"), encoding='UTF-8', maxBytes=5*1024*1024, backupCount=5)
file_handler.setLevel(LOG_LEVELS[settings.log_level])
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(name)s  %(message)s'))

logging.basicConfig(handlers=[file_handler])

# 获取Uvicorn的logger并添加文件处理器
logger = logging.getLogger("uvicorn")
logger.setLevel(LOG_LEVELS[settings.log_level])
# logger.addHandler(file_handler)  # 没打印日志到文件中,还没找到原因

uvicorn_config.json

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "default": {
            "()": "uvicorn.logging.DefaultFormatter",
            "fmt": "%(asctime)s - %(levelprefix)s %(message)s",
            "use_colors": null
        },
        "access": {
            "()": "uvicorn.logging.AccessFormatter",
            "fmt": "%(asctime)s - %(levelprefix)s %(client_addr)s - \"%(request_line)s\" %(status_code)s"
        }
    },
    "handlers": {
        "default": {
            "formatter": "default",
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stderr"
        },
        "access": {
            "formatter": "access",
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout"
        }
    },
    "loggers": {
        "fastapi": {
            "handlers": [
                "default"
            ],
            "level": "DEBUG"
        },
        "uvicorn": {
            "handlers": [
                "default"
            ],
            "level": "DEBUG"
        },
        "uvicorn.error": {
            "level": "INFO"
        },
        "uvicorn.access": {
            "handlers": [
                "access"
            ],
            "level": "INFO",
            "propagate": false
        }
    }
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以通过配置logging模块来实现这个功能。可以使用TimedRotatingFileHandler来按日期切割日志文件,FileHandler来控制将日志输出到指定文件,StreamHandler来控制将日志输出控制台。最后,在django配置文件,添加logging配置即可。以下是示例代码: ```python import logging from logging.handlers import TimedRotatingFileHandler logger = logging.getLogger('name') logger.setLevel(logging.DEBUG) # 控制台输出DEBUG日志 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) # 将日志按日期切割,并输出到指定文件 fh = TimedRotatingFileHandler('myapp.log', when='midnight', backupCount=30) fh.setLevel(logging.INFO) fh.suffix = '%Y-%m-%d.log' formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) ``` 然后,将上述代码加入django的settings.py的logging配置即可。 ```python LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.TimedRotatingFileHandler', 'filename': 'myapp.log', 'when': 'midnight', 'interval': 1, 'backupCount': 30, 'formatter': 'standard' }, 'console': { 'class': 'logging.StreamHandler', 'level': 'DEBUG', 'formatter': 'standard' }, }, 'loggers': { '': { 'handlers': ['file', 'console'], 'level': 'DEBUG', 'propagate': True } }, 'formatters': { 'standard': { 'format': '%(asctime)s - %(levelname)s - %(module)s - %(message)s' }, } } ``` 这些代码为你提供了一种实现日志分类输出的方式,你可以根据实际需要进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值