Running under uvicorn¶
Added in version 2.3.
uvicorn installs its own handlers on its
loggers uvicorn, uvicorn.error and uvicorn.access, and stops
their records from reaching the root logger. A formatter configured on the
root logger therefore never sees them.
capture_uvicorn() removes those handlers and lets the
records propagate. It does not import uvicorn.
Route uvicorn’s records through gouge¶
Call capture_uvicorn() after uvicorn has set up its
loggers, or uvicorn undoes it. A startup hook of the application is the
right place, for example a lifespan function:
>>> from contextlib import asynccontextmanager
>>> from gouge.uvicorn import capture_uvicorn
>>> @asynccontextmanager
... async def lifespan(app):
... capture_uvicorn(level="INFO")
... yield
Pass it to the application, for example FastAPI(lifespan=lifespan) or
Starlette(lifespan=lifespan), and configure gouge as usual when the
program starts.
Alternatively, start uvicorn with log_config=None. uvicorn then
installs no handlers, and only the propagation flags need to be fixed,
which capture_uvicorn() does as well.
This example stands in for uvicorn. It sets up the access logger the way uvicorn does, then captures it:
>>> import logging
>>> from gouge.colourcli import Simple
>>> handlers = Simple.basicConfig(
... pre_formatters={
... "uvicorn.access": [
... "gouge.preformatters.uvicorn_access",
... ],
... },
... )
>>> access = logging.getLogger("uvicorn.access")
>>> access.addHandler(logging.NullHandler())
>>> access.propagate = False
>>> capture_uvicorn(level="INFO")
>>> access.info(
... '%s - "%s %s HTTP/%s" %d',
... "127.0.0.1:52341",
... "GET", "/items", "1.1", 200,
... )
2026-10-27 13:34:48,899 INFO [uvicorn.access] 127.0.0.1:52341 - "GET /items HTTP/1.1" 200
On a terminal, uvicorn_access() colours the
client address, method, path, HTTP version and status code (see
Pre-formatters).
Note
access_log=False makes uvicorn stop the propagation of
uvicorn.access. Calling capture_uvicorn()
afterwards turns it on again. To keep the access log off, pass only
the other loggers:
capture_uvicorn(loggers=["uvicorn", "uvicorn.error"]).
Configure with dictConfig¶
When uvicorn reads a logging configuration file with --log-config, it
uses that configuration instead of its own, so no capture is needed. Put
gouge’s formatter and the uvicorn loggers in the file, as in the YAML
example in Configuration files.