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