Configuration Guide¶
Complete configuration guide for the Gunicorn Prometheus Exporter with all options and scenarios.
Configuration Architecture¶
The Gunicorn Prometheus Exporter uses a ConfigManager pattern with lifecycle management that follows software engineering best practices for configuration management. This ensures consistent, centralized configuration across the entire application.
ConfigManager Pattern Benefits¶
- Lifecycle Management: Proper initialization, validation, and cleanup states
- State Tracking: Clear state transitions and error handling
- Thread Safety: Safe concurrent access with proper locking mechanisms
- Validation Control: Centralized validation with detailed error reporting
- Resource Management: Proper cleanup and resource management
- Single Source of Truth: One configuration instance for the entire application
Configuration Loading Flow¶
flowchart TD
A[Application Startup] --> B[initialize_config() called]
B --> C[ConfigManager.initialize()]
C --> D[State: INITIALIZING]
D --> E[Set environment variables]
E --> F[Create ExporterConfig instance]
F --> G[Validate configuration]
G --> H{Validation successful?}
H -->|No| I[State: ERROR]
H -->|Yes| J[State: INITIALIZED]
I --> K[Raise exception]
J --> L[Configuration ready]
L --> M[Property Access - Lazy Loading]
M --> N[Environment variables read with validation]
N --> O[CLI Updates via EnvironmentManager]
O --> P[Runtime Usage - Properties read current values]
P --> Q[Application Shutdown]
Q --> R[cleanup_config() called]
R --> S[ConfigManager.cleanup()]
S --> T[State: CLEANUP]
style A fill:#e1f5fe
style C fill:#f3e5f5
style M fill:#e8f5e8
style P fill:#fff3e0
style T fill:#ffebee
Configuration Access Patterns¶
ConfigManager Access¶
# Import the config manager functions
from gunicorn_prometheus_exporter.config import get_config, initialize_config
# Initialize configuration (typically done once at startup)
initialize_config(
PROMETHEUS_METRICS_PORT="9091",
PROMETHEUS_BIND_ADDRESS="0.0.0.0",
GUNICORN_WORKERS="2"
)
# Get the configuration instance
config = get_config()
port = config.prometheus_metrics_port
redis_enabled = config.redis_enabled
Direct ConfigManager Access¶
# Import the ConfigManager class
from gunicorn_prometheus_exporter.config import ConfigManager
# Create and manage configuration
manager = ConfigManager()
manager.initialize(
PROMETHEUS_METRICS_PORT="9091",
PROMETHEUS_BIND_ADDRESS="0.0.0.0",
GUNICORN_WORKERS="2"
)
# Get configuration
config = manager.get_config()
port = config.prometheus_metrics_port
Module-Level Access¶
# Import config from the main module
from gunicorn_prometheus_exporter import get_config
# Access configuration values
config = get_config()
port = config.prometheus_metrics_port
Environment Variable Processing¶
The configuration system processes environment variables in multiple phases:
Phase 1: Module-Level Defaults¶
# config.py - Module level (read at import time)
PROMETHEUS_MULTIPROC_DIR = os.environ.get("PROMETHEUS_MULTIPROC_DIR", _default_prometheus_dir)
GUNICORN_TIMEOUT = os.environ.get("GUNICORN_TIMEOUT", 30)
GUNICORN_KEEPALIVE = os.environ.get("GUNICORN_KEEPALIVE", 2)
Phase 2: Singleton Initialization¶
# config.py - Global singleton creation
config = ExporterConfig() # Creates global instance
def __init__(self):
"""Initialize configuration with environment variables and defaults."""
self._setup_multiproc_dir() # Sets up multiprocess directory
def _setup_multiproc_dir(self):
"""Set up the Prometheus multiprocess directory."""
if not os.environ.get(self.ENV_PROMETHEUS_MULTIPROC_DIR):
os.environ[self.ENV_PROMETHEUS_MULTIPROC_DIR] = self.PROMETHEUS_MULTIPROC_DIR
Phase 3: Lazy Property Access¶
@property
def prometheus_metrics_port(self) -> int:
"""Get the Prometheus metrics server port."""
value = os.environ.get(self.ENV_PROMETHEUS_METRICS_PORT, self.PROMETHEUS_METRICS_PORT)
if value is None:
raise ValueError(f"Environment variable {self.ENV_PROMETHEUS_METRICS_PORT} must be set in production.")
return int(value)
@property
def redis_enabled(self) -> bool:
"""Check if Redis storage is enabled."""
return os.environ.get(self.ENV_REDIS_ENABLED, "").lower() in ("true", "1", "yes", "on")
Phase 4: CLI Integration¶
# hooks.py - EnvironmentManager class
def update_from_cli(self, cfg: Any) -> None:
"""Update environment variables from CLI configuration."""
self._update_workers_env(cfg)
self._update_bind_env(cfg)
self._update_worker_class_env(cfg)
def _update_workers_env(self, cfg: Any) -> None:
"""Update GUNICORN_WORKERS environment variable from CLI."""
if hasattr(cfg, "workers") and cfg.workers:
os.environ["GUNICORN_WORKERS"] = str(cfg.workers)
Configuration Validation¶
The configuration system includes comprehensive validation:
Required Variables Validation¶
@property
def prometheus_metrics_port(self) -> int:
value = os.environ.get(self.ENV_PROMETHEUS_METRICS_PORT, self.PROMETHEUS_METRICS_PORT)
if value is None:
raise ValueError(
f"Environment variable {self.ENV_PROMETHEUS_METRICS_PORT} "
f"must be set in production. "
f"Example: export {self.ENV_PROMETHEUS_METRICS_PORT}=9091"
)
return int(value)
Type Conversion and Validation¶
@property
def gunicorn_timeout(self) -> int:
"""Get the Gunicorn worker timeout."""
return int(
os.environ.get(self.ENV_GUNICORN_TIMEOUT, str(self.GUNICORN_TIMEOUT))
)
@property
def redis_ttl_seconds(self) -> int:
"""Get Redis TTL in seconds for metric keys."""
return int(
os.environ.get(self.ENV_REDIS_TTL_SECONDS, "300")
) # 5 minutes default
Configuration Categories¶
Required (Production)¶
PROMETHEUS_METRICS_PORT
- Metrics server portPROMETHEUS_BIND_ADDRESS
- Metrics server bind addressGUNICORN_WORKERS
- Number of workers
Optional (Defaults)¶
PROMETHEUS_MULTIPROC_DIR
- Multiprocess directory (defaults to~/.gunicorn_prometheus
)GUNICORN_TIMEOUT
- Worker timeout (defaults to 30)GUNICORN_KEEPALIVE
- Keepalive setting (defaults to 2)
Redis Configuration¶
REDIS_ENABLED
- Enable Redis storageREDIS_HOST
- Redis host (defaults to 127.0.0.1)REDIS_PORT
- Redis port (defaults to 6379)REDIS_DB
- Redis database (defaults to 0)REDIS_PASSWORD
- Redis passwordREDIS_KEY_PREFIX
- Key prefix (defaults to "gunicorn")REDIS_TTL_SECONDS
- TTL for keys (defaults to 300)REDIS_TTL_DISABLED
- Disable TTL
SSL/TLS Configuration¶
PROMETHEUS_SSL_CERTFILE
- SSL certificate filePROMETHEUS_SSL_KEYFILE
- SSL key filePROMETHEUS_SSL_CLIENT_CAFILE
- Client CA filePROMETHEUS_SSL_CLIENT_CAPATH
- Client CA pathPROMETHEUS_SSL_CLIENT_AUTH_REQUIRED
- Require client auth
Environment Variables Reference¶
Required Variables¶
Variable | Type | Default | Description | Example |
---|---|---|---|---|
PROMETHEUS_MULTIPROC_DIR | String | /tmp/prometheus_multiproc | Directory for multiprocess metrics | /var/tmp/prometheus_multiproc |
PROMETHEUS_METRICS_PORT | Integer | 9090 | Port for metrics endpoint | 9091 |
PROMETHEUS_BIND_ADDRESS | String | 0.0.0.0 | Bind address for metrics server | 127.0.0.1 |
GUNICORN_WORKERS | Integer | 1 | Number of workers for metrics | 4 |
Optional Variables¶
Variable | Type | Default | Description | Example |
---|---|---|---|---|
GUNICORN_KEEPALIVE | Integer | 2 | Keep-alive timeout | 5 |
CLEANUP_DB_FILES | Boolean | false | Clean up old metric files | true |
Redis Variables¶
Redis Storage (No Files Created)¶
Variable | Type | Default | Description | Example |
---|---|---|---|---|
REDIS_ENABLED | Boolean | false | Enable Redis storage (replaces file storage) | true |
REDIS_HOST | String | localhost | Redis server host | redis.example.com |
REDIS_PORT | Integer | 6379 | Redis server port | 6380 |
REDIS_DB | Integer | 0 | Redis database number | 1 |
REDIS_PASSWORD | String | None | Redis password | secret123 |
Redis Backend Configuration¶
The Redis backend provides a sophisticated architecture for metrics storage with two main components:
Architecture Components¶
backend.service
- High-Level Management¶
RedisStorageManager
: Centralized lifecycle managementsetup_redis_metrics()
: Initialize Redis storageteardown_redis_metrics()
: Clean shutdownget_redis_storage_manager()
: Global manager access
backend.core
- Low-Level Operations¶
RedisStorageClient
: Main Redis operations clientRedisStorageDict
: Dictionary-like Redis interfaceRedisValue
: Redis-backed metric valuesRedisMultiProcessCollector
: Metrics collection from RedisRedisDict
: Low-level Redis operations
Redis Backend Setup¶
Use Case: Production deployment with Redis storage backend.
# gunicorn_redis_backend.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9091")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Redis backend configuration
os.environ.setdefault("REDIS_ENABLED", "true")
os.environ.setdefault("REDIS_HOST", "localhost")
os.environ.setdefault("REDIS_PORT", "6379")
os.environ.setdefault("REDIS_DB", "0")
os.environ.setdefault("REDIS_PASSWORD", "")
os.environ.setdefault("REDIS_KEY_PREFIX", "gunicorn")
# Import Redis hooks
from gunicorn_prometheus_exporter.hooks import (
redis_when_ready,
default_on_starting,
default_worker_int,
default_on_exit,
)
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 60
keepalive = 5
# Use Redis hooks
when_ready = redis_when_ready
on_starting = default_on_starting
worker_int = default_worker_int
on_exit = default_on_exit
Benefits of Redis Backend¶
- No File System Dependencies: Eliminates multiproc directory requirements
- Better Scalability: Redis handles concurrent access efficiently
- Storage-Compute Separation: Metrics storage independent of application servers
- Centralized Aggregation: All metrics accessible from a single Redis instance
- Automatic Cleanup: Dead process keys are automatically cleaned up
- High Performance: Redis provides sub-millisecond latency for metric operations
Configuration Scenarios¶
Basic Setup¶
Use Case: Simple monitoring for a single Gunicorn instance.
# gunicorn_basic.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "2")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 2
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
Environment Variables:
export PROMETHEUS_MULTIPROC_DIR="/tmp/prometheus_multiproc"
export PROMETHEUS_METRICS_PORT="9090"
export PROMETHEUS_BIND_ADDRESS="0.0.0.0"
export GUNICORN_WORKERS="2"
High-Performance Setup¶
Use Case: High-traffic application with optimized settings.
# gunicorn_high_performance.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "8")
os.environ.setdefault("GUNICORN_KEEPALIVE", "5")
os.environ.setdefault("CLEANUP_DB_FILES", "true")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 8
worker_class = "gunicorn_prometheus_exporter.PrometheusThreadWorker"
worker_connections = 2000
max_requests = 1000
max_requests_jitter = 50
timeout = 60
keepalive = 5
preload_app = True
Async Application Setup¶
Use Case: Async application using eventlet workers.
# gunicorn_async.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
os.environ.setdefault("GUNICORN_KEEPALIVE", "5")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusEventletWorker"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 100
Production Setup¶
Use Case: Production environment with security and reliability.
# gunicorn_production.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/var/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "127.0.0.1") # Bind to localhost only
os.environ.setdefault("GUNICORN_WORKERS", "4")
os.environ.setdefault("GUNICORN_KEEPALIVE", "5")
os.environ.setdefault("CLEANUP_DB_FILES", "true")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 60
keepalive = 5
preload_app = True
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
Redis Storage Setup¶
Use Case: Distributed setup with Redis storage (no files created).
# gunicorn_redis_storage.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9092") # Different port for Redis storage
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Redis storage configuration (no files created)
os.environ.setdefault("REDIS_ENABLED", "true")
os.environ.setdefault("REDIS_HOST", "localhost")
os.environ.setdefault("REDIS_PORT", "6379")
os.environ.setdefault("REDIS_DB", "0")
os.environ.setdefault("REDIS_PASSWORD", "")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 60
keepalive = 5
Development Setup¶
Use Case: Development environment with debugging enabled.
# gunicorn_development.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "2")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 2
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
reload = True
reload_extra_files = ["app.py", "config.py"]
accesslog = "-"
errorlog = "-"
loglevel = "debug"
Worker Type Configurations¶
Sync Worker¶
# gunicorn_sync.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
Best for: CPU-bound applications, simple setups.
Thread Worker¶
# gunicorn_thread.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "2")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 2
worker_class = "gunicorn_prometheus_exporter.PrometheusThreadWorker"
threads = 4
Best for: I/O-bound applications, better concurrency.
Eventlet Worker¶
# gunicorn_eventlet.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusEventletWorker"
worker_connections = 1000
Best for: Async applications, high concurrency.
Gevent Worker¶
# gunicorn_gevent.conf.py
import os
# Environment variables must be set before imports
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusGeventWorker"
worker_connections = 1000
Best for: Async applications, high concurrency.
Advanced Configuration¶
Hooks Architecture¶
The exporter uses a modular, class-based hooks architecture for managing Gunicorn lifecycle events:
Manager Classes¶
HookManager
: Centralized logging and execution managementEnvironmentManager
: CLI-to-environment variable mappingMetricsServerManager
: Prometheus server lifecycle with retry logicWorkerManager
: Worker metrics and graceful shutdownProcessManager
: Process cleanup and termination
Benefits¶
- Modular Design: Each responsibility isolated in its own manager
- Lazy Initialization: Managers created on-demand to avoid import issues
- Enhanced Error Handling: Comprehensive exception handling with fallbacks
- Better Testability: Each manager can be tested independently
- Backward Compatible: All existing hook functions continue to work
Custom Hooks¶
Basic Custom Hooks¶
# gunicorn_custom_hooks.conf.py
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
import os
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
os.environ.setdefault("GUNICORN_WORKERS", "4")
# Custom hooks
def on_starting(server):
"""Custom startup hook."""
print("Starting Gunicorn with Prometheus exporter...")
def when_ready(server):
"""Custom ready hook."""
from gunicorn_prometheus_exporter.hooks import default_when_ready
default_when_ready(server)
print("Gunicorn is ready to accept connections")
def worker_int(worker):
"""Custom worker initialization hook."""
from gunicorn_prometheus_exporter.hooks import default_worker_int
default_worker_int(worker)
print(f"Worker {worker.pid} initialized")
def on_exit(server):
"""Custom exit hook."""
from gunicorn_prometheus_exporter.hooks import default_on_exit
default_on_exit(server)
print("Gunicorn shutting down...")
Advanced Custom Hooks with Managers¶
# gunicorn_advanced_hooks.conf.py
import os
import logging
# Environment variables (set before imports)
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
from gunicorn_prometheus_exporter.hooks import (
HookContext,
HookManager,
EnvironmentManager,
MetricsServerManager,
WorkerManager,
ProcessManager,
default_on_starting,
default_when_ready,
default_worker_int,
default_on_exit,
default_post_fork,
)
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
timeout = 300
# Custom hook with manager usage
def custom_when_ready(server):
"""Custom when_ready hook using managers directly."""
logger = logging.getLogger(__name__)
# Create context
context = HookContext(server=server, logger=logger)
# Use metrics manager for custom setup
metrics_manager = MetricsServerManager(logger)
result = metrics_manager.setup_server()
if result:
port, registry = result
logger.info(f"Custom metrics server setup on port {port}")
# Start with custom retry logic
if metrics_manager.start_server(port, registry):
logger.info("Custom metrics server started successfully")
else:
logger.error("Custom metrics server failed to start")
else:
logger.warning("Custom metrics server setup failed")
# Use custom hooks
when_ready = custom_when_ready
on_starting = default_on_starting
worker_int = default_worker_int
on_exit = default_on_exit
post_fork = default_post_fork
CLI Options and Post-Fork Hook¶
Some Gunicorn CLI options like --timeout
, --workers
, --bind
, and --worker-class
do not automatically populate environment variables. The post_fork
hook ensures these CLI options are properly configured and consistent with environment-based settings.
Why use post_fork hook for CLI options:¶
- CLI options like
--timeout
don't automatically set environment variables - The post_fork hook runs after each worker is forked and can access Gunicorn's configuration
- It ensures consistency between CLI and environment-based configuration
- It logs worker-specific configuration for debugging
- Uses
EnvironmentManager
to handle CLI-to-environment mapping
What the post_fork hook does:¶
- Configuration Logging: Logs detailed worker configuration for debugging
- Environment Updates: Updates environment variables with CLI values
- CLI Option Detection: Detects and processes CLI options like:
--workers
: Number of worker processes--bind
: Bind address and port--worker-class
: Worker class to use- Consistency Check: Ensures CLI and environment settings are consistent
Example with post_fork hook:¶
# gunicorn_with_cli.conf.py
import os
# Environment variables (set before imports)
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", "/tmp/prometheus_multiproc")
os.environ.setdefault("PROMETHEUS_METRICS_PORT", "9090")
os.environ.setdefault("PROMETHEUS_BIND_ADDRESS", "0.0.0.0")
from gunicorn_prometheus_exporter.hooks import (
default_on_starting,
default_when_ready,
default_worker_int,
default_on_exit,
default_post_fork,
)
# Gunicorn settings
bind = "0.0.0.0:8000"
workers = 4
worker_class = "gunicorn_prometheus_exporter.PrometheusWorker"
timeout = 300
# Use pre-built hooks including post_fork
when_ready = default_when_ready
on_starting = default_on_starting
worker_int = default_worker_int
on_exit = default_on_exit
post_fork = default_post_fork # Configure CLI options after worker fork
CLI usage with post_fork hook:¶
# Override timeout from CLI
gunicorn -c gunicorn_with_cli.conf.py app:app --timeout 600
# Override workers from CLI
gunicorn -c gunicorn_with_cli.conf.py app:app --workers 8
# Override bind address from CLI
gunicorn -c gunicorn_with_cli.conf.py app:app --bind 0.0.0.0:9000
# Override worker class from CLI
gunicorn -c gunicorn_with_cli.conf.py app:app --worker-class gunicorn_prometheus_exporter.PrometheusWorker
Environment Variable Updates¶
The post_fork hook automatically updates these environment variables based on CLI options:
# CLI: --workers 8
# Sets: GUNICORN_WORKERS=8
# CLI: --bind 0.0.0.0:9000
# Sets: GUNICORN_BIND=0.0.0.0:9000
# CLI: --worker-class gunicorn_prometheus_exporter.PrometheusWorker
# Sets: GUNICORN_WORKER_CLASS=gunicorn_prometheus_exporter.PrometheusWorker
Debugging Hooks¶
Enable Debug Logging¶
# gunicorn_debug.conf.py
import logging
# Enable debug logging for hooks
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# ... rest of configuration
Hook Debugging Features¶
The hooks system provides detailed logging for debugging:
- Configuration Updates: Logs when environment variables are updated
- Server Lifecycle: Logs server startup, ready, and shutdown events
- Worker Events: Logs worker initialization and shutdown
- Error Conditions: Logs errors with detailed context
- Recovery Actions: Logs fallback and recovery attempts
Example Debug Output¶
2024-01-15 10:30:00 - gunicorn_prometheus_exporter.hooks - INFO - Server starting - initializing PrometheusMaster metrics
2024-01-15 10:30:01 - gunicorn_prometheus_exporter.hooks - INFO - Updated GUNICORN_WORKERS from CLI: 4
2024-01-15 10:30:01 - gunicorn_prometheus_exporter.hooks - INFO - Updated GUNICORN_BIND from CLI: 0.0.0.0:8000
2024-01-15 10:30:02 - gunicorn_prometheus_exporter.hooks - INFO - Starting Prometheus multiprocess metrics server on :9090
2024-01-15 10:30:02 - gunicorn_prometheus_exporter.hooks - INFO - Metrics server started successfully on port 9090
2024-01-15 10:30:03 - gunicorn_prometheus_exporter.hooks - INFO - Worker 12345 received interrupt signal
2024-01-15 10:30:03 - gunicorn_prometheus_exporter.hooks - INFO - Server shutting down - cleaning up Prometheus metrics server
Error Handling¶
Graceful Degradation¶
All hooks include comprehensive error handling:
# Example of error handling in custom hooks
def custom_hook(server):
try:
# Your custom logic
pass
except Exception as e:
logger.error(f"Custom hook failed: {e}")
# Continue with fallback behavior
Common Error Scenarios¶
- Missing Configuration: Hooks handle missing environment variables gracefully
- Port Conflicts: Metrics server retry logic handles port conflicts
- Process Cleanup: Comprehensive cleanup handles orphaned processes
- Worker Shutdown: Graceful shutdown with fallback mechanisms
Recovery Mechanisms¶
- Lazy Initialization: Prevents import-time configuration issues
- Retry Logic: Automatic retry for transient failures
- Fallback Options: Alternative approaches when primary methods fail
- Timeout Handling: Prevents hanging operations