Worker processes
Gunicorn, uWSGI, and Celery run your app in worker processes. Start Vigilon inside each worker so your data is sent from the process that does the work.
Why workers need their own startup
These servers start one parent process and then copy it into several workers. Vigilon sends data from a background thread, and that thread does not survive the copy. So start Vigilon once inside each worker, after the worker has been created, and not in the parent process.
Before you begin, complete the environment configuration from the Python quickstart and create its telemetry.py helper. If you added a start_vigilon() call to your entrypoint in the quickstart, remove it. The worker hook replaces it.
For the same reason, the opentelemetry-instrument launcher from Start without code changes does not work with these servers.
Gunicorn
Add this hook to gunicorn.conf.py. Gunicorn calls post_fork inside each new worker. Keep preload_app off so your app is imported after the hook runs.
preload_app = False
def post_fork(server, worker):
from telemetry import start_vigilon
start_vigilon()
For a Flask app named app in main.py, launch with the config file. For Django, export DJANGO_SETTINGS_MODULE first and replace main:app with myproject.wsgi:application.
gunicorn --config gunicorn.conf.py main:app
If you have to preload your app, it is created before the workers exist, so Vigilon cannot hook into it at import time. Start Vigilon in post_fork, then connect it to the existing app object as described under “Connect Vigilon to an app that already exists” in the Troubleshooting guide. Avoid creating database engines or HTTP clients in the parent process.
uWSGI
Register a post-fork hook in your WSGI module. uWSGI creates your app before it creates the workers, so the hook also has to connect Vigilon to the app object that already exists. That is what the FlaskInstrumentor lines do. Replace the demo route with your application.
from uwsgidecorators import postfork
from flask import Flask
app = Flask(__name__)
@app.get("/hello")
def hello():
return {"message": "Hello from uWSGI"}
@postfork
def start_worker_telemetry():
from telemetry import start_vigilon
start_vigilon()
from opentelemetry.instrumentation.flask import FlaskInstrumentor
FlaskInstrumentor().instrument_app(
app,
excluded_urls=r"/health$,/ready$,/favicon\.ico$",
)
List every route you want left out in excluded_urls. This call does not pick up Vigilon’s defaults or VIGILON_EXCLUDED_URLS.
Run uWSGI with --enable-threads. Vigilon sends data from a background thread, and uWSGI turns Python threads off by default.
uwsgi --http :8000 --master --processes 4 --enable-threads --module wsgi:app
Celery
Celery’s prefork pool works the same way. Connect a handler to the worker_process_init signal in a module your Celery app imports at startup. You can define monitored tasks before Vigilon starts; Vigilon only has to be running by the time they execute.
from celery import Celery
from celery.signals import worker_process_init
from vigilon import with_job_monitor
app = Celery("tasks", broker="redis://localhost:6379/0")
@worker_process_init.connect
def start_worker_telemetry(**kwargs):
from telemetry import start_vigilon
start_vigilon()
@app.task
@with_job_monitor(name="add-numbers")
def add_numbers(left, right):
return left + right
With Celery’s Redis dependencies installed and Redis running, start the worker. Keep @app.task above @with_job_monitor so each task execution passes through the monitor.
celery -A tasks worker --pool=prefork