This is a follow-up to my post on Using NLog with Dependency Injection. There I showed how you can abstract away a specific logging implementation from your application by implementing a common interface and using a DI container, such as Ninject or StructureMap, to resolve the concrete implementation at runtime. This time around, I thought I would call out a few more features of NLog which I found useful.
Download the code for this blog post.
One of the most common uses of logging is to record log entries in a database table. NLog has an interesting feature that allows you to include the table definition in your NLog.config file and run a command-line utility to create the database and logging table, as well as set up other targets, such as a Windows Event Log or performance counters. To take advantage of this feature, you need to run the NLog installer, which you can download from here. This will give you the documentation, Visual Studio item templates for sample configuration files, schemas for XML intellisense in config files, extended layout renderers, and the config-driven installer utility.
To find these goodies, you need to go to the directory where NLog is installed after running the setup program, which is probably “%ProgramFiles(x86)%\NLog\.NET Framework 4.0”.
A common question is where to place the NLog config file. There are a number of options. If you want to perform logging at the class library level, and perhaps have a logging test project, you may want to place the NLog.config at the solution level, and link to it from projects where you want to perform logging. To do this, right-click on the project and select Add Existing Item. Then navigate to the solution directory where you placed the NLog.config file and, instead of clicking the Add button, select the drop-down on the Add button and choose Add As Link.
Then select properties for the linked file and set Copy to Output Directory to “Copy Always.”
For web apps, you’ll probably need to have a physical NLog.config file in the root directory (with Copy to Output Directory set to “Do Not Copy”), because placing the file in the bin directory does not seem to work (regardless of what the documentation states).
One change I wanted to make to my log output is to include the identity of the current user. If you look at the list of built-in layout renderers, you’ll find the ${identity} tag, which provides the identity of the thread’s current principal.
static void Main(string[] args) { // Set user identity Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); // Other code follows ... }
In web applications, this will give you the application pool’s identity rather the end user’s identity. The ${aspnet-user-identity} tag will return the identity of the logged on user, but you can use only this tag if you reference the NLog.Extended.dll assembly. If you’re using NLog via NuGet, add the NLog for Extended Profile package, which will add the reference.
For an ASP.NET MVC app, you’ll need to include pieces from the Internet Application MVC3 template to enable users to authenticate via a LogOn view.
All you have to do to log on is run the app and register as a new user. A database will be created according to the “ApplicationServices” connection string in web.config.
The next piece is enabling database logging. For this you simply create a logging target with xsi:type=”Database”. One of the issues I ran into is specifying the date-time in a format SQL Server understands. The ${date} tag accepts a format parameter that can be any argument accepted by DateTime.ToString(format). I found the standard “s” Sortable Format Specifier to be a good choice.
<target name="database" xsi:type="Database"> <!-- SQL command to be executed for each entry --> <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)</commandText> <!-- 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}" /> <!-- connection string --> <dbProvider>System.Data.SqlClient</dbProvider> <connectionString>Data Source=.\SQLEXPRESS;Initial Catalog=Logging;Integrated Security=True</connectionString> </target>
If you try running this straight away, you won’t notice anything unusual, even though no database logging has taken place – because you haven’t even created the database! If you want to find out if there’s a problem with logging, one thing you can do is configure the logger to throw exceptions. This will cause an exception to be raised in the logger if NLog cannot initialize a target, but problems writing to the database won’t come up. For that you’ll want to log internal NLog exceptions.
You can log them to the console, but in a real-world scenario, you’ll want to log NLog internal errors to a text file. That way, you can inspect it on the server where your app is deployed.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true" internalLogLevel="Error" internalLogFile="..\..\..\nlog-app.log">
This relative path will place the log in the solution directory for the project. However, the location of the internal log file for a web app will need to be determined at runtime, based on Server.MapPath. This means you’ll need to set the log file path in the Application_Start method of Global.asax.cs.
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { string nlogPath = Server.MapPath("nlog-web.log"); InternalLogger.LogFile = nlogPath; } }
Now that we can see errors generated by NLog, we need to create the database and table where we want to store our logs. NLog comes with a handy command-line tool called InstallNLogConfig.exe, which you can run on your NLog.config file. If the config file includes information about the definition of the database table, this tool will create the table for you. You can even have it drop and re-create the logging database. The tool will also create the windows event log or performance counters if those targets are specified.
<!-- command to install logging database --> <install-command> <text>CREATE DATABASE Logging</text> <connectionString>Data Source=.\SQLEXPRESS;Initial Catalog=Master;Integrated Security=True</connectionString> <ignoreFailures>true</ignoreFailures> </install-command> <!-- command to create logging table --> <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> <!-- command to drop logging database --> <uninstall-command> <text>DROP DATABASE Logging</text> <connectionString>Data Source=.\SQLEXPRESS;Initial Catalog=Master;Integrated Security=True</connectionString> <ignoreFailures>true</ignoreFailures> </uninstall-command>
Logging is one of those things we can take for granted – until we need it. NLog is a nice tool that can provide logging to multiple targets. I’ve just scratched the surface on what NLog can do. For more info, check out the NLog docs and download the NLog source code, which includes some good examples. You can download the code for this post here. Enjoy.
Pingback: More NLog Goodness | Security | Syngu