Best Practices for Error Handling and Logging in C# Applications Using NLog

Effective error handling and logging are key for stable C# applications. In this post, we explore best practices using NLog, covering setup, structured logging, and techniques to improve error visibility. Learn how to build reliable, maintainable applications with better logging.

Best Practices for Error Handling and Logging in C# Applications Using NLog

Error handling and logging are crucial to developing reliable, maintainable software, especially in production environments. Effective logging helps you identify and troubleshoot issues quickly, track application performance, and gain insights into user behavior. In C#, NLog is one of the most popular libraries for structured logging, known for its flexibility, performance, and ease of use.

In this post, we’ll dive into best practices for error handling and logging in C# applications, specifically focusing on NLog. We’ll cover everything from setting up NLog to structuring logs effectively, with tips on implementing consistent error handling to improve your application’s stability.

1. Setting Up NLog in a C# Application

To get started, install NLog in your project. You can use NuGet Package Manager in Visual Studio or the .NET CLI:

dotnet add package NLog
dotnet add package NLog.Config
dotnet add package NLog.Targets.Console

Once installed, create an NLog.config file in your project. This file defines log targets (where logs are stored) and rules (which logs get recorded). Here’s an example configuration to log to both a file and the console:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      throwConfigExceptions="true">
  <targets>
    <target name="logfile" xsi:type="File" fileName="logs/logfile.log" layout="${longdate} | ${level} | ${message} ${exception:format=toString,StackTrace}" />
    <target name="console" xsi:type="Console" layout="${longdate} | ${level} | ${message} ${exception:format=toString,StackTrace}" />
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="logfile, console" />
  </rules>
</nlog>

Explanation:

  • Targets specify where logs are sent. In this example, we’re logging to both a file and the console.
  • Rules define the log levels to capture (e.g., Info, Warn, Error) and direct them to the defined targets.

2. Initializing NLog in Your Application

Initialize NLog by creating a static Logger instance in each class or using dependency injection. Here’s an example of initializing NLog in a class:

using NLog;

public class ExampleService
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public void ProcessData()
    {
        Logger.Info("Starting data processing.");
        try
        {
            // Simulate some processing work
            throw new InvalidOperationException("Simulated error");
        }
        catch (Exception ex)
        {
            Logger.Error(ex, "An error occurred during data processing.");
            // Handle or rethrow exception as needed
        }
        Logger.Info("Data processing completed.");
    }
}

3. Best Practices for Error Handling with NLog

To get the most out of NLog for error handling, follow these best practices:

A. Log Contextual Information

  • Always add context to your logs, such as method names, variable values, or user information.
  • Use NLog’s structured logging feature by passing values as additional arguments, which can be formatted in your layout.
Logger.Error(ex, "Error processing order {OrderId}", orderId);

B. Use Log Levels Consistently

Use log levels appropriately to categorize log entries:

  • Info: General information on application flow.
  • Warn: Non-critical issues, such as retryable network failures.
  • Error: Critical issues that prevent a function from completing.
  • Fatal: Unrecoverable application errors.

Consistent log levels make it easier to filter and analyze logs.

C. Avoid Swallowing Exceptions

  • Log and handle exceptions, but avoid catching exceptions without either logging or rethrowing them.
  • If an exception must be ignored, log the reason why to avoid “silent failures.”

4. Practical Use Cases

A. Centralized Logging for Microservices

  • In a microservices environment, consider using a central logging system (e.g., Elastic Stack, Azure Monitor, or AWS CloudWatch).
  • Configure NLog to send logs to an external target, such as ElasticSearch, to centralize log collection and analysis across multiple services.

B. Correlation IDs for Tracing Requests

  • Implement correlation IDs to trace requests across different components.
  • Add a unique ID to each log entry within a request’s scope, making it easier to track a specific request’s journey through the application.
Logger.WithProperty("CorrelationId", correlationId).Info("Processing request.");

C. Asynchronous Logging for High-Performance Applications

  • In high-throughput applications, enable asynchronous logging in NLog to improve performance. This is especially useful for reducing blocking operations caused by logging.

Example configuration for async logging in NLog.config:

<target name="asyncFile" xsi:type="AsyncWrapper">
    <target xsi:type="File" fileName="logs/async-log.log" layout="${longdate} | ${level} | ${message} ${exception}" />
</target>

5. Advanced NLog Configurations

Using JSON Layouts for Structured Logging

JSON layouts make logs easier to parse when integrating with logging systems like Elastic Stack or Splunk.

Example JSON layout:

<target name="logfile" xsi:type="File" fileName="logs/logfile.json" layout="${json:time=${longdate}:level=${level}:message=${message}:exception=${exception:format=toString,StackTrace}}" />

B. Implementing Retention Policies for Log Files

  • Set up a file archive policy in NLog.config to avoid large log files taking up storage.
<target name="logfile" xsi:type="File" fileName="logs/logfile.log"
        archiveEvery="Day" archiveFileName="logs/archives/log.{#}.log"
        archiveNumbering="Rolling" maxArchiveFiles="7" />

6. Testing and Validating Logs

  • Verify Configuration: Test your NLog.config file in development to ensure logs appear as expected.
  • Monitor Logs in Production: Implement monitoring and alerting for critical logs (like Fatal errors) using a centralized logging platform. Alerts help you respond quickly to critical application issues.

Conclusion

Logging is a powerful tool for monitoring, debugging, and optimizing applications, especially in production environments. With NLog’s flexibility and power, you can implement a robust logging strategy that scales with your application’s needs. By following these best practices, you’ll improve error visibility, reduce debugging time, and create a maintainable logging structure.


Disclaimer: The views and opinions expressed on this website are solely those of the author and do not necessarily reflect the official policy or position of any employer or organization affiliated with the author.