Writing ILogger Diagnostics to a File

With .NET Core, diagnostic information can be written using the ILogger interface. ILogger offers provider-based functionality to write to the console, the debug window, the Windows Event Log, to Microsoft Azure Ap Services diagnostics and logs, as well as to the TraceSource and the EventSource. Adapters to write to 3rd party logging frameworks such as Log4Net and NLog are available as well. What about just logging to files?

Files

Logging with ILogger

The ILogger interface can be injected into a service, and easily used with extension methods to write information using different trace levels such trace, information, and error using the methods LogTrace, LogInformation, and LogError as shown in the following code snippet.


public class SampleController
{
private readonly ILogger<SampleController> _logger;
private readonly HttpClient _httpClient;
public SampleController(HttpClient httpClient, ILogger<SampleController> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_logger.LogTrace("ILogger injected into {0}", nameof(SampleController));
}
public async Task NetworkRequestSampleAsync(string url)
{
try
{
_logger.LogInformation(LoggingEvents.Networking, "NetworkRequestSampleAsync started with url {0}", url);
string result = await _httpClient.GetStringAsync(url);
_logger.LogInformation(LoggingEvents.Networking, "NetworkRequestSampleAsync completed, received {0} characters", result.Length);
}
catch (Exception ex)
{
_logger.LogError(LoggingEvents.Networking, ex, "Error in NetworkRequestSampleAsync, error message: {0}, HResult: {1}", ex.Message, ex.HResult);
}
}
}

The ILogger interface can also be used without dependency injection, using the LoggerFactory. The interface ILoggerFactory can also be injected using DI.

Configure Microsoft.Services.DependencyInjection

Making the application ready for using the ILogger interface, the logging is added to the DI container invoking the extension method AddLogging. Using the overload with the Action delegate, logging is configured with the providers for the console and the debug window. Configuration information is used as well by applying the section Logging with the configuration. Other than logging, the HTTP client factory is registered with the DI container as well.


static void RegisterServices(IConfiguration configuration)
{
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConfiguration(configuration.GetSection("Logging"))
.AddConsole();
#if DEBUG
builder.AddDebug();
#endif
});
services.AddHttpClient<SampleController>();
AppServices = services.BuildServiceProvider();
}

view raw

Program.cs

hosted with ❤ by GitHub

See my articles about dependency injection with Microsoft.Extensions.DependencyInjection

TraceSource to write files

The TraceSource class (namespace System.Diagnostics) to write diagnostic information was introduced with the .NET Framework 1.1. TraceSource gives a separation between tracing, providers where to log trace information, and a switch to turn tracing on or off. One of the available switches offers to differentiate trace levels such as verbose, information, and error traces. A provider needs to derive from the base class TraceListener. Derived from this class is the TextWriterTraceListener. Concrete classes deriving from TextWriterTraceListener are DelimitedListTraceListener and XmlWriterTraceListener which allow tracing to files.

To extend the .NET Core logging with trace source logging, the NuGet package Microsoft.Extensions.Logging.TraceSource is needed.

The method ConfigureTraceSourceLogging from the following code snippet creates a text file and passes the stream to the constructor of the TextWriterTraceListener. This listener can then be used with the configuration to log to the trace source.


private static TraceListener s_listener;
static void ConfigureTraceSourceLogging()
{
var writer = File.CreateText("logfile.txt");
s_listener = new TextWriterTraceListener(writer.BaseStream);
}

view raw

Program.cs

hosted with ❤ by GitHub

The extension method AddTraceSource adds trace source logging to .NET Core logging – passing a SourceSwitch to write verbose information, and the previously created TextWriterTraceListener.


static void RegisterServices(IConfiguration configuration)
{
ConfigureTraceSourceLogging();
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConfiguration(configuration.GetSection("Logging"))
.AddTraceSource(new SourceSwitch("TraceSourceLog", SourceLevels.Verbose.ToString()), s_listener)
.AddConsole();
#if DEBUG
builder.AddDebug();
#endif
});
services.AddHttpClient<SampleController>();
AppServices = services.BuildServiceProvider();
}

view raw

Program.cs

hosted with ❤ by GitHub

Running the application, the following log information is written to the log file. Because the provider is configured to write all verbose trace information, you can also see the information logged from the ClientHandler which is used with the HTTP client factory:

LoggingConfigurationSample.SampleController Information: 2002 : NetworkRequestSampleAsync started with url https://csharp.christiannagel.com
System.Net.Http.HttpClient.SampleController.LogicalHandler Information: 100 : Start processing HTTP request GET https://csharp.christiannagel.com/
System.Net.Http.HttpClient.SampleController.ClientHandler Information: 100 : Sending HTTP request GET https://csharp.christiannagel.com/
System.Net.Http.HttpClient.SampleController.ClientHandler Information: 101 : Received HTTP response after 857.3442ms - OK
System.Net.Http.HttpClient.SampleController.LogicalHandler Information: 101 : End processing HTTP request after 902.6099ms - OK
LoggingConfigurationSample.SampleController Information: 2002 : NetworkRequestSampleAsync completed, received 71575 characters

SeriLog to write files

A more modern way to write log information is offered from a third-party library Serilog. Serilog integrates with .NET Core logging and allows logging to many different sinks, e.g. to Email, Microsoft Teams, NLog, UDP, Azure Table Storage, and many others. With this library, you can also log to files.

To extend the .NET Core logging with serilog and logging to files, the NuGet packages Serilog.Extensions.Logging and Serilog.Sinks.File are needed.

To configure Serilog, either static members of the Log class in the namespace Serilog can be configured, or a new Logger can be created and supplied to the AddSerilog extension method.

With the following code snippet, the extension method AddSerilog is used to define another logging provider. Using the overload of AddSerilog to pass an ILogger, a logger is created with a new LoggerConfiguration that is configured to write to the File sink using the WriteTo property. With the configuration in place, the logger is created using the CreateLogger method.


static void RegisterServices(IConfiguration configuration)
{
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConfiguration(configuration.GetSection("Logging"))
.AddSerilog(new LoggerConfiguration().WriteTo.File("serilog.txt").CreateLogger())
.AddConsole();
#if DEBUG
builder.AddDebug();
#endif
});
services.AddHttpClient<SampleController>();
AppServices = services.BuildServiceProvider();
}

view raw

Program.cs

hosted with ❤ by GitHub

Running the application, now the log information shows up in the serilog configured log file:

2018-11-11 14:36:45.095 +01:00 [INF] NetworkRequestSampleAsync started with url https://csharp.christiannagel.com
2018-11-11 14:36:45.156 +01:00 [INF] Start processing HTTP request GET "https://csharp.christiannagel.com/"
2018-11-11 14:36:45.165 +01:00 [INF] Sending HTTP request GET "https://csharp.christiannagel.com/"
2018-11-11 14:36:46.026 +01:00 [INF] Received HTTP response after 857.3442ms - "OK"
2018-11-11 14:36:46.029 +01:00 [INF] End processing HTTP request after 902.6099ms - "OK"
2018-11-11 14:36:46.100 +01:00 [INF] NetworkRequestSampleAsync completed, received 71575 characters

Summary

The logging facility offered by .NET Core offers great flexibility. With your services you can work with ILogger and ILoggerFactory, and can use any logger provider configured. Providers for the console class and debugging are built-in. For logging to files, you can use the old and traditional trace source, or other providers such as Serilog which offers a new and modern API. No matter what you select, it’s just a configuration of the logging provider and doesn’t influence the implementation of your service classes.

If this information helped you programming your .NET Core applications, consider buying me a coffee which helps me staying up longer and writing more articles.

Buy Me A Coffee

More information on logging with .NET Core is in my book Professional C# 7 and .NET Core 2.0, and in my workshops.

Enjoy learning and programming!

Christian

Important links for the article:

Dependency Injection Articles
HTTP Client Factory with .NET Core 2.1
Configuration with .NET Core
Complete source code sample
Serilog

Photo ID 4073128 Files on shelf © Lane Erickson from Dreamstime

5 thoughts on “Writing ILogger Diagnostics to a File

  1. It appears ILoggingBuilder.AddDebug() is no longer available..

    Did something change? (Page 1176 C#7 and .NET 2. 0[ApplicationServices])
    {
    //..
    #if DEBUG
    builder.AddDebug();
    #endif
    //..
    }
    Thank you so much

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.