""" Gunicorn configuration. Sized for a container: worker count follows the CPU quota, requests are recycled to bound memory growth, and access logs are emitted as JSON so they land in Elasticsearch in the same shape as the application logs. """ import multiprocessing import os bind = os.getenv("GUNICORN_BIND", "0.0.0.0:8000") workers = int(os.getenv("GUNICORN_WORKERS", str(min(multiprocessing.cpu_count() * 2 + 1, 8)))) threads = int(os.getenv("GUNICORN_THREADS", "2")) worker_class = "gthread" timeout = int(os.getenv("GUNICORN_TIMEOUT", "45")) graceful_timeout = 30 keepalive = 5 # Recycle workers to keep any slow leak from growing without bound. max_requests = 1000 max_requests_jitter = 100 preload_app = False forwarded_allow_ips = os.getenv("GUNICORN_FORWARDED_ALLOW_IPS", "*") # Refuse absurd request lines/headers early - cheap protection against a class # of denial-of-service probes. limit_request_line = 8190 limit_request_fields = 100 limit_request_field_size = 8190 accesslog = "-" errorlog = "-" loglevel = os.getenv("GUNICORN_LOG_LEVEL", "info") access_log_format = ( '{"level":"INFO","logger":"gunicorn.access","message":"%(r)s",' '"http_status":%(s)s,"duration_ms":%(D)s,"client_ip":"%(h)s",' '"request_id":"%({x-request-id}i)s"}' )