はじめに
エンジニアニメ、ありがとうございました! nikkieです。
FastAPI製のアプリのサーブに使うASGIサーバ uvicornについて、素振りの模様をお届けします。
目次
ASGIサーバ uvicorn
encode1が開発するASGIサーバ
今回動作させるFastAPIアプリ
from fastapi import FastAPI app = FastAPI() @app.get("/") def hello(): return {"message": "Hello World!"}
動作環境
uvicorn起動!(app.pyのappを指定しています)
% uvicorn app:app
別のターミナルで
% curl http://127.0.0.1:8000 {"message":"Hello World!"}
なおFastAPIもCLIを導入しており、fastapi devやfastapi run経由でもuvicornを動かせます2。
uvicornのロギングの設定
uvicorn app:appで起動すると以下のようなロギングがされます。
色付きです。
INFO: Started server process [14182] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: 127.0.0.1:58610 - "GET / HTTP/1.1" 200 OK

uvicornのドキュメントにはロギングの設定の項目があります。
https://www.uvicorn.org/settings/#logging
--log-config <path>
これを設定してみようと思ったのですが、今の私には初手でできませんでした。
どうやら標準ライブラリのlogging.configが前提知識としている模様です。
(なおfastapiコマンドは現時点では--log-configを未サポートのようでした。ヘルプメッセージに見つけられていません)
デフォルトの色付きのログの設定
ドキュメントの記載が控えめだったので、ソースコードに潜りました。
https://github.com/encode/uvicorn/blob/0.34.0/uvicorn/main.py#L428
log_config=LOGGING_CONFIG if log_config is None else log_config,
コマンドラインから--log-configが与えられない場合はLOGGING_CONFIGを渡しています。
LOGGING_CONFIGは以下にある辞書です。
https://github.com/encode/uvicorn/blob/0.34.0/uvicorn/config.py#L67-L98
ここでlogging.config.dictConfig!
https://docs.python.org/ja/3/library/logging.config.html#logging.config.dictConfig
この辞書のスキーマはこちら
https://docs.python.org/ja/3/library/logging.config.html#dictionary-schema-details
uvicornのLOGGING_CONFIG(デフォルト)には以下が設定されています
- version
- disable_existing_loggers
- formatters
- handlers
- loggers
理解を深めるために、これと同値なYAMLの設定ファイルを作ることにしました。
デフォルトのロギング設定と同値な設定ファイルを作る
- uvicorn.errorロガーのログは、親のuvicornロガーで出力(ルートロガーには伝播しない)
- defaultハンドラによる標準エラー出力
- DefaultFormatter https://github.com/encode/uvicorn/blob/0.34.0/uvicorn/logging.py#L68
- uvicorn.accessロガーは自身がログ出力(親のuvicornロガーには伝播しない)
- accessハンドラによる標準出力
- AccessFormatter https://github.com/encode/uvicorn/blob/0.34.0/uvicorn/logging.py#L73
フォーマッタに渡しているメッセージの書式、levelprefixやclient_addr・request_line・status_codeは標準ライブラリにはないものみたいです。
https://docs.python.org/ja/3/library/logging.html#logrecord-attributes
ext://はこちら
https://docs.python.org/ja/3/library/logging.config.html#access-to-external-objects
例えば、リテラル文字列 'ext://sys.stderr' が設定中の値として与えられたら、この ext:// は剥ぎ取られ、この値の残りが普通のインポート機構で処理されます。
いざ--log-configを指定して起動!
ドキュメントによるとpip install uvicorn[standard]するとYAMLのパーサ(PyYAML)もインストールされるそうです3
% uvicorn app:app --log-config log_config.yaml INFO: Started server process [20424] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: 127.0.0.1:59204 - "GET / HTTP/1.1" 200 OK
再現できています!✌️
defaultハンドラに設定されているdefaultフォーマッタをいじると、uvicorn.errorのログにも時刻を出すことができます!
formatters:
default:
class: uvicorn.logging.DefaultFormatter
- format: '%(levelprefix)s %(message)s'
+ format: '%(asctime)s - %(name)s - %(levelprefix)s - %(message)s'

終わりに
uvicornの--log-configについて、デフォルト設定と同様のYAMLファイルを作って素振りしました。
標準ライブラリのlogging.configを使って、ロガーを設定しているんですね!
- 今まで気づいていなかったが、uvicornのログは標準エラー出力(uvicorn.error)と標準出力(uvicorn.access)で分かれていた
- uvicornは色付きで出力するフォーマッタを提供している(
uvicorn.loggingのDefaultFormatterやAccessFormatter)
ファイルを使ったuvicornのロガーの設定は理解できた気がするので、カスタマイズをしてみようと思います。
おそらくあるであろう続編でお会いしましょう!