fastapi接口里日志重复写,用metaclass 单例模式解决了

2024-08-30 14:04

本文主要是介绍fastapi接口里日志重复写,用metaclass 单例模式解决了,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

遇到这个妖

我用fastapi写接口,打印日志用我自定义的日志类,但只要是fastapi 接口[即注解@app.get(‘/’) 或者 @app.post(‘/’) ] 之内打印的都是两遍,其他地方都是正常。这我很费解。说是我日志类的问题吧,我这类放其他地方都好使;说是fastapi的问题吧,人家日志格式跟我自定义的差别又很明显。

我自定义的logging类:

import logging
from logging.handlers import RotatingFileHandler
import os
class My_Logger():def __init__(self, logger_name):self.logger_name = logger_namelogfile = "{0}/logs/{1}.log".format(os.path.abspath("."), self.logger_name)self.my_logger = logging.getLogger(self.logger_name)self.my_logger.setLevel(logging.DEBUG)formatter = logging.Formatter("%(asctime)s - %(name)s - %(process)s - %(levelname)s - %(message)s")test_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")stream_handler = logging.StreamHandler()stream_handler.setLevel(logging.DEBUG)stream_handler.setFormatter(formatter)rotating_handler = RotatingFileHandler(logfile, maxBytes=10 * 1024 * 1024, backupCount=1, encoding="utf-8")rotating_handler.setLevel(logging.DEBUG)rotating_handler.setFormatter(test_formatter)self.my_logger.addHandler(stream_handler)self.my_logger.addHandler(rotating_handler)def debug(self, msg):self.my_logger.debug(msg)def info(self, msg):self.my_logger.info(msg)def warning(self, msg):self.my_logger.warning(msg)def error(self, msg):self.my_logger.error(msg)

我调用的地方:

from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.encoders import jsonable_encoder# lifespan
@asynccontextmanager
async def lifespan(app: FastAPI):session = await init_redis_pool()app.state.redis = sessionyieldawait session.close()app = FastAPI(lifespan=lifespan)
# 日志
customer_logger = My_Logger("test")
@app.post("/abc")
async def understand(req=Depends(request_body_and_redis_cache)):# 这个日志会打印两遍customer_logger.info(f"abc api request: {req}")url = req["url"]if url == None:return {"code": 9999, "errorMessage": "back server is not online"}api_req = req["item"]content = api_req.textif content == "":return {"code": 9998, "errorMessage": "param error" }jsonData = jsonable_encoder(api_req)try:return await async_http_query(url, jsonData)except Exception as e:customer_logger.warning(f"Failed to fetch data. Error: {e}")raise HTTPException(status_code=400, detail=str(e))
if __name__ == "__main__":import uvicorn# 这个日志正常customer_logger.info("test api is starting")uvicorn.run("redis_tool:app", host="0.0.0.0", port=8008, reload=True)customer_logger.info("test api is end")

搜到一个解决办法:

fastapi使用loguru日志记录时 重复记录日志

这个大佬说:
“原因:
这个问题是因为loguru和FastAPI都使用了logging这个内置的模块进行日志记录”

放在我这里,就是我的自定义日志类和FastAPI都使用了logging这个内置的模块进行日志记录

对上了,但我还是不理解。

不过大佬说的解决方案是非常好使:单例模式

# 单例模式
class SingletonMeta(type):_instances = {}def __call__(cls, *args, **kwargs):if cls not in cls._instances:cls._instances[cls] = super().__call__(*args, **kwargs)return cls._instances[cls]class My_Logger(metaclass=SingletonMeta):# 不再赘述

那么问题又来了 metaclass 是什么?

以下来自Kimi

在Python中,元类(metaclass)是类的类,也就是说,它们定义了其他类的行为。元类在Python中是一个高级主题,通常用于框架和库的开发中,而不是日常的应用程序开发。

基本概念

  1. 类(Class):定义对象的属性和方法。
  2. 实例(Instance):根据类创建的对象。
  3. 元类(Metaclass):定义类的类。

创建元类

在Python中,你可以使用type函数来创建一个元类。type是Python中所有类的默认元类。

class Meta(type):def __new__(cls, name, bases, dct):print(f"Creating class: {name}")return super().__new__(cls, name, bases, dct)class MyClass(metaclass=Meta):pass# 输出: Creating class: MyClass

元类的作用

  1. 控制类的行为:元类可以在类创建时修改类的定义。
  2. 注册类:在类创建时自动注册到某个注册表中。
  3. 自动添加方法:为类自动添加一些方法。

示例:自动添加方法

下面是一个示例,展示如何使用元类为所有类自动添加一个方法。

class AutoMethodMeta(type):def __new__(cls, name, bases, dct):dct['hello'] = lambda self: f"Hello from {self.__class__.__name__}"return super().__new__(cls, name, bases, dct)class MyClass(metaclass=AutoMethodMeta):passobj = MyClass()
print(obj.hello())  # 输出: Hello from MyClass

常用的元类

  1. type:Python默认的元类。
  2. abc.ABCMeta:用于定义抽象基类(ABC)。
  3. enum.EnumMeta:用于定义枚举类。

注意事项

  • 元类是高级特性,通常只在需要控制类的行为时使用。
  • 过度使用元类会使代码难以理解和维护。

这篇关于fastapi接口里日志重复写,用metaclass 单例模式解决了的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121024

相关文章

IDEA和GIT关于文件中LF和CRLF问题及解决

《IDEA和GIT关于文件中LF和CRLF问题及解决》文章总结:因IDEA默认使用CRLF换行符导致Shell脚本在Linux运行报错,需在编辑器和Git中统一为LF,通过调整Git的core.aut... 目录问题描述问题思考解决过程总结问题描述项目软件安装shell脚本上git仓库管理,但拉取后,上l

解决docker目录内存不足扩容处理方案

《解决docker目录内存不足扩容处理方案》文章介绍了Docker存储目录迁移方法:因系统盘空间不足,需将Docker数据迁移到更大磁盘(如/home/docker),通过修改daemon.json配... 目录1、查看服务器所有磁盘的使用情况2、查看docker镜像和容器存储目录的空间大小3、停止dock

idea npm install很慢问题及解决(nodejs)

《ideanpminstall很慢问题及解决(nodejs)》npm安装速度慢可通过配置国内镜像源(如淘宝)、清理缓存及切换工具解决,建议设置全局镜像(npmconfigsetregistryht... 目录idea npm install很慢(nodejs)配置国内镜像源清理缓存总结idea npm in

idea突然报错Malformed \uxxxx encoding问题及解决

《idea突然报错Malformeduxxxxencoding问题及解决》Maven项目在切换Git分支时报错,提示project元素为描述符根元素,解决方法:删除Maven仓库中的resolv... 目www.chinasem.cn录问题解决方式总结问题idea 上的 maven China编程项目突然报错,是

在Ubuntu上打不开GitHub的完整解决方法

《在Ubuntu上打不开GitHub的完整解决方法》当你满心欢喜打开Ubuntu准备推送代码时,突然发现终端里的gitpush卡成狗,浏览器里的GitHub页面直接变成Whoathere!警告页面... 目录一、那些年我们遇到的"红色惊叹号"二、三大症状快速诊断症状1:浏览器直接无法访问症状2:终端操作异常

MySQL中C接口的实现

《MySQL中C接口的实现》本节内容介绍使用C/C++访问数据库,包括对数据库的增删查改操作,主要是学习一些接口的调用,具有一定的参考价值,感兴趣的可以了解一下... 目录准备mysql库使用mysql库编译文件官方API文档对象的创建和关闭链接数据库下达sql指令select语句前言:本节内容介绍使用C/

mybatis直接执行完整sql及踩坑解决

《mybatis直接执行完整sql及踩坑解决》MyBatis可通过select标签执行动态SQL,DQL用ListLinkedHashMap接收结果,DML用int处理,注意防御SQL注入,优先使用#... 目录myBATiFBNZQs直接执行完整sql及踩坑select语句采用count、insert、u

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java 日志中 Marker 的使用示例详解

《Java日志中Marker的使用示例详解》Marker是SLF4J(以及Logback、Log4j2)提供的一个接口,它本质上是一个命名对象,你可以把它想象成一个可以附加到日志语句上的标签或戳... 目录什么是Marker?为什么使用Markejavascriptr?1. 精细化的过滤2. 触发特定操作3