feat(proxy): add LITELLM_DISABLE_ACCESS_LOG_PATHS to drop noisy access logs#30818
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR adds an opt-in
Confidence Score: 5/5Safe to merge — the change is purely additive, opt-in via env var, and the default path (env var unset) is a no-op short-circuit with no effect on existing behaviour. The filter is a narrow, well-guarded addition to the logging layer. It touches no request-handling code, introduces no new database or network calls, and the opt-in env var ensures zero impact for users who do not set it. Both wiring points (non-JSON and JSON log paths) are correctly gated, addressing the earlier review concern. Tests are thorough and fully mocked. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/_logging.py | Adds _parse_disabled_access_log_paths(), HealthCheckAccessLogFilter, and wires the filter into both the non-JSON (_suppress_loggers) and JSON (_get_uvicorn_json_log_config) logging paths, correctly gated on _DISABLED_ACCESS_LOG_PATHS. |
| tests/test_litellm/test_logging.py | Adds four well-scoped unit tests for the new filter; all use monkeypatch to avoid touching the real env var or making network calls, consistent with the repository's testing policy. |
Reviews (2): Last reviewed commit: "fix(proxy): only wire healthcheck filter..." | Re-trigger Greptile
787565f to
92ea6d1
Compare
|
can you rebase it to "litellm_internal_staging"? |
|
Also please get the score to 5/5 |
…s logs
Adds a `HealthCheckAccessLogFilter` to `litellm._logging` that drops
`uvicorn.access` records whose request path matches a comma-separated
list in the new `LITELLM_DISABLE_ACCESS_LOG_PATHS` env var. The filter
is wired into both:
- the JSON `log_config` produced by `_get_uvicorn_json_log_config()`
(used when `JSON_LOGS=true`), via `dictConfig`'s native filter
binding on the access handler, and
- the plain `uvicorn.access` logger at module import (covers the
non-JSON code path).
This is useful when the proxy runs behind k8s liveness/readiness
probes, ALB health pings, or Prometheus `/metrics/` scrapes that
otherwise drown real request logs at a multi-line-per-second rate.
Example:
LITELLM_DISABLE_ACCESS_LOG_PATHS="/,/health/liveliness,/health/readiness,/metrics/"
Path matching is exact (after stripping any query string) and only
applies to the `uvicorn.access` logger -- application logs and
`uvicorn.error` are untouched.
Default behaviour is unchanged: when the env var is empty/unset the
filter short-circuits and all access lines are emitted.
Tests cover env-unset pass-through, configured-path drops, query
string stripping, and robustness to malformed log records.
…s configured Mirror the _suppress_loggers() guard so JSON_LOGS=true users who have not set LITELLM_DISABLE_ACCESS_LOG_PATHS no longer get unused dictConfig filter machinery
92ea6d1 to
7df0ca6
Compare
|
Done on both. Rebased onto Hi @Sameerlite , please help me review again. Thanks! |
458b044
into
BerriAI:litellm_oss_staging_230626
…s logs (#30818) * feat(proxy): add LITELLM_DISABLE_ACCESS_LOG_PATHS to drop noisy access logs Adds a `HealthCheckAccessLogFilter` to `litellm._logging` that drops `uvicorn.access` records whose request path matches a comma-separated list in the new `LITELLM_DISABLE_ACCESS_LOG_PATHS` env var. The filter is wired into both: - the JSON `log_config` produced by `_get_uvicorn_json_log_config()` (used when `JSON_LOGS=true`), via `dictConfig`'s native filter binding on the access handler, and - the plain `uvicorn.access` logger at module import (covers the non-JSON code path). This is useful when the proxy runs behind k8s liveness/readiness probes, ALB health pings, or Prometheus `/metrics/` scrapes that otherwise drown real request logs at a multi-line-per-second rate. Example: LITELLM_DISABLE_ACCESS_LOG_PATHS="/,/health/liveliness,/health/readiness,/metrics/" Path matching is exact (after stripping any query string) and only applies to the `uvicorn.access` logger -- application logs and `uvicorn.error` are untouched. Default behaviour is unchanged: when the env var is empty/unset the filter short-circuits and all access lines are emitted. Tests cover env-unset pass-through, configured-path drops, query string stripping, and robustness to malformed log records. * fix(proxy): only wire healthcheck filter in JSON log config when paths configured Mirror the _suppress_loggers() guard so JSON_LOGS=true users who have not set LITELLM_DISABLE_ACCESS_LOG_PATHS no longer get unused dictConfig filter machinery --------- Co-authored-by: Huynh Duc Tran <ducth6@tcbs.com.vn>
Adds the missing reference-table row for LITELLM_DISABLE_ACCESS_LOG_PATHS, the env var introduced in BerriAI/litellm#30818 (litellm/_logging.py). The litellm documentation_test_env_keys check fails on every PR until this var is documented here, since it scans litellm source for os.getenv keys and asserts each is present in this table. Claude-Session: https://claude.ai/code/session_01BXVrMmGpFwevwQGHQeRRG9
…s logs (#30818) * feat(proxy): add LITELLM_DISABLE_ACCESS_LOG_PATHS to drop noisy access logs Adds a `HealthCheckAccessLogFilter` to `litellm._logging` that drops `uvicorn.access` records whose request path matches a comma-separated list in the new `LITELLM_DISABLE_ACCESS_LOG_PATHS` env var. The filter is wired into both: - the JSON `log_config` produced by `_get_uvicorn_json_log_config()` (used when `JSON_LOGS=true`), via `dictConfig`'s native filter binding on the access handler, and - the plain `uvicorn.access` logger at module import (covers the non-JSON code path). This is useful when the proxy runs behind k8s liveness/readiness probes, ALB health pings, or Prometheus `/metrics/` scrapes that otherwise drown real request logs at a multi-line-per-second rate. Example: LITELLM_DISABLE_ACCESS_LOG_PATHS="/,/health/liveliness,/health/readiness,/metrics/" Path matching is exact (after stripping any query string) and only applies to the `uvicorn.access` logger -- application logs and `uvicorn.error` are untouched. Default behaviour is unchanged: when the env var is empty/unset the filter short-circuits and all access lines are emitted. Tests cover env-unset pass-through, configured-path drops, query string stripping, and robustness to malformed log records. * fix(proxy): only wire healthcheck filter in JSON log config when paths configured Mirror the _suppress_loggers() guard so JSON_LOGS=true users who have not set LITELLM_DISABLE_ACCESS_LOG_PATHS no longer get unused dictConfig filter machinery --------- Co-authored-by: Huynh Duc Tran <ducth6@tcbs.com.vn>
Relevant issues
Dependencies
Targets
litellm_oss_branch(external-contributor branch required by the "Verify PR source branch" guard; PRs from forks tomainare rejected).Docs for the new env var live in the separate
BerriAI/litellm-docsrepo, which thedocumentationandcode-qualityjobs check out intodocs/my-website. The matching row is in BerriAI/litellm-docs#378; that needs to merge first, otherwisetests/documentation_tests/test_env_keys.pyfails withKeys not documented in 'environment settings - Reference': {'LITELLM_DISABLE_ACCESS_LOG_PATHS'}.Linear ticket
N/A — external contributor.
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
make test-unit(4/4 new tests pass; targeted:pytest tests/test_litellm/test_logging.py -k healthcheck)@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / Proof of Fix
Before — k8s probes flood the proxy log (multiple lines per second), drowning real traffic:
After — with
LITELLM_DISABLE_ACCESS_LOG_PATHS="/,/health/liveliness,/health/readiness,/metrics/", only real traffic is logged:Other application/error logs are untouched.
Tests:
Type
🆕 New Feature
Changes
Adds an opt-in env var,
LITELLM_DISABLE_ACCESS_LOG_PATHS, that takes a comma-separated list of exact request paths whoseuvicorn.accesslog lines should be dropped. Default behaviour is unchanged: when the env var is empty/unset, the filter short-circuits and all access lines are emitted as before.Why
When the proxy runs behind kubernetes liveness/readiness probes, an ALB/NLB doing root-path health checks, and Prometheus scraping
/metrics/, the access log fills with thousands of probe lines per minute. Real request lines become hard to find without external log-pipeline filtering, and the volume meaningfully inflates ingestion cost on shared log backends.How
A new
HealthCheckAccessLogFilter(logging.Filtersubclass) inspectsrecord.args— uvicorn'sAccessFormatterpasses(client_addr, method, full_path, http_version, status)— and returnsFalsefor any record whose path (with query string stripped) is in_DISABLED_ACCESS_LOG_PATHS. The filter is robust to malformed records (returnsTrueon missing/short args).It is wired in two places:
_get_uvicorn_json_log_config()— the JSONlog_configused whenJSON_LOGS=truenow declares the filter underfilters:and binds it on theaccesshandler viadictConfig's native filter list._suppress_loggers()— for the non-JSON path, the filter is attached directly tologging.getLogger("uvicorn.access")at module import (only when the env var is non-empty, so there's no overhead for users who don't opt in).Scope
litellm.accessonly — application logs (LiteLLM,LiteLLM Proxy,LiteLLM Router) anduvicorn.errorare untouched.?stripping). No prefix/glob matching, to keep the surface small and the behaviour obvious.Files changed
litellm/_logging.py— new_parse_disabled_access_log_paths(),HealthCheckAccessLogFilter, wiring in_get_uvicorn_json_log_config()and_suppress_loggers().tests/test_litellm/test_logging.py— 4 new unit tests covering: env-unset pass-through, configured-path drops + non-matched paths still passing, query-string stripping before matching, and robustness to malformed args.Example usage