Logging setup in 5 minutes with NLog
- Posted in:
- .NET
- C#
- CodeProject
UPDATE: The latest version of NLog breaks many of these tools by including NLog data by default. The code has been updated to correct this problem by excluding it for both Sentinel and Harvester applications.
The time has come where I needed to add some sophisticated logging to Directory Monitor. I know there are a couple of frameworks out there and this comparison chart lays it out quite nicely although it may be quite dated. So after some quick investigation on the free ones I decided to go with NLog.
- ObjectGuy Framework was really appealing at only a 40KB dependency. It's really simple and actually quite powerful, I chose not to use it because as you can see in the chart, thread safety is questionable (not sure why or hasn’t been tested) and it doesn't have built in file rotation/archiving. - @Lorne set me straight, the ObjectGuy Framework does indeed have these features, however, my decision still stands for reasons beyond this.
- Log4Net is a good option but the license didn’t agree with me because I want to use it for closed source applications.
- Enterprise Library is a monster with lots of other dependencies, really only for large scale applications and if you want to extend it to do crazy things. Use it at work almost exclusively but not ideal for my small app.
- NLog is 372KB, works in Mono (enterprise library as well), and has a build for WP7, all versions of the framework, Silverlight etc. Because of this I didn't mind checking it out because it immediately allows me to take that learning anywhere, to almost any application I build. There is also a great monitoring tools you can use along with it such as Sentinel or Harvester.
Quick Setup
You can download and install it or just add it through NuGet in Visual Studio.
Configuration File
Just make a config called NLog.config in the root of your project.
When editing the configuration file, reference the XSD and you'll get intellisense and inline comments in Visual Studio: Right-click the config -> Properties -> Schemas -> "C:\Program Files (x86)\Microsoft Visual Studio 10.0\xml\Schemas\NLog.xsd"
If you have a separate config file, make sure you set the "Copy to Output Directory" is set to "Copy Always" to avoid many tears wondering why the logging doesn't work.
You get a lot of layout options to play with, this is my configuration file, it writes to the event log on error and fatal, creates a daily rolling log file (30 days max), everything is asynchronous and the configuration auto reloads on the fly if anything changes. It also writes to a location that you will have write access to in low privilege situations.
<?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" autoReload="true" throwExceptions="false"> <variable name="appName" value="YourAppName" /> <targets async="true"> <target xsi:type="File" name="default" layout="${longdate} - ${level:uppercase=true}: ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}" fileName="${specialfolder:ApplicationData}\${appName}\Debug.log" keepFileOpen="false" archiveFileName="${specialfolder:ApplicationData}\${appName}\Debug_${shortdate}.{##}.log" archiveNumbering="Sequence" archiveEvery="Day" maxArchiveFiles="30" /> <target xsi:type="EventLog" name="eventlog" source="${appName}" layout="${message}${newline}${exception:format=ToString}"/> </targets> <rules> <logger name="*" writeTo="default" minlevel="Info" /> <logger name="*" writeTo="eventlog" minlevel="Error" /> </rules> </nlog>
Code
This is the singleton class that I use which also includes targets for Sentinel and Harvester when I'm running a debug build.
using NLog; using NLog.Config; using NLog.Targets; namespace YourAppName.Logging { internal static class Log { public static Logger Instance { get; private set; } static Log() { #if DEBUG // Setup the logging view for Sentinel - http://sentinel.codeplex.com var sentinalTarget = new NLogViewerTarget() { Name = "sentinal", Address = "udp://127.0.0.1:9999", IncludeNLogData = false }; var sentinalRule = new LoggingRule("*", LogLevel.Trace, sentinalTarget); LogManager.Configuration.AddTarget("sentinal", sentinalTarget); LogManager.Configuration.LoggingRules.Add(sentinalRule); // Setup the logging view for Harvester - http://harvester.codeplex.com var harvesterTarget = new OutputDebugStringTarget() { Name = "harvester", Layout = "${log4jxmlevent:includeNLogData=false}" }; var harvesterRule = new LoggingRule("*", LogLevel.Trace, harvesterTarget); LogManager.Configuration.AddTarget("harvester", harvesterTarget); LogManager.Configuration.LoggingRules.Add(harvesterRule); #endif LogManager.ReconfigExistingLoggers(); Instance = LogManager.GetCurrentClassLogger(); } } }
Monitoring Tools
Let Sentinel or Harvester run all the time when you are debugging and it will pick up everything that your app is logging (screenshots from their respective project pages).
Start Logging
Now to start log anything, anywhere in your app, just use the static log instance.
try { Log.Instance.Debug("We're going to throw an exception now."); Log.Instance.Warn("It's gonna happen!!"); throw new ApplicationException(); } catch (ApplicationException ae) { Log.Instance.ErrorException("Error doing something...", ae); }
This is a really simple and effective setup but you can of course get really advanced from there, all in the configuration file.
The cool thing about having a separate log file is that you can send someone a modified log file that will write trace level logging or even ship the log to a web server or database for you to check out.
Logging is so important in any live application. This just makes it easy while still leaving it very powerful if you ever need to use that power. The logger itself is pretty easy to debug because of it's massive internal logging and setting to expose the internals of it.