gunicorn: faulthandler doesn't dump SIGABRT traceback on worker timeout, but does for SIGSEGV
Goal: make gunicorn's WORKER TIMEOUT kills self-diagnosing — the arbiter kills a heartbeat-timed-out worker with SIGABRT, and faulthandler should turn that abort into an all-threads Python traceback naming whatever blocked the event loop. The obvious placement, faulthandler.enable() at the top of a custom worker's init_process() (before super().init_process()), produces NO dump on SIGABRT: the worker just dies with Worker (pid:N) was sent SIGABRT! and no traceback. Confusingly, SIGSEGV does dump with the same placement, which makes faulthandler look enabled and working.
Root cause: gunicorn's Worker.SIGNALS includes SIGABRT ("ABRT HUP QUIT INT TERM USR1 USR2 WINCH CHLD"), and worker signal setup runs after your init_process preamble — uvicorn's UvicornWorker.init_signals() resets every signal in that list to SIG_DFL (gunicorn's own base init_signals similarly installs handle_abort over it). Either way faulthandler's freshly-registered SIGABRT handler is silently replaced. SIGSEGV is not in SIGNALS, so segfault dumps survive — a misleading partial success. Fix: enable faulthandler in an init_signals override, after the reset:
class MyUvicornWorker(UvicornWorker):
def init_signals(self):
super().init_signals()
faulthandler.enable()Verified by kill -ABRT <worker pid>: init_process placement → no dump; init_signals placement → Fatal Python error: Aborted + per-thread Current thread ... (most recent call first) traceback on stderr. The alternative canonical route is gunicorn's worker_abort server hook calling faulthandler.dump_traceback(), but the init_signals override works when you already ship a custom worker class and cannot touch the gunicorn config.