The mod_wsgi Python Module
When a WSGI application runs under Apache with mod_wsgi loaded, the
mod_wsgi module is built into the Python interpreter and exposes
attributes and functions that let the application introspect the
hosting environment, subscribe to lifecycle and request events,
read request and process metrics, and use mod_wsgi-specific helper
types. This page is a reference of that surface.
The built-in mod_wsgi module is installed into sys.modules
by mod_wsgi itself when an interpreter is initialised; it is not
the same module as the mod_wsgi Python package distributed on
PyPI that provides the mod_wsgi-express command. See
Detecting mod_wsgi at Runtime for reliable ways to tell whether code is
running under Apache/mod_wsgi rather than just having the PyPI
package installed.
Module attributes
versionThe mod_wsgi version as a
(major, minor, micro)tuple, reflecting the version ofmod_wsgi.soloaded into Apache.process_groupThe name of the daemon process group hosting the interpreter, or the empty string when running in embedded mode. Matches the first argument given to WSGIDaemonProcess.
application_groupThe name of the application group (sub interpreter) hosting the WSGI application. Matches the value given to WSGIApplicationGroup. The special name
%{GLOBAL}resolves to the empty string and runs in the main interpreter; any other non-empty name runs in a named sub interpreter.maximum_processesThe maximum number of processes hosting interpreters in the current process group. In daemon mode this is the
processes=value onWSGIDaemonProcess; in embedded mode it is queried from the active Apache MPM.threads_per_processThe maximum number of worker threads per process. In daemon mode this is the
threads=value onWSGIDaemonProcess; in embedded mode it is queried from the active Apache MPM.
Event subscription
subscribe_events(callback)Register
callbackto receive every event mod_wsgi publishes. The callback is invoked with the event name as a single positional argument and the event payload as keyword arguments. The expected signature is:def callback(name, **event): ...
or, when only specific keys matter, declare them as keyword-only parameters:
def callback(name, *, request_id, request_start, **event): ...
The function returns the callback unchanged so it can also be used as a decorator:
@mod_wsgi.subscribe_events def callback(name, **event): ...
If the callback returns a dict, its keys are merged into the event dict (the same dict passed as kwargs to subscribers) before subsequent subscribers of the same event firing run. The merge is shallow and applies only to this dispatch; the next event published gets a fresh event dict.
To carry data across events for the same request, for example to set a value at
request_startedand read it atrequest_finished, mutatemod_wsgi.request_data()instead. That dict is the same object referenced under therequest_datakey in every event for the request and lives for the request’s lifetime.subscribe_shutdown(callback)A shortcut for subscribing only to the
process_stoppingevent. The callback shape matchessubscribe_eventsand the callback is invoked once, when the process is shutting down. Likesubscribe_events, the function returns the callback so it can be used as a decorator:@mod_wsgi.subscribe_shutdown def cleanup(*args, **kwargs): ...
Inert in service-script daemons (
WSGIDaemonProcess threads=0); usesignal.signal()directly in those processes. See Registering Cleanup Code and the service-script notes in Subscribing to Events.subscribe_signals(callback)Register
callbackto receive theprocess_signalevent when the daemon process is sentSIGHUPorSIGUSR2. The callback shape matchessubscribe_eventsand the payload carriessigname(canonical string like"SIGHUP") andsignum(numeric value for convenience). The function returns the callback so it can be used as a decorator:@mod_wsgi.subscribe_signals def on_signal(name, *, signame, **event): if signame == 'SIGHUP': reload_config() elif signame == 'SIGUSR2': dump_diagnostics()
Only effective in request-handling daemon-mode processes. In embedded mode the call is permitted (and still returns the callback so decorator syntax does not silently nullify the user’s function symbol) but the callback is discarded; a warning and a Python stack trace identifying the registration site are emitted to the Apache error log. Service-script daemons (
WSGIDaemonProcess threads=0) are also unsupported here; usesignal.signal()directly in those processes. See Subscribing to Events for details.
Events
mod_wsgi publishes the following events. subscribe_events
callbacks see all of them; subscribe_shutdown callbacks see
only process_stopping; subscribe_signals callbacks see
only process_signal.
request_startedFires immediately before the WSGI application callable is invoked. Payload keys include
request_id,thread_id,server_pid(Apache child worker PID),request_start(seconds since epoch),application_object,callable_object(the configured callable name as a string),application_start,request_environ(the WSGI environment), andrequest_data. Daemon mode addsqueue_start,daemon_start,daemon_connectsanddaemon_restarts.response_startedFires when the WSGI application calls
start_response. Payload keys arerequest_id,response_status(the status line),response_headers,exception_info(orNone), andrequest_data.request_finishedFires after the response has been fully written. Payload includes the request-start fields plus timing fields (
application_finish,application_time,input_time,output_time), counter fields (input_reads,input_length,output_writes,output_length,status), CPU fields (cpu_user_time,cpu_system_time,cpu_time), GIL-contention fields (gil_wait_time,gil_wait_count), andrequest_data.request_exceptionFires when an uncaught exception propagates out of the WSGI application callable. Payload keys are
request_id,exception_info(a(type, value, traceback)tuple as produced bysys.exc_info), andrequest_data.process_signalFires in a daemon process when
SIGHUPorSIGUSR2is delivered to the daemon. Payload keys aresigname(a string,"SIGHUP"or"SIGUSR2") andsignum(the numeric value of the signal on the current platform). Subscribers should branch onsignamebecause numeric signal values vary across platforms. Embedded mode never publishes this event; see Subscribing to Events for details.process_stoppingFires once per process when mod_wsgi is shutting it down. Payload key
shutdown_reasonis one of:"shutdown_signal"— SIGTERM (Apache stop or restart)."graceful_signal"— SIGUSR1 graceful drain."eviction_signal"— operator-driven eviction."maximum_requests"—maximum-requestslimit reached."restart_interval"—restart-intervalreached."inactivity_timeout"—inactivity-timeoutexpired."request_timeout"—request-timeouttriggered."startup_timeout"—startup-timeouttriggered."deadlock_timeout"—deadlock-timeouttriggered."cpu_time_limit"—cpu-time-limitexceeded."script_reload"— WSGI script reload.
The request_id field shared across the request events is the
same identifier Apache uses as the request log ID — the value
substituted by %L in LogFormat and ErrorLogFormat
directives. Subscribers can use it to cross-correlate event data
with Apache’s access and error logs.
Per-request state
request_data()Return the per-request scratchpad dict for the current thread. The dict is created at the start of each request and is the same object passed to subscribers as the
request_dataevent payload key. The application and subscribers share it, so keys an event subscriber sets become visible to the application and vice versa. RaisesRuntimeErrorif called outside the context of an active request.active_requestsA dict, keyed by request ID, of requests currently being handled by the process. Populated automatically as requests start and finish; subscribers can read it to inspect concurrent in-flight work.
Metrics
start_recording_metrics()Enable per-request metrics accounting and seed the per-reader baselines so subsequent calls to
request_metrics()andprocess_metrics()return data. Idempotent; safe to call unconditionally at application import time. When external telemetry reporting is enabled (the WSGITelemetryService directive), the external reporter is the canonical metrics consumer and this call has no observable effect from the Python API: the two accessors below still returnNoneto signal the configured mode. Without an explicit call to this function, both accessors returnNoneeven when telemetry is off.request_metrics()Return a dict of timing and resource counters for the sample interval since the last call. Useful for emitting per-request observability data from the application. Returns
Noneifstart_recording_metrics()has not been called or if external telemetry reporting is enabled.process_metrics()Return a dict of process-level aggregates: served-request count, request busy time, current and peak resident memory, CPU user and system time, current active-request count, and similar counters covering the lifetime of the process. Returns
Noneunder the same conditions asrequest_metrics().server_metrics()Return a dict reflecting the Apache scoreboard view of the server: per-process and per-worker state, total requests served, bytes transferred, and similar server-wide aggregates. The data is gated by configuration; see WSGIServerMetrics for the embedded-mode gate and
WSGIDaemonProcessfor the daemon-mode gate.
Types
FileWrapperA callable that wraps a file-like object so the WSGI adapter can return its contents using
sendfile()or equivalent optimisations. The same callable is also exposed in the WSGI environment under the keywsgi.file_wrapper. See File Wrapper Extension.RequestTimeoutAn exception class derived directly from
BaseException, raised in a daemon-process worker thread when the configuredrequest-timeout=is exceeded and the daemon monitor injects a timeout into the worker. Deriving fromBaseException(notException) means well-written code does not accidentally catch it viaexcept Exception:. Application code may catch it for per-request cleanup but should re-raise so the WSGI adapter can return504. See WSGIDaemonProcess forrequest-timeout=semantics.
Companion apache module
Alongside mod_wsgi, mod_wsgi installs an apache module
into sys.modules carrying introspection attributes about the
Apache server hosting the application. Application code can
import apache to inspect the server configuration; the module
is available in both embedded and daemon mode.
versionApache server version as a
(major, minor, patchlevel)tuple, distinct frommod_wsgi.version(which is the mod_wsgi extension version).maximum_processesMaximum number of processes hosting interpreters. Same value as
mod_wsgi.maximum_processes.threads_per_processMaximum number of worker threads per process. Same value as
mod_wsgi.threads_per_process.descriptionThe Apache server description string, as returned by
ap_get_server_description(), e.g."Apache/2.4.58 (Unix)".mpm_nameThe name of the active Apache MPM, e.g.
"event","worker"or"prefork".build_dateThe date on which the running Apache binary was built.
WSGI environ keys
The WSGI environ dict passed to the application has the
standard WSGI keys plus mod_wsgi-specific keys mirroring the
module attributes and adding per-request information.
mod_wsgi.version(major, minor, micro)tuple, same value as the module-levelversion. Reliable runtime test that the request is being served by Apache/mod_wsgi.mod_wsgi.process_groupSame value as the module-level
process_group.mod_wsgi.application_groupSame value as the module-level
application_group.mod_wsgi.request_idThe Apache request log ID — the same identifier substituted by
%LinLogFormatandErrorLogFormat. Useful for cross-correlating application data with Apache’s access and error logs. Same value as therequest_idfield on the request events.mod_wsgi.connection_idThe Apache connection log ID, set when Apache has generated one for the underlying connection.
mod_wsgi.thread_idNumeric ID of the worker thread handling this request.
mod_wsgi.server_pidProcess ID of the Apache child worker that accepted the request, as a decimal string. In embedded mode this is the process the WSGI application is running in; in daemon mode it is the originating Apache child, distinct from the daemon process serving the request (the daemon’s own PID is available via
os.getpid()).
The four request-timing keys below are Python float values
in seconds since the epoch. They carry the same instants as the
identically-named fields on the request_started and
request_finished event payloads.
mod_wsgi.request_startTime Apache received the request.
mod_wsgi.queue_startTime Apache wrote the request onto the daemon socket.
0.0in embedded mode (no queue phase).mod_wsgi.daemon_startTime the daemon process accepted the request and began handling it.
0.0in embedded mode.mod_wsgi.application_startTime the worker thread was about to invoke the WSGI application callable for this request.
WSGI script __name__
mod_wsgi imports each WSGI script file as if it were a module, but
because WSGI scripts can live anywhere on disk and not on
sys.path, the imported name is synthesised from a hash of the
script path with the prefix _mod_wsgi_. A WSGI script can
detect that it is being loaded by mod_wsgi (rather than executed
directly) with:
if __name__.startswith('_mod_wsgi_'):
...
See Detecting mod_wsgi at Runtime for other reliable ways to detect the mod_wsgi runtime.