Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
C#

C#: Use Interpolated String Handler for High-Performance Logging

- 30.05.26 - ErcanOPAK

⚡ Zero Allocation Until You Log

Logging with string interpolation allocates memory even when log level is off. Interpolated string handlers defer formatting until needed.

❌ Regular Interpolation (Always Allocates)

// Even if log level is Error and this is Debug
// string is still allocated!
_logger.LogDebug($"User {userId} logged in at {DateTime.Now}");

// 2 allocations: string + DateTime.ToString()

✅ LoggerMessage (No Allocation)

// No string allocation if log level disabled
_logger.LogDebug("User {UserId} logged in at {Time}", userId, DateTime.Now);

// Parameters passed directly, formatted only if needed

📝 Custom Interpolated Handler

[InterpolatedStringHandler]
public ref struct DebugLogHandler
{
    private readonly bool _enabled;
    private StringBuilder? _builder;
    
    public DebugLogHandler(int literalLength, int formattedCount, bool enabled, out bool shouldAppend)
    {
        _enabled = enabled;
        shouldAppend = enabled;
        if (enabled)
        {
            _builder = new StringBuilder(literalLength + formattedCount * 8);
        }
    }
    
    public void AppendLiteral(string s)
    {
        if (_enabled) _builder!.Append(s);
    }
    
    public void AppendFormatted(T value)
    {
        if (_enabled) _builder!.Append(value);
    }
    
    internal string GetFormattedString() => _builder?.ToString() ?? ";
}

public static class Debug
{
    public static void WriteLine(DebugLogHandler handler)
    {
        if (Debugger.IsAttached)
        {
            Console.WriteLine(handler.GetFormattedString());
        }
    }
}

// Usage - no allocation if debugger not attached!
Debug.WriteLine($"Processing {userId} at {DateTime.Now}");

✅ Built-in Logger Support

// .NET 6+ ILogger supports this automatically
_logger.LogDebug("User {UserId} logged in", userId);

// Even better: LoggerMessage.Define (source generator)
private static readonly Action _userLoggedIn =
    LoggerMessage.Define(
        LogLevel.Debug,
        EventIds.UserLoggedIn,
        "User {UserId} logged in");

// Usage - ultra-efficient
_userLoggedIn(_logger, userId, null);

💡 Performance Impact

  • Regular $”{value}”: Always allocates (~100ns + allocation)
  • Logger with params: Allocates only if log level enabled
  • LoggerMessage.Define: Zero allocation overhead
  • High-frequency logs: Can save millions of allocations

“20,000 debug logs per second in development. GC was killing performance. Switched to LoggerMessage.Define. GC pressure dropped 90%. Production logs are free when disabled.”

— Performance Engineer

Related posts:

C# “Lock Contention” — Why lock(this) Breaks Apps

C# Records — Why They Still Beat Classes for Domain Models

Why async void Should Almost Never Exist

Post Views: 5

Post navigation

C#: Use Throw Helpers for Concise Null Checks (.NET 6+)
Visual Studio: Navigate Backward (Ctrl + -) Like a Time Machine

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

June 2026
M T W T F S S
1234567
891011121314
15161718192021
22232425262728
2930  
« May    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (953)
  • How to add default value for Entity Framework migrations for DateTime and Bool (882)
  • Get the First and Last Word from a String or Sentence in SQL (838)
  • How to select distinct rows in a datatable in C# (808)
  • How to make theater mode the default for Youtube (805)
  • Add Constraint to SQL Table to ensure email contains @ (580)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (579)
  • Average of all values in a column that are not zero in SQL (538)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (505)
  • Find numbers with more than two decimal places in SQL (454)

Recent Posts

  • C#: Use String Interpolation Instead of Concatenation
  • C#: Use Tuples to Return Multiple Values from Methods
  • SQL: Use ISNULL and NULLIF for Smart NULL Handling
  • .NET Core: Use Data Annotations for Model Validation
  • Git: Use Git Clean to Remove Untracked Files
  • Ajax: Add Custom Headers to Fetch Requests
  • JavaScript: Use console.table to Display Arrays as Tables
  • HTML: Use Spellcheck Attribute to Enable Browser Spell Check
  • CSS: Use user-select to Prevent Text Selection
  • Windows 11: Use Snipping Tool for Instant Screenshots

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (953)
  • How to add default value for Entity Framework migrations for DateTime and Bool (882)
  • Get the First and Last Word from a String or Sentence in SQL (838)
  • How to select distinct rows in a datatable in C# (808)
  • How to make theater mode the default for Youtube (805)

Recent Posts

  • C#: Use String Interpolation Instead of Concatenation
  • C#: Use Tuples to Return Multiple Values from Methods
  • SQL: Use ISNULL and NULLIF for Smart NULL Handling
  • .NET Core: Use Data Annotations for Model Validation
  • Git: Use Git Clean to Remove Untracked Files

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com