diff --git a/README.md b/README.md index b924805..48db483 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,12 @@ public class CustomizedMaskedLogs [LogMasked(ShowFirst = 3, ShowLast = 3)] public string? ShowFirstAndLastThreeAndDefaultMaskInTheMiddle { get; set; } + /// + /// 123456789 results in "123456789", no mask applied + /// + [LogMasked(ShowFirst = -1, ShowLast = -1)] + public string? ShowFirstAndLastInvalidValues { get; set; } + /// /// 123456789 results in "123***789" /// @@ -278,7 +284,7 @@ public class CustomizedMaskedLogs public string? ShowFirstAndLastThreeAndCustomMaskInTheMiddlePreservedLengthIgnored { get; set; } } ``` -snippet source | anchor +snippet source | anchor ## 7. Masking a string property with regular expressions diff --git a/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs b/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs index 8802c3e..64f5624 100644 --- a/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs +++ b/src/Destructurama.Attributed.Tests/AttributedDestructuringTests.cs @@ -83,6 +83,8 @@ public void AttributesAreConsultedWhenDestructuring() props["ScalarAnyway"].LiteralValue().ShouldBeOfType(); props["Struct1"].LiteralValue().ShouldBeOfType(); props["Struct2"].LiteralValue().ShouldBeOfType(); + props["StructReturningNull"].LiteralValue().ShouldBeNull(); + props["StructNull"].LiteralValue().ShouldBeNull(); var str = sv.ToString(); str.Contains("This is a username").ShouldBeTrue(); @@ -152,6 +154,12 @@ public class Customized public Struct1 Struct1 { get; set; } public Struct2 Struct2 { get; set; } + + [LogAsScalar(isMutable: true)] + public StructReturningNull StructReturningNull { get; set; } + + [LogAsScalar(isMutable: true)] + public StructReturningNull? StructNull { get; set; } } public class UserAuthData @@ -174,4 +182,10 @@ public struct Struct2 public int SomeProperty { get; set; } public override string ToString() => "BBB"; } + + public struct StructReturningNull + { + public int SomeProperty { get; set; } + public override string ToString() => null!; + } } diff --git a/src/Destructurama.Attributed.Tests/MaskedAttributeTests.cs b/src/Destructurama.Attributed.Tests/MaskedAttributeTests.cs index 567ee1d..fc53f6f 100644 --- a/src/Destructurama.Attributed.Tests/MaskedAttributeTests.cs +++ b/src/Destructurama.Attributed.Tests/MaskedAttributeTests.cs @@ -105,6 +105,12 @@ public class CustomizedMaskedLogs [LogMasked(ShowFirst = 3, ShowLast = 3)] public string? ShowFirstAndLastThreeAndDefaultMaskInTheMiddle { get; set; } + /// + /// 123456789 results in "123456789", no mask applied + /// + [LogMasked(ShowFirst = -1, ShowLast = -1)] + public string? ShowFirstAndLastInvalidValues { get; set; } + /// /// 123456789 results in "123***789" /// @@ -359,6 +365,25 @@ public void LogMaskedAttribute_Shows_First_NChars_And_Last_NChars_Replaces_Value props["ShowFirstAndLastThreeAndDefaultMaskInTheMiddlePreservedLength"].LiteralValue().ShouldBe("123*456"); } + [Test] + public void LogMaskedAttribute_With_Invalid_Values_Should_Return_Value_As_Is() + { + // [LogMasked(ShowFirst = -1, ShowLast = -1)] + // -> "123456789", no mask applied + var customized = new CustomizedMaskedLogs + { + ShowFirstAndLastInvalidValues = "123456789" + }; + + var evt = DelegatingSink.Execute(customized); + + var sv = (StructureValue)evt.Properties["Customized"]; + var props = sv.Properties.ToDictionary(p => p.Name, p => p.Value); + + props.ContainsKey("ShowFirstAndLastInvalidValues").ShouldBeTrue(); + props["ShowFirstAndLastInvalidValues"].LiteralValue().ShouldBe("123456789"); + } + [Test] public void LogMaskedAttribute_Shows_First_NChars_And_Last_NChars_Then_Replaces_All_Other_Chars_With_Custom_Mask() { diff --git a/src/Destructurama.Attributed.Tests/ReplacedAttributeTests.cs b/src/Destructurama.Attributed.Tests/ReplacedAttributeTests.cs index 614cf30..b2d2f47 100644 --- a/src/Destructurama.Attributed.Tests/ReplacedAttributeTests.cs +++ b/src/Destructurama.Attributed.Tests/ReplacedAttributeTests.cs @@ -32,6 +32,12 @@ public class CustomizedRegexLogs /// [LogReplaced(REGEX_WITH_VERTICAL_BARS, "***|$2|****")] public string? RegexReplaceFirstThird { get; set; } + + /// + /// LogReplaced works only for string properties. + /// + [LogReplaced("does not matter", "does not matter")] + public int RegexReplaceForInt { get; set; } } [TestFixture] @@ -134,4 +140,20 @@ public void LogReplacedAttribute_Replaces_First_And_Third() props.ContainsKey("RegexReplaceThird").ShouldBeTrue(); props["RegexReplaceThird"].LiteralValue().ShouldBe("123|456|***"); } + + [Test] + public void LogReplacedAttribute_Should_Work_Only_For_String_Properties() + { + var customized = new CustomizedRegexLogs + { + RegexReplaceForInt = 42, + }; + + var evt = DelegatingSink.Execute(customized); + + var sv = (StructureValue)evt.Properties["Customized"]; + var props = sv.Properties.ToDictionary(p => p.Name, p => p.Value); + + props.ContainsKey("RegexReplaceForInt").ShouldBeFalse(); + } } diff --git a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs index bbb8d74..6dbebc6 100644 --- a/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs +++ b/src/Destructurama.Attributed/Attributed/AttributedDestructuringPolicy.cs @@ -68,25 +68,27 @@ private static IEnumerable GetPropertiesRecursive(Type type) private CacheEntry CreateCacheEntry(Type type) { - var classDestructurer = type.GetCustomAttribute(); + static T GetCustomAttribute(PropertyInfo propertyInfo) => propertyInfo.GetCustomAttributes().OfType().FirstOrDefault(); + + var classDestructurer = type.GetCustomAttributes().OfType().FirstOrDefault(); if (classDestructurer != null) return new(classDestructurer.CreateLogEventPropertyValue); var properties = GetPropertiesRecursive(type).ToList(); if (!_options.IgnoreNullProperties && properties.All(pi => - pi.GetCustomAttribute() == null - && pi.GetCustomAttribute() == null)) + GetCustomAttribute(pi) == null + && GetCustomAttribute(pi) == null)) { return CacheEntry.Ignore; } var optionalIgnoreAttributes = properties - .Select(pi => new { pi, Attribute = pi.GetCustomAttribute() }) + .Select(pi => new { pi, Attribute = GetCustomAttribute(pi) }) .Where(o => o.Attribute != null) .ToDictionary(o => o.pi, o => o.Attribute); var destructuringAttributes = properties - .Select(pi => new { pi, Attribute = pi.GetCustomAttribute() }) + .Select(pi => new { pi, Attribute = GetCustomAttribute(pi) }) .Where(o => o.Attribute != null) .ToDictionary(o => o.pi, o => o.Attribute); diff --git a/src/Destructurama.Attributed/Attributed/LogMaskedAttribute.cs b/src/Destructurama.Attributed/Attributed/LogMaskedAttribute.cs index 6caaafc..c2b90f0 100644 --- a/src/Destructurama.Attributed/Attributed/LogMaskedAttribute.cs +++ b/src/Destructurama.Attributed/Attributed/LogMaskedAttribute.cs @@ -53,15 +53,16 @@ private object FormatMaskedValue(string val) if (string.IsNullOrEmpty(val)) return PreserveLength ? val : Text; - if (ShowFirst == 0 && ShowLast == 0) + return (ShowFirst, ShowLast) switch { - if (PreserveLength) - return new string(Text[0], val.Length); - - return Text; - } + (0, 0) => PreserveLength ? new string(Text[0], val.Length) : Text, + ( > 0, 0) => ShowOnlyFirst(), + (0, > 0) => ShowOnlyLast(), + ( > 0, > 0) => ShowFirstAndLast(), + _ => val + }; - if (ShowFirst > 0 && ShowLast == 0) + string ShowOnlyFirst() { var first = val.Substring(0, Math.Min(ShowFirst, val.Length)); @@ -73,10 +74,9 @@ private object FormatMaskedValue(string val) mask = new(Text[0], val.Length - ShowFirst); return first + mask; - } - if (ShowFirst == 0 && ShowLast > 0) + string ShowOnlyLast() { var last = ShowLast > val.Length ? val : val.Substring(val.Length - ShowLast); @@ -90,7 +90,7 @@ private object FormatMaskedValue(string val) return mask + last; } - if (ShowFirst > 0 && ShowLast > 0) + string ShowFirstAndLast() { if (ShowFirst + ShowLast >= val.Length) return val; @@ -104,8 +104,6 @@ private object FormatMaskedValue(string val) return first + (mask ?? Text) + last; } - - return val; } /// diff --git a/src/Destructurama.Attributed/Util/AttributeFinder.cs b/src/Destructurama.Attributed/Util/AttributeFinder.cs deleted file mode 100644 index 6b5cc29..0000000 --- a/src/Destructurama.Attributed/Util/AttributeFinder.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 Destructurama Contributors, Serilog Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Reflection; - -namespace Destructurama.Util; - -internal static class AttributeFinder -{ - public static T GetCustomAttribute(this Type typeInfo) => - typeInfo.GetCustomAttributes().OfType().FirstOrDefault(); - - public static T GetCustomAttribute(this PropertyInfo propertyInfo) => - propertyInfo.GetCustomAttributes().OfType().FirstOrDefault(); -} diff --git a/src/Destructurama.Attributed/Util/CacheEntry.cs b/src/Destructurama.Attributed/Util/CacheEntry.cs index b1033b1..114df3b 100644 --- a/src/Destructurama.Attributed/Util/CacheEntry.cs +++ b/src/Destructurama.Attributed/Util/CacheEntry.cs @@ -17,7 +17,7 @@ namespace Destructurama.Util; -internal struct CacheEntry +internal readonly struct CacheEntry { public CacheEntry(Func destructureFunc) {