profile for TheDaveJay at Stack Overflow, Q&A for professional and enthusiast programmers

Thursday 12 April 2012

Custom logging handler for NLOG

Just a quick one - below is a custom Nlog helper which offers a centralized location for logging in a windows service. Its compatible to use with elmahs add proc. It allows you to easily log to multiple NLog settings using a flagged enum.

Class code (Must add the nlog dll as a reference)


 using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Security.Principal;
    using System.Text;
    using NLog;
    using PixelTrackingQueueService.Exceptions;
  
    public class LogHelper
    {
        public static LogHelper CurrentHelper = new LogHelper();

        [Flags]
        public enum LogType
        {
            Error = 0x01,
            CriticalError = 0x02,
            Warning = 0x04,
            Info = 0x08,
            Debug = 0x10,
            Trace = 0x20
        }

      
        public void Log(string message, LogType logType, Exception ex = null, Type currentType = null)
        {
            Logger logger = (currentType == null)
                                ? LogManager.GetCurrentClassLogger()
                                : LogManager.GetLogger(currentType.FullName);

            Action<string> log = null;
            Action<string, Exception> exLog = null;

            foreach (LogType flag in Enum.GetValues(typeof(LogType)))
            {

                LogType logLevel = (flag & logType);
                LogLevel level = LogLevel.Off;
                switch (logLevel)
                {
                    case LogType.Warning:
                        level = LogLevel.Warn;
                        break;
                    case LogType.CriticalError:
                        level = LogLevel.Fatal;
                        break;
                    case LogType.Error:
                        level = LogLevel.Error;
                        break;
                    case LogType.Info:
                        level = LogLevel.Info;
                        break;
                    case LogType.Trace:
                        level = LogLevel.Trace;
                        break;
                    case LogType.Debug:
                        level = LogLevel.Debug;
                        break;
                    default:
                        continue;
                }

                DoLog(logger, message, level, ex, currentType);
            }
        }


        private static void DoLog(Logger logger, string message, LogLevel level, Exception ex = null, Type type = null)
        {
            LogEventInfo info = new LogEventInfo(level,
                                                ((type == null) ? typeof(LogManager).FullName : type.FullName),
                                                null,
                                                message,null, ex);

            info.Properties["ApplicationName"] = ConfigurationManager.AppSettings["ApplicationName"];
            info.Properties["Host"] = System.Environment.MachineName;
            info.Properties["Type"] = (ex == null) ? level.ToString() : ex.GetType().FullName;
            info.Properties["CurrentUser"] = WindowsIdentity.GetCurrent().Name;
            info.Properties["Source"] = ex == null ? level.ToString() : ex.Source;

            string error = ex == null ? message : ex.ToString();
            string stackTrace = ex == null ? level.ToString() : ex.StackTrace;

            info.Properties["Error"] =
                string.Format(
                    @"<error host=""{0}"" type=""{1}"" message=""{2}"" source=""{3}"" detail=""{4}"" ><serverVariables>
                        <item name=""SERVICE_NAME""><value string=""{5}""/></item>              
                        <item name=""LOGON_USER""><value string=""{6}""/></item>
                        </serverVariables></error>",
                    System.Environment.MachineName,
                    info.Properties["Type"],
                    System.Security.SecurityElement.Escape(message),
                     info.Properties["Source"],
                    System.Security.SecurityElement.Escape(error),
                    "ServiceName",
                    info.Properties["CurrentUser"]);

            

            logger.Log(info);

        }
   
        private Logger GetLoggerFromType(Type currentType)
        {
             return  (currentType == null)
                                ? LogManager.GetCurrentClassLogger()
                                : LogManager.GetLogger(currentType.FullName);
        }
      
    }

 Nlog Config (with elmah proc) 

Add this file to your solution, calling it Nlog.config. This is the file Nlog will look for:

<?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" >

  <!--
  See http://nlog-project.org/wiki/Configuration_file
  for information on customizing logging rules and outputs.
   -->
  <targets>
    <!-- add your targets here -->
   
    <!--
    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    -->
    <target name="elmah" xsi:type="Database" keepConnection="true" useTransactions="true"
        dbProvider="sqlserver" connectionString="Data Source=im-dev01\;Initial Catalog=Elmah;Integrated Security=False;User ID=Elmah;Password=1234;">
      <commandText>
        DECLARE @ID UNIQUEIDENTIFIER = NEWID(), @time DATETIME = GETUTCDATE()
        SELECT @time
        EXEC ELMAH_LogError @ID ,'${event-context:item=ApplicationName}', '${event-context:item=Host}',
        '${event-context:item=Type}', '${event-context:item=Source}', '${message}','${event-context:item=CurrentUser}','${event-context:Error}', 1, @time
      </commandText>     
    </target>
    <target xsi:type="Mail" name="mailman" smtpServer="Mail.MailService.local" from="serviceerror@somewhere.com"
            html="true" to="someone@somewhere.com"
            subject="Service Error"
            cc="someoneelse@somewhere.com"
            body="&lt;h1&gt;Date and Time&lt;/h1&gt;${longdate}    &lt;h1&gt;Message&lt;/h1&gt;${message} &lt;h1&gt;Exception Details&lt;/h1&gt;
                  &lt;h2&gt;Error Details&lt;/h2&gt; ${exception:format=ToString} &lt;h2&gt;Stack Trace Details&lt;/h2&gt; ${exception:format=StackTrace}" />
    <target name="TraceLog" xsi:type="File" fileName="Logs/Traces.log" createDirs="true"/>
    <target name="ErrorLog" xsi:type="File" fileName="Logs/Errors.log" createDirs="true" layout="${message}\r\n${exception:format=ToString}"/>
    <target name="WarningLog" xsi:type="File" fileName="Logs/Warnings.log" createDirs="true"/>
    <target name="InfoLog" xsi:type="File" fileName="Logs/Info.log" createDirs="true"/>
  </targets>

  <rules>
    <logger name="*" level="Fatal" writeTo="mailman" />   
    <logger name="*" level="Error" writeTo="elmah" />
    <logger name="*" level="Info" writeTo="elmah" />
    <logger name="*" level="Trace" writeTo="elmah" />
    <logger name="*" level="Warn" writeTo="elmah" />
    <logger name="*" level="Debug" writeTo="elmah" />
  </rules> 
</nlog>


Example on using the code:

LogHelper.CurrentHelper.Log("Error has occured", LogHelper.LogType.Debug | LogHelper.LogType.CriticalError, exceptionObject, this.GetType());       

will log to both elmah and send a mail at the same time.

4 comments:

  1. not working for me plz help its not sending mail..and what is this.GetType()??

    ReplyDelete
    Replies
    1. Hi Neel, Are you getting an exception? this.GetType() will return the class type you are calling the method from. In NLog, you can configure different outcomes according to the type of the class.

      Delete
    2. Hello thanx for the reply and issue is fixed it was related to my smtp configurations now Nlog is working like a charm!! great post :)

      Delete