Skip to content

feat(proxy): add LITELLM_DISABLE_ACCESS_LOG_PATHS to drop noisy access logs#30818

Merged
Sameerlite merged 2 commits into
BerriAI:litellm_oss_staging_230626from
hdt12a1:feat/disable-access-log-paths
Jun 23, 2026
Merged

feat(proxy): add LITELLM_DISABLE_ACCESS_LOG_PATHS to drop noisy access logs#30818
Sameerlite merged 2 commits into
BerriAI:litellm_oss_staging_230626from
hdt12a1:feat/disable-access-log-paths

Conversation

@hdt12a1

@hdt12a1 hdt12a1 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Dependencies

Targets litellm_oss_branch (external-contributor branch required by the "Verify PR source branch" guard; PRs from forks to main are rejected).

Docs for the new env var live in the separate BerriAI/litellm-docs repo, which the documentation and code-quality jobs check out into docs/my-website. The matching row is in BerriAI/litellm-docs#378; that needs to merge first, otherwise tests/documentation_tests/test_env_keys.py fails with Keys 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

  • I have added meaningful tests
  • My PR passes all unit tests on make test-unit (4/4 new tests pass; targeted: pytest tests/test_litellm/test_logging.py -k healthcheck)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Before — k8s probes flood the proxy log (multiple lines per second), drowning real traffic:

{"message": "10.82.13.64:58658 - \"GET /health/readiness HTTP/1.1\" 200", ...}
{"message": "10.82.13.30:10944 - \"GET / HTTP/1.1\" 200", ...}
{"message": "10.82.13.140:38093 - \"GET / HTTP/1.1\" 200", ...}
{"message": "10.82.13.64:60478 - \"GET /health/liveliness HTTP/1.1\" 200", ...}
{"message": "100.65.52.208:48838 - \"GET /metrics/ HTTP/1.1\" 401", ...}

After — with LITELLM_DISABLE_ACCESS_LOG_PATHS="/,/health/liveliness,/health/readiness,/metrics/", only real traffic is logged:

{"message": "10.0.0.1:1 - \"GET /v1/chat/completions HTTP/1.1\" 200", ...}

Other application/error logs are untouched.

Tests:

$ uv run pytest tests/test_litellm/test_logging.py -k healthcheck -x -q
....                                                                     [100%]
4 passed, 10 deselected in 0.16s

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 whose uvicorn.access log 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.Filter subclass) inspects record.args — uvicorn's AccessFormatter passes (client_addr, method, full_path, http_version, status) — and returns False for any record whose path (with query string stripped) is in _DISABLED_ACCESS_LOG_PATHS. The filter is robust to malformed records (returns True on missing/short args).

It is wired in two places:

  1. _get_uvicorn_json_log_config() — the JSON log_config used when JSON_LOGS=true now declares the filter under filters: and binds it on the access handler via dictConfig's native filter list.
  2. _suppress_loggers() — for the non-JSON path, the filter is attached directly to logging.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.access only — application logs (LiteLLM, LiteLLM Proxy, LiteLLM Router) and uvicorn.error are untouched.
  • Path matching is exact (after ? stripping). No prefix/glob matching, to keep the surface small and the behaviour obvious.
  • Behind an env var so it is opt-in and trivially disabled by clearing the var.

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

# k8s deployment
env:
  - name: LITELLM_DISABLE_ACCESS_LOG_PATHS
    value: "/,/health/liveliness,/health/readiness,/metrics/"

@CLAassistant

CLAassistant commented Jun 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed-hq

codspeed-hq Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing hdt12a1:feat/disable-access-log-paths (787565f) with main (343e453)

Open in CodSpeed

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/litellm_core_utils/litellm_logging.py 54.54% 5 Missing ⚠️
litellm/_logging.py 84.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an opt-in LITELLM_DISABLE_ACCESS_LOG_PATHS env var that accepts a comma-separated list of exact request paths; any matching uvicorn.access log line is silently dropped, leaving application/error logs untouched.

  • A new HealthCheckAccessLogFilter (logging.Filter subclass) reads paths from the module-level _DISABLED_ACCESS_LOG_PATHS frozenset (evaluated at import time) and drops records whose path (query-string stripped) is in the set. It degrades gracefully on malformed args.
  • The filter is wired into both the non-JSON path (via _suppress_loggers()) and the JSON path (via a conditional filters entry in the dictConfig returned by _get_uvicorn_json_log_config()), correctly guarded by if _DISABLED_ACCESS_LOG_PATHS in both places, addressing the previous review comment. Four new unit tests cover env-unset pass-through, path matching, query-string stripping, and malformed-args robustness — all mocked, no network calls.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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

Comment thread litellm/_logging.py Outdated
@hdt12a1 hdt12a1 changed the base branch from main to litellm_oss_branch June 20, 2026 03:56
@hdt12a1 hdt12a1 force-pushed the feat/disable-access-log-paths branch from 787565f to 92ea6d1 Compare June 20, 2026 04:02
@Sameerlite

Copy link
Copy Markdown
Collaborator

can you rebase it to "litellm_internal_staging"?

@Sameerlite

Copy link
Copy Markdown
Collaborator

Also please get the score to 5/5

Huynh Duc Tran and others added 2 commits June 22, 2026 22:47
…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
@hdt12a1 hdt12a1 force-pushed the feat/disable-access-log-paths branch from 92ea6d1 to 7df0ca6 Compare June 22, 2026 15:51
@hdt12a1 hdt12a1 changed the base branch from litellm_oss_branch to litellm_internal_staging June 22, 2026 15:51
@hdt12a1

hdt12a1 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Done on both. Rebased onto litellm_internal_staging (also retargeted the PR base to it), and addressed the Greptile P2 by guarding the JSON log config the same way _suppress_loggers() does: the healthcheck filter and its access-handler attachment are now only wired when LITELLM_DISABLE_ACCESS_LOG_PATHS is set, so JSON_LOGS=true users who haven't opted in get no extra dictConfig machinery.

Hi @Sameerlite , please help me review again. Thanks!

@hdt12a1

hdt12a1 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@Sameerlite Sameerlite changed the base branch from litellm_internal_staging to litellm_oss_staging_230626 June 23, 2026 12:46
@Sameerlite Sameerlite merged commit 458b044 into BerriAI:litellm_oss_staging_230626 Jun 23, 2026
3 checks passed
Sameerlite pushed a commit that referenced this pull request Jun 24, 2026
…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>
mubashir1osmani pushed a commit to BerriAI/litellm-docs that referenced this pull request Jun 27, 2026
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
Sameerlite pushed a commit that referenced this pull request Jun 29, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants