NLog tutorial

Hi, folks!

Today I am going to show you how to set logging of your project with minimum effort using NLog. NLog is a simple to use logging platform with good documentation, tutorials and examples and also available source code. You can install package using NuGet selecting NLog.Config, which will also install it’s dependencies (NLog and NLog.Schema). Installation adds NLog.dll to our project refrences, NLog.config for configuration and NLog.xsd (which won’t be needed in our case).

Initial configuration is simple and allows basic internal logging. But our goal is to set new log file each day with date in it’s name and email notifications in case of warnings and (fatal) errors.

<variable name="logFilePath" value="Logs\NLog.${shortdate}.log" />

Inside of variable tag we declared log file path with short date in it’s name. With date in it’s name new log file will be created every day. After that we have to create a file target and mail target:

<targets>
  <target name="logfile" 
          xsi:type="File"
          fileName="${logFilePath}"
          layout="${longdate}   LEVEL=${level:upperCase=true}: ${message}${newline} (${stacktrace}) ${exception:format=tostring}"
          keepFileOpen="true" />

  <target name="mail" 
          xsi:type="Mail"
          smtpServer="my.smtp.server.com"
          smtpPort="port"
          smtpUserName="user.name"
          smtpPassword="password"
          subject="${machinename} - My subject string (${shortdate:format=dd. MM. yyyy})"
          from="address.to.send.from@mail.com"
          to="address.to.send.to@mail.com"
          layout="${longdate}   LEVEL=${uppercase:${level}},   LOCATION=${callsite:className=true:includeSourcePath=true:methodName=true},               
                  MESSAGE=${message}${newline} EXCEPTION=${exception:format=tostring,StackTrace}${newline}" />
</targets>

Now we have two targets. First named “logfile” will add new entries to our log. Eeach entry will consist from long date, level and message. After message there will be new line with stacktrace and exception message. Second named “mail” will send email to address.to.send.to@mail.com. Entry will be quite similar as previous with added location (class name and method name). But we have a problem – each log entry will be sent separately! If we want to send all entries in one single email, we have to wrap our “mail” target with target of type BufferingWrapper:

<target name="mailbuffer" xsi:type="BufferingWrapper" slidingTimeout="false" bufferSize="100" flushTimeout="-1">
  <target name="mail" 
          xsi:type="Mail"
          smtpServer="my.smtp.server.com"
          smtpPort="port"
          smtpUserName="user.name"
          smtpPassword="password"
          subject="${machinename} - My subject string (${shortdate:format=dd. MM. yyyy})"
          from="address.to.send.from@mail.com"
          to="address.to.send.to@mail.com"
          layout="${longdate}   LEVEL=${uppercase:${level}},   LOCATION=${callsite:className=true:includeSourcePath=true:methodName=true},               
                  MESSAGE=${message}${newline} EXCEPTION=${exception:format=tostring,StackTrace}${newline}" />
</target>

Problem solved. 🙂 Now we will get all entries inside of a single email. After that we have to set rules, which will tell NLog which level of entries must be written to which target.

<rules>
  <logger name="*" minlevel="Info" writeTo="logfile" />
  <logger name="*" minlevel="Warn" writeTo="mailbuffer"/>
</rules>

With those rules two rules we told NLog to log to file everything with level equal or higher to Info and to send an email of everything with level equal or higher to warning. Out logger is now configured. After that we have to add namespace to each class we want to log (using NLog;). We also have to initialize our logger. After that we can start to create logs:

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

  static void Main(string[] args)
  {
    logger.Info("This is the first line of Main method.");

    try
    {
      throw new Exception("This is one badass exception. :)");
    }
    catch (Exception ex)
    {
      logger.Error(ex, "It seems the exception happened. :(");
    }

    logger.Warn("This is your last warning!");
    logger.Fatal("And this is fatal error...");
  }
}

Here you can download config file and short code snippet: NLog config and short code snippet. Before using you have to configure SMTP parameters (you can find all possible parameters in documentation or you can check this example (both on GitHub)).

MS Word field functions: OR function

At work I am using hybrid of Interop and OpenXml to create some reports on client side. These reports are created from MS Word templates. Because MS Word is our preferred text editor I also can use MS Word functions for some operations. And today I was having fun using function OR. MS Word functions are very simple to use, but if you make one itsy bitsy tiny mistake in syntax some generic error appears which tells you less than nothing (something like “!Syntax Error ,,“).

I needed to check inside of IF field if document property has equal value as one of two other values and then do something. In pseudo code it would look something like this:

if(value is equal value1 OR value is equal value2)
  then do something
else 
  do something else

The thing is that OR function works perfectly with numbers, but not with strings. With numbers it looks like:

{ =OR(1 = 1; 1 = 5) }

This would return 1, as expected (warning: you must use semicolon , not comma). But with strings it would return syntax error. With strings you need to use field COMPARE:

{ =OR( { COMPARE "value" = "value1" }; { COMPARE "value" = "value2" }) }

COMPARE returns 1 if condition is met or returns 0 if condition is not met. If one of COMPAREs returns 1, condition inside OR is met – OR returns 1. In other case it returns 0. IF field would look like this:

{ IF { =OR( { COMPARE "value" = "value1" }; { COMPARE "value" = "value2" })} = 1 "when TRUE, use this" "when FALSE, use this"}

As I said, very simple to use, but when you have a very long sausage of text with IF fields and nested functions every small mistake becomes great pain in the arse (pardon my language).