Logging

ASP.NET Core supports the logging API, which works with a wide variety of logging providers. We can write logs to one or more locations and we can also plug in third-party logging frameworks, such as NLog, Serilog, and so on. In this section, we will look at out-of-the box logging, as it is sufficient for the logging needs of most apps.

Let's see the architecture of the logging infrastructure. The code map diagram for logging is shown here:

As we can see, it consists of the following main components:

  • ILogger/ ILogger<TCategoryName>: We will use this in our app to log messages. We can see it has theIsEnabled() method to check whether logging is enabled and Log to write the log.
  • ILoggerFactory: It has a method to add a provider and logger.
  • ILoggerProvider: It has a method to create the logger and it will control the output location of the log.

We also notice the EventId, LogLevel enumeration, and extensions for registering the logging.

We can configure logging at the time of building the WebHost in the Program.cs file, as shown here:

  public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) => {
logging.AddConfiguration(hostingContext.
Configuration.GetSection("Logging"));

logging.AddConsole();
logging.AddDebug();
})
.Build();

This code configures the logging to read the logging configuration section provided in appsettings.json and adds console and debug logging. Alternatively, we can inject ILoggerFactory as a parameter in the Configure method of Startup.cs and configure loggerFactory , as shown here:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
loggerFactory.AddDebug();
//// Other methods omitted for brevity.
}

 Like any other type or service in the ASP.NET Core application, the logger is also injected into a class or controller through DI.  Here we inject ILogger<T>, where T is the name of the class. The type parameter T is used to define the category as it follows the ILogger<TCategoryName> interface.

For example, to write a log message in an ASP.NET Core controller, HomeController, we would inject the ILogger<HomeController> and call one of the logging extension methods on ILogger:

public class HomeController: Controller 
{
private readonly ILogger<HomeController> logger;

public HomeController(ILogger<HomeController> logger)
{
this.logger = logger;
}

public IActionResult Index()
{
logger.LogInformation($"Calling {nameof(this.Index)}");
return View();
}
}

This will write a log message to each output of the configured logging providers. The following is what it would look like in the console:

ASP.NET Core includes numerous logging providers out of the box, which we can use to write log messages to various locations:

  • Console provider: To write log messages to the console
  • Debug provider: To write log messages to the Debug window while debugging in Visual Studio
  • EventSource provider: To write log messages using Event Tracing for Windows
  • Event Log provider: To write log messages to the Windows event log

There is support for third-party structured logging as well, which is greatly useful, as it makes finding and diagnosing issues easier in production. Structured logging involves associating key-value pairs with each log entry, instead of a simple string of messages. We will not delve into this discussion, but it's good to know and the reader should take it up as an exercise to explore and implement structured logging with ASP.NET Core apps.

category is included with each log that is created. We specify the category while creating an ILogger object. The category may be any string, but the convention is to use the qualified name of the class from which the logs are written, like we did in our previous example. We can also specify the log level, which indicates the degree of severity or importance of the log. For example, typically we use an information log when a method executes normally, a warning log when a method returns a 404 return code, and an error log when an unexpected exception is caught. Log methods that include the level in the method name are extension methods for ILogger , such asLogError,LogWarning, LogInformation, and LogTrace. Behind the scenes, these methods call the Log method whcih takes a LogLevel parameter. ASP.NET Core defines the following log levels, ordered here from least to highest severity. The documentation of the code makes it intuitive to understand:

    //
// Summary:
// Defines logging severity levels.
public enum LogLevel
{
//
// Summary:
// Logs that contain the most detailed messages. These messages
may contain sensitive
// application data. These messages are disabled by default and
should never be
// enabled in a production environment.
Trace = 0,
//
// Summary:
// Logs that are used for interactive investigation during
development. These logs
// should primarily contain information useful for debugging
and have no long-term
// value.
Debug = 1,
//
// Summary:
// Logs that track the general flow of the application. These
logs should have long-term
// value.
Information = 2,
//
// Summary:
// Logs that highlight an abnormal or unexpected event in the
application flow,
// but do not otherwise cause the application execution to
stop.
Warning = 3,
//
// Summary:
// Logs that highlight when the current flow of execution is
stopped due to a failure.
// These should indicate a failure in the current activity, not
an application-wide
// failure.
Error = 4,
//
// Summary:
// Logs that describe an unrecoverable application or system
crash, or a catastrophic
// failure that requires immediate attention.
Critical = 5,
//
// Summary:
// Not used for writing log messages. Specifies that a logging
category should not
// write any messages.
None = 6
}

We can set minimum trace levels while configuring the logger as well, with the following code, which creates the WebHost in the Program.cs file:

.ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning))

We can also do scope-based logging; that is, logging, on a logical group of operations within a scope in order to attach same data to each log. The BeginScope method is specifically made for this purpose and the following example shows the sample usage:

 using (this.logger.BeginScope($"Logging scope demo"))
{
this.logger.LogInformation($"Calling {nameof(this.Index)}");
return View();
}

Before we conclude, let's have a look at the configuration for Logging in the appSettings.json file. Consider the following Logging configuration:

{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"LogLevel": {
"Microsoft.AspNetCore.Mvc.Razor": "Error",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}

The preceding configuration defines four logging filters. The following is the interpretation of the configuration:

  1. Scope-based logging is disabled, as we have IncludeScopes as false. One of the primary use cases of scope-based logging comes in transactional data access or operations, where you may want to attach the same identifier to all the operations that happen in the transaction.
  2. The default log level of the Debug log provider is Information for all categories.
  3. The default log level of the Console log provider is Information for all categories.
  4. The log level of categories beginning with Microsoft.AspNetCore.Mvc.Razor is Error for the Console log provider.
  5. The default log level for all log providers for all categories is Debug.

For a detailed and deep dive into logging in ASP.NET Core 2.0, readers should visit https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x.

With this, we conclude our discussion of logging, and this chapter as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset