Debugging
Debug.WriteLine
Release Build
Software Development
.NET

Debug.WriteLine in release build

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Debug.WriteLine is completely removed from Release builds in .NET. The Debug class in System.Diagnostics is decorated with [Conditional("DEBUG")], which means the compiler strips all Debug.* calls when the DEBUG symbol is not defined. In Release configuration, DEBUG is not defined by default, so Debug.WriteLine statements produce zero overhead — no method call, no string formatting, no output. For logging in Release builds, use Trace.WriteLine, a logging framework (Serilog, NLog), or ILogger.

How It Works

csharp
1using System.Diagnostics;
2
3public void ProcessOrder(Order order)
4{
5    Debug.WriteLine($"Processing order: {order.Id}");  // REMOVED in Release
6    Debug.Assert(order != null, "Order cannot be null"); // REMOVED in Release
7
8    // Business logic...
9    Trace.WriteLine($"Order processed: {order.Id}");   // KEPT in Release
10}

Compiler Behavior

csharp
1// In Debug configuration (DEBUG symbol defined):
2Debug.WriteLine("Hello");  // Compiled and executed
3
4// In Release configuration (DEBUG symbol NOT defined):
5// Debug.WriteLine("Hello");  // Entire line is removed by the compiler
6// The compiled IL contains no trace of this call

The [Conditional("DEBUG")] attribute on the Debug class tells the C# compiler to exclude calls to its methods when the DEBUG preprocessor symbol is absent.

Debug vs Trace vs ILogger

csharp
1using System.Diagnostics;
2using Microsoft.Extensions.Logging;
3
4public class OrderService
5{
6    private readonly ILogger<OrderService> _logger;
7
8    public OrderService(ILogger<OrderService> logger)
9    {
10        _logger = logger;
11    }
12
13    public void Process(Order order)
14    {
15        // Debug.WriteLine — REMOVED in Release
16        Debug.WriteLine($"Debug: Processing {order.Id}");
17
18        // Trace.WriteLine — KEPT in Release (controlled by TRACE symbol)
19        Trace.WriteLine($"Trace: Processing {order.Id}");
20
21        // ILogger — KEPT in Release, configurable level
22        _logger.LogDebug("Processing {OrderId}", order.Id);
23        _logger.LogInformation("Order {OrderId} processed", order.Id);
24    }
25}
MethodDebug BuildRelease BuildConfigurable
Debug.WriteLineActiveRemovedNo (compile-time)
Trace.WriteLineActiveActiveListeners in config
ILogger.LogDebugActiveActiveLog level in config
Console.WriteLineActiveActiveNo (always runs)

Verifying the Behavior

csharp
1public class DebugTest
2{
3    public static void Main()
4    {
5        Console.WriteLine("Console: Always visible");
6        Debug.WriteLine("Debug: Only in Debug builds");
7        Trace.WriteLine("Trace: In both builds");
8
9        #if DEBUG
10        Console.WriteLine("Compiled with DEBUG symbol");
11        #else
12        Console.WriteLine("Compiled WITHOUT DEBUG symbol");
13        #endif
14    }
15}
bash
1# Debug build
2dotnet run -c Debug
3# Console: Always visible
4# Debug: Only in Debug builds    (visible in Output window)
5# Trace: In both builds
6# Compiled with DEBUG symbol
7
8# Release build
9dotnet run -c Release
10# Console: Always visible
11# Trace: In both builds
12# Compiled WITHOUT DEBUG symbol

Using Trace for Release Logging

csharp
1using System.Diagnostics;
2
3// Add trace listeners in app.config
4// <system.diagnostics>
5//   <trace autoflush="true">
6//     <listeners>
7//       <add name="file" type="System.Diagnostics.TextWriterTraceListener"
8//            initializeData="app.log" />
9//     </listeners>
10//   </trace>
11// </system.diagnostics>
12
13public void ProcessData()
14{
15    Trace.TraceInformation("Starting data processing");
16    Trace.TraceWarning("Large dataset detected");
17    Trace.TraceError("Processing failed for record 42");
18
19    // Trace with category
20    Trace.WriteLine("Custom message", "MyCategory");
21}

Trace is controlled by the TRACE preprocessor symbol, which is defined in both Debug and Release configurations by default.

Custom Conditional Methods

csharp
1// Create your own conditionally compiled methods
2[Conditional("DEBUG")]
3public static void DebugLog(string message)
4{
5    Console.WriteLine($"[DEBUG] {DateTime.Now:HH:mm:ss} {message}");
6    File.AppendAllText("debug.log", $"{DateTime.Now}: {message}\n");
7}
8
9[Conditional("VERBOSE")]
10public static void VerboseLog(string message)
11{
12    Console.WriteLine($"[VERBOSE] {message}");
13}
14
15// Usage
16DebugLog("Processing started");    // Removed in Release
17VerboseLog("Step 1 complete");     // Only if VERBOSE symbol is defined
xml
1<!-- Define custom symbols in .csproj -->
2<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
3  <DefineConstants>DEBUG;TRACE;VERBOSE</DefineConstants>
4</PropertyGroup>
5<PropertyGroup Condition="'$(Configuration)' == 'Release'">
6  <DefineConstants>TRACE</DefineConstants>
7</PropertyGroup>

Modern Logging with ILogger

csharp
1// Program.cs (.NET 6+)
2var builder = WebApplication.CreateBuilder(args);
3
4// Configure logging levels per environment
5// appsettings.Development.json
6// { "Logging": { "LogLevel": { "Default": "Debug" } } }
7
8// appsettings.Production.json
9// { "Logging": { "LogLevel": { "Default": "Warning" } } }
10
11var app = builder.Build();
12
13app.MapGet("/", (ILogger<Program> logger) =>
14{
15    logger.LogDebug("Debug message");       // Only in Development
16    logger.LogInformation("Info message");  // Development + Staging
17    logger.LogWarning("Warning message");   // All environments
18    logger.LogError("Error message");       // All environments
19    return "OK";
20});

ILogger is the recommended approach for .NET applications. Log levels are configured per environment without recompilation.

Common Pitfalls

  • Expecting Debug.WriteLine in production logs: Debug.WriteLine produces no output in Release builds — not even in the Output window. If you need logging in production, use Trace, ILogger, or a logging framework like Serilog.
  • Side effects in Debug calls: Debug.WriteLine(ExpensiveComputation()) — the entire expression including ExpensiveComputation() is removed in Release. If the method has side effects your code depends on, those effects disappear in Release builds.
  • Assuming Trace is always active: Trace requires the TRACE preprocessor symbol. While it is defined by default in both Debug and Release configurations, it can be removed. Verify your .csproj defines TRACE in Release.
  • String formatting overhead: Debug.WriteLine($"Processing {JsonSerializer.Serialize(largeObject)}") — in Debug builds, the string is constructed even though the output may go nowhere (no listener attached). Use Debug.WriteLineIf or check Debugger.IsAttached for expensive formatting.
  • Confusing #if DEBUG with [Conditional("DEBUG")]: #if DEBUG removes the code block at compile time. [Conditional("DEBUG")] removes the call sites but keeps the method definition. With [Conditional], the method exists in the assembly but is never called.

Summary

  • Debug.WriteLine is completely removed from Release builds — zero overhead
  • The [Conditional("DEBUG")] attribute causes the compiler to strip all Debug.* calls when DEBUG is not defined
  • Use Trace.WriteLine for logging that persists in Release builds
  • Use ILogger (Microsoft.Extensions.Logging) for configurable, environment-aware logging
  • Never rely on side effects inside Debug.* calls — they are removed in Release
  • #if DEBUG removes code blocks; [Conditional("DEBUG")] removes call sites

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.