Logging

Part 03 - Logging using NLog

NLog can be used to log information about the application's performance, errors, and other events. The library provides various targets, such as file, database, and event log, that can be used to store the log data. The configuration of NLog is typically done in an XML file, which allows for easy customization of the logging behavior.

csproj
<Project Sdk="Microsoft.NET.Sdk.Web">

	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="NLog.Web.AspNetCore" Version="5.*" />
		<PackageReference Include="NLog" Version="5.*" />
	</ItemGroup>

</Project>

Notes:
notes.

Runtime Package
install NLog.Web.AspNetCore (latest version) using NuGet Package Manager

Notes:
none.

ExceptionController
using BPX.Website.Models;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace BPX.Website.Controllers
{
    public class ExceptionController : BaseController<ExceptionController>
    {
        public ExceptionController(ILogger<ExceptionController> logger) : base(logger)
        {
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Index(int? statusCode = null)
        {
            string errorStatusCode = string.Empty;
            string errorMessage = string.Empty;
            string errorURL = string.Empty;

            if (statusCode != null)
            {
                var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

                if (statusCodeReExecuteFeature is not null)
                {
                    errorURL = string.Join(statusCodeReExecuteFeature.OriginalPathBase,
                                            statusCodeReExecuteFeature.OriginalPath,
                                            statusCodeReExecuteFeature.OriginalQueryString);
                }

                switch (statusCode.Value)
                {
                    // 400 Bad Request
                    case 400:
                        errorStatusCode = statusCode.Value.ToString() + " Bad Request";
                        errorMessage = string.Join("Your request is not properly formed: ", errorURL);
                        break;

                    // 401 Unauthorized
                    case 401:
                        errorStatusCode = statusCode.Value.ToString() + " Unauthorized";
                        errorMessage = string.Join("The page you requested is no authorized: ", errorURL);
                        break;

                    // 403 Forbidden
                    case 403:
                        errorStatusCode = statusCode.Value.ToString() + " Forbidden";
                        errorMessage = string.Join("The page you requested is forbidden: ", errorURL);
                        break;

                    // 404 Not Found
                    case 404:
                        errorStatusCode = statusCode.Value.ToString() + " Not Found";
                        errorMessage = string.Join("The page you requested is not found: ", errorURL);
                        break;

                    // 405 Method Not Allowed
                    case 405:
                        errorStatusCode = statusCode.Value.ToString() + " Method Not Allowed";
                        errorMessage = string.Join("The method you requested is not allowed: ", errorURL);
                        break;
                }

                // code to log the exception
                logger.Log(LogLevel.Error, errorStatusCode + Environment.NewLine + errorMessage + Environment.NewLine);
            }

            return View(new ErrorVM { ErrorStatusCode = errorStatusCode, ErrorMessage = errorMessage });
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            string errorMessage = string.Empty;
            var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionFeature != null)
            {
                // exception data from IExceptionHandlerPathFeature
                errorMessage = exceptionFeature.Error.Message;

                // code to log the exception
                logger.Log(LogLevel.Error, exceptionFeature.Path + Environment.NewLine + exceptionFeature.Error.Message + Environment.NewLine + exceptionFeature.Error.StackTrace);
            }

            return View(new ErrorVM { ErrorMessage = errorMessage });
        }
    }
}

Notes:
none.

appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Notes:
none

nlog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      internalLogFile="c:\temp\internal-nlog-AspNetCore.txt">

	<!-- enable asp.net core layout renderers -->
	<extensions>
		<add assembly="NLog.Web.AspNetCore"/>
	</extensions>

	<!-- the targets to write to -->
	<targets>
		<!-- File Target for all log messages with basic details -->
		<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
				layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />

		<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
		<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore-own-${shortdate}.log"
				layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" />

		<!-- Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
		<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
	</targets>

	<!-- rules to map from logger name to target -->
	<rules>
		<logger name="System.*" finalMinLevel="Warn" />
		<logger name="Microsoft.*" finalMinLevel="Warn" />
		<logger name="Microsoft.Hosting.Lifetime*" finalMinLevel="Info" />
		<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
	</rules>
</nlog>

Notes:
none

Program.cs
using NLog;
using NLog.Web;

var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main");

try
{
    var builder = WebApplication.CreateBuilder(args);

    // Add services to the container.
    builder.Services.AddControllersWithViews();

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    builder.Host.UseNLog();

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    app.UseExceptionHandler("/Exception/Error");
    app.UseStatusCodePagesWithReExecute("/Exception/Index", "?statusCode={0}");

    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    app.Run();
}
catch (Exception exception)
{
    // NLog: catch setup errors
    logger.Error(exception, "Stopped program because of exception");
    throw;
}
finally
{
    // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
    NLog.LogManager.Shutdown();
}

Notes:
none



 



Ginger CMS
the future of cms, a simple and intuitive content management system ...

ASP.NET MVC Application
best practices like Repository, LINQ, Dapper, Domain objects ...

CFTurbine
cf prototyping engine, generates boilerplate code and views ...

Search Engine LITE
create your own custom search engine for your web site ...

JRun monitor
monitors the memory footprint of JRun engine and auto-restarts a hung engine ...

Validation Library
complete validation library for your web forms ...