using System.Collections; using Microsoft.Extensions.Logging; namespace MS.Microservice.Core.Security.SensitiveData; public static class SensitiveLogExtensions { /// Projects a flat DTO before submitting structured values to any ILogger backend. public static void LogSensitive(this ILogger logger, LogLevel level, string eventName, T value) where T : class { if (!logger.IsEnabled(level)) return; ArgumentException.ThrowIfNullOrWhiteSpace(eventName); var fields = SensitiveValues.Project(value); logger.Log(level, new EventId(5100, eventName), new ProtectedState(eventName, fields), null, static (state, _) => state.ToString()); } private sealed class ProtectedState(string name, IReadOnlyDictionary fields) : IReadOnlyList> { private readonly KeyValuePair[] _values = [.. fields, new("{OriginalFormat}", name)]; public int Count => _values.Length; public KeyValuePair this[int index] => _values[index]; public IEnumerator> GetEnumerator() => ((IEnumerable>)_values).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public override string ToString() => name + " " + string.Join(", ", fields.Select(field => $"{field.Key}={field.Value}")); } }