This package makes it possible to manipulate how objects are logged to Serilog using attributes.
Install from NuGet:
Install-Package Destructurama.Attributed
Modify logger configuration:
using Destructurama;
...
var log = new LoggerConfiguration()
.Destructure.UsingAttributes()
...
Apply the LogWithName
attribute:
using Destructurama.Attributed;
...
public class PersonalData
{
[LogWithName("FullName")]
public string? Name { get; set; }
}
Apply the NotLogged
attribute:
using Destructurama.Attributed;
...
public class LoginCommand
{
public string? Username { get; set; }
[NotLogged]
public string? Password { get; set; }
}
When the object is passed using {@...}
syntax the attributes will be consulted.
var command = new LoginCommand { Username = "logged", Password = "not logged" };
log.Information("Logging in {@Command}", command);
Apply the NotLoggedIfDefault
attribute:
public class LoginCommand
{
public string Username { get; set; }
[NotLoggedIfDefault]
public string Password { get; set; }
[NotLoggedIfDefault]
public DateTime TimeStamp { get; set; }
}
Apply the NotLoggedIfNull
attribute:
public class LoginCommand
{
/// <summary>
/// `null` value results in removed property
/// </summary>
[NotLoggedIfNull]
public string Username { get; set; }
/// <summary>
/// Can be applied with [LogMasked] or [LogReplaced] attributes
/// `null` value results in removed property
/// "123456789" results in "***"
/// </summary>
[NotLoggedIfNull]
[LogMasked]
public string Password { get; set; }
/// <summary>
/// Attribute has no effect on non-reference and non-nullable types
/// </summary>
[NotLoggedIfNull]
public int TimeStamp { get; set; }
}
Ignore null properties can be globally applied during logger configuration without need to apply attributes:
var log = new LoggerConfiguration()
.Destructure.UsingAttributes(x => x.IgnoreNullProperties = true)
...
To prevent destructuring of a type or property at all, apply the LogAsScalar
attribute.
Apply the LogMasked
attribute with various settings:
Note that masking also works for properties of type IEnumerable<string>
or derived from it, for example, string[]
or List<string>
.
using Destructurama.Attributed;
...
public class CustomizedMaskedLogs
{
/// <summary>
/// 123456789 results in "***"
/// </summary>
[LogMasked]
public string? DefaultMasked { get; set; }
/// <summary>
/// [123456789,123456789,123456789] results in [***,***,***]
/// </summary>
[LogMasked]
public string[]? DefaultMaskedArray { get; set; }
/// <summary>
/// 123456789 results in "*********"
/// </summary>
[LogMasked(PreserveLength = true)]
public string? DefaultMaskedPreserved { get; set; }
/// <summary>
/// "" results in "***"
/// </summary>
[LogMasked]
public string? DefaultMaskedNotPreservedOnEmptyString { get; set; }
/// <summary>
/// 123456789 results in "#"
/// </summary>
[LogMasked(Text = "_REMOVED_")]
public string? CustomMasked { get; set; }
/// <summary>
/// 123456789 results in "#"
/// </summary>
[LogMasked(Text = "")]
public string? CustomMaskedWithEmptyString { get; set; }
/// <summary>
/// 123456789 results in "#########"
/// </summary>
[LogMasked(Text = "#", PreserveLength = true)]
public string? CustomMaskedPreservedLength { get; set; }
/// <summary>
/// 123456789 results in "123******"
/// </summary>
[LogMasked(ShowFirst = 3)]
public string? ShowFirstThreeThenDefaultMasked { get; set; }
/// <summary>
/// 123456789 results in "123******"
/// </summary>
[LogMasked(ShowFirst = 3, PreserveLength = true)]
public string? ShowFirstThreeThenDefaultMaskedPreservedLength { get; set; }
/// <summary>
/// 123456789 results in "***789"
/// </summary>
[LogMasked(ShowLast = 3)]
public string? ShowLastThreeThenDefaultMasked { get; set; }
/// <summary>
/// 123456789 results in "******789"
/// </summary>
[LogMasked(ShowLast = 3, PreserveLength = true)]
public string? ShowLastThreeThenDefaultMaskedPreservedLength { get; set; }
/// <summary>
/// 123456789 results in "123REMOVED"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowFirst = 3)]
public string? ShowFirstThreeThenCustomMask { get; set; }
/// <summary>
/// 123456789 results in "123_REMOVED_"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowFirst = 3, PreserveLength = true)]
public string? ShowFirstThreeThenCustomMaskPreservedLengthIgnored { get; set; }
/// <summary>
/// 123456789 results in "_REMOVED_789"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowLast = 3)]
public string? ShowLastThreeThenCustomMask { get; set; }
/// <summary>
/// 123456789 results in "_REMOVED_789"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowLast = 3, PreserveLength = true)]
public string? ShowLastThreeThenCustomMaskPreservedLengthIgnored { get; set; }
/// <summary>
/// 123456789 results in "123***789"
/// </summary>
[LogMasked(ShowFirst = 3, ShowLast = 3)]
public string? ShowFirstAndLastThreeAndDefaultMaskInTheMiddle { get; set; }
/// <summary>
/// 123456789 results in "123***789"
/// </summary>
[LogMasked(ShowFirst = 3, ShowLast = 3, PreserveLength = true)]
public string? ShowFirstAndLastThreeAndDefaultMaskInTheMiddlePreservedLength { get; set; }
/// <summary>
/// 123456789 results in "123_REMOVED_789"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowFirst = 3, ShowLast = 3)]
public string? ShowFirstAndLastThreeAndCustomMaskInTheMiddle { get; set; }
/// <summary>
/// 123456789 results in "123_REMOVED_789". PreserveLength is ignored"
/// </summary>
[LogMasked(Text = "_REMOVED_", ShowFirst = 3, ShowLast = 3, PreserveLength = true)]
public string? ShowFirstAndLastThreeAndCustomMaskInTheMiddlePreservedLengthIgnored { get; set; }
}
Apply the LogReplaced
attribute on a string to apply a RegEx replacement during Logging.
This is applicable in scenarios when a string contains both Sensitive and Non-Sensitive information. An example of this could be a string such as “Sensitive|NonSensitive”. Then apply the attribute like the following snippet:
[LogReplaced(@"([a-zA-Z0-9]+)\|([a-zA-Z0-9]+)", "***|$2")]
public property Information { get; set; }
// Will log: "***|NonSensitive"
LogReplaced
attribute is available with the following constructor:
LogReplaced(string pattern, string replacement)
Constructor arguments:
Available properties:
public class WithRegex
{
private const string REGEX_WITH_VERTICAL_BARS = @"([a-zA-Z0-9]+)\|([a-zA-Z0-9]+)\|([a-zA-Z0-9]+)";
/// <summary>
/// 123|456|789 results in "***|456|789"
/// </summary>
[LogReplaced(REGEX_WITH_VERTICAL_BARS, "***|$2|$3")]
public string? RegexReplaceFirst { get; set; }
/// <summary>
/// 123|456|789 results in "123|***|789"
/// </summary>
[LogReplaced(REGEX_WITH_VERTICAL_BARS, "$1|***|$3")]
public string? RegexReplaceSecond { get; set; }
}