| File: Buffering\GlobalBufferIncludeScopesTests.cs | Web Access |
| Project: src\test\Libraries\Microsoft.Extensions.Telemetry.Tests\Microsoft.Extensions.Telemetry.Tests.csproj (Microsoft.Extensions.Telemetry.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #if NET9_0_OR_GREATER using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Test; using Microsoft.Extensions.Logging.Testing; using Xunit; namespace Microsoft.Extensions.Diagnostics.Buffering.Test; public class GlobalBufferIncludeScopesTests { [Fact] public void AddsScopesToAttributesOfBufferedLogRecords() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = CreateLoggerFactory(provider, includeScopes: true); ILogger logger = factory.CreateLogger("my category"); using (logger.BeginScope("plain scope")) using (logger.BeginScope(new Dictionary<string, string> { ["Tenant"] = "contoso" })) using (logger.BeginScope("Order {OrderId}", 42)) { logger.LogWarning("buffered {Number}", 1); } GetBuffer(factory).Flush(); FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal( new KeyValuePair<string, string?>[] { new("Number", "1"), new("OrderId", "42"), new("{OriginalFormat}", "buffered {Number}") }, record.StructuredState); } [Fact] public void WhenLogRecordHasNoOriginalFormat_AddsScopesAfterItsAttributes() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = CreateLoggerFactory(provider, includeScopes: true); ILogger logger = factory.CreateLogger("my category"); using (logger.BeginScope("Order {OrderId}", 42)) { logger.Log(LogLevel.Warning, default, new KeyValuePair<string, object?>[] { new("Number", 1) }, null, static (_, _) => "buffered"); } GetBuffer(factory).Flush(); FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal(new KeyValuePair<string, string?>[] { new("Number", "1"), new("OrderId", "42") }, record.StructuredState); } [Fact] public void WhenIncludeScopesIsDisabled_DoesNotAddScopes() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = CreateLoggerFactory(provider, includeScopes: false); ILogger logger = factory.CreateLogger("my category"); using (logger.BeginScope("Order {OrderId}", 42)) { logger.LogWarning("buffered"); } GetBuffer(factory).Flush(); FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal(new KeyValuePair<string, string?>[] { new("{OriginalFormat}", "buffered") }, record.StructuredState); } [Fact] public void AddsScopeValuesAsTheyAreWhenLogRecordIsBuffered() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = CreateLoggerFactory(provider, includeScopes: true); ILogger logger = factory.CreateLogger("my category"); var scope = new MutableKeyValueScope { Value = "original" }; using (logger.BeginScope(scope)) { logger.LogWarning("buffered"); } scope.Value = "changed"; GetBuffer(factory).Flush(); FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal("original", record.GetStructuredStateValue("Key")); } [Fact] public void AddsActivityScope() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = CreateLoggerFactory( provider, includeScopes: true, builder => builder.Configure(options => options.ActivityTrackingOptions = ActivityTrackingOptions.TraceId | ActivityTrackingOptions.SpanId)); ILogger logger = factory.CreateLogger("my category"); using var activity = new Activity("logging"); activity.Start(); logger.LogWarning("buffered"); GetBuffer(factory).Flush(); FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal(activity.TraceId.ToHexString(), record.GetStructuredStateValue("TraceId")); Assert.Equal(activity.SpanId.ToHexString(), record.GetStructuredStateValue("SpanId")); } [Fact] public void ScopeValuesCountTowardMaxLogRecordSize() { using var provider = new FakeLoggerProvider(); using ILoggerFactory factory = Utils.CreateLoggerFactory(builder => { builder.AddProvider(provider); builder.AddGlobalBuffer(options => { options.IncludeScopes = true; options.MaxLogRecordSizeInBytes = 1024; options.Rules.Add(new LogBufferingFilterRule(logLevel: LogLevel.Warning)); }); }); ILogger logger = factory.CreateLogger("my category"); logger.LogWarning("buffered"); using (logger.BeginScope("{Value}", new string('a', 1024))) { logger.LogWarning("too big to be buffered"); } // The log record which exceeds the size limit together with its scopes is emitted right away. FakeLogRecord record = Assert.Single(provider.Collector.GetSnapshot()); Assert.Equal("too big to be buffered", record.Message); } private static ILoggerFactory CreateLoggerFactory(ILoggerProvider provider, bool includeScopes, Action<ILoggingBuilder>? configure = null) => Utils.CreateLoggerFactory(builder => { builder.AddProvider(provider); builder.AddGlobalBuffer(options => { options.IncludeScopes = includeScopes; options.Rules.Add(new LogBufferingFilterRule(logLevel: LogLevel.Warning)); }); configure?.Invoke(builder); }); private static GlobalLogBuffer GetBuffer(ILoggerFactory factory) => ((Utils.DisposingLoggerFactory)factory).ServiceProvider.GetRequiredService<GlobalLogBuffer>(); private sealed class MutableKeyValueScope : IEnumerable<KeyValuePair<string, object?>> { public string? Value { get; set; } public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() { yield return new KeyValuePair<string, object?>("Key", Value); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } #endif