• Stars
    star
    227
  • Rank 169,539 (Top 4 %)
  • Language
    C#
  • License
    Apache License 2.0
  • Created about 9 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Use attributes to control how complex types are logged to Serilog.

Destructurama.Attributed

Build status NuGet Status

This package makes it possible to manipulate how objects are logged to Serilog using attributes.

Enabling the module:

Install from NuGet:

Install-Package Destructurama.Attributed

Modify logger configuration:

var log = new LoggerConfiguration()
  .Destructure.UsingAttributes()
  ...

Changing a property name

Apply the LogWithName attribute:

public class PersonalData
{
    [LogWithName("FullName")]
    public string Name { get; set; }
}

snippet source | anchor

Ignoring a property

Apply the NotLogged attribute:

public class LoginCommand
{
    public string Username { get; set; }

    [NotLogged]
    public string Password { get; set; }
}

snippet source | anchor

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);

snippet source | anchor

Treating types and properties as scalars

To prevent destructuring of a type or property at all, apply the [LogAsScalar] attribute.

Masking a string property

Apply the LogMasked attribute with various settings:

  • Text: If set, the property value will be set to this text.
  • ShowFirst: Shows the first x characters in the property value.
  • ShowLast: Shows the last x characters in the property value.
  • PreserveLength: If set, it will swap out each character with the default value. Note that this property will be ignored if Text has been set to custom value.

Note that masking also works for properties of type IEnumerable<string> or derived from it, for example, string[] or List<string>.

Examples

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>
    ///  123456789 results in "#"
    /// </summary>
    [LogMasked(Text = "_REMOVED_")]
    public string CustomMasked { 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; }
}

snippet source | anchor

Masking a string property with regular expressions

Apply the LogReplaced attribute on a string to apply a RegEx replacement during Logging.

This is applicable in scenarios which 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:

  • pattern: The pattern that should be applied on value.
  • replacement: The string that will be applied by RegEx.

Available properties:

  • Options: The RegexOptions that will be applied. Defaults to RegexOptions.None
  • Timeout: A time-out interval to evaluate regular expression. Defaults to Regex.InfiniteMatchTimeout

Examples

public class WithRegex
{
    const string RegexWithVerticalBars = @"([a-zA-Z0-9]+)\|([a-zA-Z0-9]+)\|([a-zA-Z0-9]+)";

    /// <summary>
    /// 123|456|789 results in "***|456|789"
    /// </summary>
    [LogReplaced(RegexWithVerticalBars, "***|$2|$3")]
    public string RegexReplaceFirst { get; set; }

    /// <summary>
    /// 123|456|789 results in "123|***|789"
    /// </summary>
    [LogReplaced(RegexWithVerticalBars, "$1|***|$3")]
    public string RegexReplaceSecond { get; set; }
}

snippet source | anchor