CodeBlanks Logo Icon CodeBlanks

Structured logging

C# course · Advanced · Lesson 76/83

Learn structured logging and placeholders.

Structured logging

Structured logging writes the message not as a flat string but with named placeholders: logger.LogInformation("User {User} signed in", user). The logger parses the placeholders; the values are stored as fields.

Benefit: log sinks (Serilog, Elasticsearch) make fields queryable. Searching for all events where "User=Ayşe" is easy. With a flat string only full-text search would work. Placeholder names should be meaningful and consistent.

Placeholder syntax

A placeholder is {Name}; the name starts with a capital (PascalCase). Values bind in order: LogInformation("{User} {Action}", user, action). Here {User} takes the first argument, {Action} the second.

Note: $"User {user}" differs from LogInformation("User {User}", user). The former pre-formats the string (structure is lost); the latter passes the template to the logger. For structured logging keep the template separate.

Exercise Apply structured logging

Define an ILogger<T> interface with void LogInformation(string message, params object[] args). ConsoleLogger<T> implements it: each {...} placeholder is replaced in order by the next argument, then Console.WriteLine("[INF] " + message) is called. In Main, create a ConsoleLogger<Program> and call LogInformation("Kullanıcı {User} {Action} yaptı", "Ayşe", "giriş").

Show example solution
Define interface ILogger<T> { void LogInformation(string message, params object[] args); }. Write class ConsoleLogger<T> : ILogger<T> { public void LogInformation(string message, params object[] args) { int i = 0; while (i < args.Length) { int s = message.IndexOf('{'); if (s < 0) break; int e = message.IndexOf('}', s); if (e < 0) break; message = message.Substring(0, s) + args[i] + message.Substring(e + 1); i++; } Console.WriteLine("[INF] " + message); } }. In Main: var l = new ConsoleLogger<Program>(); l.LogInformation("Kullanıcı {User} {Action} yaptı", "Ayşe", "giriş");
Example solution:
using System;

public interface ILogger<T>
{
    void LogInformation(string message, params object[] args);
}

public class ConsoleLogger<T> : ILogger<T>
{
    public void LogInformation(string message, params object[] args)
    {
        int i = 0;
        while (i < args.Length)
        {
            int s = message.IndexOf('{');
            if (s < 0) break;
            int e = message.IndexOf('}', s);
            if (e < 0) break;
            message = message.Substring(0, s) + args[i] + message.Substring(e + 1);
            i++;
        }
        Console.WriteLine("[INF] " + message);
    }
}

class Program
{
    static void Main()
    {
        var logger = new ConsoleLogger<Program>();
        logger.LogInformation("Kullanıcı {User} {Action} yaptı", "Ayşe", "giriş");
    }
}

Test yourself

  1. Match structured logging terms

    {User}
    Named placeholder
    params object[] args
    Placeholder values
    message template
    String containing placeholders
    field
    Queryable field
  2. Complete the structured message

    logger.LogInformation("Kullanıcı ____ giriş yaptı", user);
    Show answer
    logger.LogInformation("Kullanıcı {User} giriş yaptı", user);
  3. What is the main gain of structured logging?

    • Lower memory use
    • Making fields queryable
    • More colorful output
    • Shorter code
    Show answer

    Making fields queryable — Correct. Named placeholders turn values into queryable fields.

All lessons in Advanced