Introduction
The primary objective of this article is to provide C# Code Snippets to help developers log using log4net. In the process Visual Studio Code Snippets are introduced and log4net is also covered.
Hopefully people who have not used either of these technologies will be able to get a good start on them from the information provided and people who are familiar with these technologies can get the specifics about the Snippets without being inundated with information they already know.
And for those that want to get Straight into it; the log4net Snippets are available in the source. The demo project shows a couple of the log4net Snippets in use and a general how-to for log4net.
log4net's licensing information is available from it's website (linked at the bottom) and obviously included with the demo project.
Visual Studio Code Snippets
Visual Studio 2005 introduced a Code Snippets feature. This feature allowed very simple, common, code blocks to be reused without requiring the same thing to be typed out repeatedly, or copied and hacked. The Code Snippets feature allows inserting code, wrapping existing code and replacement of key parts of the Snippet.
Some of the Code Snippets that are included with Visual Studio include properties, constructors, Console.Writeline and while loops.
Code Snippets and Logging
So what do Code Snippets have to do with logging?
Logging code is painful and time consuming to write, does not contribute to core functionality and may lack consistency.
Code Snippets provide an effective way to help address these issues. These log4net Snippets take care of the plumbing code and provide a consistent format for all logging code, allowing the developer to get back to the more interesting stuff.
Using Snippets
To get the most out of the log4net Snippets we will do a quick refresher on how to use Snippets.
First extract the log4net Snippets to a folder - something like %USERPROFILE%\My Documents\Visual Studio 2008\Code Snippets\log4net.
The Snippet Manager can be opened via the ctrl-k, ctrl-b shortcut or can be added to a menu from the tools menu (it is not displayed by default). Use the Snippet Manager to add the folder the log4net Snippets were extracted to.
Once the Snippets have been added they can be accessed from the Snippets context menu (ctrl-k, ctrl-x or right click > insert snippet) or via shortcuts. The context menu will present the Snippets in the folder structure setup in the snippet manager. Otherwise all the log4net Snippets shortcuts begin with log. So to create a static log in a class you would type log, tab, tab.
Logging with log4net
As this article only covers the basics of log4net, and then only that related to the log4net Snippets; A list of articles are provided at the bottom for further information.
Before we can get started with logging we must configure log4net. There are four Snippets for variations on this.
Configuring log4net
Snippet |
Shortcut |
Description |
Replacements |
log4net Load Configuration File Application Config File |
logConfigureApp |
Loads log4net logging configurion from App.Config/Web.Config file |
None |
log4net Load Configuration File from using Assembly Attribute |
logConfigureAssembly |
Loads log4net configuration from a file using an assembly attribute. This only loads the configuration based on the first assembly to be loaded with this attribute in it. |
configuration file |
log4net Load Configuration File from Assembly Directory |
logConfigureLocal |
Loads the specified configuration file from the assembly location |
configuration file |
log4net Load Configuration File from URL |
logConfigureURL |
Loads log4net configuration file from the specified URL. This is untested |
configuration file URL |
log4net.Config.XmlConfigurator.Configure();
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "logging.config", Watch = true)]
Once log4net has been configured we need to make sure there is a valid log4net configuration available in the expected location. The sample applications app.config file forms a good base for anyone to get started - it will log to a file in the application directory and the debug output (visible as an output in Visual Studio or via Sysinternals Debug View (dbgview.exe) ā a wonderful creation from Mark Russonovich and Bryce Cogswell).
I should probably mention here that if you are having trouble getting log4net configured one of the articles listed at the bottom provides information on how to enable internal logging.
The second step is creating a logger for your class to use. The Snippet log4net Logger (shortcut: log) creates a static logger in the class using reflection to get the classes type. This is a standard approach to creating a logger but the performance is better if reflection is not used. For this there is a log4net Fixed Type Logger (shortcut: logType) which uses a function (The same function as the constructor Snippet) to determine the classes type at the time the Snippet is used. The disadvantage of this approach is that things may go wrong when the logger is copied from one class to another.
Creating a logger
Snippet |
Shortcut |
Description |
Replacements |
log4net Logger |
log |
Creates a static read only logger |
None |
log4net Fixed Type Logger |
logType |
Creates a static read only logger with a fixed type |
Type (automatically completed) |
#region log4net
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
#endregion
#region log4net
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(FTrimWS));
#endregion
And now comes the fun part ā logging. log4net Logs in a level based approach with the levels Debug, Info, Warn, Error and Fatal in that order. For each level there are two Snippets; Level and LevelFormat. This allows access to both the plain text and formatted text versions of each level of the logger. Upon using any of these you will also notice the if (log.IsLevelEnabled) {log something;} structure. This performs a very fast check to see if logging at a particular level is enabled and only then does the comparatively slow string formatting and message generation.
Logging at levels
Snippet |
Shortcut |
Description |
Replacements |
log4net Debug |
logDebug |
logs a plain text message at debug level |
Log Message |
log4net Debug Format |
logDebugFormat |
logs a formatted text message at debug level |
Log Message, Format arguments |
log4net Info |
logDebug |
logs a plain text message at info level |
Log Message |
log4net Info Format |
logDebugFormat |
logs a formatted text message at info level |
Log Message, Format arguments |
log4net Warn |
logDebug |
logs a plain text message at warn level |
Log Message |
log4net Warn Format |
logDebugFormat |
logs a formatted text message at warn level |
Log Message, Format arguments |
log4net Error |
logDebug |
logs a plain text message at error level |
Log Message |
log4net Error Format |
logDebugFormat |
logs a formatted text message at error level |
Log Message, Format arguments |
log4net Fatal |
logDebug |
logs a plain text message at fatal level |
Log Message |
log4net Fatal Format |
logDebugFormat |
logs a formatted text message at fatal level |
Log Message, Format arguments |
if (log.IsDebugEnabled) { log.Debug("Logging Debug"); }
if (log.IsDebugEnabled) { log.DebugFormat("Logging Debug Format {0}{1}", "Values", 1); }
Customising the Code Snippets
As an example of customising the Snippets I have included a Log Exception Snippet which adds the catch part of a try catch block, logs the exception as an error and re-throws the original exception. Iām not suggesting that this is the best way to handle exceptions but the Snippet is an example of how the Snippets can be customised to suit an application or organisations needs and provide a consistent approach to logging.
Exceptions
Snippet |
Shortcut |
Description |
Replacements |
log4net Log Exception |
logEx |
Creates catch exception, log exception, throw exception block for standardised error handling |
Activity |
catch (Exception ex)
{
if (log.IsErrorEnabled) { log.ErrorFormat("Exception occured executing a task: {0}", ex); }
throw;
}
I would suggest that any specific logging requirement has a snippet created and shared via source control (TFS or Subversion are both fine).
Finally
In addition to the Snippets detailed above I have included log4net Assembly Info (shortcut: logAssemblyInfo) to log basic information about the currently executing assembly and an example application called Trim Working Set. I find the Assembly Info Snippet useful when an application starts so that I know which version of the application to compare the log to. The Trim Working Set application is a working application that I wrote to trim the working set of a selected or all applications. This I use at work to keep computer working as speedily as possible with limited RAM.
Miscellaneous
Snippet |
Shortcut |
Description |
Replacements |
log4net Assembly Info |
logAssemblyInfo |
logs basic information about the currently executing assembly |
Log Message, Format |
if (log.IsWarnEnabled)
{
try
{
log.WarnFormat("Assembly {1} version {0}. Executing as user {3} from {2}",
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version,
System.Reflection.Assembly.GetExecutingAssembly().GetName().Name,
System.Reflection.Assembly.GetExecutingAssembly().Location,
System.Security.Principal.WindowsIdentity.GetCurrent().Name);
}
catch
{
if (log.IsWarnEnabled) { log.Warn("Failed to log assembly info"); }
}
}
Unfortunately I have not provided VB.Net equivalents. Though, if there is a demand I will re-write them.
References / Articles on log4net
History
2009-03-19 Original article ready for submission
2009-03-26 Fixed Log Exception snippet which cleared the stack trace when throwing the exception (thanks to Jacobs76 for pointing that out)