diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index e110b23..8ff0130 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -38,7 +38,7 @@ jobs: tool: 'benchmarkdotnet' output-file-path: src/Benchmarks/BenchmarkDotNet.Artifacts/results/Combined.Benchmarks.json github-token: ${{ secrets.GITHUB_TOKEN }} - alert-threshold: '110%' + alert-threshold: '130%' comment-on-alert: true - name: Push benchmarks results if: github.event_name != 'pull_request' diff --git a/README.md b/README.md index f894ca2..1a52736 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ public class PersonalData public string? Name { get; set; } } ``` -snippet source | anchor +snippet source | anchor ## 2. Ignoring a property diff --git a/src/Destructurama.Attributed.Tests/Approval/Destructurama.Attributed.approved.txt b/src/Destructurama.Attributed.Tests/Approval/Destructurama.Attributed.approved.txt index e112361..894160f 100644 --- a/src/Destructurama.Attributed.Tests/Approval/Destructurama.Attributed.approved.txt +++ b/src/Destructurama.Attributed.Tests/Approval/Destructurama.Attributed.approved.txt @@ -4,6 +4,7 @@ namespace Destructurama.Attributed { public AttributedDestructuringPolicyOptions() { } public bool IgnoreNullProperties { get; set; } + public bool RespectLogPropertyIgnoreAttribute { get; set; } } public interface IPropertyDestructuringAttribute { diff --git a/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs b/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs index 64f5624..8abd804 100644 --- a/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs +++ b/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs @@ -1,4 +1,5 @@ using Destructurama.Attributed.Tests.Support; +using Microsoft.Extensions.Logging; using NUnit.Framework; using Serilog.Events; using Shouldly; @@ -63,6 +64,7 @@ public void AttributesAreConsultedWhenDestructuring() MutableScalar = new(), NotAScalar = new(), Ignored = "Hello, there", + Ignored2 = "Hello, there again", ScalarAnyway = new(), AuthData = new() { @@ -71,11 +73,14 @@ public void AttributesAreConsultedWhenDestructuring() } }; - var evt = DelegatingSink.Execute(customized); + var evt = DelegatingSink.Execute(customized, configure: opt => opt.RespectLogPropertyIgnoreAttribute = true); var sv = (StructureValue)evt.Properties["Customized"]; var props = sv.Properties.ToDictionary(p => p.Name, p => p.Value); + props.ShouldNotContainKey("Ignored"); + props.ShouldNotContainKey("Ignored2"); + props["ImmutableScalar"].LiteralValue().ShouldBeOfType(); props["MutableScalar"].LiteralValue().ShouldBe(new MutableScalar().ToString()); props["NotAScalar"].ShouldBeOfType(); @@ -145,6 +150,9 @@ public class Customized [NotLogged] public string? Ignored { get; set; } + [LogPropertyIgnore] + public string? Ignored2 { get; set; } + [LogAsScalar] public NotAScalar? ScalarAnyway { get; set; } diff --git a/src/Destructurama.Attributed.Tests/Destructurama.Attributed.Tests.csproj b/src/Destructurama.Attributed.Tests/Destructurama.Attributed.Tests.csproj index c5e68bc..8f7cb82 100644 --- a/src/Destructurama.Attributed.Tests/Destructurama.Attributed.Tests.csproj +++ b/src/Destructurama.Attributed.Tests/Destructurama.Attributed.Tests.csproj @@ -5,9 +5,11 @@ net8.0 false $(NoWarn);1591 + $(DefineConstants);CODE_GENERATION_ATTRIBUTES + diff --git a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs index 6dbebc6..6b3edf8 100644 --- a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs +++ b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs @@ -68,7 +68,18 @@ private static IEnumerable GetPropertiesRecursive(Type type) private CacheEntry CreateCacheEntry(Type type) { - static T GetCustomAttribute(PropertyInfo propertyInfo) => propertyInfo.GetCustomAttributes().OfType().FirstOrDefault(); + IPropertyDestructuringAttribute? GetPropertyDestructuringAttribute(PropertyInfo propertyInfo) + { + var attr = propertyInfo.GetCustomAttributes().OfType().FirstOrDefault(); + if (attr != null) + return attr; + + // Do not check attribute explicitly to not take dependency from Microsoft.Extensions.Telemetry.Abstractions package. + // https://github.com/serilog/serilog/issues/1984 + return _options.RespectLogPropertyIgnoreAttribute && propertyInfo.GetCustomAttributes().Any(a => a.GetType().FullName == "Microsoft.Extensions.Logging.LogPropertyIgnoreAttribute") + ? NotLoggedAttribute.Instance + : null; + } var classDestructurer = type.GetCustomAttributes().OfType().FirstOrDefault(); if (classDestructurer != null) @@ -76,19 +87,19 @@ private CacheEntry CreateCacheEntry(Type type) var properties = GetPropertiesRecursive(type).ToList(); if (!_options.IgnoreNullProperties && properties.All(pi => - GetCustomAttribute(pi) == null - && GetCustomAttribute(pi) == null)) + GetPropertyDestructuringAttribute(pi) == null + && pi.GetCustomAttributes().OfType().FirstOrDefault() == null)) { return CacheEntry.Ignore; } var optionalIgnoreAttributes = properties - .Select(pi => new { pi, Attribute = GetCustomAttribute(pi) }) + .Select(pi => new { pi, Attribute = pi.GetCustomAttributes().OfType().FirstOrDefault() }) .Where(o => o.Attribute != null) .ToDictionary(o => o.pi, o => o.Attribute); var destructuringAttributes = properties - .Select(pi => new { pi, Attribute = GetCustomAttribute(pi) }) + .Select(pi => new { pi, Attribute = GetPropertyDestructuringAttribute(pi)! }) .Where(o => o.Attribute != null) .ToDictionary(o => o.pi, o => o.Attribute); diff --git a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicyOptions.cs b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicyOptions.cs index 3f2c4b6..4d00e9a 100644 --- a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicyOptions.cs +++ b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicyOptions.cs @@ -13,4 +13,10 @@ public class AttributedDestructuringPolicyOptions /// and affected by this property only in case at least one property (or the type itself) has Destructurama attribute applied. /// public bool IgnoreNullProperties { get; set; } + + /// + /// Respect Microsoft.Extensions.Logging.LogPropertyIgnoreAttribute to not include property when destructuring an object for logging. + /// This works the same as when applying to the property but may help if you have no access to it's source code. + /// + public bool RespectLogPropertyIgnoreAttribute { get; set; } } diff --git a/src/Destructurama.Attributed/Attributed/NotLoggedAttribute.cs b/src/Destructurama.Attributed/Attributed/NotLoggedAttribute.cs index 8a3f830..40c4369 100644 --- a/src/Destructurama.Attributed/Attributed/NotLoggedAttribute.cs +++ b/src/Destructurama.Attributed/Attributed/NotLoggedAttribute.cs @@ -24,6 +24,8 @@ namespace Destructurama.Attributed; [AttributeUsage(AttributeTargets.Property)] public class NotLoggedAttribute : Attribute, IPropertyDestructuringAttribute { + internal static readonly NotLoggedAttribute Instance = new(); + /// public bool TryCreateLogEventProperty(string name, object? value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out LogEventProperty? property) { diff --git a/src/Destructurama.Attributed/Attributed/NotLoggedIfDefaultAttribute.cs b/src/Destructurama.Attributed/Attributed/NotLoggedIfDefaultAttribute.cs index 71751cb..af09497 100644 --- a/src/Destructurama.Attributed/Attributed/NotLoggedIfDefaultAttribute.cs +++ b/src/Destructurama.Attributed/Attributed/NotLoggedIfDefaultAttribute.cs @@ -48,7 +48,6 @@ bool IPropertyOptionalIgnoreAttribute.ShouldPropertyBeIgnored(string name, objec { if (value != null) { - if (type.IsValueType) { if (!_cache.TryGetValue(type, out CachedValue cachedValue))