# Skipping health/metrics endpoints (noise reduction) Health checks and readiness probes run every few seconds. With 12 replicas and a 5-second interval, that's 17,000 log entries per day per endpoint—before a single real request. This noise buries actual traffic and inflates log storage costs. fapilog's path filtering skips these endpoints while logging everything else. ## The Problem A typical Kubernetes deployment generates massive log volume from health checks: ``` # Per instance, per endpoint 1 check × 12 checks/minute × 60 minutes × 24 hours = 17,280 logs/day # With 10 instances and 3 health endpoints (/health, /ready, /metrics) 17,280 × 10 × 3 = 518,400 logs/day ``` These logs are identical, contain no useful information, and obscure the logs that matter—actual user requests, errors, and business events. ## The Solution Skip health endpoints with `skip_paths()`: ```python from fastapi import FastAPI from fapilog.fastapi import FastAPIBuilder app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths([ "/health", "/healthz", "/ready", "/readiness", "/metrics", "/ping", ]) .build() ) ``` Requests to these paths are processed normally—they just don't generate log entries. ## Common Patterns to Skip | Pattern | Used By | Purpose | |---------|---------|---------| | `/health`, `/healthz` | Kubernetes, AWS ALB | Liveness checks | | `/ready`, `/readiness` | Kubernetes | Readiness probes | | `/metrics` | Prometheus | Metrics scraping | | `/ping` | Load balancers, uptime monitors | Basic availability | | `/livez` | Kubernetes (newer) | Liveness (alternative) | ### Kubernetes-Focused Setup ```python app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths(["/health", "/healthz", "/ready", "/readiness", "/livez"]) .build() ) ``` ### AWS / Generic Load Balancer Setup ```python app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths(["/health", "/ping", "/_health"]) .build() ) ``` ## Pattern Matching Syntax `skip_paths` uses exact string matching: | Path in Request | `skip_paths=["/health"]` | Logged? | |-----------------|--------------------------|---------| | `/health` | Match | No | | `/health/` | No match | Yes | | `/health/db` | No match | Yes | | `/healthz` | No match | Yes | | `/api/health` | No match | Yes | This means: - **Case-sensitive**: `/Health` and `/health` are different - **No wildcards**: `/health/*` won't match anything (it's a literal string) - **No regex**: Use exact paths only - **Trailing slashes matter**: Include both `/health` and `/health/` if your API accepts both ### Complete Health Check Coverage If your endpoints accept trailing slashes or subpaths, list each variant: ```python app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths([ "/health", "/health/", "/ready", "/ready/", "/metrics", ]) .build() ) ``` ## Manual Middleware Configuration If you need more control, configure the middleware directly: ```python from fastapi import FastAPI from fapilog.fastapi.logging import LoggingMiddleware from fapilog.fastapi.context import RequestContextMiddleware app = FastAPI() app.add_middleware(RequestContextMiddleware) app.add_middleware( LoggingMiddleware, skip_paths=["/health", "/metrics"], ) ``` This gives identical behavior to `FastAPIBuilder().skip_paths(...)`. ## Impact on Log Volume Skipping health endpoints typically reduces log volume by 50-90%, depending on: - **Check frequency**: 5-second intervals generate more than 30-second intervals - **Number of instances**: More replicas = more health checks - **Number of health endpoints**: Some setups have 3-5 different health paths - **Actual traffic**: Low-traffic services see the biggest percentage reduction ### Before and After | Metric | Before | After (with skip_paths) | |--------|--------|------------------------| | Logs per day (10 instances) | 520,000 | 52,000 | | Storage cost | $15/month | $1.50/month | | Time to find errors | Minutes | Seconds | The real benefit isn't just cost—it's signal-to-noise ratio. When health checks are 90% of your logs, finding an actual error requires wading through noise. ## Errors on Skipped Paths By default, unhandled exceptions on skipped paths are still logged at ERROR level. This ensures visibility into crashes even on health endpoints: ```python @app.get("/health") async def health(): raise RuntimeError("Database connection failed") # Logged as request_failed ``` When the handler crashes, you'll see `request_failed` logs for `/health` even though successful health checks are skipped. **Note:** HTTPExceptions (like `HTTPException(status_code=503)`) are not logged because they're handled by FastAPI's exception handler and converted to responses—they're expected error responses, not crashes. ### Opting Out If you need complete silence on skipped paths (no logs at all, even for crashes), use `log_errors_on_skip(False)`: ```python app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths(["/health", "/metrics"]) .log_errors_on_skip(False) # Complete silence on skipped paths .build() ) ``` This is useful when health endpoints are monitored externally and you don't want any fapilog log entries for them. ## Combining with Sampling For high-traffic endpoints that aren't health checks, combine path filtering with sampling: ```python app = FastAPI( lifespan=FastAPIBuilder() .with_preset("fastapi") .skip_paths(["/health", "/metrics"]) # Skip entirely .sample_rate(0.1) # Log 10% of other requests .build() ) ``` This setup: - Never logs `/health` or `/metrics` - Logs 10% of all other successful requests - Always logs errors (sample_rate doesn't affect error logs) ## Going Deeper - [FastAPI Integration Guide](../user-guide/fastapi.md) - Complete middleware options - [Log Sampling and Rate Limiting](log-sampling-rate-limiting.md) - Sampling and rate limiting for traffic spikes - [Why Fapilog?](../why-fapilog.md) - How fapilog compares to other logging libraries