Logging in .NET Mono on Linux and Windows using NLog

Here is a simple application that will log to console and syslog demon. On Windows platform we can use free edition of Kiwi Syslog Server. On Linux application will use both local and remote syslog. As a logger library I use NLog.

Sample solution can be downloaded from here https://github.com/mchudinov/LoggingNLog. It is compatible with Visual Studio 2012, Mono Develop 5, and Xamarin Studio 5.

1. Add NLog to the solution

NLog package
For logging to syslog demon on Linux we need NLog.Targets.Syslog

2. Configure NLog

NLog can be configured either in App.config or in an external file. I always configure NLog right in App.config, then I have less files to take care about. Simple App.config configuration look like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
  </configSections>
  <nlog>
    <variable name="Layout" value="${longdate} ${level:upperCase=true} ${message} (${callsite:includSourcePath=true})"/>
    <targets>
      <target name="debugger" type="Debugger" layout="${Layout}" />
      <target name="console" type="ColoredConsole" layout="${Layout}"/>
    </targets>
    <rules>
      <logger name="*" minlevel="Trace" writeTo="console,debugger" />
    </rules>
  </nlog>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

nlog section contains a Layout variable that defines format for log output, two targets: console and Visual Studio output and one rule with logging level. nlog section must be defined in configSection section.

3. Test NLog

Instance Logger and use it:

private static Logger logger = LogManager.GetCurrentClassLogger();
    class Program
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        static void Main(string[] args)
        {
            logger.Trace("Sample trace message");
            logger.Debug("Sample debug message");
            logger.Info("Sample informational message");
            logger.Warn("Sample warning message");
            logger.Error("Sample error message");
            logger.Fatal("Sample fatal error message");
        }
    }

 

4. Use async targets

Set async=”true” for targets.

<targets async="true">
    <target name="debugger" type="Debugger" layout="${Layout}" />
    <target name="console" type="ColoredConsole" layout="${Layout}"/>
....
</targets>

 

5. Log to syslog

Remote Syslog is disabled by default on Ubuntu-flavor Linux by default.
To enable it edit Rsyslog config fil /etc/rsyslog.conf
The following lines must be uncomment it this configuration file:

#################
#### MODULES ####
#################
$ModLoad imuxsock # provides support for local system logging
$ModLoad imklog   # provides kernel logging support
#$ModLoad immark  # provides --MARK-- message capability
# provides UDP syslog reception
$ModLoad imudp     #THIS LINE!
$UDPServerRun 514  #THIS LINE!

Restart syslog demon sudo service rsyslog restart

root@mikael ~# service rsyslog restart
rsyslog stop/waiting
rsyslog start/running, process 2995

As a syslog on Windows a free version of syslog Kiwi can be used. Kiwi works only as a remote syslog server, it is available on standard 514 UDP syslog port.

Add NLog.Targets.Syslog package
https://www.nuget.org/packages/NLog.Targets.Syslog/

Add assembly reference to NLog.Targets.Syslog in App.config inside nlog section and add new target type Syslog. The new App.config looks like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
  </configSections>
  <nlog>
    <variable name="Layout" value="${longdate} ${level:upperCase=true} ${message} (${callsite:includSourcePath=true})"/>
    <variable name="LayoutSyslog" value="${message} (${callsite:includSourcePath=true})"/>

    <extensions>
      <add assembly="NLog.Targets.Syslog" />
    </extensions>
    
    <targets async="true">
      <target name="debugger" type="Debugger" layout="${Layout}" />
      <target name="console" type="ColoredConsole" layout="${Layout}"/>
      <target name="syslog" type="Syslog" syslogserver="127.0.0.1" port="514" facility="Local0" sender="MyProgram" layout="${LayoutSyslog}"/>
    </targets>
    <rules>
      <logger name="*" minlevel="Trace" writeTo="console,debugger,syslog" />
    </rules>
  </nlog>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

 

This is a new section that should be placed in App.config for NLog version 4

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

 

6. Logging from Quartz.NET

Quartz.NET uses Common.Logging. We must add a sectionGroup named common to configsection of App.config file:

<configSections>
   <sectionGroup name="common">
     <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
   </sectionGroup>
   <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
</configSections>

And add a common/logging section that describes Common.Logging configuration that will be used by Quartz.NET:

  <common>
    <logging>
      <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog31">
        <arg key="configType" value="INLINE" />
      </factoryAdapter>
    </logging>
  </common>

With an external NLog.config configuration file it will look like this:

<common>
 <logging>
   <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog31">
     <arg key="configType" value="FILE" />
     <arg key="configFile" value="~/NLog.config" />
   </factoryAdapter>
 </logging>
</common>

Final App.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
  </configSections>
  <common>
    <logging>
      <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog31">
        <arg key="configType" value="INLINE" />
      </factoryAdapter>
    </logging>
  </common>  
  <nlog>
    <variable name="Layout" value="${longdate} ${level:upperCase=true} ${message} (${callsite:includSourcePath=true})" />
    <variable name="LayoutSyslog" value="${message} (${callsite:includSourcePath=true})" />
    <extensions>
      <add assembly="NLog.Targets.Syslog" />
    </extensions>
    <targets async="true">
      <target name="debugger" type="Debugger" layout="${Layout}" />
      <target name="console" type="ColoredConsole" layout="${Layout}" />
      <target name="syslog" type="Syslog" syslogserver="127.0.0.1" port="514" facility="Local0" sender="EvryHemitParser" layout="${LayoutSyslog}" />
    </targets>
    <rules>
      <logger name="*" minlevel="Info" writeTo="console,debugger,syslog" />
    </rules>
  </nlog>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

 

Colored console output:
nlogconsole

Kiwi Syslog server on Windows output:
nlogkiwi

On Linux
Mono Develop output
nlogmonodevelop

Syslog on Linux
nlogsyslogd

Sample solution can be downloaded from here https://github.com/mchudinov/LoggingNLog. It is compatible with Visual Studio 2012, Mono Develop 5, and Xamarin Studio 5.