Skip to content

fix(azure_sentinel): resolve audit stream from AZURE_SENTINEL_AUDIT_STREAM_NAME - #32010

Merged
yucheng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_lit_4174_azure_sentinel_audit_stream
Jul 3, 2026
Merged

fix(azure_sentinel): resolve audit stream from AZURE_SENTINEL_AUDIT_STREAM_NAME#32010
yucheng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_lit_4174_azure_sentinel_audit_stream

Conversation

@yucheng-berri

@yucheng-berri yucheng-berri commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Resolves LIT-4174

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 CI/CD checks (e.g., lint, format, unit tests)
  • 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

AzureSentinelLogger already supports an audit_stream_name so audit logs can target a separate DCR stream, but when the logger is resolved from the string callback name azure_sentinel it is constructed with no arguments, so audit_stream_name was always None and fell back to the standard AZURE_SENTINEL_STREAM_NAME. Audit events then land on the access-log DCR stream, whose schema is built from StandardLoggingPayload, and Azure Monitor Logs Ingestion silently drops the undeclared audit columns, so audit rows arrive with only TimeGenerated.

This was verified end to end against a real Azure Monitor Logs Ingestion workspace: a real Data Collection Endpoint, one Data Collection Rule with two streams (an access-log stream Custom-LiteLLM_CL and a dedicated audit stream Custom-LiteLLM_Audit_CL, each mapped to its own custom table), and a service principal granted Monitoring Metrics Publisher on the DCR. A live proxy was run with the ticket's config, a real admin action created a real audit log, and the rows were queried back from the workspace. Audit-log generation is an enterprise feature so the license check was satisfied for the run; the AzureSentinelLogger code under test is unmodified.

Config (the exact setup from the ticket):

litellm_settings:
  store_audit_logs: true
  callbacks: ["azure_sentinel"]
  audit_log_callbacks: ["azure_sentinel"]
AZURE_SENTINEL_STREAM_NAME="Custom-LiteLLM_CL"             # access-log stream
AZURE_SENTINEL_AUDIT_STREAM_NAME="Custom-LiteLLM_Audit_CL" # dedicated audit stream
AZURE_SENTINEL_DCR_IMMUTABLE_ID / ENDPOINT / TENANT_ID / CLIENT_ID / CLIENT_SECRET=...

Trigger (a real admin action that produces a real audit log):

curl -sS http://localhost:4010/team/new \
  -H "Authorization: Bearer sk-..." -H "Content-Type: application/json" \
  -d '{"team_alias":"lit4174-demo"}'

Before the fix, with AZURE_SENTINEL_AUDIT_STREAM_NAME set, the audit event ingests into the access-log table LiteLLM_CL, which has no audit columns, so everything except TimeGenerated is dropped:

// KQL: LiteLLM_CL | project TimeGenerated, id, call_type, model, status
TimeGenerated                 Call_type   Model   Status
2026-07-03T18:53:10.6733165Z  (empty)     (empty) (empty)

// KQL: LiteLLM_Audit_CL | count  ->  unchanged; the audit event never arrived here

After the fix, the same action with the same environment ingests into the dedicated audit table LiteLLM_Audit_CL with every audit column populated:

// KQL: LiteLLM_Audit_CL | project TimeGenerated, action, table_name, object_id, changed_by
TimeGenerated                 action    table_name          object_id                              changed_by
2026-07-03T18:21:30.9233039Z  created   LiteLLM_TeamTable   645386d8-0029-4055-a616-f9401802f626   default_user_id
2026-07-03T18:21:30.9233039Z  created   LiteLLM_TeamTable   3b68857f-c9a5-49a6-be84-72afd54a9c7a   default_user_id
2026-07-03T18:20:30.6118873Z  created   LiteLLM_TeamTable   4c7a5511-dda5-4c44-8a71-00cd6fe7bd69   default_user_id

// KQL: LiteLLM_CL | count  ->  0; audit events did not leak into the access-log table

The object_id values are the team IDs returned by the create-team calls, confirming these rows are the events those requests produced. Live proxy log for one flush: Azure Sentinel - about to flush 1 audit logs then Azure Sentinel: Response from API status_code: 204.

The standard access-log endpoint is unchanged; api_endpoint and audit_api_endpoint are built independently in __init__, so the fix only affects the audit stream

Type

🐛 Bug Fix

Changes

AzureSentinelLogger.__init__ now resolves the audit stream as audit_stream_name or os.getenv("AZURE_SENTINEL_AUDIT_STREAM_NAME") or resolved_stream_name, mirroring the AZURE_SENTINEL_STREAM_NAME fallback already used for the standard stream. Because every string-callback construction site (_init_custom_logger_compatible_class for both callbacks and audit_log_callbacks) builds the logger with no arguments, this single change lets audit logs target a separate DCR stream without a custom callbacks file. A regression test constructs the logger with no audit_stream_name and only the env var set, and asserts the resolved audit endpoint uses the env var stream while the standard endpoint keeps the access-log stream

CI note

The AZURE_SENTINEL_AUDIT_STREAM_NAME env var is documented in the separate env-settings reference via BerriAI/litellm-docs#477 (merged), which the documentation and code-quality checks validate


Note

Low Risk
Narrow logging-callback configuration fix with tests; no auth or core proxy request-path changes.

Overview
Fixes audit logs being ingested into the standard DCR stream when the proxy registers azure_sentinel as a string callback with no constructor args.

AzureSentinelLogger now resolves the audit stream as audit_stream_nameAZURE_SENTINEL_AUDIT_STREAM_NAME → standard stream (same pattern as AZURE_SENTINEL_STREAM_NAME). That updates audit_api_endpoint only; access-log ingestion is unchanged. Docstring reflects the new env var.

A regression test asserts env-based resolution splits audit vs standard endpoints, and that an explicit audit_stream_name still overrides the env var.

Reviewed by Cursor Bugbot for commit 460600c. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a one-line bug in AzureSentinelLogger.__init__ where audit_stream_name was never resolved from the AZURE_SENTINEL_AUDIT_STREAM_NAME environment variable, causing audit logs to be silently ingested into the standard access-log DCR stream instead of the dedicated audit stream.

  • Core fix (azure_sentinel.py): resolved_audit_stream_name now follows the same three-tier resolution pattern (argument → env var → standard stream fallback) already used for the standard stream, so string-callback constructions (which pass no arguments) correctly pick up the env var.
  • New test (test_azure_sentinel.py): Adds a mock-only pytest.mark.asyncio test that sets both env vars via monkeypatch and asserts the resulting audit_api_endpoint and api_endpoint target separate streams, plus a second case verifying an explicit constructor argument takes precedence.

Confidence Score: 5/5

Safe to merge — the change is a single-line addition that fills a missing env-var lookup, matches the existing resolution pattern used for every other Azure Sentinel config parameter, and is covered by a new mock test.

The fix is minimal and isolated: one new os.getenv call inserted into an already-established fallback chain inside init. No logic is removed or restructured, no other callers are affected, and the new test exercises both the env-var path and the explicit-argument override path without making real network calls.

No files require special attention.

Important Files Changed

Filename Overview
litellm/integrations/azure_sentinel/azure_sentinel.py Adds os.getenv("AZURE_SENTINEL_AUDIT_STREAM_NAME") as a fallback in the audit stream resolution chain, mirroring the existing pattern used for the standard stream — minimal, correct one-liner fix.
tests/test_litellm/integrations/test_azure_sentinel.py Adds a focused mock-only test that uses monkeypatch.setenv to validate both env-var resolution and explicit-argument override — no real network calls, assertions cover the exact endpoints that were broken.

Reviews (2): Last reviewed commit: "fix(azure_sentinel): resolve audit strea..." | Re-trigger Greptile

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…TREAM_NAME

When AzureSentinelLogger is resolved from the string callback name
"azure_sentinel", it is constructed with no arguments, so audit_stream_name is
always None and resolved_audit_stream_name fell back to the standard
resolved_stream_name. Audit logs then ingested into the access-log DCR stream
whose schema is built from StandardLoggingPayload, so Azure Monitor Logs
Ingestion silently dropped the audit-specific columns and audit rows arrived
effectively empty.

Add an AZURE_SENTINEL_AUDIT_STREAM_NAME env var fallback in __init__, mirroring
the AZURE_SENTINEL_STREAM_NAME idiom already used for the standard stream, so
audit logs can target a separate DCR stream without a custom callbacks file.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit_4174_azure_sentinel_audit_stream branch from 4e9a9f8 to 460600c Compare July 3, 2026 01:27
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 460600c. Configure here.

@yucheng-berri
yucheng-berri merged commit 93cdcca into litellm_internal_staging Jul 3, 2026
126 of 128 checks passed
@yucheng-berri
yucheng-berri deleted the litellm_lit_4174_azure_sentinel_audit_stream branch July 3, 2026 19:09
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.

2 participants