Common.Logging with PostSharp and NLog 2.0

3.1k Views Asked by At

I use Common.Logging as a wrapper around NLog 2.0. I've done this so that I can replace NLog with another logging provider in the future.

I also use PostSharp to not write a try catch block everytime I need one. I have a class that inherits the OnMethodBoundaryAspect:

[Serializable]
public class LogMethodAttribute : OnMethodBoundaryAspect
{
    private ILog logger;

    public LogMethodAttribute()
    {
        this.logger = LogManager.GetCurrentClassLogger();
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        logger.Debug(string.Format("Entering {0}.{1}.", args.Method.DeclaringType.Name, args.Method.Name));
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        logger.Debug(string.Format("Leaving {0}.{1}.", args.Method.DeclaringType.Name, args.Method.Name));
    }

    public override void OnException(MethodExecutionArgs args)
    {
        logger.Error(args.Exception.Message,args.Exception);
    }
}

I have configured Common.Logging as follows in my web.config:

<configSections>
<sectionGroup name="common">
  <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
</configSections>
<common>
<logging>
  <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog20">
    <arg key="configType" value="FILE" />
    <arg key="configFile" value="~/NLog.config" />
  </factoryAdapter>
</logging>
</common>

NLog.Config looks like this:

<?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"
  throwExceptions="true"
  internalLogLevel="Debug"
  internalLogToConsoleError="true"
  internalLogFile="c:\new projects/nlog-app.txt"
>

<!-- 
See http://nlog-project.org/wiki/Configuration_file 
for information on customizing logging rules and outputs.
 -->
<targets>
<target name="database"
        xsi:type="Database"
        commandText="INSERT INTO LogEvent(EventDateTime, EventLevel, UserName, MachineName, EventMessage, ErrorSource, ErrorClass, ErrorMethod, ErrorMessage, InnerErrorMessage) VALUES(@EventDateTime, @EventLevel, @UserName, @MachineName, @EventMessage, @ErrorSource, @ErrorClass, @ErrorMethod, @ErrorMessage, @InnerErrorMessage)"

        dbProvider="System.Data.SqlClient">
  <connectionString>
    Data Source=...;Initial Catalog=myDB;User Id=user;Password=pass;
  </connectionString>
  <installConnectionString>
     Data Source=...;Initial Catalog=myDB;User Id=user;Password=pass;
  </installConnectionString>

    <!-- parameters for the command -->
  <parameter name="@EventDateTime" layout="${date:s}" />
  <parameter name="@EventLevel" layout="${level}" />
  <parameter name="@UserName" layout="${identity}" />
  <parameter name="@MachineName" layout="${machinename}" />
  <parameter name="@EventMessage" layout="${message}" />
  <parameter name="@ErrorSource" layout="${event-context:item=error-source}" />
  <parameter name="@ErrorClass" layout="${event-context:item=error-class}" />
  <parameter name="@ErrorMethod" layout="${event-context:item=error-method}" />
  <parameter name="@ErrorMessage" layout="${event-context:item=error-message}" />
  <parameter name="@InnerErrorMessage" layout="${event-context:item=inner-error-message}" />
  <!-- commands to install database -->
  <install-command>
    <text>CREATE DATABASE myDB</text>
    <connectionString> Data Source=...;Initial Catalog=myDB;User Id=user;Password=pass;</connectionString>
    <ignoreFailures>true</ignoreFailures>
  </install-command>

  <install-command>
    <text>
      CREATE TABLE LogEvent(
      EventId int primary key not null identity(1,1),
      EventDateTime datetime,
      EventLevel nvarchar(50),
      UserName nvarchar(50),
      MachineName nvarchar(1024),
      EventMessage nvarchar(MAX),
      ErrorSource nvarchar(1024),
      ErrorClass nvarchar(1024),
      ErrorMethod nvarchar(1024),
      ErrorMessage nvarchar(MAX),
      InnerErrorMessage nvarchar(MAX));
    </text>
  </install-command>

  <!-- commands to uninstall database -->
  <uninstall-command>
    <text>DROP DATABASE myDB</text>
    <connectionString> Data Source=...;Initial Catalog=myDB;User Id=user;Password=pass;</connectionString>
    <ignoreFailures>true</ignoreFailures>
  </uninstall-command>

</target>
</targets>

<rules>
<logger name="*" levels="Error" writeTo="database" />
</rules>
</nlog>

The problem is that nothing is inserted in my table. When I put a logger in for example my HomeController on the index page and I call my logger.Error("an error") it adds a record to my table.

Can somebody help me?

2

There are 2 best solutions below

2
On

Are you decorating your controller methods with the LogMethodAttribute that you created?

Also, you'll want to adjust your logger rule to include more levels outside of just "Error", otherwise that's all you'll log.

Give this a try:

<rules>
<logger name="*" minLevel="Trace" writeTo="database" />
</rules>

Edit:

Have you tried moving your logger initialization into your method?

public override void OnEntry(MethodExecutionArgs args)
{
     this.logger = LogManager.GetCurrentClassLogger();
     logger.Debug(string.Format("Entering {0}.{1}.", args.Method.DeclaringType.Name, args.Method.Name));
}

Per Donald Belcham's Pluralsight course, aspect constructors are not executed at runtime, so perhaps your logger is not getting set properly.

1
On

add a static property logger in your class Aspect

public class LogAspect : OnMethodBoundaryAspect
{
    /// <summary>
    /// Gets or sets the logger.
    /// </summary>
    public static ILogger logger { get; set; }

set logger variable in your application init method with your ILogger class and exclude all methods before this initialization with AttributeExclude.

    [LogAspect(AttributeExclude = true)]
    protected void Application_Start()
    {
        _windsorContainer = new WindsorContainer();
        ApplicationDependencyInstaller.RegisterLoggingFacility(_windsorContainer);
        LogAspect.logger = _windsorContainer.Resolve<ILogger>();