Logging¶
The binary prints what your application prints under uvicorn: the server's own lines (startup, access log,
errors, shutdown) and your application's logging records, formatted by the handlers and formatters your
code configures, on the same streams. The log conformance (tests/conformance.py --logs) checks it line for
line against the Python server.
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
2026-10-09 22:31:04,326 - app.views - WARNING - quota reached for tenant 7
INFO: 10.0.0.4:0 - "GET /items?limit=2 HTTP/1.1" 200 OK
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/app/app/views.py", line 42, in export
rows = build(items)
~~~~~^^^^^^^
File "/app/app/export.py", line 18, in build
return {r.id: r for r in rows}[0]
~~~~~~~~~~~~~~~~~~~~~~~^^^
KeyError: 0
Running the binary like uvicorn¶
The binary takes uvicorn's command-line options, so a container command can stay as it was:
| Option | Effect, as in uvicorn 0.54 |
|---|---|
--host, --port |
listening address (default 127.0.0.1:8000; without any option, the binary keeps HOST/PORT, default 0.0.0.0:8080) |
--log-level critical\|error\|warning\|info\|debug\|trace |
level of the uvicorn.error, uvicorn.access and uvicorn.asgi loggers |
--access-log / --no-access-log |
the access log (on by default: one line per response, on stdout) |
--use-colors / --no-use-colors |
colours of uvicorn's formatters (default: when stdout is a terminal) |
--log-config FILE.json |
a dictConfig file instead of uvicorn's LOGGING_CONFIG |
--proxy-headers / --no-proxy-headers, --forwarded-allow-ips |
client address and scheme from X-Forwarded-For / X-Forwarded-Proto when the peer is trusted (default 127.0.0.1,::1, or FORWARDED_ALLOW_IPS) |
--timeout-graceful-shutdown SECONDS |
bound of the wait at shutdown (the binary's default is 25 s, see Graceful shutdown) |
--workers, --factory, the application (api.main:app) |
accepted and ignored: the application is compiled in, one process serves |
The same options are read from UVICORN_* variables (UVICORN_PORT, UVICORN_LOG_LEVEL...), as click
reads them for uvicorn.
What is native¶
- The server's lines:
Started server process [pid], the lifespan lines (Waiting for application startup.,Application startup complete., and their failures with the traceback),Uvicorn running on ...,Shutting down,Waiting for application shutdown.,Application shutdown complete.,Finished server process [pid]; the access lineclient - "METHOD path?query HTTP/1.1" status phrasewhen the response starts;Exception in ASGI applicationwith the traceback of an unhandled exception. They are records of theuvicorn.erroranduvicorn.accessloggers, configured as uvicorn configures them before importing your application: your code can reconfigure them (logging.getLogger("uvicorn.access").addFilter(...),setLevel,handlers = []) with the same effect. - Loggers:
logging.getLogger(name), the hierarchy (a dotted name's ancestors),setLevel(number or name), effective levels,propagate,disabled,handlersandfiltersas lists your code can assign or modify,hasHandlers,getChild,isEnabledFor,logging.disable,addLevelName,getLevelName; the root logger and the module functions (logging.info(...)configures the root withbasicConfig()when it has no handler, as in Python);lastResort(WARNING and above, the message only, on stderr) when no handler is found. - Records: every
LogRecordattribute (asctime,created,msecs,relativeCreated,name,levelname,levelno,pathname,filename,module,funcName,linenoof the Python call site,process,processName,thread,threadName,taskName),extra=(a key overwriting an attribute is Python'sKeyError),exc_info=(True, an exception, a tuple),logger.exception,stack_info=True,%-style arguments and a single mapping, messages that are not strings. - Handlers:
StreamHandler(sys.stdout,sys.stderr),FileHandler,NullHandler, their level, formatter, filters, name and terminator; a formatting error is reported as Python reports it (--- Logging error ---, then the program goes on). - Formatters:
logging.Formatter(fmt, datefmt, style, validate, defaults=)in the three styles (%,{,$), Python's validation errors,asctimefromtime.localtime(default_time_format,default_msec_format), exceptions and stacks appended as Python appends them. - Filters:
logging.Filter(name), a project subclass oflogging.Filterwhosefilter(record)reads or sets record attributes (a correlation id), a plain function; returning a record replaces it (3.12). - Configuration:
logging.basicConfig(...)(every argument),logging.config.dictConfig(...)(version 1: formatters, filters, StreamHandler/FileHandler/NullHandler, loggers, root,disable_existing_loggers),LoggerAdapter(extra,merge_extra). - python-json-logger 4.0:
pythonjsonlogger.json.JsonFormatterand the oldpythonjsonlogger.jsonlogger.JsonFormatter, withrename_fields,static_fields,reserved_attrs,timestamp,defaults,prefix,json_indent,json_ensure_ascii; extra attributes and those set by filters are added in the order Python adds them. - Tracebacks (
logger.exception,exc_info, unhandled exceptions):Traceback (most recent call last):, aFile "...", line N, in functionentry for each project frame the exception went through, with its source line(s) and carets exactly as CPython prints them (the rules of the Python version running py2axum: 3.12 and 3.13+ differ),raise ... from ...and implicit chaining ("During handling of the above exception, another exception occurred:"). - Sentry:
LoggingIntegrationreceives the records a logger lets through, as in Python (Sentry).
Differences¶
- A traceback holds the project's frames only: the frames of libraries (FastAPI, Starlette, uvicorn, SQLAlchemy, the standard library) do not exist in the binary. The log conformance drops them on both sides before comparing.
- A database error does not quote its statement: SQLAlchemy's
[SQL: ...],[parameters: ...]and(Background on this error at: ...)lines and psycopg'sLINE n:excerpt are absent (the binary's SQL is its own rendering). The message, psycopg'sDETAIL/HINTlines and the chained psycopg exception ("The above exception was the direct cause...") are as in Python. - An exception raised once the response has started (while the body streams, or by a raw ASGI middleware
after the response) has no frame of the raw ASGI middlewares around it in its traceback: in the binary it
does not go back through them (Middleware). An exception of a
Server-Sent Events stream is printed as Python prints it (an
ExceptionGroupof anyio's TaskGroup holding it). Without raw ASGI middlewares, the output is the same. stack_info=Trueprints the logging call's own frame underStack (most recent call last):; Python prints every frame of the call stack (uvicorn's, the middlewares' included).- The
warningsmodule is not part of the subset: the warnings libraries print on stderr (pydantic's serializer warnings, PyJWT'sInsecureKeyLengthWarning, a deprecation, a coroutine never awaited) are not printed by the binary. The log conformance drops them on both sides. - The records some libraries log themselves are reproduced where the binary reimplements the library with
its logging (aiormq's
error when creating transport); python-multipart's parser warnings on a malformed body (python_multipart.multipart) are not. pathnameis built fromPY2AXUM_SOURCE_ROOT(default: the project's directory at translation), so set it to the directory the Python process imported the project from if your formats print paths.- A hybrid application (
--python-side): what runs in the Python process (the python-side routes, a python-side lifespan, the modules only they import) prints its records on the Python process's streams, not on the binary's. - One process:
--workers Nprints what uvicorn prints with one worker.thread(an id),taskName(Task-N) and the process id differ between any two runs, Python's included. threadNameisAnyIO worker threadfor the whole request of adefendpoint (in Python, its async dependencies run onMainThread).- Refused at translation, with the file and line:
stacklevel=other than 1, a project subclass oflogging.Handlerorlogging.Formatter,logging.config.fileConfig, handlers outside the list above (RotatingFileHandler,QueueHandler,SysLogHandler...).--log-configtakes JSON only (no YAML, no INI). tenacity.before_sleep_logand the other records made by libraries the binary reimplements carry no Python call site (pathnameis(unknown file)).