csharp.lang.best-practice.structured-logging.structured-logging

profile photo of returntocorpreturntocorp
Author
unknown
Download Count*

String interpolation in log message obscures the distinction between variables and the log message. Use structured logging instead, where the variables are passed as additional arguments and the interpolation is performed by the logging library. This reduces the possibility of log injection and makes it easier to search through logs.

Run Locally

Run in CI

Defintion

rules:
  - id: structured-logging
    patterns:
      - pattern-either:
          - pattern: $LOG.Debug($"...")
          - pattern: $LOG.Error($"...")
          - pattern: $LOG.Fatal($"...")
          - pattern: $LOG.Information($"...")
          - pattern: $LOG.Verbose($"...")
          - pattern: $LOG.Warning($"...")
          - pattern: $LOG.LogCritical($"...")
          - pattern: $LOG.LogDebug($"...")
          - pattern: $LOG.LogError($"...")
          - pattern: $LOG.LogInformation($"...")
          - pattern: $LOG.LogTrace($"...")
          - pattern: $LOG.LogWarning($"...")
          - pattern: $LOG.Info($"...")
          - pattern: $LOG.Trace($"...")
          - pattern: $LOG.Warn($"...")
      - metavariable-regex:
          metavariable: $LOG
          regex: .*(log|LOG|Log)
    message: String interpolation in log message obscures the distinction between
      variables and the log message. Use structured logging instead, where the
      variables are passed as additional arguments and the interpolation is
      performed by the logging library. This reduces the possibility of log
      injection and makes it easier to search through logs.
    languages:
      - csharp
    severity: INFO
    metadata:
      cwe:
        - "CWE-117: Improper Output Neutralization for Logs"
      owasp:
        - A09:2021 - Security Logging and Monitoring Failures
      technology:
        - .net
        - serilog
        - nlog
      confidence: LOW
      references:
        - https://github.com/NLog/NLog/wiki/How-to-use-structured-logging
        - https://softwareengineering.stackexchange.com/questions/312197/benefits-of-structured-logging-vs-basic-logging
      category: best-practice
      subcategory:
        - audit
      likelihood: LOW
      impact: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

structured-logging.cs

using Microsoft.Extensions.Logging;
using Serilog;
using NLog;

class Program
{
    public static void SerilogSample()
    {
        using var serilog = new LoggerConfiguration().WriteTo.Console().CreateLogger();

        var position = new { Latitude = 25, Longitude = 134 };
        var elapsedMs = 34;

        // ok: structured-logging
        serilog.Information("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

        // ruleid: structured-logging
        serilog.Information($"Processed {position} in {elapsedMs:000} ms.");
    }

    public static void MicrosoftSample()
    {
        var loggerFactory = LoggerFactory.Create(builder => {
                builder.AddConsole();
            }
        );

        var logger = loggerFactory.CreateLogger<Program>();

        var position = new { Latitude = 25, Longitude = 134 };
        var elapsedMs = 34;

        // ok: structured-logging
        logger.LogInformation("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

        // ruleid: structured-logging
        logger.LogInformation($"Processed {position} in {elapsedMs:000} ms.");
    }

    public static void NLogSample()
    {
        var logger = NLog.LogManager.Setup().LoadConfiguration(builder => {
            builder.ForLogger().WriteToConsole();
        }).GetCurrentClassLogger();

        var position = new { Latitude = 25, Longitude = 134 };
        var elapsedMs = 34;

        // ok: structured-logging
        logger.Info("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

        // ruleid: structured-logging
        logger.Info($"Processed {position} in {elapsedMs:000} ms.");

        // try with different name
        var _LOG = logger;

        // ruleid: structured-logging
        _LOG.Info($"Processed {position} in {elapsedMs:000} ms.");
    }

    public static void NotLogging()
    {
        // System.Web.TraceContext.Warn does not support structured logging
        var traceContext = new FakeTraceContext();

        // ok: structured-logging
        traceContext.Warn($"hello world");
    }

    public static void Main()
    {
        SerilogSample();
        MicrosoftSample();
        NLogSample();
        NotLogging();
    }
}

class FakeTraceContext
{
    public void Warn(string foo) { }
}