Search code examples
c#.netserilog

Using a common property in serilog Logs


I am currently trying to understand serilogs for structured Logging. Is there a way to enforce common Property to be used in serilog . Like if I have a log written in code like below

log.Info("Disk Quota {​​​​​​​DiskQuota}​​​​​​​ exceeded by user {​​​​​​​Username}​​​​​​​", 100, "User1")

How can I use message template to ensure that any future log written in 2 to 3 classes where disk quota exceeding warning could be thrown and needs to be logged, uses always {Username} only and not {user} or {userid} etc.

log.Info("Disk Quota {​​​​​​​DiskQuota}​​​​​​​ exceeded by user {​​​​​​​User}​​​​​​​", 100, "User2") // Disallow , Possibly ??

Solution

  • There is nothing out-of-the-box in Serilog that does that for you.

    A good solution to this would be to implement your own source code analyzer, that can perform these checks during the build and emit warnings and/or errors when messages are similar but have different property names.

    You'd have to define a String metric to decide what "similar" means to you. There are a number of String metrics you can use, and each of them are similar but have some different characteristics, with the popular ones being:

    • Levenshtein Distance: The minimum number of single-character edits required to change one word into the other. Strings do not have to be the same length;
    • Hamming Distance: The number of characters that are different in two equal length strings;
    • Smith–Waterman: A family of algorithms for computing variable sub-sequence similarities;
    • Sørensen–Dice Coefficient: A similarity algorithm that computes difference coefficients of adjacent character pairs.

    You also leverage parts of the source code of the SerilogAnalyzer project to find message templates in code, and perform the checks.


    A much more simple solution (but not as effective because relies on good developer behavior) would be to declare these standard message templates in a class, inside of a project that is shared across your solution, so that you can reference them instead everywhere instead of typing.

    e.g.

    public static class LogMessageTemplates
    {
        public const string DiskQuotaExceededByUsername = "Disk Quota {​​​​​​​DiskQuota}​​​​​​​ exceeded by user {​​​​​​​Username}​​​​​​​";
    }
    

    And then everywhere you'd rely on developers to always use these pre-defined message templates, instead of typing the strings directly. e.g.:

    log.Info(LogMessageTemplates.DiskQuotaExceededByUsername, 100, "User1");
    

    If you use this approach you'll probably want to install SerilogAnalyzer in the project to help you identify places where the message template has X parameters but you are using Y given that it's now more difficult to spot how many properties a message template has just by looking at the name.