Click here to Skip to main content
15,881,870 members
Articles / Web Development / HTML

Error logging in ASP.NET Core MVC (similar to ELMAH) for .Net Core 1.0

Rate me:
Please Sign up or sign in to vote.
3.12/5 (7 votes)
9 Jan 2017CPOL4 min read 24.9K   531   9   5
This article describes how to integrate simple error logger for .Net Core MVC 1.0 web project.

Introduction

Before the .Net Core was relased with it's Middleware architecture, the most conveniet way for logging errors in the web application was ELMAH or similar. But since .Net Core has different architecture, not module based, but middleware based, the ELMAH is not in action any more. This article introduces how to integrate a simple logger very similar to ELMAH that stores latest 200 errors in memory.

To see it in action, follow steps below:

  1. Download the source code, open project locally, make sure that you have .Net Core App 1.0 installed and latest updates for VS2015. Compile the project successfully.
  2. Run the web application and open http://localhost:28157/Home/EmulateError in browser, to simulate application exception in the controller action. Refresh the page several times.
  3. Open MVC Logger error history page http://localhost:28157/ML to see the information about recent application exceptions. Navigate to the details page by clicking on the first left column link.

Background

The only proposed by Misrosoft error logging solution - is Application Insights, which is payeble Azure based service. It has a lot of different metrics, boards, is heavy with major goal to stabilize application and improve reliability. So most of the developers, who is doing web applications based on .Net platform need a simple, light weight, free application logger, to easily access error history with details. The following way you can create your own customizable .Net Core logger for Middleware architecture.

Using the code

The proposed solution is based on the Microsoft Elm https://github.com/aspnet/Diagnostics, but has significant modifications. Below is explanation of the code base.

The logger itself is a Middleware layer. That's why it needs to set proper configuration to get it involved in application execution flow. In the application Startup.cs file (located at the root), need to register logger Middleware service:

C#
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcLogger(loggerOptions =>
    {
        loggerOptions.Filter = (loggerName, loglevel) => loglevel == LogLevel.Error;
    });

    ........
}

The next step is to configure the logger page and error capture in the execution pipeline:

C#
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    ........

    if (env.IsDevelopment())
    {
        app.UseMvcLoggerPage();
        app.UseMvcLoggerCapture();
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    ........
}

Thats it. The configuration is done. Now you can access the logger screen on the same host with 'ML' path -> http(s)://...host.../ML . That Url can be simply changed to any other path in the CoreMvcLogger/MLCaptureMiddleware.cs -> 

C#
public class MVCLoggerOptions
{
    public PathString Path { get; set; } = new PathString("/Ml");

    ..........
}

The default ASP.NET Core MVC error details page is NOT replaced by this error logging middleware, so in case of any error on the development environment you would still see the default error details pretty view:

default core mvc error screen

The newly integrated MVC Logger error list screen:

the error list shown by MVC logger

When clicking on the Url link in the first left column, all scope variables along with exception details are show:

error details screen by mvc logger

The advantages of using own simple error logger are significant, let's count just some of them:

  • it's free of charge, originaly was distributed by Microsoft for developers;
  • MVC logger captures all application errors that happened for any user of the system, in all Middleware layers, stores the details and allows easily access for the issue investigation;
  • detailed information about exception and all environment ( request ) variables can be accessed in two mouse clicks;
  • the MVC logger errors list view is located on the same host with application, so accessing it is simple and fast.
  • both screens are available: the detailed, default MVC Error report page and MVC logger historical error snapshot page.
  • since the source code is available it can be easily modified per current needs, for example capturing application - specific information during request, filtering out errors that are not interested, etc.

Explaining Logger code

Originally, the sample .Net Core logger has been published by Microsoft on their GitHub Account https://github.com/aspnet/Diagnostics/tree/dev/src but there are multiple issues with that logger. It requires specific version of the .Net Core (currently there are three versions 'preview', 'release candidate', 'final'). There are issues with expception details rendering on the error screen, because there is razor parser engine parser / generator is in use. Error filtering by LogLevel Severity is not working, etc. All of these issues are fixed in the the stable MVC Logger, which is attached to the current article. Your own MVC Logger can be reworked as you wish, because it's not compiled into dll, but should be included into project. 

mvc error logger files structure in the project

MVC Logger service entities are added in the MVCLoggerServiceCollectionExtensions.cs file

C#
public static IServiceCollection AddMl(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    services.AddOptions();
    services.TryAddSingleton<MVCLoggerStore>();
    services.TryAddSingleton<MVCLoggerProvider>();

    return services;
}

Registering middlewares for page render and error capture in done in the MLExtensions.cs file

C#
public static IApplicationBuilder UseMvcLoggerCapture(this IApplicationBuilder app)
{
    if (app == null)
    {
        throw new ArgumentNullException(nameof(app));
    }

    // add the mvc logger provider to the factory here so the logger can start capturing logs immediately
    var factory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
    var provider = app.ApplicationServices.GetRequiredService<MVCLoggerProvider>();
    factory.AddProvider(provider);

    return app.UseMiddleware<MLCaptureMiddleware>();
}

public static IApplicationBuilder UseMvcLoggerPage(this IApplicationBuilder app)
{
    if (app == null)
    {
        throw new ArgumentNullException(nameof(app));
    }

    return app.UseMiddleware<MVCLoggerPageMiddleware>();
}

Every http request is represented by scope. Everything, which is happening during that http request belongs to particular scope. That's why when an exception is captured it is saved inside root scope, instead of just pushing to common list. The code for starting a new scope is in the MVCLogger.cs file 

C#
public IDisposable BeginScope<TState>(TState state)

The actual code for error or trace info capturing is also in the MVCLogger.cs file 

C#
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
                          Func<TState, Exception, string> formatter)

There is a code in the MVCLoggerPageMiddleware.cs file for filtering out all scopes, when rendering list page, that don't have any logged information that feets the filter criteria, this code should be customized per your needs:

C#
private bool HasAnyRelevantMessage(MVCLoggerScopeNode node, MVCLoggerOptions options)
{
    var hasAnyRelevantMessage = node.Messages.Count > 0 && node.Messages.Any(ms => options.Filter("any", ms.Severity));
            
    foreach (var nd in node.Children)
    {
        hasAnyRelevantMessage = hasAnyRelevantMessage | HasAnyRelevantMessage(nd, options);
    }

    return hasAnyRelevantMessage;
}

The code for rendering Error list html page and Error details html page is located in the MVCLoggerPageMiddleware.cs class.

C#
private void RenderMainLogPage(MVCLoggerViewOptions options, HttpContext context)

private void RenderDetailsPage(MVCLoggerViewOptions options, HttpContext context)

n case you need to change amount of error reports, stored in memory, you can do that in the MVCLoggerStore.cs file:

C#
public class MVCLoggerStore
{
    private const int Capacity = 200;

    ........
}

History

Released by Sem on Monday, 9th of January 2017

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Ukraine Ukraine
Individual software developer, good experience with PHP, Java, .Net, NodeJs, Ruby on Rails, JavaScript, LESS, CSS3, HTML5, jQuery, CoffeeScript, ExtJS, Backbone, AngularJS, AJAX, Amazon s3 services, Azure. Programming is not just my work, but more like a hobby. I'm glad to learn new pattern and technologies, making research and study new things. I work remotely for couple of years. More than nine years I'm writing commercial projects.

Comments and Discussions

 
QuestionReally useful Pin
Darren Y29-Jan-18 2:04
Darren Y29-Jan-18 2:04 
Questionmaybe a language thing, but... Pin
tlang334-Sep-17 14:04
tlang334-Sep-17 14:04 
I understand english might not be your first language. So it would have been better to use 'Elmah-like....' in your title. Then people will get the idea and not feel like they've been deceived. I'm sure that wasn't your intention. Otherwise great article.
AnswerRe: maybe a language thing, but... Pin
Sem Shekhovtsov25-Sep-17 4:13
professionalSem Shekhovtsov25-Sep-17 4:13 
QuestionThis is not Elmah! Pin
Brady Kelly27-Aug-17 4:40
Brady Kelly27-Aug-17 4:40 
AnswerRe: This is not Elmah! Pin
Sem Shekhovtsov25-Sep-17 4:10
professionalSem Shekhovtsov25-Sep-17 4:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.