Configure Logfire with Gunicorn
See every request your app handles when it runs under Gunicorn, across all of Gunicorn’s worker processes, as spans (each span is one unit of work with a name, a start, and a duration) in Logfire.
Gunicorn is a Python web server that runs your app in several worker processes at once, forking a fresh process for each. Because those workers are created after Gunicorn starts, Logfire has to be set up inside each worker rather than once at startup. This page shows where.
- Each request as a span, no matter which worker process handled it
- The duration and status of every request across all workers
- Any errors raised while handling a request
You’ll need a Logfire project. Open Add data in your project (top navigation) and follow the
setup for your language: it signs your machine in with logfire auth (a browser sign-in, no token
to copy) and, for production or other languages, creates a write token (the credential your app
uses to send data). New to Logfire? Start with Getting Started.
Install logfire:
pip install logfire
uv add logfire
conda install -c conda-forge logfire
If you also want to instrument the web framework you run under Gunicorn (Flask, for example), install its extra too. Choose the relevant framework from the web framework integrations.
Call logfire.configure() in Gunicorn’s
post_fork hook (the function Gunicorn
runs in each worker process right after it forks) so every worker sends data:
import logfire
def post_fork(server, worker):
logfire.configure()
Then start Gunicorn with that configuration file, where myapp:app is your WSGI application:
gunicorn myapp:app --config gunicorn_config.py
Start Gunicorn and open one of your pages in the browser. Then open the Live view. Within a few seconds you’ll see a span for the request, regardless of which worker handled it.
Not seeing your requests in Logfire? Check that logfire.configure() is called inside the post_fork
hook (not at module top level, where it runs before workers fork), and that your write token is set. For
Flask, check that instrument_flask(worker.wsgi) runs inside post_worker_init, as shown below. Other
frameworks can require different setup; choose the relevant framework from the
web framework integrations.
Here you also instrument a Flask app running under Gunicorn, so each request becomes a span.
The Flask application (myapp.py):
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello from Flask + Gunicorn!'
Configure Logfire after each worker is forked, then instrument the application after Gunicorn loads it
(gunicorn_config.py):
import logfire
def post_fork(server, worker):
logfire.configure()
def post_worker_init(worker):
logfire.instrument_flask(worker.wsgi)
Then start Gunicorn:
gunicorn myapp:app --config gunicorn_config.py
Logfire now records a span for every request the Flask app handles, in every worker.
- Gunicorn server hooks: where configuration and application instrumentation run.
logfire.instrument_flask(): to instrument a Flask app, as shown above.