Repository navigation
fix: Add opt-in diagnostic logging for OData query validation failures - #1592
Conversation
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
There was a problem hiding this comment.
Pull request overview
This PR introduces opt-in structured diagnostic logging for OData query validation failures (notably invalid $select/$expand property access) and unifies the logging behavior across both controller-based (EnableQueryAttribute) and minimal API (ODataQueryEndpointFilter) pipelines, without changing HTTP responses or thrown exceptions.
Changes:
- Added
QueryValidationErrorLoggerand integrated it into bothEnableQueryAttributeandODataQueryEndpointFilterfailure paths behind opt-in configuration. - Introduced new configuration knobs for enabling the diagnostic and selecting its
LogLevel(ODataOptionsfor controllers,ODataMiniOptionsfor minimal APIs), plus public API surface updates. - Added unit + E2E coverage (including “logger throws” safety cases) and fixed an async test deadlock risk by removing
.Result.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.AspNetCore.OData.Tests/Query/EnableQueryAttributeTests.cs | Adds unit coverage for opt-in logging behavior, global vs per-attribute precedence, and safety when logging is unavailable/throws. |
| test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl | Updates public API baseline for newly exposed options. |
| test/Microsoft.AspNetCore.OData.Tests/Batch/DefaultODataBatchHandlerTest.cs | Replaces blocking .Result with await in an async test. |
| test/Microsoft.AspNetCore.OData.TestCommon/ThrowingLoggerProvider.cs | Adds a test logger provider that throws to validate “logging must not affect outcome” behavior. |
| test/Microsoft.AspNetCore.OData.TestCommon/CapturingLoggerProvider.cs | Adds a capturing provider to assert on structured logging fields in tests. |
| test/Microsoft.AspNetCore.OData.E2E.Tests/Query/QueryValidationErrorLogging/*.cs | Adds controller-pipeline E2E coverage for enabled/disabled/global/custom-level scenarios. |
| test/Microsoft.AspNetCore.OData.E2E.Tests/MinimalApis/*.cs | Adds minimal API E2E coverage for enabled/disabled/global/global-level and logger-failure scenarios. |
| src/Microsoft.AspNetCore.OData/Query/QueryValidationErrorLogger.cs | New shared structured logging helper for query validation failures. |
| src/Microsoft.AspNetCore.OData/Query/ODataQueryEndpointFilter.cs | Wraps validation to log (opt-in) on failure while preserving thrown exception behavior. |
| src/Microsoft.AspNetCore.OData/Query/EnableQueryAttribute.cs | Logs (opt-in) when producing bad request results from validation failures; captures processed options for post-action validation path. |
| src/Microsoft.AspNetCore.OData/Query/EnableQueryAttribute.Config.cs | Adds EnableQueryValidationErrorLogging attribute property with tri-state backing behavior. |
| src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt | Declares new public API members for the added options. |
| src/Microsoft.AspNetCore.OData/ODataOptions.cs | Adds global controller-pipeline configuration for enable + log level. |
| src/Microsoft.AspNetCore.OData/ODataMiniOptions.cs | Adds minimal API configuration for enable + log level and merges into UpdateFrom. |
| src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml | Updates XML docs to reflect new APIs and methods. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7a044a3 to
2bb5e33
Compare
|
/AzurePipelines run |
|
No pipelines are associated with this pull request. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07e2efd6-30b9-4867-9de3-44a8c60512b8
SendAsync_Works_ForBatchRequestWithInsertedEntityReferencedInAnotherRequest blocked on client.SendAsync(...).Result inside an async xUnit test. Under xUnit's AsyncTestSyncContext this can deadlock when a continuation resumes on the captured context, causing the ~3m47s CI hang that surfaced as TaskCanceledException. Await the call like every other request in the file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 93c49a7f-9734-49c7-ba83-073262b9b7f4
2bb5e33 to
23886d9
Compare
| // validation settings | ||
| private ODataValidationSettings _validationSettings; | ||
| private string _allowedOrderByProperties; | ||
| private bool? _enableQueryValidationErrorLogging; |
There was a problem hiding this comment.
can we use the ILogger directly?
I mean can we not use this configuration, instead, can we rely on customer's setting about the ILogger?
Fetch the certain 'ILogger' , if set, use it, otherwise, not log the error?
There was a problem hiding this comment.
Using ILogger directly is possible, but I kept the explicit opt-in to avoid introducing new validation-failure logs for existing apps by default. Without the opt-in, any app with this category enabled would start getting warnings. If you prefer always-on behavior gated only by ILogger/category levels, I can update this PR to remove the opt-in switches.
There was a problem hiding this comment.
I replied in-thread above with the tradeoff and can switch this PR to always-on ILogger-gated logging if you want that direction.
Summary
When an OData request has a
$select/$expandthat queries a property that does not exist on the target type, the query parser throws while validating: the controller path (EnableQueryAttribute) returnsHTTP 400and the minimal API path surfaces theODataExceptionfromODataQueryEndpointFilter. The parse throws before the parsed query options are populated, so telemetry that reads them sees an empty set and the failure gives no hint of the offending property, type, options, or endpoint.This PR adds an opt-in structured
ILoggerdiagnostic on the failure path (shared by both paths) that captures those details from request state that is still available. It is off by default; the HTTP response, its body, and the thrown exception are never changed.Configuration (default is a no-op)
Off by default (
EnableQueryValidationErrorLogging = false).[EnableQuery(EnableQueryValidationErrorLogging = true)].WithODataOptions(o => o.SetQueryValidationErrorLogging(true))ODataOptions.EnableQueryValidationErrorLogging = trueservices.AddOData(o => o.SetQueryValidationErrorLogging(true))Warning)ODataOptions.QueryValidationErrorLogLevel = ...services.AddOData(o => o.SetQueryValidationErrorLogLevel(...))After - opt-in
Example (console renderer, default level):