From 5303d89451df787e9cb80f52a703e0e71285178b Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Fri, 29 Mar 2024 14:30:00 -0700 Subject: [PATCH 1/4] Introduce a logger provider that counts warnings and errors ...if an `IMeterFactory` is present (and does nothing otherwise). This will provide insight into things like how often unknown key identifiers are encountered. --- ...taProtectionServiceCollectionExtensions.cs | 1 + .../src/MetricsLoggerProvider.cs | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs diff --git a/src/DataProtection/DataProtection/src/DataProtectionServiceCollectionExtensions.cs b/src/DataProtection/DataProtection/src/DataProtectionServiceCollectionExtensions.cs index 5441d535db0c..dc656461c9c6 100644 --- a/src/DataProtection/DataProtection/src/DataProtectionServiceCollectionExtensions.cs +++ b/src/DataProtection/DataProtection/src/DataProtectionServiceCollectionExtensions.cs @@ -34,6 +34,7 @@ public static IDataProtectionBuilder AddDataProtection(this IServiceCollection s ArgumentNullThrowHelper.ThrowIfNull(services); services.TryAddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); services.AddOptions(); AddDataProtectionServices(services); diff --git a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs new file mode 100644 index 000000000000..03cd1a9e27d2 --- /dev/null +++ b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Metrics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Xml.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Win32; + +namespace Microsoft.AspNetCore.DataProtection; + +internal sealed class MetricsLoggerProvider : ILoggerProvider +{ + public const string MeterName = "Microsoft.AspNetCore.DataProtection"; + + private readonly Meter? _meter; + + private readonly ILogger _logger; + + public MetricsLoggerProvider() + { + _logger = NullLogger.Instance; + } + + public MetricsLoggerProvider(IMeterFactory meterFactory) + { + _meter = meterFactory.Create(MeterName); + + var errorCounter = _meter.CreateCounter( + "errors", + unit: "{error}", + description: "Number of errors that have occurred."); + var warningCounter = _meter.CreateCounter( + "warnings", + unit: "{warning}", + description: "Number of warnings that have occurred."); + + _logger = new MetricsLogger(errorCounter, warningCounter); + } + + ILogger ILoggerProvider.CreateLogger(string _categoryName) => _logger; + + void IDisposable.Dispose() => _meter?.Dispose(); + + private sealed class MetricsLogger : ILogger + { + private readonly Counter _errorCounter; + private readonly Counter _warningCounter; + + public MetricsLogger(Counter errorCounter, Counter warningCounter) + { + _errorCounter = errorCounter; + _warningCounter = warningCounter; + } + + IDisposable? ILogger.BeginScope(TState state) => null; + + bool ILogger.IsEnabled(LogLevel logLevel) => logLevel switch + { + LogLevel.Error => _errorCounter.Enabled, + LogLevel.Warning => _warningCounter.Enabled, + _ => false, + }; + + void ILogger.Log(LogLevel logLevel, EventId eventId, TState _state, Exception? _exception, Func _formatter) + { + switch(logLevel) + { + case LogLevel.Error: + if (_errorCounter.Enabled) + { + var tags = new TagList([new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture))]); + _errorCounter.Add(1, tags); + } + break; + case LogLevel.Warning: + if (_warningCounter.Enabled) + { + var tags = new TagList([new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture))]); + _warningCounter.Add(1, tags); + } + break; + } + } + } +} From 4cd363331e7d223add90d39862331375a6e84c31 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 1 Apr 2024 12:37:16 -0700 Subject: [PATCH 2/4] Scope to data protection categories and make level a tag --- .../src/MetricsLoggerProvider.cs | 76 ++++++++++--------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs index 03cd1a9e27d2..6ac743ef07a2 100644 --- a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs +++ b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.DataProtection; internal sealed class MetricsLoggerProvider : ILoggerProvider { public const string MeterName = "Microsoft.AspNetCore.DataProtection"; + private const string CategoryNamePrefix = "Microsoft.AspNetCore.DataProtection"; private readonly Meter? _meter; @@ -31,61 +32,66 @@ public MetricsLoggerProvider(IMeterFactory meterFactory) { _meter = meterFactory.Create(MeterName); - var errorCounter = _meter.CreateCounter( - "errors", - unit: "{error}", - description: "Number of errors that have occurred."); - var warningCounter = _meter.CreateCounter( - "warnings", - unit: "{warning}", - description: "Number of warnings that have occurred."); + var counter = _meter.CreateCounter( + "aspnetcore.dataprotection.log_messages", + unit: "{message}", + description: "Number of messages that have been logged."); - _logger = new MetricsLogger(errorCounter, warningCounter); + _logger = new MetricsLogger(counter); } - ILogger ILoggerProvider.CreateLogger(string _categoryName) => _logger; + ILogger ILoggerProvider.CreateLogger(string categoryName) => + categoryName.StartsWith(CategoryNamePrefix, StringComparison.Ordinal) + ? _logger + : NullLogger.Instance; void IDisposable.Dispose() => _meter?.Dispose(); private sealed class MetricsLogger : ILogger { - private readonly Counter _errorCounter; - private readonly Counter _warningCounter; + private readonly Counter _counter; - public MetricsLogger(Counter errorCounter, Counter warningCounter) + public MetricsLogger(Counter counter) { - _errorCounter = errorCounter; - _warningCounter = warningCounter; + _counter = counter; } IDisposable? ILogger.BeginScope(TState state) => null; - bool ILogger.IsEnabled(LogLevel logLevel) => logLevel switch + bool ILogger.IsEnabled(LogLevel logLevel) { - LogLevel.Error => _errorCounter.Enabled, - LogLevel.Warning => _warningCounter.Enabled, - _ => false, - }; + switch (logLevel) + { + case LogLevel.Critical: + case LogLevel.Error: + case LogLevel.Warning: + return _counter.Enabled; + default: + return false; + } + } void ILogger.Log(LogLevel logLevel, EventId eventId, TState _state, Exception? _exception, Func _formatter) { - switch(logLevel) + var levelName = logLevel switch { - case LogLevel.Error: - if (_errorCounter.Enabled) - { - var tags = new TagList([new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture))]); - _errorCounter.Add(1, tags); - } - break; - case LogLevel.Warning: - if (_warningCounter.Enabled) - { - var tags = new TagList([new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture))]); - _warningCounter.Add(1, tags); - } - break; + LogLevel.Critical => "critical", + LogLevel.Error => "error", + LogLevel.Warning => "warning", + _ => null, + }; + + if (levelName is null || !_counter.Enabled) + { + return; } + + var tags = new TagList( + [ + new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture)), + new("level", levelName), + ]); + _counter.Add(1, tags); } } } From 78a4a55f220c6d020d222dcb1efcd9d6744a7759 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 2 Apr 2024 17:12:38 -0700 Subject: [PATCH 3/4] Attempt to follow OTel naming conventions --- .../DataProtection/src/MetricsLoggerProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs index 6ac743ef07a2..924adc1ddfb8 100644 --- a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs +++ b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs @@ -33,7 +33,7 @@ public MetricsLoggerProvider(IMeterFactory meterFactory) _meter = meterFactory.Create(MeterName); var counter = _meter.CreateCounter( - "aspnetcore.dataprotection.log_messages", + "aspnetcore.data_protection.log_messages", unit: "{message}", description: "Number of messages that have been logged."); @@ -88,8 +88,8 @@ void ILogger.Log(LogLevel logLevel, EventId eventId, TState _state, Exce var tags = new TagList( [ - new("id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture)), - new("level", levelName), + new("aspnetcore.data_protection.log_message_id", eventId.Name ?? eventId.Id.ToString(CultureInfo.InvariantCulture)), + new("aspnetcore.data_protection.log_level", levelName), ]); _counter.Add(1, tags); } From 62a4aa50d9a032f3126cb4a685c1e04ca5ccf834 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Fri, 12 Apr 2024 09:16:27 -0700 Subject: [PATCH 4/4] Refine description Co-authored-by: Reiley Yang --- src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs index 924adc1ddfb8..8c2a4723600e 100644 --- a/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs +++ b/src/DataProtection/DataProtection/src/MetricsLoggerProvider.cs @@ -35,7 +35,7 @@ public MetricsLoggerProvider(IMeterFactory meterFactory) var counter = _meter.CreateCounter( "aspnetcore.data_protection.log_messages", unit: "{message}", - description: "Number of messages that have been logged."); + description: "Number of log records that have been logged."); _logger = new MetricsLogger(counter); }