Skip to content

Optionally preserve logging scopes in buffered log records - #7807

Closed
evgenyfedorov2 wants to merge 1 commit into
mainfrom
evgenyfedorov2-buffered-logging-scopes
Closed

evgenyfedorov2 wants to merge 1 commit into
mainfrom
evgenyfedorov2-buffered-logging-scopes

Conversation

@evgenyfedorov2

@evgenyfedorov2 evgenyfedorov2 commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Log buffering drops ILogger.BeginScope data today: buffered records are emitted without the scopes that were active when they were logged. This was raised in the #5635 review. This PR adds an opt-in way to keep that data by adding the scopes' name/value pairs to the attributes of buffered records.

New API (experimental, EXTEXP0003)

  • GlobalLogBufferingOptions.IncludeScopes
  • PerRequestLogBufferingOptions.IncludeScopes

Both default to false and bind from configuration. The buffers read them each time they buffer a record, so options reloads take effect. The name matches OpenTelemetryLoggerOptions.IncludeScopes and ConsoleFormatterOptions.IncludeScopes, which are also opt-in. I avoided CaptureScopes because LoggerFilterOptions.CaptureScopes is the factory-wide switch and defaults to true. The delegate overload of AddPerIncomingRequestBuffer copies the value to the global fallback buffer, the same way it already copies Rules.

How it works

  • When a record matches a buffering rule and IncludeScopes is on, GlobalBuffer and IncomingRequestLogBuffer read the current scopes, including ActivityTrackingOptions scopes, and add each scope's name/value pairs to the record's attributes.
    • The pairs go before the record's {OriginalFormat}, which stays the last attribute. ExtendedLogger guarantees that since [Telemetry, Logging] Emit {OriginalFormat} as the last property of the log record #6111, and OpenTelemetry up to 1.15.0 only looks for the message template in the last attribute.
    • Values are converted to strings like the other attributes and count toward the size limits.
    • Only scopes that implement IEnumerable<KeyValuePair<string, object?>> are added, without their {OriginalFormat} pairs. Other scopes, such as strings and Dictionary<string, string> instances, are skipped. Console's JSON formatter and OpenTelemetry also treat only such scopes as name/value pairs.
  • IBufferedLogger implementations get the pairs in BufferedLogRecord.Attributes. Other loggers get them in the state passed to ILogger.Log.
  • The buffers read scopes from an IExternalScopeProvider they get through constructor injection. AddGlobalBuffer registers one with TryAddSingleton, and ExtendedLoggerFactory already uses an IExternalScopeProvider from DI when there is one, so the factory and the buffers share it.

Worth a careful look

  • With buffering registered, the logger factory's scope provider now comes from DI. As a result, BeginScope always records into it, even when IncludeScopes is off.
    • If no logger provider implements ISupportExternalScope, this costs 256 bytes per BeginScope (measured on net10.0). The Console and OpenTelemetry providers implement it, so in typical apps the factory records scopes anyway.
    • Avoiding the cost would break existing apps. The factory would have to either stop recording scopes into an IExternalScopeProvider that an app registers itself, or decide at startup, which needs a DI registration that returns null. Autofac throws on that.
  • Scope pairs are added after the record's own attributes, so a scope key can duplicate an attribute key.

Testing

  • New tests cover:

    • scopes added as attributes before {OriginalFormat}, with string scopes, Dictionary<string, string> scopes and the scopes' {OriginalFormat} pairs skipped
    • scopes added after all attributes of a record that has no {OriginalFormat}
    • nothing added when the option is off
    • values taken when the record is buffered
    • the Activity tracking scope
    • size accounting
    • an end-to-end per-request flow on TestServer, including ASP.NET Core's request scope

    The config-binding tests and the per-request delegate overload tests are extended.

  • The full Microsoft.Extensions.Telemetry.Tests and Microsoft.AspNetCore.Diagnostics.Middleware.Tests suites pass on net8.0, net9.0, and net10.0.

The learn.microsoft.com log buffering limitations page will need a follow-up in dotnet/docs.

Fixes: #7781

Microsoft Reviewers: Open in CodeFlow

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 69.93 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 67.66 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 85
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1624548&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 71.61 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 67.21 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 85
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1625111&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.Telemetry Line 93 92.9 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 69.93 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 67.21 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 84
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1626028&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.Telemetry Line 93 92.91 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 69.93 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 67.96 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 85
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1626139&view=codecoverage-tab

@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.Telemetry Line 93 92.8 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 69.93 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 67.21 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 85
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1626234&view=codecoverage-tab

Adds the experimental GlobalLogBufferingOptions.IncludeScopes and PerRequestLogBufferingOptions.IncludeScopes
options (EXTEXP0003). When an option is enabled, the buffer adds the name/value pairs of the logging scopes
that are active when a log record is buffered to the record's attributes. IBufferedLogger implementations
get them in BufferedLogRecord.Attributes, and other loggers get them in the state passed to ILogger.Log.

- Only scopes that implement IEnumerable<KeyValuePair<string, object?>> are added, without their
  "{OriginalFormat}" pairs. The log record's own "{OriginalFormat}" stays the last attribute.
- Scope values are converted to strings and count toward the size limits.
- The buffers get the IExternalScopeProvider through constructor injection. AddGlobalBuffer registers one
  with TryAddSingleton, and ExtendedLoggerFactory uses it too, so the factory and the buffers share it.
- Both options bind from configuration, and the delegate overload of AddPerIncomingRequestBuffer copies
  IncludeScopes to the global buffer options.

Fixes #7781

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@evgenyfedorov2
evgenyfedorov2 force-pushed the evgenyfedorov2-buffered-logging-scopes branch from 808400a to 6d8e6a9 Compare October 7, 2026 12:18
@dotnet-comment-bot

Copy link
Copy Markdown
Collaborator

‼️ Found issues ‼️

Project Coverage Type Expected Actual
Microsoft.Extensions.Diagnostics.Testing Line 99 98.65 🔻
Microsoft.Extensions.Telemetry Line 93 92.8 🔻
Microsoft.Extensions.AI.OpenAI Line 75 68.32 🔻
Microsoft.Extensions.AI.OpenAI Branch 75 57.59 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Line 75 4.46 🔻
Microsoft.Extensions.DataIngestion.MarkItDown Branch 75 0 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Line 99 96.03 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring Branch 99 92.76 🔻
Microsoft.Extensions.Diagnostics.ResourceMonitoring.Kubernetes Line 99 97.73 🔻
Microsoft.Extensions.ServiceDiscovery.Dns Line 75 71.61 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Line 75 42.11 🔻
Microsoft.Extensions.ServiceDiscovery.Abstractions Branch 75 42.86 🔻
Microsoft.Extensions.ServiceDiscovery Line 75 68.42 🔻
Microsoft.Extensions.ServiceDiscovery Branch 75 71.43 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Line 75 73.85 🔻
Microsoft.Extensions.ServiceDiscovery.Yarp Branch 75 70 🔻
Microsoft.Extensions.VectorData.Abstractions Line 75 37.39 🔻
Microsoft.Extensions.VectorData.Abstractions Branch 75 22.73 🔻

🎉 Good job! The coverage increased 🎉
Update MinCodeCoverage in the project files.

Project Expected Actual
Microsoft.Extensions.Http.Diagnostics 94 95
Microsoft.Gen.BuildMetadata 97 100
Microsoft.Gen.MetadataExtractor 57 73
Microsoft.Gen.MetricsReports 67 69
Microsoft.Extensions.AI.Abstractions 82 86
Microsoft.Extensions.AI.Evaluation.NLP 0 78
Microsoft.Extensions.Caching.Hybrid 82 85
Microsoft.Extensions.DataIngestion 75 89
Microsoft.Extensions.DataIngestion.Markdig 75 90
Microsoft.Extensions.Http.Resilience 97 100

Full code coverage report: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1626352&view=codecoverage-tab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optionally preserve logging scopes in buffered records

2 participants