System.Diagnostics.Debug
Standard .NET debug output class. Automatically disabled in production builds through DEBUG conditional compilation. Specialized for debug sessions in IDEs like Visual Studio, lightweight debug tool used for trace information output during development.
Library
System.Diagnostics.Debug
Overview
System.Diagnostics.Debug is a debugging logging class that comes standard with .NET Framework/.NET Core. It supports conditional compilation through ConditionalAttribute and only operates when the DEBUG symbol is defined. Specialized for simple debug output during the development phase, it provides lightweight logging functionality that is automatically disabled in production environments. It outputs logs to the Output Window and trace listeners during debugging in Visual Studio, functioning as the most basic debugging support tool for .NET developers.
Details
System.Diagnostics.Debug 2025 edition maintains its position as the oldest logging API in the .NET ecosystem, boasting over 20 years of history and stability. Through conditional compilation functionality, logging code is executed only in Debug builds and completely removed in Release builds, resulting in zero performance impact in production environments. With thread-safe design, it can be safely used in multi-threaded environments, and logs are directly displayed in Visual Studio's output window via DefaultTraceListener. While it lacks advanced features such as structured logging and log level control, it provides intuitive debugging support through simple string output.
Key Features
- Conditional Compilation: Operates only in DEBUG builds, completely removed in production
- Thread-Safe Design: Safe log output in multi-threaded environments
- Visual Studio Integration: Direct display to output window functionality
- Trace Listener Support: Flexible output destination configuration and customization
- Lightweight Implementation: Design that minimizes overhead
- Standard Equipment: No additional libraries required, included with .NET standard
Pros and Cons
Pros
- No additional dependencies or installation required as .NET standard equipment
- Zero performance impact in production environments through conditional compilation
- Perfect integration with Visual Studio development environment and seamless debugging experience
- Simple API with low learning cost, allowing immediate usage
- Thread-safe design ensuring safety during multi-threaded development
- Overwhelming stability and backward compatibility through over 20 years of proven track record
Cons
- No structured logging support, unsuitable for complex log analysis
- No log level control functionality, only same-level output available
- No asynchronous logging support, I/O performance constraints
- Insufficient functionality for serious application logging
- Difficult integration with modern log management tools
- No support for structured output formats like JSON
Reference Pages
- Debug Class - Microsoft Learn
- Logging and tracing in .NET - Microsoft Learn
- System.Diagnostics Namespace - Microsoft Learn
Code Examples
Installation and Setup
// No special installation required as .NET standard equipment
// Project file (.csproj) configuration example
// Define DEBUG symbol in debug build
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
// Define only TRACE in release build
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
// Make Debug class available with using statement
using System.Diagnostics;
// Configuration check at program start
Debug.WriteLine("System.Diagnostics.Debug initialization complete");
Debug.WriteLine($"Debug build: {Debugger.IsAttached}");
Basic Log Output
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
// Basic string output
Debug.WriteLine("Application started");
Debug.WriteLine("Processing...");
// Formatted output
string userName = "John Doe";
int userId = 12345;
Debug.WriteLine($"User info: {userName} (ID: {userId})");
// Difference between WriteLine and Write
Debug.Write("Progress: ");
Debug.WriteLine("100% complete");
// Indent control
Debug.Indent();
Debug.WriteLine("Process started");
Debug.WriteLine("Step 1 executing");
Debug.WriteLine("Step 2 executing");
Debug.Unindent();
Debug.WriteLine("Process completed");
// Multi-line log output
string[] steps = { "Initialize", "Authenticate", "Fetch Data", "Complete" };
foreach (string step in steps)
{
Debug.WriteLine($"Executing: {step}");
Thread.Sleep(500); // Process simulation
}
Debug.WriteLine("Application terminated");
}
}
// Debug output example within methods
public class UserService
{
public User GetUser(int id)
{
Debug.WriteLine($"GetUser called: ID={id}");
if (id <= 0)
{
Debug.WriteLine("Warning: Invalid user ID");
return null;
}
var user = Database.FindUser(id);
Debug.WriteLine($"User search result: {user?.Name ?? "Not found"}");
return user;
}
}
Conditional Logging
using System.Diagnostics;
public class OrderProcessor
{
public void ProcessOrder(Order order)
{
Debug.WriteLine("Order processing started");
// Conditional method using ConditionalAttribute
LogOrderDetails(order);
// Direct DEBUG symbol check
#if DEBUG
Debug.WriteLine($"Order content: {order.ToDebugString()}");
Debug.WriteLine($"Memory usage: {GC.GetTotalMemory(false)} bytes");
#endif
ProcessPayment(order);
Debug.WriteLine("Order processing completed");
}
// Conditional compilation method
[Conditional("DEBUG")]
private void LogOrderDetails(Order order)
{
Debug.WriteLine("=== Order Detail Debug Info ===");
Debug.WriteLine($"Order ID: {order.Id}");
Debug.WriteLine($"Customer ID: {order.CustomerId}");
Debug.WriteLine($"Item count: {order.Items.Count}");
Debug.WriteLine($"Total amount: {order.TotalAmount:C}");
Debug.WriteLine("===============================");
}
// Debug-specific detailed trace
[Conditional("DEBUG")]
private void TraceExecution(string methodName, string details = "")
{
var timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
var threadId = Thread.CurrentThread.ManagedThreadId;
Debug.WriteLine($"[{timestamp}] Thread-{threadId}: {methodName} {details}");
}
private void ProcessPayment(Order order)
{
TraceExecution(nameof(ProcessPayment), $"Amount: {order.TotalAmount}");
// Payment processing implementation...
TraceExecution(nameof(ProcessPayment), "Completed");
}
}
Log Level Configuration
using System.Diagnostics;
// Custom log level implementation (Debug.WriteLine is single level)
public enum DebugLevel
{
Error = 1,
Warning = 2,
Info = 3,
Verbose = 4
}
public static class DebugLogger
{
private static DebugLevel _currentLevel = DebugLevel.Info;
public static void SetLevel(DebugLevel level)
{
_currentLevel = level;
Debug.WriteLine($"Debug level set: {level}");
}
public static void Error(string message)
{
if (_currentLevel >= DebugLevel.Error)
{
Debug.WriteLine($"[ERROR] {DateTime.Now:HH:mm:ss} {message}");
}
}
public static void Warning(string message)
{
if (_currentLevel >= DebugLevel.Warning)
{
Debug.WriteLine($"[WARN] {DateTime.Now:HH:mm:ss} {message}");
}
}
public static void Info(string message)
{
if (_currentLevel >= DebugLevel.Info)
{
Debug.WriteLine($"[INFO] {DateTime.Now:HH:mm:ss} {message}");
}
}
public static void Verbose(string message)
{
if (_currentLevel >= DebugLevel.Verbose)
{
Debug.WriteLine($"[DEBUG] {DateTime.Now:HH:mm:ss} {message}");
}
}
}
// Usage example
public class DatabaseService
{
public void Initialize()
{
DebugLogger.SetLevel(DebugLevel.Verbose);
DebugLogger.Info("Database initialization started");
DebugLogger.Verbose("Loading connection string");
try
{
// Database connection process
DebugLogger.Verbose("Attempting database connection");
// ...connection process...
DebugLogger.Info("Database connection successful");
}
catch (Exception ex)
{
DebugLogger.Error($"Database connection failed: {ex.Message}");
throw;
}
}
}
.NET-Specific Features
using System.Diagnostics;
public class DotNetSpecificDebugging
{
public void DemonstrateFeatures()
{
// Visual Studio debugger detection
if (Debugger.IsAttached)
{
Debug.WriteLine("Visual Studio debugger is attached");
// Debugger breakpoint (pause execution when code runs)
if (Debugger.IsAttached)
{
Debugger.Break(); // Pause in debugger
}
}
// Application domain information
Debug.WriteLine($"Application domain: {AppDomain.CurrentDomain.FriendlyName}");
Debug.WriteLine($"Base directory: {AppDomain.CurrentDomain.BaseDirectory}");
// Assembly information
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
Debug.WriteLine($"Assembly name: {assembly.GetName().Name}");
Debug.WriteLine($"Version: {assembly.GetName().Version}");
// Stack trace information
var stackTrace = new StackTrace(true);
Debug.WriteLine($"Call stack depth: {stackTrace.FrameCount}");
// Process information
var process = Process.GetCurrentProcess();
Debug.WriteLine($"Process ID: {process.Id}");
Debug.WriteLine($"Process name: {process.ProcessName}");
Debug.WriteLine($"Physical memory: {process.WorkingSet64 / 1024 / 1024} MB");
// Performance counters (simplified version)
Debug.WriteLine($"Managed memory: {GC.GetTotalMemory(false) / 1024 / 1024} MB");
Debug.WriteLine($"Gen0 GC count: {GC.CollectionCount(0)}");
Debug.WriteLine($"Gen1 GC count: {GC.CollectionCount(1)}");
Debug.WriteLine($"Gen2 GC count: {GC.CollectionCount(2)}");
}
// Custom TraceListener for file output
public void SetupFileLogging()
{
// Text file output configuration
var fileListener = new TextWriterTraceListener("debug.log");
Debug.Listeners.Add(fileListener);
// Add console output as well
var consoleListener = new ConsoleTraceListener();
Debug.Listeners.Add(consoleListener);
Debug.WriteLine("File and console output configuration complete");
// Listener cleanup
Debug.Flush();
fileListener.Close();
Debug.Listeners.Remove(fileListener);
}
}
// ASP.NET Core integration example
public class DebugMiddleware
{
private readonly RequestDelegate _next;
public DebugMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
Debug.WriteLine($"Request started: {context.Request.Method} {context.Request.Path}");
await _next(context);
stopwatch.Stop();
Debug.WriteLine($"Request completed: {context.Response.StatusCode} ({stopwatch.ElapsedMilliseconds}ms)");
}
}
Performance Considerations
using System.Diagnostics;
public class PerformanceOptimizedDebugging
{
// Execute heavy processing only during debug
[Conditional("DEBUG")]
private void ExpensiveDebugOperation(object data)
{
// This processing is completely removed in RELEASE builds
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(data, Formatting.Indented);
Debug.WriteLine($"Object details:\n{serialized}");
}
// String interpolation optimization
public void OptimizedStringLogging(User user)
{
// NG: String is constructed every time
// Debug.WriteLine($"User processing: {user.Name} ({user.Id})");
// OK: String construction only when conditional
if (IsDebugEnabled())
{
Debug.WriteLine($"User processing: {user.Name} ({user.Id})");
}
// More efficient: Lazy evaluation with lambda expression
DebugWriteLineIf(() => $"User processing: {user.Name} ({user.Id})");
}
[Conditional("DEBUG")]
private static bool IsDebugEnabled() => true;
// Performance optimization through lazy evaluation
[Conditional("DEBUG")]
private static void DebugWriteLineIf(Func<string> messageFunc)
{
Debug.WriteLine(messageFunc());
}
// Efficiency through bulk logging
public void BulkLogging(IEnumerable<string> messages)
{
#if DEBUG
var sb = new StringBuilder();
sb.AppendLine("=== Bulk Log Start ===");
foreach (var message in messages)
{
sb.AppendLine($"- {message}");
}
sb.AppendLine("=== Bulk Log End ===");
Debug.WriteLine(sb.ToString());
#endif
}
// Memory usage monitoring
[Conditional("DEBUG")]
public void LogMemoryUsage(string checkpoint)
{
var process = Process.GetCurrentProcess();
var workingSet = process.WorkingSet64 / 1024 / 1024;
var managedMemory = GC.GetTotalMemory(false) / 1024 / 1024;
Debug.WriteLine($"[MEMORY] {checkpoint}: Physical={workingSet}MB, Managed={managedMemory}MB");
}
// High-frequency logging control
private static DateTime _lastLogTime = DateTime.MinValue;
private static readonly TimeSpan LogInterval = TimeSpan.FromSeconds(1);
public void ThrottledLogging(string message)
{
var now = DateTime.Now;
if (now - _lastLogTime >= LogInterval)
{
Debug.WriteLine($"[THROTTLED] {message}");
_lastLogTime = now;
}
}
}
// Debug timer for benchmarking
public class DebugTimer : IDisposable
{
private readonly string _name;
private readonly Stopwatch _stopwatch;
public DebugTimer(string name)
{
_name = name;
_stopwatch = Stopwatch.StartNew();
Debug.WriteLine($"[TIMER] {_name} started");
}
public void Dispose()
{
_stopwatch.Stop();
Debug.WriteLine($"[TIMER] {_name} completed: {_stopwatch.ElapsedMilliseconds}ms");
}
}
// Usage example
public void PerformanceTest()
{
using (new DebugTimer("Database processing"))
{
// Process implementation
Thread.Sleep(100);
}
// Processing time is automatically logged
}