import logging import re _PROBE_PATH = re.compile( r"^(?:/live|/ready|/metrics|/healthz|/health[^ ]*|/ping)/?(?:\?|$)" ) class _ProbePathFilter(logging.Filter): def filter(self, record: logging.LogRecord) -> bool: request = getattr(record, "args", None) if isinstance(request, dict): path = request.get("U") or request.get("r", "") else: path = record.getMessage() return not _PROBE_PATH.search(path) _filter = _ProbePathFilter() def on_starting(server): logging.getLogger("gunicorn.access").addFilter(_filter) def post_worker_init(worker): logging.getLogger("gunicorn.access").addFilter(_filter) from library.apps import _run_startup_probe, _should_skip_probe if not _should_skip_probe(): try: _run_startup_probe() except Exception as exc: logging.getLogger("library.apps").warning( "Startup probe crashed: %s", exc, exc_info=True ) def worker_exit(server, worker): # Neomodel lazily creates a neo4j.Driver on first cypher_query and # holds it for the process lifetime. Newer neo4j drivers warn (and # will eventually fail to clean up) if the driver is destroyed # without an explicit close. Close it here so each gunicorn worker # shuts down cleanly. try: from neomodel import db db.close_connection() except Exception as exc: logging.getLogger("neomodel").warning( "Failed to close neomodel driver on worker exit: %s", exc )