日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

追了源码,做了测试,终于实现python的uvicorn日志自行配置

發(fā)布時(shí)間:2025/3/19 python 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 追了源码,做了测试,终于实现python的uvicorn日志自行配置 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、背景

有人提出,message日志不能放我們自己的服務(wù)的日志,需要將該日志單獨(dú)搞到一個(gè)地方。

二、先說結(jié)論

本文直接上結(jié)論,可能是全CSDN唯一的解釋。哈哈哈哈哈哈哈哈。

在uvicorn啟動(dòng)的時(shí)候,傳入log_config參數(shù)

那么,這個(gè)參數(shù)如何傳呢?我先給個(gè)樣例

LOGGING_CONFIG = {"version": 1,"disable_existing_loggers": False,"formatters": {"default": {"()": "uvicorn.logging.DefaultFormatter","fmt": "%(levelprefix)s %(message)s","use_colors": None,},"access": {"()": "uvicorn.logging.AccessFormatter","fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',},},"handlers": {"default": {"formatter": "default","class": "logging.handlers.TimedRotatingFileHandler","filename": "./log"},"access": {"formatter": "access","class": "logging.handlers.TimedRotatingFileHandler","filename": "./log"},},"loggers": {"": {"handlers": ["default"], "level": "INFO"},"uvicorn.error": {"level": "INFO"},"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},}, }

這樣的配置,會(huì)使unicorn 的日志,按照TimedRotatingFileHandler的默認(rèn)切割方法,將日志寫到我們配置的./log文件

三、源碼解析

首先,我們先在一個(gè)代碼文件中,將服務(wù)跑起來,用官網(wǎng)的命令行方式也是一樣,這里不過多介紹

from typing import Optionalimport uvicorn from fastapi import FastAPIapp = FastAPI()@app.get("/") def read_root():return {"Hello": "World"}@app.get("/items/{item_id}") def read_item(item_id: int, q: Optional[str] = None):return {"item_id": item_id, "q": q}if __name__ == "__main__":uvicorn.run(app=app,host="0.0.0.0",port=12345,)

運(yùn)行結(jié)果如下:

遇到了問題,先去看了看官方文檔。

但官方文檔,就這么一點(diǎn)點(diǎn),只說有個(gè)參數(shù)能配置,十分令人生氣。(迅速調(diào)整情緒)開始讀源碼。

點(diǎn)住 run 方法,進(jìn)去看看

再點(diǎn)開Config進(jìn)去看看

原來,這里給了一個(gè)默認(rèn)的日志配置,再往下點(diǎn) LOGGING_CONFIG

就找到了默認(rèn)的配置:

LOGGING_CONFIG = {"version": 1,"disable_existing_loggers": False,"formatters": {"default": {"()": "uvicorn.logging.DefaultFormatter","fmt": "%(levelprefix)s %(message)s","use_colors": None,},"access": {"()": "uvicorn.logging.AccessFormatter","fmt": '%(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": {"": {"handlers": ["default"], "level": "INFO"},"uvicorn.error": {"level": "INFO"},"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},}, }

再繼續(xù)往下追,看看這個(gè) 配置使如何處理的呢

來到233行,對字典的配置的處理,繼續(xù)往下點(diǎn),來到了關(guān)鍵的代碼:這一塊對字典中的內(nèi)容進(jìn)行解析。

?

?

?來到了

self.configure_logger(name, loggers[name])——————————————再往里面進(jìn)入,點(diǎn)一層

?來看這個(gè)函數(shù),我們就看到了本質(zhì),本質(zhì)上,還是給logger.addHandler方法。

def common_logger_config(self, logger, config, incremental=False):"""Perform configuration which is common to root and non-root loggers."""level = config.get('level', None)if level is not None:logger.setLevel(logging._checkLevel(level))if not incremental:#Remove any existing handlersfor h in logger.handlers[:]:logger.removeHandler(h)handlers = config.get('handlers', None)if handlers:self.add_handlers(logger, handlers)filters = config.get('filters', None)if filters:self.add_filters(logger, filters)def add_handlers(self, logger, handlers):"""Add handlers to a logger from a list of names."""for h in handlers:try:logger.addHandler(self.config['handlers'][h])except Exception as e:raise ValueError('Unable to add handler %r' % h) from e

這段代碼追到這里就可以停止了。回頭追解析部分。

for name in sorted(handlers):try:handler = self.configure_handler(handlers[name])

?話不多說,直接把函數(shù)貼上來了。

def configure_handler(self, config):"""Configure a handler from a dictionary."""config_copy = dict(config) # for restoring in case of errorformatter = config.pop('formatter', None)if formatter:try:formatter = self.config['formatters'][formatter]except Exception as e:raise ValueError('Unable to set formatter ''%r' % formatter) from elevel = config.pop('level', None)filters = config.pop('filters', None)if '()' in config:c = config.pop('()')if not callable(c):c = self.resolve(c)factory = celse:cname = config.pop('class')klass = self.resolve(cname)#Special case for handler which refers to another handlerif issubclass(klass, logging.handlers.MemoryHandler) and\'target' in config:try:th = self.config['handlers'][config['target']]if not isinstance(th, logging.Handler):config.update(config_copy) # restore for deferred cfgraise TypeError('target not configured yet')config['target'] = thexcept Exception as e:raise ValueError('Unable to set target handler ''%r' % config['target']) from eelif issubclass(klass, logging.handlers.SMTPHandler) and\'mailhost' in config:config['mailhost'] = self.as_tuple(config['mailhost'])elif issubclass(klass, logging.handlers.SysLogHandler) and\'address' in config:config['address'] = self.as_tuple(config['address'])factory = klassprops = config.pop('.', None)kwargs = {k: config[k] for k in config if valid_ident(k)}try:result = factory(**kwargs)except TypeError as te:if "'stream'" not in str(te):raise#The argument name changed from strm to stream#Retry with old name.#This is so that code can be used with older Python versions#(e.g. by Django)kwargs['strm'] = kwargs.pop('stream')result = factory(**kwargs)if formatter:result.setFormatter(formatter)if level is not None:result.setLevel(logging._checkLevel(level))if filters:self.add_filters(result, filters)if props:for name, value in props.items():setattr(result, name, value)return result

請注意以下兩行
?

klass = self.resolve(cname) factory = klass result = factory(**kwargs)

再結(jié)合resolve方法,發(fā)現(xiàn),這就是個(gè)通過類名獲取該類的方法。然后再把參數(shù)傳給該類。

我理解,就是JAVA中的反射機(jī)制

源碼就看到這里了。

四、實(shí)戰(zhàn)解析

1.首先找到我們想要配置的Handler

可以參考我之前的文章

Python日志詳解【兩篇就夠了系列】--第一篇logging_康雨城的博客-CSDN博客

2.修改已有配置為自己的配置

?

?不難發(fā)現(xiàn),class為類名,后面的參數(shù)就是類的參數(shù)名。

五、啟動(dòng)驗(yàn)證

from typing import Optionalimport uvicorn from fastapi import FastAPIapp = FastAPI()@app.get("/") def read_root():return {"Hello": "World"}@app.get("/items/{item_id}") def read_item(item_id: int, q: Optional[str] = None):return {"item_id": item_id, "q": q}LOGGING_CONFIG = {"version": 1,"disable_existing_loggers": False,"formatters": {"default": {"()": "uvicorn.logging.DefaultFormatter","fmt": "%(levelprefix)s %(message)s","use_colors": None,},"access": {"()": "uvicorn.logging.AccessFormatter","fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',},},"handlers": {"default": {"formatter": "default","class": "logging.handlers.TimedRotatingFileHandler","filename": "./log"},"access": {"formatter": "access","class": "logging.handlers.TimedRotatingFileHandler","filename": "./log"},},"loggers": {"": {"handlers": ["default"], "level": "INFO"},"uvicorn.error": {"level": "INFO"},"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},}, }if __name__ == "__main__":uvicorn.run(app=app,host="0.0.0.0",port=12345,log_config=LOGGING_CONFIG,)

可以發(fā)現(xiàn),日志打到了文件里面

?

總結(jié)

以上是生活随笔為你收集整理的追了源码,做了测试,终于实现python的uvicorn日志自行配置的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。