How to use Log4Net with ASP.NET MVC

This post shows you how to use log4net in c# asp.net web application. We will use log4net to log error, then write the error to file or database.

Log4net is part of the Apache Logging Services project at the Apache Software Foundation. The Logging Services project is intended to provide cross-language logging services for purposes of application debugging and auditing. You can see more details at https://logging.apache.org/log4net/

Open your Manage Nuget Packages from the visual studio, then install log4net.

Next, open your web.config file then add config to log4net as below to write log to your sql database.

<configuration>
  <configSections>    
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>  
  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="AdoNetAppender" />
    </root>
    <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
      <bufferSize value="1" />
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <connectionString value="data source=.;initial catalog=Log;user id=sa;password=foxlearn;" />
      <commandText value="sp_Log_Insert" />
      <commandType value="StoredProcedure" />
      <parameter>
        <parameterName value="@date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>
      <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%thread" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@level" />
        <dbType value="String" />
        <size value="50" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%level" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@logger" />
        <dbType value="String" />
        <size value="255" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@message" />
        <dbType value="String" />
        <size value="4000" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%message" />
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@exception" />
        <dbType value="String" />
        <size value="2000" />
        <layout type="log4net.Layout.ExceptionLayout" />
      </parameter>
    </appender>
  </log4net>
</configuration>

Open SQL Server then run the script below to create a Log table

CREATE TABLE [dbo].[Log] (
    [Id] [int] IDENTITY (1, 1) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Thread] [varchar] (255) NOT NULL,
    [Level] [varchar] (50) NOT NULL,
    [Logger] [varchar] (255) NOT NULL,
    [Message] [varchar] (4000) NOT NULL,
    [Exception] [varchar] (2000) NULL
)

Run the script below to create a stored procedure insert data to the Log table

CREATE PROCEDURE sp_Log_Insert
(
    @Date DateTime,
    @Thread VARCHAR(255),
    @Level VARCHAR(50),
    @Logger VARCHAR(255),
    @Message VARCHAR(4000),
    @Exception VARCHAR(2000)
)
AS
    INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception])
    VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)

Create an ILogging interface as below to write error and infomation to file or database

public interface ILogging
{
    void WriteError(Exception ex);
    void WriteInfo(string message);
}

Create a Log4NetRepository class, then implement ILogging interface as below to log error and information

public class Log4NetRepository : ILogging
{
    log4net.ILog log;

    public Log4NetRepository()
    {
        log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }

    string BuildExceptionMessage(Exception ex)
    {
        string error = "Error in Path:" + HttpContext.Current.Request.Path;
        error += string.Format("{0}Raw Url:{1}", Environment.NewLine, HttpContext.Current.Request.RawUrl);
        error += string.Format("{0}Message:{1}", Environment.NewLine, ex.Message);
        error += string.Format("{0}Source:{1}", Environment.NewLine, ex.Source);
        error += string.Format("{0}Stack Trace:{1}", Environment.NewLine, ex.StackTrace);
        error += string.Format("{0}TargetSite:{1}", Environment.NewLine, ex.TargetSite);
        return error;
    }

    public void WriteError(Exception ex)
    {
        log.Error(BuildExceptionMessage(ex));
    }

    public void WriteInfo(string message)
    {
        log.Info(message);
    }
}

You can create a HandleErrorLoggingAttribute inheriting HandleErrorAttribute class, then override OnException method as shown below to filter the errors.

public class HandleErrorLoggingAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.ExceptionHandled = true;
            HandleErrorInfo handleError = new HandleErrorInfo(filterContext.Exception, "Controller", "Action");
            filterContext.Result = new ViewResult()
            {
                ViewName = "Error",
                ViewData = new ViewDataDictionary(handleError)
            };
            AppService.Logging.WriteError(filterContext.Exception);
        }
        else
            base.OnException(filterContext);
    }
}

Open the FilterConfig.cs file, then add HandleErrorAttribute to RegisterGlobalFilters which will allow you to filter errors and save errors to your database.

using System.Web;
using System.Web.Mvc;

namespace CsharpCode
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }
}

You can call directly Log4NetRepository or using dependency injection to manage your class. If you want to use DI, you should install autofac library from nuget package manager into your project, then add AppService class as below.

public class AppService
{
    static IContainer Container { get; set; }
    static AppService()
    {
        ContainerBuilder builder = new ContainerBuilder();        
        builder.RegisterType<Log4NetRepository>().As<ILogging>();        
        Container = builder.Build();
    }    
    public static ILogging Logging => Container.Resolve<ILogging>();    
}

I hope so you can integrate log4net to your web application to log error. It's very helpful for debug and track your error code.