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?
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 theLoggerFactory
. The interfaceILoggerFactory
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static TraceListener s_listener; | |
static void ConfigureTraceSourceLogging() | |
{ | |
var writer = File.CreateText("logfile.txt"); | |
s_listener = new TextWriterTraceListener(writer.BaseStream); | |
} |
The extension method AddTraceSource
adds trace source logging to .NET Core logging – passing a SourceSwitch to write verbose information, and the previously created TextWriterTraceListener
.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
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.
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
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
LikeLike
Hi Ozzy,
thanks for getting in contact. I created a issue on Github: https://github.com/ProfessionalCSharp/ProfessionalCSharp7/issues/52
Can you give me more details? Do you have an issue compiling the sample from the book or did you try to write the code in your own projects? Maybe a NuGet package for this logging provider is missing.
Greetings,
Christian
LikeLike
Can you share the details on how to use serilog.sinks.microsoftteams? I tried, but it didn’t worked. Thanks
LikeLike