*
Previous File I/O in C# Serialization and deserialization in C# Next

🛠️ Exception Handling & Logging in C#

Exception Handling and Logging in C#

Exception handling and logging are crucial for building robust and maintainable C# applications. Exception handling deals with runtime errors and allows the application to recover gracefully, while logging records events and exceptions for monitoring and debugging.

Exception Handling in C#

How it works: try, catch, finally, and throw

  • try: Encloses the code that might throw an exception.
  • catch: Catches an exception of a specific type. You can have multiple catch blocks to handle different exceptions.
  • finally: Contains code that is guaranteed to run, regardless of whether an exception was thrown or caught. This is useful for cleaning up resources.
  • throw: Throws a new exception or re-throws a caught exception to a higher level of the call stack.

🛠️ Exception Handling & Logging in C#

Exception handling allows your application to gracefully respond to unexpected errors. Logging helps you record those errors for diagnostics and monitoring. Together, they form the backbone of robust and maintainable applications.

⚙️ Basic Exception Handling Syntax

  try {
      // Code that may throw an exception
  }
  catch (InvalidOperationException ex) {
      Console.WriteLine("Handled specific exception: " + ex.Message);
  }
  catch (Exception ex) {
      Console.WriteLine("General exception: " + ex.Message);
  }
  finally {
      Console.WriteLine("Cleanup code always runs.");
  }
  

đź§± Best Practices

  • Catch specific exceptions before general ones.
  • Use finally or using blocks to release resources.
  • Don’t catch exceptions you can’t handle meaningfully.
  • Use guard clauses to fail fast and avoid deep nesting.
  • Preserve stack trace using throw; instead of throw ex;.

đź§ľ Logging Exceptions

  public static class Logger {
      public static void LogError(Exception ex, string message) {
          Console.WriteLine("❌ Error: " + message);
          Console.WriteLine("Details: " + ex.Message);
      }
  }

  try {
      // Risky code
  }
  catch (Exception ex) {
      Logger.LogError(ex, "Something went wrong");
  }
  

📦 Logging Frameworks

  • Microsoft.Extensions.Logging: Built-in logging for .NET Core.
  • Serilog: Structured logging with sinks (file, console, DB).
  • NLog: Flexible and widely used logging framework.
  • log4net: Apache-style logging for legacy apps.

📌 When to Use Exception Handling

  • To catch and respond to runtime errors.
  • To log unexpected failures for diagnostics.
  • To clean up resources like file handles or DB connections.
  • To prevent application crashes and provide user-friendly messages.

đźš« When Not to Use

  • For normal control flow—use conditionals instead.
  • To suppress errors silently—always log or rethrow.
  • To catch every exception—let some bubble up to higher layers.

⚠️ Precautions

  • Don’t expose internal exception details to users—log them securely.
  • Avoid swallowing exceptions without logging.
  • Be careful with async code—use try/catch inside async methods.
  • Use exception filters (catch when) for conditional handling.

🎯 Advantages

  • Resilience: Prevents crashes and improves stability.
  • Diagnostics: Logs help trace and fix bugs.
  • Security: Controlled error exposure protects sensitive info.
  • Maintainability: Clear error handling improves code quality.

Exception Handling Example

using System;
using System.IO;

public class FileProcessor
{
public void ReadFile(string path)
{
StreamReader? reader = null;
try
{
reader = new StreamReader(path);
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
// Log the specific exception
Console.WriteLine($"Error: The file '{path}' was not found.");
// Good practice: log the full exception details
LogException(ex);
}
catch (Exception ex)
{
// Catch other, less specific exceptions and re-throw
Console.WriteLine("An unexpected error occurred.");
LogException(ex);
throw; // Re-throw to propagate to the caller
}
finally
{
// The finally block ensures the resource is cleaned up
if (reader != null)
{
reader.Dispose();
}
}
}
private void LogException(Exception ex)
{
    // Placeholder for logging
    Console.WriteLine($"LOG: An exception of type {ex.GetType().Name} occurred.");
    Console.WriteLine($"LOG: Message: {ex.Message}");
    Console.WriteLine($"LOG: Stack Trace: {ex.StackTrace}");
}

} 

Exception Handling Best Practices

  • Catch specific exceptions first: Handle specific exceptions (like FileNotFoundException) before general ones (like Exception).
  • Use using for disposable objects: Use using for objects that implement IDisposable to ensure proper cleanup.
  • Don't swallow exceptions: Avoid empty catch blocks. Always log exceptions for easier debugging.
  • Re-throw correctly: Use throw; instead of throw ex; to preserve the original stack trace.
  • Use custom exceptions: Define custom exception classes for specific business logic errors.

Logging in C#

What is Logging?

Logging is the practice of recording important events and data during an application's execution. It provides a historical record for diagnostics, performance monitoring, and compliance. The modern .NET logging framework uses the ILogger interface.

Install Logging Provider

dotnet add package Microsoft.Extensions.Logging.Console

Logging Example using ILogger

using Microsoft.Extensions.Logging;

public class MyService
{
private readonly ILogger _logger;


public MyService(ILogger<MyService> logger)
{
    _logger = logger;
}

public void DoWork(int value)
{
    _logger.LogInformation("Starting work on value: {Value}", value);

    if (value < 0)
    {
        _logger.LogWarning("Negative value passed: {Value}", value);
    }

    try
    {
        if (value == 13)
        {
            throw new ArgumentException("Value 13 is considered bad luck.");
        }
        _logger.LogInformation("Work completed successfully.");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "An error occurred during work with value {Value}", value);
    }
}
}

public class Program
{
public static void Main(string[] args)
{
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});

    var service = new MyService(loggerFactory.CreateLogger<MyService>());

    service.DoWork(5);
    service.DoWork(-1);
    service.DoWork(13); // This will trigger the exception
}

} 

Logging Best Practices

  • Use structured logging: Use message templates with named parameters for machine-readable logs.
  • Use appropriate log levels:
    • Trace/Debug: Detailed messages for development.
    • Information: General application flow tracking.
    • Warning: Abnormal but recoverable events.
    • Error: Failures that require attention.
    • Critical: Severe errors that require immediate action.
  • Centralize log management: Use systems like Seq, ELK Stack, or Azure Application Insights.
  • Enrich logs with context: Include user ID, session ID, or transaction ID for easier debugging.
  • Avoid sensitive information: Never log passwords, credit card details, or other personal data.

📝 Conclusion

Exception handling and logging are essential for building reliable and maintainable C# applications. By following best practices and using the right tools, you can ensure your app recovers gracefully and provides meaningful insights when things go wrong.

Back to Index
Previous File I/O in C# Serialization and deserialization in C# Next
*
*