diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs index 9a8f48d4bbb..d08792a0776 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs @@ -316,7 +316,7 @@ void GenPropertyLoads(LoggingMethod lm, string stateName, out int numReservedUnc { if (!member.HasDataClassification) { - var propName = PropertyChainToString(propertyChain, member, "_", omitReferenceName: p.OmitReferenceName); + var propName = PropertyChainToString(propertyChain, member, ".", omitReferenceName: p.OmitReferenceName); var accessExpression = PropertyChainToString(propertyChain, member, "?.", nonNullSeparator: "."); var ts = ShouldStringifyProperty(member) @@ -367,7 +367,7 @@ void GenPropertyLoads(LoggingMethod lm, string stateName, out int numReservedUnc { if (member.HasDataClassification) { - var propName = PropertyChainToString(propertyChain, member, "_", omitReferenceName: p.OmitReferenceName); + var propName = PropertyChainToString(propertyChain, member, ".", omitReferenceName: p.OmitReferenceName); var accessExpression = PropertyChainToString(propertyChain, member, "?.", nonNullSeparator: "."); var value = ShouldStringifyProperty(member) @@ -391,7 +391,7 @@ void GenPropertyLoads(LoggingMethod lm, string stateName, out int numReservedUnc { if (member.HasDataClassification) { - var propName = PropertyChainToString(propertyChain, member, "_", omitReferenceName: p.OmitReferenceName); + var propName = PropertyChainToString(propertyChain, member, ".", omitReferenceName: p.OmitReferenceName); var accessExpression = PropertyChainToString(propertyChain, member, "?.", nonNullSeparator: "."); var value = ShouldStringifyProperty(member) @@ -418,7 +418,7 @@ void GenPropertyLoads(LoggingMethod lm, string stateName, out int numReservedUnc } else { - var propName = PropertyChainToString(propertyChain, member, "_", omitReferenceName: p.OmitReferenceName); + var propName = PropertyChainToString(propertyChain, member, ".", omitReferenceName: p.OmitReferenceName); var accessExpression = PropertyChainToString(propertyChain, member, "?.", nonNullSeparator: "."); var ts = ShouldStringifyProperty(member) diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderNormalizer.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderNormalizer.cs new file mode 100644 index 00000000000..600811d598a --- /dev/null +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderNormalizer.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Compliance.Classification; + +internal static class HeaderNormalizer +{ + public static string[] PrepareNormalizedHeaderNames(KeyValuePair[] headers, string prefix) + { + var normalizedHeaders = new string[headers.Length]; + + for (int i = 0; i < headers.Length; i++) + { + normalizedHeaders[i] = prefix + Normalize(headers[i].Key); + } + + return normalizedHeaders; + } + + [SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", + Justification = "Normalization to lower case is required by OTel's semantic conventions")] + private static string Normalize(string header) + { + return header.ToLowerInvariant(); + } +} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs index eb5792dad40..3c02adb498f 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs @@ -16,28 +16,32 @@ internal sealed class HeaderReader { private readonly IRedactorProvider _redactorProvider; private readonly KeyValuePair[] _headers; + private readonly string[] _normalizedHeaders; - public HeaderReader(IDictionary headersToLog, IRedactorProvider redactorProvider) + public HeaderReader(IDictionary headersToLog, IRedactorProvider redactorProvider, string prefix) { _redactorProvider = redactorProvider; _headers = headersToLog.Count == 0 ? [] : headersToLog.ToArray(); + _normalizedHeaders = HeaderNormalizer.PrepareNormalizedHeaderNames(_headers, prefix); } - public void Read(IHeaderDictionary headers, IList> logContext, string prefix) + public void Read(IHeaderDictionary headers, IList> logContext) { if (headers.Count == 0) { return; } - foreach (var header in _headers) + for (int i = 0; i < _headers.Length; i++) { + var header = _headers[i]; + if (headers.TryGetValue(header.Key, out var headerValue)) { var provider = _redactorProvider.GetRedactor(header.Value); var redacted = provider.Redact(headerValue.ToString()); - logContext.Add(new(prefix + header.Key, redacted)); + logContext.Add(new(_normalizedHeaders[i], redacted)); } } } diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs index 330c7d0c376..050f0bb092d 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs @@ -55,8 +55,8 @@ public HttpLoggingRedactionInterceptor( _requestPathLogMode = EnsureRequestPathLoggingModeIsValid(optionsValue.RequestPathLoggingMode); _parameterRedactionMode = optionsValue.RequestPathParameterRedactionMode; - _requestHeadersReader = new(optionsValue.RequestHeadersDataClasses, redactorProvider); - _responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider); + _requestHeadersReader = new(optionsValue.RequestHeadersDataClasses, redactorProvider, HttpLoggingTagNames.RequestHeaderPrefix); + _responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider, HttpLoggingTagNames.ResponseHeaderPrefix); _excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray(); } @@ -126,7 +126,7 @@ public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext) if (logContext.TryDisable(HttpLoggingFields.RequestHeaders)) { - _requestHeadersReader.Read(context.Request.Headers, logContext.Parameters, HttpLoggingTagNames.RequestHeaderPrefix); + _requestHeadersReader.Read(context.Request.Headers, logContext.Parameters); } return default; @@ -138,7 +138,7 @@ public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext) if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders)) { - _responseHeadersReader.Read(context.Response.Headers, logContext.Parameters, HttpLoggingTagNames.ResponseHeaderPrefix); + _responseHeadersReader.Read(context.Response.Headers, logContext.Parameters); } // Don't enrich if we're not going to log any part of the response diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs index 548c82babfc..4ce5bb6055b 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs @@ -19,7 +19,7 @@ public static class HttpLoggingTagNames /// /// HTTP Host. /// - public const string Host = "Host"; + public const string Host = "server.address"; /// /// HTTP Method. @@ -34,12 +34,12 @@ public static class HttpLoggingTagNames /// /// HTTP Request Headers prefix. /// - public const string RequestHeaderPrefix = "RequestHeader."; + public const string RequestHeaderPrefix = "http.request.header."; /// /// HTTP Response Headers prefix. /// - public const string ResponseHeaderPrefix = "ResponseHeader."; + public const string ResponseHeaderPrefix = "http.response.header."; /// /// HTTP Request Body. diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs index 8d0d8b028ab..6388e51db97 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs @@ -2,7 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Redaction; @@ -17,7 +18,8 @@ namespace Microsoft.AspNetCore.Diagnostics.Logging; /// internal sealed class RequestHeadersLogEnricher : ILogEnricher { - private readonly FrozenDictionary _headersDataClasses; + private readonly KeyValuePair[] _headersDataClasses; + private readonly string[] _normalizedHeaders; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IRedactorProvider? _redactorProvider; @@ -33,11 +35,10 @@ public RequestHeadersLogEnricher(IHttpContextAccessor httpContextAccessor, IOpti var opt = Throw.IfMemberNull(options, options.Value); _httpContextAccessor = httpContextAccessor; - _headersDataClasses = opt.HeadersDataClasses.Count == 0 - ? FrozenDictionary.Empty - : opt.HeadersDataClasses.ToFrozenDictionary(StringComparer.Ordinal); + _headersDataClasses = opt.HeadersDataClasses.Count == 0 ? [] : opt.HeadersDataClasses.ToArray(); + _normalizedHeaders = HeaderNormalizer.PrepareNormalizedHeaderNames(_headersDataClasses, HttpLoggingTagNames.RequestHeaderPrefix); - if (_headersDataClasses.Count > 0) + if (_headersDataClasses.Length > 0) { _redactorProvider = Throw.IfNull(redactorProvider); } @@ -54,20 +55,22 @@ public void Enrich(IEnrichmentTagCollector collector) var request = _httpContextAccessor.HttpContext.Request; - if (_headersDataClasses.Count == 0) + if (_headersDataClasses.Length == 0) { return; } - if (_headersDataClasses.Count != 0) + if (_headersDataClasses.Length != 0) { - foreach (var header in _headersDataClasses) + for (int i = 0; i < _headersDataClasses.Length; i++) { + var header = _headersDataClasses[i]; + if (request.Headers.TryGetValue(header.Key, out var headerValue) && !string.IsNullOrEmpty(headerValue)) { var redactor = _redactorProvider!.GetRedactor(header.Value); var redacted = redactor.Redact(headerValue); - collector.Add(header.Key, redacted); + collector.Add(_normalizedHeaders[i], redacted); } } } diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json index c5efcc382d5..e76e745c5f1 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json @@ -31,7 +31,7 @@ { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Host", "Stage": "Stable", - "Value": "Host" + "Value": "server.address" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Method", @@ -51,7 +51,7 @@ { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.RequestHeaderPrefix", "Stage": "Stable", - "Value": "RequestHeader." + "Value": "http.request.header." }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseBody", @@ -61,7 +61,7 @@ { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseHeaderPrefix", "Stage": "Stable", - "Value": "ResponseHeader." + "Value": "http.response.header." }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.StatusCode", diff --git a/src/Libraries/Microsoft.AspNetCore.HeaderParsing/HeaderParsingFeature.cs b/src/Libraries/Microsoft.AspNetCore.HeaderParsing/HeaderParsingFeature.cs index 86f49b3aaeb..b3974603efb 100644 --- a/src/Libraries/Microsoft.AspNetCore.HeaderParsing/HeaderParsingFeature.cs +++ b/src/Libraries/Microsoft.AspNetCore.HeaderParsing/HeaderParsingFeature.cs @@ -198,12 +198,12 @@ private void CopyTo(Box to) } } - [LoggerMessage(LogLevel.Debug, "Can't parse header '{headerName}' due to '{error}'.")] + [LoggerMessage(LogLevel.Debug, "Can't parse header '{HeaderName}' due to '{Error}'.")] private partial void LogParsingError(string headerName, string error); - [LoggerMessage(LogLevel.Debug, "Using a default value for header '{headerName}'.")] + [LoggerMessage(LogLevel.Debug, "Using a default value for header '{HeaderName}'.")] private partial void LogDefaultUsage(string headerName); - [LoggerMessage(LogLevel.Debug, "Header '{headerName}' not found.")] + [LoggerMessage(LogLevel.Debug, "Header '{HeaderName}' not found.")] private partial void LogNotFound(string headerName); } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs index ea0807b0d44..60c4017c63c 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs @@ -14,22 +14,22 @@ public static class ApplicationEnricherTags /// /// Application name. /// - public const string ApplicationName = "AppName"; + public const string ApplicationName = "service.name"; /// /// Environment name. /// - public const string EnvironmentName = "CloudEnv"; + public const string EnvironmentName = "deployment.environment"; /// /// Deployment ring. /// - public const string DeploymentRing = "CloudDeploymentRing"; + public const string DeploymentRing = "DeploymentRing"; /// /// Build version. /// - public const string BuildVersion = "CloudRoleVer"; + public const string BuildVersion = "service.version"; /// /// Gets a list of all dimension names. diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ProcessEnricherTagNames.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ProcessEnricherTagNames.cs index 807b1c122ef..fc7698f3caa 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ProcessEnricherTagNames.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ProcessEnricherTagNames.cs @@ -14,12 +14,12 @@ public static class ProcessEnricherTagNames /// /// Process ID. /// - public const string ProcessId = "pid"; + public const string ProcessId = "process.pid"; /// /// Thread ID. /// - public const string ThreadId = "tid"; + public const string ThreadId = "thread.id"; /// /// Gets a list of all dimension names. diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Microsoft.Extensions.Diagnostics.Extra.json b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Microsoft.Extensions.Diagnostics.Extra.json index ef9baefce11..92164273c8a 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Microsoft.Extensions.Diagnostics.Extra.json +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Microsoft.Extensions.Diagnostics.Extra.json @@ -26,22 +26,22 @@ { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ApplicationEnricherTags.ApplicationName", "Stage": "Stable", - "Value": "AppName" + "Value": "service.name" }, { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ApplicationEnricherTags.BuildVersion", "Stage": "Stable", - "Value": "CloudRoleVer" + "Value": "service.version" }, { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ApplicationEnricherTags.DeploymentRing", "Stage": "Stable", - "Value": "CloudDeploymentRing" + "Value": "DeploymentRing" }, { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ApplicationEnricherTags.EnvironmentName", "Stage": "Stable", - "Value": "CloudEnv" + "Value": "deployment.environment" } ], "Properties": [ @@ -260,12 +260,12 @@ { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ProcessEnricherTagNames.ProcessId", "Stage": "Stable", - "Value": "pid" + "Value": "process.pid" }, { "Member": "const string Microsoft.Extensions.Diagnostics.Enrichment.ProcessEnricherTagNames.ThreadId", "Stage": "Stable", - "Value": "tid" + "Value": "thread.id" } ], "Properties": [ diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.TagCollector.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.TagCollector.cs index c9b9dbe6296..501ce699aad 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.TagCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.TagCollector.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging; public partial class LoggerMessageState : ITagCollector { - private const char Separator = '_'; + private const char Separator = '.'; /// void ITagCollector.Add(string tagName, object? tagValue) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common/Log.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common/Log.cs index 7c4927540ee..8ed8eaf7639 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common/Log.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common/Log.cs @@ -8,13 +8,13 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks; internal static partial class Log { - [LoggerMessage(0, LogLevel.Warning, "Process reporting unhealthy: {status}. Health check entries are {entries}")] + [LoggerMessage(0, LogLevel.Warning, "Process reporting unhealthy: {Status}. Health check entries are {Entries}")] public static partial void Unhealthy( ILogger logger, HealthStatus status, StringBuilder entries); - [LoggerMessage(1, LogLevel.Debug, "Process reporting healthy: {status}.")] + [LoggerMessage(1, LogLevel.Debug, "Process reporting healthy: {Status}.")] public static partial void Healthy( ILogger logger, HealthStatus status); diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Log.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Log.cs index 9e241aa0a4d..d81e431fa7e 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Log.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Log.cs @@ -14,6 +14,6 @@ internal static partial class Log [LoggerMessage(1, LogLevel.Error, "Unable to gather utilization statistics.")] public static partial void HandledGatherStatisticsException(ILogger logger, Exception e); - [LoggerMessage(2, LogLevel.Error, "Publisher `{publisher}` was unable to publish utilization statistics.")] + [LoggerMessage(2, LogLevel.Error, "Publisher `{Publisher}` was unable to publish utilization statistics.")] public static partial void HandlePublishUtilizationException(ILogger logger, Exception e, string publisher); } diff --git a/src/Libraries/Microsoft.Extensions.Hosting.Testing/HostTerminatorService.cs b/src/Libraries/Microsoft.Extensions.Hosting.Testing/HostTerminatorService.cs index 54b807a1a14..d72936a312d 100644 --- a/src/Libraries/Microsoft.Extensions.Hosting.Testing/HostTerminatorService.cs +++ b/src/Libraries/Microsoft.Extensions.Hosting.Testing/HostTerminatorService.cs @@ -60,7 +60,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _host.Dispose(); } - [LoggerMessage(0, LogLevel.Warning, "FakeHostOptions.TimeToLive set to {timeToLive} is up, disposing the host.")] + [LoggerMessage(0, LogLevel.Warning, "FakeHostOptions.TimeToLive set to {TimeToLive} is up, disposing the host.")] private partial void LogTimeToLiveUp(TimeSpan timeToLive); [LoggerMessage(1, LogLevel.Information, "Debugger is attached. The host won't be automatically disposed.")] diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpClientLatencyLogEnricher.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpClientLatencyLogEnricher.cs index 12cc3d98149..bad9b23a415 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpClientLatencyLogEnricher.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Latency/Internal/HttpClientLatencyLogEnricher.cs @@ -50,7 +50,7 @@ public void Enrich(IEnrichmentTagCollector collector, HttpRequestMessage request AppendCheckpoints(lc, stringBuilder); } - collector.Add("latencyInfo", stringBuilder.ToString()); + collector.Add("LatencyInfo", stringBuilder.ToString()); _builderPool.Return(stringBuilder); } diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpClientLoggingTagNames.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpClientLoggingTagNames.cs index 134db3f16e6..803eb03d082 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpClientLoggingTagNames.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpClientLoggingTagNames.cs @@ -14,47 +14,47 @@ public static class HttpClientLoggingTagNames /// /// HTTP Request duration. /// - public const string Duration = "duration"; + public const string Duration = "Duration"; /// /// HTTP Host. /// - public const string Host = "httpHost"; + public const string Host = "server.address"; /// /// HTTP Method. /// - public const string Method = "httpMethod"; + public const string Method = "http.request.method"; /// /// HTTP Path. /// - public const string Path = "httpPath"; + public const string Path = "url.path"; /// /// HTTP Request Body. /// - public const string RequestBody = "httpRequestBody"; + public const string RequestBody = "RequestBody"; /// /// HTTP Response Body. /// - public const string ResponseBody = "httpResponseBody"; + public const string ResponseBody = "ResponseBody"; /// /// HTTP Request Headers prefix. /// - public const string RequestHeaderPrefix = "httpRequestHeader_"; + public const string RequestHeaderPrefix = "http.request.header."; /// /// HTTP Response Headers prefix. /// - public const string ResponseHeaderPrefix = "httpResponseHeader_"; + public const string ResponseHeaderPrefix = "http.response.header."; /// /// HTTP Status Code. /// - public const string StatusCode = "httpStatusCode"; + public const string StatusCode = "http.response.status_code"; /// /// Gets a list of all tag names. diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/Log.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/Log.cs index eda39c9833b..f7e6896b14b 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/Log.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/Log.cs @@ -12,10 +12,10 @@ namespace Microsoft.Extensions.Http.Logging.Internal; /// Logs , and the exceptions due to errors of request/response. /// [SuppressMessage("Major Code Smell", "S109:Magic numbers should not be used", Justification = "Event ID's.")] -internal static partial class Log +internal static class Log { internal const string OriginalFormat = "{OriginalFormat}"; - internal const string OriginalFormatValue = "{httpMethod} {httpHost}/{httpPath}"; + private const string NullString = "(null)"; private const int MinimalPropertyCount = 4; @@ -28,10 +28,10 @@ internal static partial class Log $"{{{HttpClientLoggingTagNames.Method}}} {{{HttpClientLoggingTagNames.Host}}}/{{{HttpClientLoggingTagNames.Path}}}"; private const string LoggerContextMissingMessage = - $"The logger couldn't read its context for {{requestState}} request: {{{HttpClientLoggingTagNames.Method}}} {{{HttpClientLoggingTagNames.Host}}}"; + $"The logger couldn't read its context for {{RequestState}} request: {{{HttpClientLoggingTagNames.Method}}} {{{HttpClientLoggingTagNames.Host}}}"; private const string EnrichmentErrorMessage = - "An error occurred in enricher '{enricherType}' while enriching the logger context for request: " + + "An error occurred in enricher '{Enricher}' while enriching the logger context for request: " + $"{{{HttpClientLoggingTagNames.Method}}} {{{HttpClientLoggingTagNames.Host}}}/{{{HttpClientLoggingTagNames.Path}}}"; private static readonly Func _originalFormatValueFMTFunc = OriginalFormatValueFMT; @@ -46,17 +46,114 @@ public static void OutgoingRequestError(ILogger logger, LogRecord record, Except OutgoingRequest(logger, LogLevel.Error, 2, nameof(OutgoingRequestError), record, exception); } - [LoggerMessage(LogLevel.Error, RequestReadErrorMessage)] - public static partial void RequestReadError(ILogger logger, Exception exception, HttpMethod httpMethod, string? httpHost, string? httpPath); + public static void RequestReadError(ILogger logger, Exception exception, HttpMethod method, string? host, string? path) + { + var state = LoggerMessageHelper.ThreadLocalState; + + _ = state.ReserveTagSpace(4); + state.TagArray[3] = new(HttpClientLoggingTagNames.Method, method); + state.TagArray[2] = new(HttpClientLoggingTagNames.Host, host); + state.TagArray[1] = new(HttpClientLoggingTagNames.Path, path); + state.TagArray[0] = new(OriginalFormat, RequestReadErrorMessage); + + logger.Log( + LogLevel.Error, + new(0, nameof(RequestReadError)), + state, + exception, + static (s, _) => + { + var method = s.TagArray[3].Value ?? NullString; + var host = s.TagArray[2].Value ?? NullString; + var path = s.TagArray[1].Value ?? NullString; + return FormattableString.Invariant( + $"An error occurred while reading the request data to fill the logger context for request: {method} {host}/{path}"); + }); + + state.Clear(); + } - [LoggerMessage(LogLevel.Error, ResponseReadErrorMessage)] - public static partial void ResponseReadError(ILogger logger, Exception exception, HttpMethod httpMethod, string httpHost, string httpPath); + public static void ResponseReadError(ILogger logger, Exception exception, HttpMethod method, string host, string path) + { + var state = LoggerMessageHelper.ThreadLocalState; - [LoggerMessage(LogLevel.Error, LoggerContextMissingMessage)] - public static partial void LoggerContextMissing(ILogger logger, Exception? exception, string requestState, HttpMethod httpMethod, string? httpHost); + _ = state.ReserveTagSpace(4); + state.TagArray[3] = new(HttpClientLoggingTagNames.Method, method); + state.TagArray[2] = new(HttpClientLoggingTagNames.Host, host); + state.TagArray[1] = new(HttpClientLoggingTagNames.Path, path); + state.TagArray[0] = new(OriginalFormat, ResponseReadErrorMessage); + + logger.Log( + LogLevel.Error, + new(0, nameof(ResponseReadError)), + state, + exception, + static (s, _) => + { + var method = s.TagArray[3].Value ?? NullString; + var host = s.TagArray[2].Value ?? NullString; + var path = s.TagArray[1].Value ?? NullString; + return FormattableString.Invariant( + $"An error occurred while reading the response data to fill the logger context for request: {method} {host}/{path}"); + }); + + state.Clear(); + } + + public static void LoggerContextMissing(ILogger logger, Exception? exception, string requestState, HttpMethod method, string? host) + { + var state = LoggerMessageHelper.ThreadLocalState; - [LoggerMessage(LogLevel.Error, EnrichmentErrorMessage)] - public static partial void EnrichmentError(ILogger logger, Exception exception, string? enricherType, HttpMethod httpMethod, string httpHost, string httpPath); + _ = state.ReserveTagSpace(4); + state.TagArray[3] = new("RequestState", requestState); + state.TagArray[2] = new(HttpClientLoggingTagNames.Method, method); + state.TagArray[1] = new(HttpClientLoggingTagNames.Host, host); + state.TagArray[0] = new(OriginalFormat, LoggerContextMissingMessage); + + logger.Log( + LogLevel.Error, + new(0, nameof(LoggerContextMissing)), + state, + exception, + (s, _) => + { + var requestState = s.TagArray[3].Value ?? NullString; + var method = s.TagArray[2].Value ?? NullString; + var host = s.TagArray[1].Value ?? NullString; + return FormattableString.Invariant($"The logger couldn't read its context for {requestState} request: {method} {host}"); + }); + + state.Clear(); + } + + public static void EnrichmentError(ILogger logger, Exception exception, string? enricher, HttpMethod method, string host, string path) + { + var state = LoggerMessageHelper.ThreadLocalState; + + _ = state.ReserveTagSpace(5); + state.TagArray[4] = new("Enricher", enricher); + state.TagArray[3] = new(HttpClientLoggingTagNames.Method, method); + state.TagArray[2] = new(HttpClientLoggingTagNames.Host, host); + state.TagArray[1] = new(HttpClientLoggingTagNames.Path, path); + state.TagArray[0] = new(OriginalFormat, EnrichmentErrorMessage); + + logger.Log( + LogLevel.Error, + new(0, nameof(EnrichmentError)), + state, + exception, + (s, _) => + { + var enricher = s.TagArray[4].Value ?? NullString; + var method = s.TagArray[3].Value ?? NullString; + var host = s.TagArray[2].Value ?? NullString; + var path = s.TagArray[1].Value ?? NullString; + return FormattableString.Invariant( + $"An error occurred in enricher '{enricher}' while enriching the logger context for request: {method} {host}/{path}"); + }); + + state.Clear(); + } // Using the code below instead of generated logging method because we have a custom formatter and custom tag keys for headers. private static void OutgoingRequest( diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/LoggerMessageStateExtensions.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/LoggerMessageStateExtensions.cs index 744152fec0f..fa9912be06e 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/LoggerMessageStateExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/LoggerMessageStateExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.Http.Logging.Internal; @@ -25,7 +26,7 @@ public static void AddRequestHeaders(this LoggerMessageState state, List p + x, + static (x, p) => p + Normalize(x), HttpClientLoggingTagNames.RequestHeaderPrefix); state.TagArray[index++] = new(key, items[i].Value); @@ -45,10 +46,17 @@ public static void AddResponseHeaders(this LoggerMessageState state, List p + x, + static (x, p) => p + Normalize(x), HttpClientLoggingTagNames.ResponseHeaderPrefix); state.TagArray[index++] = new(key, items[i].Value); } } + + [SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", + Justification = "Normalization to lower case is required by OTel's semantic conventions")] + private static string Normalize(string header) + { + return header.ToLowerInvariant(); + } } diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.csproj b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.csproj index a74f7ef6f10..b541f10f960 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.csproj +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.csproj @@ -27,7 +27,7 @@ normal - 94 + 91 78 diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.json b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.json index 66b635f00b2..eabd53cbf75 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.json +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Microsoft.Extensions.Http.Diagnostics.json @@ -82,47 +82,47 @@ { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.Duration", "Stage": "Stable", - "Value": "duration" + "Value": "Duration" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.Host", "Stage": "Stable", - "Value": "httpHost" + "Value": "server.address" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.Method", "Stage": "Stable", - "Value": "httpMethod" + "Value": "http.request.method" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.Path", "Stage": "Stable", - "Value": "httpPath" + "Value": "url.path" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.RequestBody", "Stage": "Stable", - "Value": "httpRequestBody" + "Value": "RequestBody" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.RequestHeaderPrefix", "Stage": "Stable", - "Value": "httpRequestHeader_" + "Value": "http.request.header." }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.ResponseBody", "Stage": "Stable", - "Value": "httpResponseBody" + "Value": "ResponseBody" }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.ResponseHeaderPrefix", "Stage": "Stable", - "Value": "httpResponseHeader_" + "Value": "http.response.header." }, { "Member": "const string Microsoft.Extensions.Http.Logging.HttpClientLoggingTagNames.StatusCode", "Stage": "Stable", - "Value": "httpStatusCode" + "Value": "http.response.status_code" } ], "Properties": [ diff --git a/test/Generators/Microsoft.Gen.Logging/Generated/LogMethodTests.cs b/test/Generators/Microsoft.Gen.Logging/Generated/LogMethodTests.cs index 09838f84079..2753fcc9a2b 100644 --- a/test/Generators/Microsoft.Gen.Logging/Generated/LogMethodTests.cs +++ b/test/Generators/Microsoft.Gen.Logging/Generated/LogMethodTests.cs @@ -751,7 +751,7 @@ public void AtSymbolsTest() collector.Clear(); AtSymbolsTestExtensions.M3(logger, LogLevel.Debug, o); - Assert.Equal("42", collector.LatestRecord.StructuredState!.GetValue("event_class")); + Assert.Equal("42", collector.LatestRecord.StructuredState!.GetValue("event.class")); collector.Clear(); AtSymbolsTestExtensions.M5(logger, LogLevel.Debug, o); @@ -892,7 +892,7 @@ public void FormattableTest() collector.Clear(); FormattableTestExtensions.Method2(logger, new FormattableTestExtensions.ComplexObj()); Assert.Equal(1, collector.Count); - Assert.Equal("Formatted!", collector.LatestRecord.StructuredState!.GetValue("p1_P1")); + Assert.Equal("Formatted!", collector.LatestRecord.StructuredState!.GetValue("p1.P1")); collector.Clear(); FormattableTestExtensions.Method3(logger, default); diff --git a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesRedactionTests.cs b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesRedactionTests.cs index a8171954bfe..c42b599b31f 100644 --- a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesRedactionTests.cs +++ b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesRedactionTests.cs @@ -33,7 +33,7 @@ public void RedactsWhenRedactorProviderIsAvailableInTheInstance() var expectedState = new Dictionary { ["P0"] = "----", - ["p1_StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), + ["p1.StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), ["{OriginalFormat}"] = "LogProperties with redaction: {P0}" }; @@ -59,7 +59,7 @@ public void RedactsWhenDefaultAttrCtorAndRedactorProviderIsInTheInstance() var expectedState = new Dictionary { ["p0"] = "----", - ["p1_StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), + ["p1.StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), }; collector.LatestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -81,14 +81,14 @@ public void RedactsWhenLogMethodIsStaticNoParams() var expectedState = new Dictionary { - ["classToLog_StringProperty"] = new('+', classToRedact.StringProperty.Length), - ["classToLog_StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), - ["classToLog_SimplifiedNullableIntProperty"] = classToRedact.SimplifiedNullableIntProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_GetOnlyProperty"] = new('-', classToRedact.GetOnlyProperty.Length), - ["classToLog_TransitiveProp_TransitiveNumberProp"] = classToRedact.TransitiveProp.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), - ["classToLog_TransitiveProp_TransitiveStringProp"] = new('-', classToRedact.TransitiveProp.TransitiveStringProp.Length), - ["classToLog_NoRedactionProp"] = classToRedact.NoRedactionProp, - ["classToLog_NonFormattableProperty"] = new('-', classToRedact.NonFormattableProperty.ToString().Length), + ["classToLog.StringProperty"] = new('+', classToRedact.StringProperty.Length), + ["classToLog.StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), + ["classToLog.SimplifiedNullableIntProperty"] = classToRedact.SimplifiedNullableIntProperty.ToString(CultureInfo.InvariantCulture), + ["classToLog.GetOnlyProperty"] = new('-', classToRedact.GetOnlyProperty.Length), + ["classToLog.TransitiveProp.TransitiveNumberProp"] = classToRedact.TransitiveProp.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), + ["classToLog.TransitiveProp.TransitiveStringProp"] = new('-', classToRedact.TransitiveProp.TransitiveStringProp.Length), + ["classToLog.NoRedactionProp"] = classToRedact.NoRedactionProp, + ["classToLog.NonFormattableProperty"] = new('-', classToRedact.NonFormattableProperty.ToString().Length), ["{OriginalFormat}"] = "No template params" }; @@ -112,14 +112,14 @@ public void RedactsWhenDefaultAttrCtorAndIsStaticNoParams() var expectedState = new Dictionary { - ["classToLog_StringProperty"] = new('+', classToRedact.StringProperty.Length), - ["classToLog_StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), - ["classToLog_SimplifiedNullableIntProperty"] = classToRedact.SimplifiedNullableIntProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_GetOnlyProperty"] = new('-', classToRedact.GetOnlyProperty.Length), - ["classToLog_TransitiveProp_TransitiveNumberProp"] = classToRedact.TransitiveProp.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), - ["classToLog_TransitiveProp_TransitiveStringProp"] = new('-', classToRedact.TransitiveProp.TransitiveStringProp.Length), - ["classToLog_NoRedactionProp"] = classToRedact.NoRedactionProp, - ["classToLog_NonFormattableProperty"] = new('-', classToRedact.NonFormattableProperty.ToString().Length), + ["classToLog.StringProperty"] = new('+', classToRedact.StringProperty.Length), + ["classToLog.StringPropertyBase"] = new('-', classToRedact.StringPropertyBase.Length), + ["classToLog.SimplifiedNullableIntProperty"] = classToRedact.SimplifiedNullableIntProperty.ToString(CultureInfo.InvariantCulture), + ["classToLog.GetOnlyProperty"] = new('-', classToRedact.GetOnlyProperty.Length), + ["classToLog.TransitiveProp.TransitiveNumberProp"] = classToRedact.TransitiveProp.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), + ["classToLog.TransitiveProp.TransitiveStringProp"] = new('-', classToRedact.TransitiveProp.TransitiveStringProp.Length), + ["classToLog.NoRedactionProp"] = classToRedact.NoRedactionProp, + ["classToLog.NonFormattableProperty"] = new('-', classToRedact.NonFormattableProperty.ToString().Length), }; collector.LatestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -142,8 +142,8 @@ public void RedactsWhenLogMethodIsStaticTwoParams() var expectedState = new Dictionary { ["StringProperty"] = "-----------", - ["complexParam_TransitiveNumberProp"] = classToRedact.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), - ["complexParam_TransitiveStringProp"] = new('-', classToRedact.TransitiveStringProp.Length), + ["complexParam.TransitiveNumberProp"] = classToRedact.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), + ["complexParam.TransitiveStringProp"] = new('-', classToRedact.TransitiveStringProp.Length), ["{OriginalFormat}"] = "Only {StringProperty} as param" }; @@ -168,8 +168,8 @@ public void RedactsWhenDefaultAttrCtorAndIsStaticTwoParams() var expectedState = new Dictionary { ["stringProperty"] = "-----------", - ["complexParam_TransitiveNumberProp"] = classToRedact.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), - ["complexParam_TransitiveStringProp"] = new('-', classToRedact.TransitiveStringProp.Length), + ["complexParam.TransitiveNumberProp"] = classToRedact.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), + ["complexParam.TransitiveStringProp"] = new('-', classToRedact.TransitiveStringProp.Length), }; collector.LatestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); diff --git a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs index 5aca5bbe07e..a840082f8e6 100644 --- a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs +++ b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs @@ -43,9 +43,9 @@ public void LogPropertiesEnumerables() Assert.Equal(1, _logger.Collector.Count); var ss = _logger.LatestRecord.StructuredState!.ToDictionary(x => x.Key, x => x.Value); - Assert.Equal("[\"1\",\"2\",\"3\"]", ss["myProps_P5"]); - Assert.Equal("[\"4\",\"5\",\"6\"]", ss["myProps_P6"]); - Assert.Equal("{\"Seven\"=\"7\",\"Eight\"=\"8\",\"Nine\"=\"9\"}", ss["myProps_P7"]); + Assert.Equal("[\"1\",\"2\",\"3\"]", ss["myProps.P5"]); + Assert.Equal("[\"4\",\"5\",\"6\"]", ss["myProps.P6"]); + Assert.Equal("{\"Seven\"=\"7\",\"Eight\"=\"8\",\"Nine\"=\"9\"}", ss["myProps.P7"]); } [Fact] @@ -122,29 +122,29 @@ public void LogPropertiesSpecialTypes() Assert.Equal(19, state.Count); #endif - Assert.Equal(props.P0.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P0")); - Assert.Equal(props.P1.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P1")); - Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), state!.GetValue("p_P2")); - Assert.Equal(props.P3.ToString(), state!.GetValue("p_P3")); - Assert.Equal(props.P4.ToString(), state!.GetValue("p_P4")); - Assert.Equal(props.P5.ToString(), state!.GetValue("p_P5")); - Assert.Equal(props.P6.ToString(), state!.GetValue("p_P6")); - Assert.Equal(props.P7.ToString(), state!.GetValue("p_P7")); - Assert.Equal(props.P8.ToString(), state!.GetValue("p_P8")); - Assert.Equal(props.P9.ToString(), state!.GetValue("p_P9")); - Assert.Equal(props.P10.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P10")); - Assert.Equal(props.P11.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P11")); - Assert.Equal(props.P12.ToString(), state!.GetValue("p_P12")); - Assert.Equal(props.P13.ToString(), state!.GetValue("p_P13")); - Assert.Equal(props.P14.ToString(), state!.GetValue("p_P14")); - Assert.Equal(props.P15.ToString(), state!.GetValue("p_P15")); - Assert.Equal(props.P16.ToString(), state!.GetValue("p_P16")); - Assert.Equal(props.P17.ToString(), state!.GetValue("p_P17")); - Assert.Equal(props.P18.ToString(), state!.GetValue("p_P18")); + Assert.Equal(props.P0.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P0")); + Assert.Equal(props.P1.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P1")); + Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), state!.GetValue("p.P2")); + Assert.Equal(props.P3.ToString(), state!.GetValue("p.P3")); + Assert.Equal(props.P4.ToString(), state!.GetValue("p.P4")); + Assert.Equal(props.P5.ToString(), state!.GetValue("p.P5")); + Assert.Equal(props.P6.ToString(), state!.GetValue("p.P6")); + Assert.Equal(props.P7.ToString(), state!.GetValue("p.P7")); + Assert.Equal(props.P8.ToString(), state!.GetValue("p.P8")); + Assert.Equal(props.P9.ToString(), state!.GetValue("p.P9")); + Assert.Equal(props.P10.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P10")); + Assert.Equal(props.P11.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P11")); + Assert.Equal(props.P12.ToString(), state!.GetValue("p.P12")); + Assert.Equal(props.P13.ToString(), state!.GetValue("p.P13")); + Assert.Equal(props.P14.ToString(), state!.GetValue("p.P14")); + Assert.Equal(props.P15.ToString(), state!.GetValue("p.P15")); + Assert.Equal(props.P16.ToString(), state!.GetValue("p.P16")); + Assert.Equal(props.P17.ToString(), state!.GetValue("p.P17")); + Assert.Equal(props.P18.ToString(), state!.GetValue("p.P18")); #if NET6_0_OR_GREATER - Assert.Equal(props.P19.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P19")); - Assert.Equal(props.P20.ToString(CultureInfo.InvariantCulture), state!.GetValue("p_P20")); + Assert.Equal(props.P19.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P19")); + Assert.Equal(props.P20.ToString(CultureInfo.InvariantCulture), state!.GetValue("p.P20")); #endif } @@ -175,17 +175,17 @@ public void LogPropertiesNullHandling() var ss = collector.LatestRecord.StructuredState!.ToDictionary(x => x.Key, x => x.Value); Assert.Equal(11, ss.Count); - Assert.Null(ss["p_P0"]); - Assert.Null(ss["p_P1"]); - Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), ss["p_P2"]); - Assert.Null(ss["p_P3"]); - Assert.Equal("I refuse to be formatted", ss["p_P4"]); - Assert.Null(ss["p_P5"]); - Assert.Null(ss["p_P6"]); - Assert.Equal("-", ss["p_P7"]); - Assert.Null(ss["p_P8"]); - Assert.Equal("------------------------", ss["p_P9"]); - Assert.Equal("null", ss["p_P10"]); + Assert.Null(ss["p.P0"]); + Assert.Null(ss["p.P1"]); + Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), ss["p.P2"]); + Assert.Null(ss["p.P3"]); + Assert.Equal("I refuse to be formatted", ss["p.P4"]); + Assert.Null(ss["p.P5"]); + Assert.Null(ss["p.P6"]); + Assert.Equal("-", ss["p.P7"]); + Assert.Null(ss["p.P8"]); + Assert.Equal("------------------------", ss["p.P9"]); + Assert.Equal("null", ss["p.P10"]); collector.Clear(); LogPropertiesNullHandlingExtensions.M1(logger, props); @@ -193,10 +193,10 @@ public void LogPropertiesNullHandling() ss = collector.LatestRecord.StructuredState!.ToDictionary(x => x.Key, x => x.Value); Assert.Equal(4, ss.Count); - Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), ss["p_P2"]); - Assert.Equal("I refuse to be formatted", ss["p_P4"]); - Assert.Equal("-", ss["p_P7"]); - Assert.Equal("------------------------", ss["p_P9"]); + Assert.Equal(props.P2.ToString(null, CultureInfo.InvariantCulture), ss["p.P2"]); + Assert.Equal("I refuse to be formatted", ss["p.P4"]); + Assert.Equal("-", ss["p.P7"]); + Assert.Equal("------------------------", ss["p.P9"]); } [Fact] @@ -231,60 +231,60 @@ public void LogPropertiesTest() var expectedState = new Dictionary { ["classToLog_StringProperty_1"] = "microsoft.com", - ["classToLog_StringProperty"] = classToLog.StringProperty, - ["classToLog_SimplifiedNullableIntProperty"] = null, - ["classToLog_ExplicitNullableIntProperty"] = classToLog.ExplicitNullableIntProperty.ToString(), - ["classToLog_GetOnlyProperty"] = classToLog.GetOnlyProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_VirtualPropertyBase"] = classToLog.VirtualPropertyBase, - ["classToLog_NonVirtualPropertyBase"] = classToLog.NonVirtualPropertyBase, - ["classToLog_TransitivePropertyArray"] = LoggerMessageHelper.Stringify(classToLog.TransitivePropertyArray), - ["classToLog_TransitiveProperty_TransitiveNumberProp"] + ["classToLog.StringProperty"] = classToLog.StringProperty, + ["classToLog.SimplifiedNullableIntProperty"] = null, + ["classToLog.ExplicitNullableIntProperty"] = classToLog.ExplicitNullableIntProperty.ToString(), + ["classToLog.GetOnlyProperty"] = classToLog.GetOnlyProperty.ToString(CultureInfo.InvariantCulture), + ["classToLog.VirtualPropertyBase"] = classToLog.VirtualPropertyBase, + ["classToLog.NonVirtualPropertyBase"] = classToLog.NonVirtualPropertyBase, + ["classToLog.TransitivePropertyArray"] = LoggerMessageHelper.Stringify(classToLog.TransitivePropertyArray), + ["classToLog.TransitiveProperty.TransitiveNumberProp"] = classToLog.TransitiveProperty.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), - ["classToLog_TransitiveProperty_TransitiveStringProp"] = classToLog.TransitiveProperty.TransitiveStringProp, - ["classToLog_TransitiveProperty_InnerTransitiveProperty_IntegerProperty"] + ["classToLog.TransitiveProperty.TransitiveStringProp"] = classToLog.TransitiveProperty.TransitiveStringProp, + ["classToLog.TransitiveProperty.InnerTransitiveProperty.IntegerProperty"] = classToLog.TransitiveProperty.InnerTransitiveProperty.IntegerProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_TransitiveProperty_InnerTransitiveProperty_DateTimeProperty"] + ["classToLog.TransitiveProperty.InnerTransitiveProperty.DateTimeProperty"] = classToLog.TransitiveProperty.InnerTransitiveProperty.DateTimeProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_AnotherTransitiveProperty_IntegerProperty"] = null, // Since AnotherTransitiveProperty is null - ["classToLog_StringPropertyBase"] = classToLog.StringPropertyBase, - ["classToLog_VirtualInterimProperty"] = classToLog.VirtualInterimProperty.ToInvariantString(), - ["classToLog_InterimProperty"] = classToLog.InterimProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_TransitiveProperty_TransitiveDerivedProp"] = classToLog.TransitiveProperty.TransitiveDerivedProp.ToInvariantString(), - ["classToLog_TransitiveProperty_TransitiveVirtualProp"] = classToLog.TransitiveProperty.TransitiveVirtualProp.ToInvariantString(), - ["classToLog_TransitiveProperty_TransitiveGenericProp_GenericProp"] + ["classToLog.AnotherTransitiveProperty.IntegerProperty"] = null, // Since AnotherTransitiveProperty is null + ["classToLog.StringPropertyBase"] = classToLog.StringPropertyBase, + ["classToLog.VirtualInterimProperty"] = classToLog.VirtualInterimProperty.ToInvariantString(), + ["classToLog.InterimProperty"] = classToLog.InterimProperty.ToString(CultureInfo.InvariantCulture), + ["classToLog.TransitiveProperty.TransitiveDerivedProp"] = classToLog.TransitiveProperty.TransitiveDerivedProp.ToInvariantString(), + ["classToLog.TransitiveProperty.TransitiveVirtualProp"] = classToLog.TransitiveProperty.TransitiveVirtualProp.ToInvariantString(), + ["classToLog.TransitiveProperty.TransitiveGenericProp.GenericProp"] = classToLog.TransitiveProperty.TransitiveGenericProp.GenericProp, - ["classToLog_PropertyOfGenerics_GenericProp"] = classToLog.PropertyOfGenerics.GenericProp.ToInvariantString(), - ["classToLog_CustomStructProperty_LongProperty"] = classToLog.CustomStructProperty.LongProperty.ToInvariantString(), - ["classToLog_CustomStructProperty_TransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.PropertyOfGenerics.GenericProp"] = classToLog.PropertyOfGenerics.GenericProp.ToInvariantString(), + ["classToLog.CustomStructProperty.LongProperty"] = classToLog.CustomStructProperty.LongProperty.ToInvariantString(), + ["classToLog.CustomStructProperty.TransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructProperty.TransitiveStructProperty.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructProperty_NullableTransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.CustomStructProperty.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructProperty.NullableTransitiveStructProperty?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructProperty_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] + ["classToLog.CustomStructProperty.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = classToLog.CustomStructProperty.NullableTransitiveStructProperty2?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty_LongProperty"] = classToLog.CustomStructNullableProperty?.LongProperty.ToInvariantString(), - ["classToLog_CustomStructNullableProperty_TransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty.LongProperty"] = classToLog.CustomStructNullableProperty?.LongProperty.ToInvariantString(), + ["classToLog.CustomStructNullableProperty.TransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty?.TransitiveStructProperty.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty_NullableTransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty?.NullableTransitiveStructProperty?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty?.NullableTransitiveStructProperty2?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty2_LongProperty"] = classToLog.CustomStructNullableProperty2?.LongProperty.ToInvariantString(), - ["classToLog_CustomStructNullableProperty2_TransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty2.LongProperty"] = classToLog.CustomStructNullableProperty2?.LongProperty.ToInvariantString(), + ["classToLog.CustomStructNullableProperty2.TransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty2?.TransitiveStructProperty.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty2_NullableTransitiveStructProperty_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty2.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty2?.NullableTransitiveStructProperty?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["classToLog_CustomStructNullableProperty2_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] + ["classToLog.CustomStructNullableProperty2.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = classToLog.CustomStructNullableProperty2?.NullableTransitiveStructProperty2?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), ["{OriginalFormat}"] = "Only {classToLog_StringProperty_1} as param" @@ -306,7 +306,7 @@ public void LogPropertiesInTemplateTest() { ["StringProperty"] = StringProperty, ["ComplexParam"] = classToLog.ToString(), - ["complexParam_MyProperty"] = classToLog.MyProperty.ToInvariantString(), + ["complexParam.MyProperty"] = classToLog.MyProperty.ToInvariantString(), ["{OriginalFormat}"] = "Both {StringProperty} and {ComplexParam} as params" }; @@ -329,9 +329,9 @@ public void LogPropertiesNonStaticClassTest() var expectedState = new Dictionary { ["P0"] = StringParamValue, - ["p1_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["p1_MyStringProperty"] = classToLog.MyStringProperty, - ["p1_AnotherStringProperty"] = classToLog.AnotherStringProperty, + ["p1.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["p1.MyStringProperty"] = classToLog.MyStringProperty, + ["p1.AnotherStringProperty"] = classToLog.AnotherStringProperty, ["{OriginalFormat}"] = "LogProperties: {P0}" }; @@ -352,14 +352,14 @@ public void LogPropertyTestCustomStruct() var expectedState = new Dictionary { - ["structParam_LongProperty"] = structToLog.LongProperty.ToInvariantString(), - ["structParam_TransitiveStructProperty_DateTimeOffsetProperty"] + ["structParam.LongProperty"] = structToLog.LongProperty.ToInvariantString(), + ["structParam.TransitiveStructProperty.DateTimeOffsetProperty"] = structToLog.TransitiveStructProperty.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["structParam_NullableTransitiveStructProperty_DateTimeOffsetProperty"] + ["structParam.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = structToLog.NullableTransitiveStructProperty?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["structParam_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] + ["structParam.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = structToLog.NullableTransitiveStructProperty2.Value.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), ["{OriginalFormat}"] = "Testing non-nullable struct here..." @@ -382,14 +382,14 @@ public void LogPropertyTestCustomNullableStruct() var expectedState = new Dictionary { - ["structParam_LongProperty"] = structToLog.Value.LongProperty.ToInvariantString(), - ["structParam_TransitiveStructProperty_DateTimeOffsetProperty"] + ["structParam.LongProperty"] = structToLog.Value.LongProperty.ToInvariantString(), + ["structParam.TransitiveStructProperty.DateTimeOffsetProperty"] = structToLog.Value.TransitiveStructProperty.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["structParam_NullableTransitiveStructProperty_DateTimeOffsetProperty"] + ["structParam.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = structToLog.Value.NullableTransitiveStructProperty.Value.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), - ["structParam_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] + ["structParam.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = structToLog.Value.NullableTransitiveStructProperty2?.DateTimeOffsetProperty.ToString(CultureInfo.InvariantCulture), ["{OriginalFormat}"] = "Testing nullable struct here..." @@ -411,10 +411,10 @@ public void LogPropertyTestCustomExplicitNullableStruct() var expectedState = new Dictionary { - ["structParam_LongProperty"] = null, - ["structParam_TransitiveStructProperty_DateTimeOffsetProperty"] = null, - ["structParam_NullableTransitiveStructProperty_DateTimeOffsetProperty"] = null, - ["structParam_NullableTransitiveStructProperty2_DateTimeOffsetProperty"] = null, + ["structParam.LongProperty"] = null, + ["structParam.TransitiveStructProperty.DateTimeOffsetProperty"] = null, + ["structParam.NullableTransitiveStructProperty.DateTimeOffsetProperty"] = null, + ["structParam.NullableTransitiveStructProperty2.DateTimeOffsetProperty"] = null, ["{OriginalFormat}"] = "Testing explicit nullable struct here..." }; @@ -437,7 +437,7 @@ public void LogPropertiesDefaultAttrCtor() var expectedState = new Dictionary { - ["complexParam_MyProperty"] = classToLog.MyProperty.ToInvariantString() + ["complexParam.MyProperty"] = classToLog.MyProperty.ToInvariantString() }; latestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -464,8 +464,8 @@ public void LogPropertiesInterfaceArgument() var expectedState = new Dictionary { - ["complexParam_IntProperty"] = classToLog.IntProperty.ToInvariantString(), - ["complexParam_TransitiveProp_IntegerProperty"] = classToLog.TransitiveProp.IntegerProperty.ToInvariantString(), + ["complexParam.IntProperty"] = classToLog.IntProperty.ToInvariantString(), + ["complexParam.TransitiveProp.IntegerProperty"] = classToLog.TransitiveProp.IntegerProperty.ToInvariantString(), ["{OriginalFormat}"] = "Testing interface-typed argument here..." }; @@ -488,10 +488,10 @@ public void LogPropertiesRecordClassArgument() var expectedState = new Dictionary { - ["p0_Value"] = recordToLog.Value.ToInvariantString(), - ["p0_class"] = recordToLog.@class, - ["p0_GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), - ["p0_event"] = recordToLog.@event.ToString(CultureInfo.InvariantCulture) + ["p0.Value"] = recordToLog.Value.ToInvariantString(), + ["p0.class"] = recordToLog.@class, + ["p0.GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), + ["p0.event"] = recordToLog.@event.ToString(CultureInfo.InvariantCulture) }; latestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -514,9 +514,9 @@ public void LogPropertiesRecordStructArgument() var expectedState = new Dictionary { ["p0"] = recordToLog.ToString(), - ["p0_IntValue"] = recordToLog.IntValue.ToInvariantString(), - ["p0_StringValue"] = recordToLog.StringValue, - ["p0_GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), + ["p0.IntValue"] = recordToLog.IntValue.ToInvariantString(), + ["p0.StringValue"] = recordToLog.StringValue, + ["p0.GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), ["{OriginalFormat}"] = "Struct is: {p0}" }; @@ -540,9 +540,9 @@ public void LogPropertiesReadonlyRecordStructArgument() var expectedState = new Dictionary { ["p0"] = recordToLog.ToString(), - ["p0_IntValue"] = recordToLog.IntValue.ToInvariantString(), - ["p0_StringValue"] = recordToLog.StringValue, - ["p0_GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), + ["p0.IntValue"] = recordToLog.IntValue.ToInvariantString(), + ["p0.StringValue"] = recordToLog.StringValue, + ["p0.GetOnlyValue"] = recordToLog.GetOnlyValue.ToInvariantString(), ["{OriginalFormat}"] = "Readonly struct is: {p0}" }; diff --git a/test/Generators/Microsoft.Gen.Logging/Generated/TagProviderTests.cs b/test/Generators/Microsoft.Gen.Logging/Generated/TagProviderTests.cs index 2ecbbfaf193..1ce9ca8564a 100644 --- a/test/Generators/Microsoft.Gen.Logging/Generated/TagProviderTests.cs +++ b/test/Generators/Microsoft.Gen.Logging/Generated/TagProviderTests.cs @@ -37,7 +37,7 @@ public void LogsWithObject() var expectedState = new Dictionary { ["Param"] = obj.ToString(), - ["param_ToString"] = obj + " ProvidePropertiesCall", + ["param.ToString"] = obj + " ProvidePropertiesCall", ["{OriginalFormat}"] = "Custom provided properties for {Param}." }; @@ -61,8 +61,8 @@ public void LogsWhenNonStaticClass() { ["P0"] = StringParamValue, ["P1"] = classToLog.ToString(), - ["p1_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["p1_Custom_property_name"] = classToLog.MyStringProperty, + ["p1.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["p1.Custom_property_name"] = classToLog.MyStringProperty, ["{OriginalFormat}"] = "LogProperties with provider: {P0}, {P1}" }; @@ -87,8 +87,8 @@ public void LogsWhenDefaultAttrCtorInNonStaticClass() var expectedState = new Dictionary { ["p0"] = StringParamValue, - ["p1_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["p1_Custom_property_name"] = classToLog.MyStringProperty + ["p1.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["p1.Custom_property_name"] = classToLog.MyStringProperty }; latestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -109,8 +109,8 @@ public void LogsWhenDefaultAttrCtorInStaticClass() var expectedState = new Dictionary { - ["param_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["param_Custom_property_name"] = classToLog.MyStringProperty + ["param.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["param.Custom_property_name"] = classToLog.MyStringProperty }; latestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -141,7 +141,7 @@ public void LogsWithNullable() var expectedState = new Dictionary { - ["param_P1"] = "42", + ["param.P1"] = "42", }; latestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); @@ -250,8 +250,8 @@ public void LogsWhenNonNullStronglyTypedObject() var expectedState = new Dictionary { ["Param"] = classToLog.ToString(), - ["param_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["param_Custom_property_name"] = classToLog.MyStringProperty, + ["param.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["param.Custom_property_name"] = classToLog.MyStringProperty, ["{OriginalFormat}"] = "Custom provided properties for {Param}." }; @@ -271,8 +271,8 @@ public void LogsWhenStruct() var expectedState = new Dictionary { - ["param_MyIntProperty"] = structToLog.MyIntProperty.ToInvariantString(), - ["param_Custom_property_name"] = structToLog.MyStringProperty, + ["param.MyIntProperty"] = structToLog.MyIntProperty.ToInvariantString(), + ["param.Custom_property_name"] = structToLog.MyStringProperty, ["{OriginalFormat}"] = "Custom provided properties for struct." }; @@ -292,8 +292,8 @@ public void LogsWhenInterface() var expectedState = new Dictionary { - ["param_MyIntProperty"] = interfaceToLog.MyIntProperty.ToInvariantString(), - ["param_Custom_property_name"] = interfaceToLog.MyStringProperty, + ["param.MyIntProperty"] = interfaceToLog.MyIntProperty.ToInvariantString(), + ["param.Custom_property_name"] = interfaceToLog.MyStringProperty, ["{OriginalFormat}"] = "Custom provided properties for interface." }; @@ -313,11 +313,11 @@ public void LogsWhenProviderCombinedWithLogProperties() var expectedState = new Dictionary { - ["param1_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["param1_MyStringProperty"] = classToLog.MyStringProperty, - ["param1_AnotherStringProperty"] = classToLog.AnotherStringProperty, - ["param2_MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), - ["param2_Custom_property_name"] = classToLog.MyStringProperty, + ["param1.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["param1.MyStringProperty"] = classToLog.MyStringProperty, + ["param1.AnotherStringProperty"] = classToLog.AnotherStringProperty, + ["param2.MyIntProperty"] = classToLog.MyIntProperty.ToInvariantString(), + ["param2.Custom_property_name"] = classToLog.MyStringProperty, ["{OriginalFormat}"] = "No params." }; @@ -341,10 +341,10 @@ public void LogsTwoStronglyTypedParams() var expectedState = new Dictionary { ["StringParam"] = StringParamValue, - ["param_MyIntProperty"] = classToLog1.MyIntProperty.ToInvariantString(), - ["param_Custom_property_name"] = classToLog1.MyStringProperty, - ["param2_Another_property_name"] = classToLog2.MyStringProperty.ToUpperInvariant(), - ["param2_MyIntProperty_test"] = classToLog2.MyIntProperty.ToInvariantString(), + ["param.MyIntProperty"] = classToLog1.MyIntProperty.ToInvariantString(), + ["param.Custom_property_name"] = classToLog1.MyStringProperty, + ["param2.Another_property_name"] = classToLog2.MyStringProperty.ToUpperInvariant(), + ["param2.MyIntProperty_test"] = classToLog2.MyIntProperty.ToInvariantString(), ["{OriginalFormat}"] = "Custom provided properties for both complex params and {StringParam}." }; @@ -356,8 +356,8 @@ public void LogsTwoStronglyTypedParams() TagProviderExtensions.LogMethodCustomPropsProviderTwoParams(_logger, StringParamValue, classToLog1, classToLog2); Assert.Equal(2, _logger.Collector.Count); - expectedState["param_MyIntProperty"] = classToLog1.MyIntProperty.ToInvariantString(); - expectedState["param2_MyIntProperty_test"] = classToLog2.MyIntProperty.ToInvariantString(); + expectedState["param.MyIntProperty"] = classToLog1.MyIntProperty.ToInvariantString(); + expectedState["param2.MyIntProperty_test"] = classToLog2.MyIntProperty.ToInvariantString(); _logger.Collector.LatestRecord.StructuredState.Should().NotBeNull().And.Equal(expectedState); } @@ -379,9 +379,9 @@ public void LogsTwoObjectParams() var expectedState = new Dictionary { ["StringParam"] = StringParamValue, - ["param_ToString"] = obj1 + " ProvidePropertiesCall", - ["param2_Type"] = obj2.GetType().ToString(), - ["param2_ToString"] = obj2 + " ProvideOtherPropertiesCall", + ["param.ToString"] = obj1 + " ProvidePropertiesCall", + ["param2.Type"] = obj2.GetType().ToString(), + ["param2.ToString"] = obj2 + " ProvideOtherPropertiesCall", ["{OriginalFormat}"] = "Custom provided properties for both complex params and {StringParam}." }; @@ -404,8 +404,8 @@ public void LogsTwoNullObjectParams() var expectedState = new Dictionary { ["StringParam"] = StringParamValue, - ["param2_Type"] = null, - ["param2_ToString"] = " ProvideOtherPropertiesCall", + ["param2.Type"] = null, + ["param2.ToString"] = " ProvideOtherPropertiesCall", ["{OriginalFormat}"] = "Custom provided properties for both complex params and {StringParam}." }; diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs index 1e195026f57..6eb305c95a1 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs @@ -285,6 +285,9 @@ async static (logCollector, client) => { const string Content = "Client: hello!"; + const string NormalizedRequestHeader = "accept"; + const string NormalizedResponseHeader = "transfer-encoding"; + using var request = new HttpRequestMessage(HttpMethod.Post, "/") { Content = new StringContent(Content) @@ -313,7 +316,7 @@ async static (logCollector, client) => Assert.DoesNotContain(requestState, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); Assert.DoesNotContain(requestState, x => x.Key == HttpLoggingTagNames.StatusCode); Assert.DoesNotContain(requestState, x => x.Key == HttpLoggingTagNames.Duration); - Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); + Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + NormalizedRequestHeader); Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); @@ -323,7 +326,7 @@ async static (logCollector, client) => Assert.Single(requestBodyState, x => x.Key == "Body" && x.Value == Content); Assert.Equal(2, responseState!.Count); - Assert.Single(responseState, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + HeaderNames.TransferEncoding); + Assert.Single(responseState, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + NormalizedResponseHeader); Assert.Single(responseState, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); Assert.Equal(2, responseBodyState!.Count); @@ -346,6 +349,9 @@ await RunAsync( }), async static (logCollector, client) => { + const string NormalizedRequestHeader = "accept"; + const string NormalizedResponseHeader = "transfer-encoding"; + using var httpMessage = new HttpRequestMessage(HttpMethod.Get, "/"); httpMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); @@ -365,8 +371,8 @@ async static (logCollector, client) => var state = lastRecord.StructuredState; Assert.Equal(9, state!.Count); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + HeaderNames.TransferEncoding); + Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + NormalizedRequestHeader); + Assert.Single(state, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + NormalizedResponseHeader); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); @@ -378,10 +384,10 @@ async static (logCollector, client) => Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); Assert.DoesNotContain(state, x => - x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix) && !x.Key.EndsWith(HeaderNames.Accept)); + x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix) && !x.Key.EndsWith(NormalizedRequestHeader)); Assert.DoesNotContain(state, x => - x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix) && !x.Key.EndsWith(HeaderNames.TransferEncoding)); + x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix) && !x.Key.EndsWith(NormalizedResponseHeader)); }); } diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderNormalizerTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderNormalizerTests.cs new file mode 100644 index 00000000000..481a186a08d --- /dev/null +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderNormalizerTests.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Microsoft.Extensions.Compliance.Classification; +using Xunit; + +public class HeaderNormalizerTests +{ + [Fact] + public void PrepareNormalizedHeaderNamesTest() + { + const string Prefix = "prefix."; + + var headers = HeaderNormalizer.PrepareNormalizedHeaderNames(new[] + { + new KeyValuePair("Accept-Charset", DataClassification.Unknown), + new KeyValuePair("CONTENT-TYPE", DataClassification.Unknown) + }, + Prefix); + + Assert.Equal(2, headers.Length); + Assert.Equal(Prefix + "accept-charset", headers[0]); + Assert.Equal(Prefix + "content-type", headers[1]); + } +} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs index b3af0edffae..f6c59fc9704 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs @@ -17,40 +17,49 @@ public class HeaderReaderTests [Fact] public void ShouldNotAddHeaders_WhenFilteringSetEmpty() { - var reader = new HeaderReader(new Dictionary(), null!); + var reader = new HeaderReader(new Dictionary(), null!, string.Empty); var listToFill = new List>(); var headers = new HeaderDictionary(new Dictionary { [HeaderNames.Accept] = MediaTypeNames.Text.Plain }); - reader.Read(headers, listToFill, string.Empty); + reader.Read(headers, listToFill); Assert.Empty(listToFill); } [Fact] public void ShouldNotAddHeaders_WhenHeadersCollectionEmpty() { - var reader = new HeaderReader(new Dictionary { [HeaderNames.Accept] = DataClassification.Unknown }, null!); + var reader = new HeaderReader(new Dictionary { [HeaderNames.Accept] = DataClassification.Unknown }, null!, string.Empty); var listToFill = new List>(); - reader.Read(new HeaderDictionary(), listToFill, string.Empty); + reader.Read(new HeaderDictionary(), listToFill); Assert.Empty(listToFill); } [Fact] public void ShouldAddHeaders_WhenHeadersCollectionNotEmpty() { - var headersToLog = new Dictionary { [HeaderNames.Accept] = DataClassification.Unknown }; - var reader = new HeaderReader(headersToLog, new FakeRedactorProvider(new FakeRedactorOptions { RedactionFormat = "" })); + const string Prefix = "prefix."; + const string NormalizedHeader = Prefix + "accept-charset"; + + var headersToLog = new Dictionary + { + [HeaderNames.AcceptCharset] = DataClassification.Unknown + }; + + var reader = new HeaderReader(headersToLog, new FakeRedactorProvider( + new FakeRedactorOptions { RedactionFormat = "" }), Prefix); + var headers = new Dictionary { - [HeaderNames.Accept] = MediaTypeNames.Text.Xml, + [HeaderNames.AcceptCharset] = MediaTypeNames.Text.Xml, [HeaderNames.ContentType] = MediaTypeNames.Application.Pdf }; var listToFill = new List>(); - reader.Read(new HeaderDictionary(headers), listToFill, string.Empty); + reader.Read(new HeaderDictionary(headers), listToFill); Assert.Single(listToFill); var redacted = listToFill[0]; - Assert.Equal(HeaderNames.Accept, redacted.Key); + Assert.Equal(NormalizedHeader, redacted.Key); Assert.Equal($"", redacted.Value); } } diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs index 879fe93abb9..e841feb3636 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs @@ -23,6 +23,11 @@ public class RequestHeadersEnricherTests private const string RequestId = "RequestIdTestValue"; private const string TestValue = "TestValue"; + private const string NormalizedHeaderKey1 = HttpLoggingTagNames.RequestHeaderPrefix + "x-requestid"; + private const string NormalizedHeaderKey2 = HttpLoggingTagNames.RequestHeaderPrefix + "host"; + private const string NormalizedHeaderKey3 = HttpLoggingTagNames.RequestHeaderPrefix + "nullheader"; + private const string NormalizedHeaderKey4 = HttpLoggingTagNames.RequestHeaderPrefix + "x-platform"; + private readonly Mock _accessorMock; private readonly Mock _redactorProviderMock; @@ -97,8 +102,8 @@ public void RequestHeadersEnricher_GivenEnricherOptions_HeaderKeysDataClasses_En // Assert Assert.True(enrichedState.Count == 2); - Assert.Equal($"redacted:{RequestId}", enrichedState[HeaderKey1].ToString()); - Assert.Equal(TestValue, enrichedState[HeaderKey4].ToString()); + Assert.Equal($"redacted:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString()); + Assert.Equal(TestValue, enrichedState[NormalizedHeaderKey4].ToString()); } [Fact] @@ -127,8 +132,8 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsEmpty_He // Assert Assert.Single(enrichedState); - Assert.Equal($"REDACTED:{RequestId}", enrichedState[HeaderKey1].ToString()); - Assert.False(enrichedState.ContainsKey(HeaderKey2)); + Assert.Equal($"REDACTED:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString()); + Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey2)); } [Fact] @@ -153,8 +158,8 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsNull_Hea // Assert Assert.Single(enrichedState); - Assert.Equal(RequestId, enrichedState[HeaderKey1].ToString()); - Assert.False(enrichedState.ContainsKey(HeaderKey3)); + Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString()); + Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey3)); } [Fact] @@ -179,8 +184,8 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsMissing_ var enrichedState = enrichedProperties.Properties; // Assert - Assert.Equal(RequestId, enrichedState[HeaderKey1].ToString()); - Assert.False(enrichedState.ContainsKey(headerKey2)); + Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString()); + Assert.False(enrichedState.ContainsKey(HttpLoggingTagNames.RequestHeaderPrefix + headerKey2)); } [Fact] diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageStateTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageStateTests.cs index 62ddebbe72a..e2123fc7010 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageStateTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageStateTests.cs @@ -93,16 +93,16 @@ public static void CollectorContract() collector.Add(PropName, Value); Assert.Equal(1, lms.TagsCount); - Assert.Equal(PropertyNamPrefix + "_" + PropName, lms.TagArray[0].Key); + Assert.Equal(PropertyNamPrefix + "." + PropName, lms.TagArray[0].Key); Assert.Equal(Value, lms.TagArray[0].Value); collector.Add(PropName, Value, FakeClassifications.PrivateData); Assert.Equal(1, lms.TagsCount); - Assert.Equal(PropertyNamPrefix + "_" + PropName, lms.TagArray[0].Key); + Assert.Equal(PropertyNamPrefix + "." + PropName, lms.TagArray[0].Key); Assert.Equal(Value, lms.TagArray[0].Value); Assert.Equal(1, lms.ClassifiedTagsCount); - Assert.Equal(PropertyNamPrefix + "_" + PropName, lms.ClassifiedTagArray[0].Name); + Assert.Equal(PropertyNamPrefix + "." + PropName, lms.ClassifiedTagArray[0].Name); Assert.Equal(Value, lms.ClassifiedTagArray[0].Value); Assert.Equal(FakeClassifications.PrivateData, lms.ClassifiedTagArray[0].Classification); diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Latency/Internal/HttpClientLatencyLogEnricherTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Latency/Internal/HttpClientLatencyLogEnricherTest.cs index 7c298cc46d5..71941df0e7e 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Latency/Internal/HttpClientLatencyLogEnricherTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Latency/Internal/HttpClientLatencyLogEnricherTest.cs @@ -48,7 +48,7 @@ public void HttpClientLatencyLogEnricher_Enriches_OnResponseWithoutHeader() Mock mockEnrichmentPropertyBag = new Mock(); enricher.Enrich(mockEnrichmentPropertyBag.Object, null!, httpResponseMessage, null); - mockEnrichmentPropertyBag.Verify(m => m.Add(It.Is(s => s.Equals("latencyInfo")), It.Is(s => s.Contains("a/b"))), Times.Once); + mockEnrichmentPropertyBag.Verify(m => m.Add(It.Is(s => s.Equals("LatencyInfo")), It.Is(s => s.Contains("a/b"))), Times.Once); } [Fact] @@ -70,6 +70,6 @@ public void HttpClientLatencyLogEnricher_Enriches_OnResponseWithHeader() Mock mockEnrichmentPropertyBag = new Mock(); enricher.Enrich(mockEnrichmentPropertyBag.Object, null!, httpResponseMessage, null); - mockEnrichmentPropertyBag.Verify(m => m.Add(It.Is(s => s.Equals("latencyInfo")), It.Is(s => s.Contains("a/b") && s.Contains(serverName))), Times.Once); + mockEnrichmentPropertyBag.Verify(m => m.Add(It.Is(s => s.Equals("LatencyInfo")), It.Is(s => s.Contains("a/b") && s.Contains(serverName))), Times.Once); } } diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/AcceptanceTests.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/AcceptanceTests.cs index 3b9b1327183..57e40989b92 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/AcceptanceTests.cs @@ -287,7 +287,7 @@ public async Task AddHttpClientLogging_WithTypedHttpClients_WorksCorrectly() [InlineData(HttpRouteParameterRedactionMode.Strict, "v1/unit/REDACTED/users/REDACTED:123")] [InlineData(HttpRouteParameterRedactionMode.Loose, "v1/unit/999/users/REDACTED:123")] [InlineData(HttpRouteParameterRedactionMode.None, "/v1/unit/999/users/123")] - public async Task AddHttpClientLogging_RedactSensitivePrams(HttpRouteParameterRedactionMode parameterRedactionMode, string redactedPath) + public async Task AddHttpClientLogging_RedactSensitiveParams(HttpRouteParameterRedactionMode parameterRedactionMode, string redactedPath) { const string RequestPath = "https://fake.com/v1/unit/999/users/123"; @@ -321,13 +321,13 @@ public async Task AddHttpClientLogging_RedactSensitivePrams(HttpRouteParameterRe var collector = sp.GetFakeLogCollector(); var logRecord = collector.GetSnapshot().Single(logRecord => logRecord.Category == LoggingCategory); var state = logRecord.State as List>; - state!.Single(kvp => kvp.Key == "httpPath").Value.Should().Be(redactedPath); + state!.Single(kvp => kvp.Key == HttpClientLoggingTagNames.Path).Value.Should().Be(redactedPath); } [Theory] [InlineData(HttpRouteParameterRedactionMode.Strict, "v1/unit/REDACTED/users/REDACTED:123")] [InlineData(HttpRouteParameterRedactionMode.Loose, "v1/unit/999/users/REDACTED:123")] - public async Task AddHttpClientLogging_NamedHttpClient_RedactSensitivePrams(HttpRouteParameterRedactionMode parameterRedactionMode, string redactedPath) + public async Task AddHttpClientLogging_NamedHttpClient_RedactSensitiveParams(HttpRouteParameterRedactionMode parameterRedactionMode, string redactedPath) { const string RequestPath = "https://fake.com/v1/unit/999/users/123"; @@ -362,7 +362,7 @@ public async Task AddHttpClientLogging_NamedHttpClient_RedactSensitivePrams(Http var collector = sp.GetFakeLogCollector(); var logRecord = collector.GetSnapshot().Single(logRecord => logRecord.Category == LoggingCategory); var state = logRecord.State as List>; - state!.Single(kvp => kvp.Key == "httpPath").Value.Should().Be(redactedPath); + state!.Single(kvp => kvp.Key == HttpClientLoggingTagNames.Path).Value.Should().Be(redactedPath); } [Fact] diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs index 95d2f7b5844..0473f3b8394 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs @@ -32,10 +32,10 @@ namespace Microsoft.Extensions.Http.Logging.Test; public class HttpClientLoggerTest { - private const string TestRequestHeader = "RequestHeader"; - private const string TestResponseHeader = "ResponseHeader"; - private const string TestExpectedRequestHeaderKey = $"{HttpClientLoggingTagNames.RequestHeaderPrefix}{TestRequestHeader}"; - private const string TestExpectedResponseHeaderKey = $"{HttpClientLoggingTagNames.ResponseHeaderPrefix}{TestResponseHeader}"; + private const string TestRequestHeader = "Request-Header"; + private const string TestResponseHeader = "Response-Header"; + private const string TestExpectedRequestHeaderKey = $"{HttpClientLoggingTagNames.RequestHeaderPrefix}request-header"; + private const string TestExpectedResponseHeaderKey = $"{HttpClientLoggingTagNames.ResponseHeaderPrefix}response-header"; private const string TextPlain = "text/plain"; @@ -164,8 +164,8 @@ public async Task HttpLoggingHandler_AllOptions_LogsOutgoingRequest() var testEnricher = new TestEnricher(); - var testSharedRequestHeaderKey = $"{HttpClientLoggingTagNames.RequestHeaderPrefix}Header3"; - var testSharedResponseHeaderKey = $"{HttpClientLoggingTagNames.ResponseHeaderPrefix}Header3"; + var testSharedRequestHeaderKey = $"{HttpClientLoggingTagNames.RequestHeaderPrefix}header3"; + var testSharedResponseHeaderKey = $"{HttpClientLoggingTagNames.ResponseHeaderPrefix}header3"; var expectedLogRecord = new LogRecord { diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/LoggerMessageStateExtensionsTests.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/LoggerMessageStateExtensionsTests.cs new file mode 100644 index 00000000000..00722f0317b --- /dev/null +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/LoggerMessageStateExtensionsTests.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Microsoft.Extensions.Http.Logging.Internal; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.Extensions.Http.Logging.Test; + +public class LoggerMessageStateExtensionsTests +{ + [Fact] + public void AddRequestHeadersTest() + { + AddHeadersTest( + LoggerMessageStateExtensions.AddRequestHeaders, + HttpClientLoggingTagNames.RequestHeaderPrefix); + } + + [Fact] + public void AddResponseHeadersTest() + { + AddHeadersTest( + LoggerMessageStateExtensions.AddResponseHeaders, + HttpClientLoggingTagNames.ResponseHeaderPrefix); + } + + private static void AddHeadersTest(AddHeadersDelegate addHeaders, string prefix) + { + const string Header1 = "Accept-Charset"; + const string Header2 = "CONTENT-TYPE"; + + const string NormalizedHeader1 = "accept-charset"; + const string NormalizedHeader2 = "content-type"; + + List> headers = [new(Header1, "v1"), new(Header2, "v2")]; + + var state = new LoggerMessageState(); + int index = 0; + + state.ReserveTagSpace(2); + addHeaders(state, headers, ref index); + + Assert.Equal(prefix + NormalizedHeader1, state.TagArray[0].Key); + Assert.Equal("v1", state.TagArray[0].Value); + + Assert.Equal(prefix + NormalizedHeader2, state.TagArray[1].Key); + Assert.Equal("v2", state.TagArray[1].Value); + } + + private delegate void AddHeadersDelegate(LoggerMessageState state, List> items, ref int index); +}