Last updated: 2006-09-18
This website is based on NLog v20060918.
Click here to view the documentation
for other versions.
| Introduction to tracing applications with NLogAuthor: Jarosław Kowalski <jaak@jkowalski.net>
Once upon a time, when there were no debuggers in the world and software
was mostly console-based, programmers used to output tracing messages
using printf ()
statements.
Today's world has seen huge advancement in technology
and printf() was replaced with Console.WriteLine()...
We've all written code similiar to the following: Console.WriteLine() statements in the above example are called "tracing statements" because they are only used to report our application's control flow and have no other function. The output of Console.WriteLine() is called the program trace. In this example, the tracing statements produce output that tells us if the DoSomething() method has finished execution or not. After giving the application some testing, we tend to remove the tracing code in order to improve performance (tracing can take a lot of time). Tracing instructions are usually commented out so that they can be re-enabled in the future. Unfortunately this requires our program to be recompiled. Some time later, after removing hundreds of the comments and putting them back for the Nth time, we get the feeling that our tracing solution is not perfect and we could benefit from:
It may seem, that in the age of graphical debuggers, the usefulness of tracing-based solutions is limited. Sometimes tracing turns out to be the only tool available, which can be used to locate bug in a mission-critical system that cannot be switched off for a single minute. What is NLog?NLog (http://www.nlog-project.org) is a .NET library, which enables you to add sophisticated tracing code to your application, delivering the functionality mentioned above and much, much more. NLog lets you write rules which control the flow of diagnostic traces from their sources to targets, which could be:
In addition, each tracing message can be augmented with pieces of contextual information, which will be sent with it to the target. The contextual information can include:
Each tracing message is associated with a log level which describes its severity. NLog supports the following levels.
NLog is a open source library distributed at no cost under the terms of
BSD license
Our first NLog-enabled applicationLet's create our first application that uses NLog using Visual Studio 2005. We'll start with a simple example that only logs to the console, and we'll be adding features that demonstrate how easy it is to control logging configuration in NLog. The first step is to create a Visual Studio project (in this example we'll be using C#) and add NLog configuration file using "Add New Item..." dialog. Let's add an "Empty NLog Configuration File" and save it as "NLog.config": ![]() Notice how a reference to NLog.dll has automatically been added to our project. The contents of NLog.config file, which we'll be modifying in this tutorial, are: You only need to do one more thing: Change "Copy To Output Directory" option for this file to "Copy always". This way our configuration file will be placed in the same directory as the *.exe file and NLog will be able to pick it up without any special configuration. ![]() It's time to configure log output. In the <targets /> section we'll add a entry that defines console output and the required output layout: Notice how Visual Studio suggests the possible XML element names and attribute names/values. Once we type xsi:, we get a list of available log targets. ![]() In the <rules /> section we'll add a rule that routes all message whose level is Debug or higher to the console. The XML is quite self-explanatory. In order to send a diagnostic message, we use a Logger object, whose methods are named after supported log levels (Debug(), Info(), Fatal() and so on). Logger objects can be created thanks to the LogManager class. It's recommended that logger names correspond to the full class names in the application. The LogManager object has a GetCurrentClassLogger() method that automatically creates a logger based on the class its being called from. Let's modify the wizard-generated C# file by adding "using NLog" statement at the beginning, the code to create logger and example log message. Note that you can quickly type the logger-creation statement by using the Code Snippet which is installed with NLog. Just type "nlogger" and press TAB twice. The result of running this program is a message written to the console which includes the current date, log level (Debug) and the Hello World message. Let's see how we achieved this:
More advanced logging scenario
Let's record our log messages, along with some contextual information
such as the the current stack trace, to both a file and the console. To
do this we need to defined another Here's the C# source code that emits some more log messages, additional methods are introduced here to observe the stack trace feature. When we run the program, the following information will be written to the "file.txt" in the application directory: At the same time, we get this fancy colored output on the console: ![]() Now let's modify our configuration a bit. The typical requirement is to have a different level of detail depending on the output. For example, we only want messages whose level is Info and higher written to the console and we want ALL messages to be written to the file. With NLog, you only need to change the <rules /> section. No changes to the C# source code are necessary: After running the program we find that Trace and Debug messages are only found in the file and aren't displayed on the console. Logging configurationIt's now time to describe the NLog configuration mechanism. Unlike other tools, NLog attempts to automatically configure itself on startup, by looking for the configuration files in some standard places. The following locations will be searched when executing a stand-alone *.exe application:
In case of an ASP.NET application, the following files are searched:
The .NET Compact Framework doesn't recognize application configuration files (*.exe.config) nor environmental variables, so NLog only looks in these locations:
Configuration file formatNLog supports two configuration file formats:
In the first variant, we use a standard configSections mechanism, which makes our file look like this: The simplified format is the pure XML having the <nlog /> element as its root. The use of namespaces is optional, but it enables the Intellisense in Visual Studio. Note that NLog config file is case-insensitive when not using namespaces and is case-sensitive when you use them. Intellisense only works with case-sensitive configurations. Configuration elementsYou can use the following elements as children to <nlog />. The first two elements from the list are required to be present in all NLog configuration files, the remaining ones are optional and can be useful in advanced scenarios.
TargetsThe <targets /> section defines log targets/outputs. Each target is represented by the <target /> element. There are two attributes required for each target:
In addition to these, targets usually accept other parameters, which influence the way diagnostic traces are written. Each target has a different set of parameters, they are described in detail on project's homepage and they are context-sensitive Intellisense is also available in Visual Studio. For example - the "File" target accepts the fileName parameter which defines output file name and the Console target has the error parameter which determines whether the diagnostic traces are written to standard error (stderr) instead of standard output (stdout) of the process. NLog provides many predefined targets. They are described on the project's homepage. It's actually very easy to create your own target - it requires about 15-20 lines of code and is described in the documentation. RulesLog routing rules are defined in the <rules /> section. It is a simple routing table, where we define the list of targets that should be written to for each combination of source/logger name and log level. Rules are processed starting with the first rule in the list. When a rule matches, log messages are directed to target(s) in that rule. If a rule is marked as final, rules beneath the current rule are not processed. Each routing table entry is a <logger /> element, which accepts the following attributes:
Some examples:
In the simplest cases the entire logging configuration consists of a single <target /> and a single <logger /> rule that routes messages to this target depending on their level. As the application grows, adding more targets and rules is very simple. Contextual informationOne of NLog's strongest assets is the ability to use layouts. They include pieces of text surrounded by a pair of "${" (dollar sign + left curly brace) and "}" (right curly brace). The markup denotes "layout renderers" which can be used to insert pieces of contextual information into the text. Layouts can be used in many places, for example they can control the format of information written on the screen or sent to a file, but also to control the file names themselves. This is very powerful, which we'll see in a moment. Let's assume, that we want to augment each message written to the console with:
This is very easy: We can make each messages for each logger go to a separate file, as in the following example: Ay you can see the ${logger} layout renderer was used in the fileName attribute, which causes each log message to be written to the file whose name includes the logger name. The above example will create the following files:
It's a frequent requirement to be able to keep log files for each day separate. This is trivial, too, thanks to the ${shortdate} layout renderer: How about givin each employee their own log file? The ${windows-identity} layout renderer will do the trick: Thanks to this simple setting NLog will create a set of files named after our employees' logins:
More complex cases are of course possible. The following sample demonstrates the way of creating a distinct log file for each person per day. Log files for each day are stored in a separate directory: This creates the following files:
NLog provides many predefined layout renderers. They are described on the http://www.nlog-project.org/layoutrenderers.html page. It's very easy to create your own layout renderer. It just takes 15-20 lines of code and is described in the documentation section of the project website. Include filesIt's sometimes desired to split the configuration file into many smaller ones. NLog provides an include file mechanism for that. To include an external file, you simply use It's worth noting that the fileName attribute, just like most attributes in NLog config file(s), may include dynamic values using the familiar ${var} notation, so it's possible to include different files based on environmental properties. The following configuration example demonstrates this, by loading file whose name is derived from the name of the machine we're running on. Variables let us write complex or repeatable expression (such as file names) in a concise manner. To define a variable we use the <variable name="var" value="xxx" /> syntax. Once defined, variables can be used as if they were layout renderers - by using ${var} syntax, as demonstrated in the following example: Automatic reconfigurationThe configuration file is read automatically at program startup. In a long running process (such as a Windows service or an ASP.NET application) it's sometimes desirable to temporarily increase the log level without stopping the application. NLog can monitor logging configuration files and re-read them each time they are modified. To enable this mechanism, you simply set <nlog autoReload="true" /> in your configuration file. Note that automatic reconfiguration supports include files, so each time one of the files include is changed, the entire configuration gets reloaded. Troubleshooting loggingSometimes our application doesn't write anything to the log files, even though we have supposedly configured logging properly. There can be many reasons for logs not being written. The most common problem are permissions, usually in an ASP.NET process, where the aspnet_wp.exe or w3wp.exe process may not have write access to the directory where we want to store logs. NLog is designed to swallow run-time exceptions that may result from logging. The following settings can change this behavior and/or redirect these messages.
Asynchronous processing, wrapper and compound targetsNLog provides wrapper and compound targets which modify other targets' behaviour by adding features like:
To define a wrapper or compound target in the configuration file, simply nest a target node within another target node. You can even wrap a wrapper target. There are no limits on depth. For example, to add asynchronous logging with retry-on-error functionality add this to your configuration file: Because asynchronous processing is a common scenario, NLog supports a shorthand notation to enable it for all targets without the need to specify explicit wrappers. You simply set <targets async="true" /> and all your targets will be wrapped with the AsyncWrapper target. Programmatic configurationIn certain cases you may choose not to use configuration file, but to configure NLog using the provided API. Full description of this feature is beyond the scope of this article, so let's just outline the steps necessary to make it work. To configure NLog in code you need to:
This sample demonstrates the programmatic creation of two targets: one is a colored console and the other is a file and rules that send messages to them for messages whose level is Debug or higher. What else is possible with NLog?NLog supports some more logging scenarios, which couldn't be fully described here. See the links below for more information:
Last updated: 2006-07-10 11:32:55 | |||||||
| Copyright © 2004-2006 by Jarosław Kowalski. |