If you are writing server code in C# or to a lesser extent desktop/client then it's a good idea to include logging code so when something goes wrong you know where to start looking. The Java world has done the same for years and Apache log4j is a very popular open source logging framework.
If you shorten that url to Apache logging services you'll see that there are versions for C++, .NET, and php. In this article I'll show you how to use Apache log4net.
This isn't the only .NET logging framework, there are very many. I chose the Apache one because (a) it's a well trusted name and the original Java logging framework has been around over 15 years, (b) it's one I've used and (c) you can log with UDP messages.
If you want to find others, take a look at the excellent German website (It's in English!) .NET logging. They are selling a commercial logging system but their listing and comparison of features of logging systems is very good and disclaimer: I have no connection at all with them, financial or otherwise.
Why Use A Logging Framework?
If an application or server crashes, you are left wondering why. Was it a hardware failure, malware, maybe a Denial of Service attack, or some very odd combination of keys that manages to bypass all your code checks? You just don't know. Even worse is if it fails when starting.
When an application or server crashes you need to find out why so it can be corrected. With logging enabled you might see why it happened though there is one situation you should watch out for; I've seen this happen once or twice. A server crashed at work because the log file had filled the disk!
Sending an email in response to an unforeseen exception would avoid a full disk.
Getting Started
If you have Visual Studio, you can just install the binaries from Nuget into an open project or else download the log4net zip file from the Apache log4net website and unzip somewhere.
If you download the sources, there are two solution files for Visual Studio 2008 and 2010 in the log4net src folder so open the appropriate one and build it. In Visual Studio Express you may have to remove the test sub-project or it'll generate nearly 400 errors due to always compiling the test sub project first.
After successfully compiling it leaves a log4net.dll in the build/bin/net/2.0 debug or release folder. Just add a reference to that into your project.
Using log4net
This supports seven levels of logging from none to all. These are:
- OFF
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
- ALL
The idea is that the higher levels include all the lower ones. When debugging, using DEBUG will show all but on production you might only be interested in FATAL. Also this can be done at the component level programmatically or in an XML Config file.
Loggers and Appenders
For great flexibility, log4net uses loggers, appenders and layouts. A logger is an object that controls logging and is an implementation of the ILog interface which specifies five boolean methods (isDebugEnabled, IsInfoEnabled etc) and five methods (Debug, Info, Warn, Eror, Fatal) along with overloads and five Formatted String version. You can see the full ILog interface in the log4net online manual.
Loggers are assigned one of the levels but not ALL or OFF, only the other five.
Appenders control where the logging goes. It can be into a database via ADO, to an in memory buffer, to the console, to a remote host, to a text file with rolling logs, the WIndows Event Log, or even to email via SMTP. There are 22 different appenders in all and they can be combined so plenty of choice. Appenders are appended (hence the name) to a logger.
Filtering Events
Appenders can filter events by matching substrings, event level etc to be accepted or rejected.
Finally there are seven layouts. These control how the event's message is logged and can include exception text, timestamp layouts, even XmlLayout.
Configuring With XML
Although configuring can be done programmatically, it can also be done with XML Config files. Why would you prefer config files over code changes? Simple, it's far easier to have a support guy make a change to a config file than have to get a programmer to change code, test and redeploy a new version. So config files are definitely best.
We'll go for the simplest possible and use just App.config. You can add it to your project with right click on the Project Name (under the word Solution if its the first or only project) then Add -> New Item. Select General under Visual C# Items then Application Configuration File.
This is the App.config file I used:
I'm not going to explain the config file fields as I would write a dozen articles to fully cover the variations with all the different appenders but the log4net online documentation will explain. I recommend you get config file examples from this log4net config examples page .
Having setup App.config, the log4net configuration can be configured using assembly-level attributes:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Orspecified programmatically using apis such asXmlConfigurator.ConfigureXXX():
XmlConfigurator.ConfigureAndWatch(configFile);
Plus the actual logger has to be fetched with a call to LogManager.GetLogger(...). The GetLogger is usually called with the typeof(class) that it's used in but this function call also fetches that:
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType
I've left both in with one commented, so you can choose. Here is the code to use it.
using log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace TestLog4net
{
class Program
{
private static readonly ILog log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod
().DeclaringType) ;
//private static readonly ILog log = LogManager.GetLogger(typeof (Program)) ;
static void Main(string[] args)
{
Set up a simple configuration that logs on the console.
// BasicConfigurator.Configure();
// Type type = typeof(Program);
// string assemblyPath = Assembly.GetAssembly(type).EscapedCodeBase;
// string path = assemblyPath + ".config";
// FileInfo configFile = new FileInfo(new Uri(path).LocalPath);
// XmlConfigurator.ConfigureAndWatch(configFile);
log.Debug("Application Starting") ;
}
}
}