I've discussed using XML to configure lognet, during which I briefly touched on log4net appenders. An appender is an object that persists your log messages someplace. In this post, I'd like to give you a quick overview of the wealth of apppenders available in log4net and show you how you can use them to direct a single log message to multiple places.
A Quick Survey
Here is a quick sampling of the appenders available in the log4net distribution:
Type Name | Description |
log4net.Appender.AdoNetAppender | Appender that logs to a database. |
log4net.Appender.AnsiColorTerminalAppender | Appends logging events to the terminal using ANSI color escape sequences. |
log4net.Appender.AspNetTraceAppender | Appends log events to the ASP.NET TraceContext system. |
log4net.Appender.BufferingForwardingAppender | Buffers events and then forwards them to attached appenders. |
log4net.Appender.ColoredConsoleAppender | Appends logging events to the console. |
log4net.Appender.ConsoleAppender | Appends logging events to the console. |
log4net.Appender.DebugAppender | Appends log events to the Debug system. |
log4net.Appender.EventLogAppender | Writes events to the system event log. |
log4net.Appender.ForwardingAppender | This appender forwards logging events to attached appenders. |
log4net.Appender.FileAppender | Appends logging events to a file. |
log4net.Appender.LocalSyslogAppender | Logs events to a local syslog service. |
log4net.Appender.MemoryAppender | Stores logging events in an array. |
log4net.Appender.NetSendAppender | Logs entries by sending network messages using the NetMessageBufferSend native function. |
log4net.Appender.OutputDebugStringAppender | Appends log events to the OutputDebugString system. |
log4net.Appender.RemoteSyslogAppender | Logs events to a remote syslog daemon. |
log4net.Appender.RemotingAppender | Delivers logging events to a remote logging sink. |
log4net.Appender.RollingFileAppender | Appender that rolls log files based on size or date or both. |
log4net.Appender.SmtpAppender | Send an e-mail when a specific logging event occurs, typically on errors or fatal errors. |
log4net.Appender.SmtpPickupDirAppender | Send an email when a specific logging event occurs, typically on errors or fatal errors. Rather than sending via smtp it writes a file that another service, such as the IIS SMTP agent, can use to manage sending the messages. |
log4net.Appender.TelnetAppender | Appender that allows clients to connect via Telnet to receive log messages. |
log4net.Appender.TraceAppender | Appends log events to the Trace system. |
log4net.Appender.UdpAppender | Sends logging events as connectionless UDP datagrams to a remote host or multicast using the UdpClient class. |
There's a lot of them, so to keep this post reasonable I'm focusing on the most basic appenders: the console appenders, debug appenders, and file appenders. However, all of the available appenders are covered in detail in the log4net documentation if you want to learn more about them, and I'll be describing other appenders in upcoming posts as well.
ConsoleAppender and ColoredConsoleAppender
These appenders are useful only for console applications, where they provide immediate feedback during program execution.
There is one configurable aspect of the ConsoleAppender - the target property identifies what output stream to use when outputting log entries:
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<target value="Console.Error" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
It can be either "Console.Out" (the default value if left uspecified) to use the standard output stream or "Console.Error" to use the standard error stream.
I always favor ColoredConsoleAppender over ConsoleAppender, because the ColoredConsoleAppender will colorize the log message by its severity level. This makes finding relevant log messages amidst the program output a lot easier. For example, compare this output from the ConsoleAppender:
to the same output from the ColoredConsoleAppender:
The color mapping is completely configurable, as shown in this example appender configuration XML:
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<target value="Console.Error" />
<mapping>
<level value="FATAL" />
<foreColor value="Red" />
<backColor value="White" />
</mapping>
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="WARN" />
<foreColor value="Yellow" />
</mapping>
<mapping>
<level value="INFO" />
<foreColor value="Cyan" />
</mapping>
<mapping>
<level value="DEBUG" />
<foreColor value="Green" />
</mapping>
<layout type="log4net.Layout.SimpleLayout" />
</appender>
Each <mapping /> element must contain a severity level, and can also contain a <foreColor /> and/or <backColor /> element. Color selection is limited to the following choices:
- Blue
- Green
- Red
- Yellow
- Purple
- Cyan
- White
In addition, you can include "HighIntensity" in the color value to use a brighter shade of a color, as shown in the ERROR color mapping in the above configuration example.
These appenders are useful only for console-based programs. It won't hurt anything if you try to use this appender from a UI or web application, you just won't see any of your log messages.
DebugAppender and TraceAppender
These appenders act as adapters on the .NET System.Diagnostics.Debug and System.Diagnostics.Trace classes. Because of this, there is very little option in their configurations; both appenders have a single configurable property named ImmediateFlush that controls whether log output is flushed to the Debug or Trace writers immediately:
<appender name="DebugAppender" type="log4net.Appender.DebugAppender">
<immediateFlush value="true" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
I use the DebugAppender routinely to help work problems in the debugger - the log output shows up right in the debug output window of Visual Studio. This can be invaluable when trying to isolate race conditions or realtime effects where a breakpoint would ruin the reproducability of the issue.
I've used the TraceAppender when extending applications where custom TraceListener implementations were already in use; this allowed existing modules using log4net to adapt immediately to the proprietary TraceListeners, no code required.
FileAppender and RollingFileAppender
These appenders write log messages to files. Their configuration is a bit more involved than the other appenders we've discussed. A typical configuration for the FileAppender might look like this:
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log-file.txt" />
<appendToFile value="true" />
<encoding value="utf-8" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
Here's a quick summary of the more common configuration attributes for the FileAppender:
- file: the full or relative path to the log file;
- appendToFile: boolean indicating whether the log file should be appended (true) or overwritten (false). If false, the file overwrite occurs during log4net initialization. If unspecified, the log file is appended;
- immediateFlush: boolean indicating whether to flush the log file TextWriter after each log message is written. The default is true (flush each message after its written);
- lockingModel: allows control over the log file locking strategy. This can be either "log4net.Appender.FileAppender+MinimalLock" to allow for loose file locking or "log4net.Appender.FileAppender+ExclusiveLock" to lock the file during program execution. The default is the exlusive lock, and I highly recommend you stick with that lock for performance reasons.
Now that I've described the FileAppender, heed this advice:
Do not use the FileAppender, ever ever ever. Use the RollingFileAppender instead.
The log file managed by FileAppender will be allowed to grow without bounds. This will Tonya-Harding your application's stability when given enough time. The RollingFileAppender provides rudimentary log file management, configurable size- or date-boxing of the log file, and limited rolling backups of the log file.
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log-file.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="1MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
There is a lot of configurability to the RollingFileAppender; in addition to the options available to the FileAppender, the RollingFileAppender includes these parameters:
- rollingStyle: this controls how log files are "rolled," and can be one of the following values:
- Once: the log file is rolled every time log4net is initialized (typically at application startup);
- Size: the log file is rolled once it breaches a certain size;
- Date: the log file is rolled based on the current date;
- Composite: the log file is rolled based on size constraints and the current date;
- maximumFileSize: the size cap on the log file. This is an expression of size in the form of "#(KB|MB|GB)". For instance, "100KB" or "10MB";
- maxSizeRollBackups: the maximum number of rolled log file backups to maintain when rollingStyle is SIZE; when rollingStyle is COMPOSITE, this indicates the maximum number of roll-offs maintained per day; this property has no effect when rollingStyle is ONCE or DATE;
- datePattern: the date pattern used to roll files based on date. The value of this parameter needs to adhere to the format used by the SimpleDateFormatter class;
- staticLogFileName: a bit of a misnomer - when true this setting indicates whether log4net should actively write logs to the configured file (log-file.txt in our example configuration) and maintain rolling backups by copy. When false, this setting indicates that log4net will actively log to the latest roll-off file (e.g., log-file1.txt, log-file2.txt, log-file3.txt, etc);
- countDirection: indicates how roll-off file numbering is managed. When this parameter is >= 0, the newest log file will have the largest number; e.g., log-file.txt.5 will be newer than log-file.txt.4. When countDirection < 0, the newest log file will have the lowest number; e.g., log-file.txt.1 will be newer than log-file.txt.2. If unspecified, countDirection defaults to (-1);
Keep in mind that when using a file appender, the user running the logging process must have rights to create and/or modify the log file in order for log messages to be written properly. In addition, log4net will create the log file if it does not exist, but it will not create directories in the log file path that do not already exist. If log4net encounters a problem initializing the file appender (e.g., it cannot create the log file for security reasons), the log file will not be written but your application will continue to execute normally.
Using Multiple Appenders
With all of these appenders available, it may seem difficult to choose just one to meet your logging needs. But why choose one when you can have as many as you want?
Using multiple appenders is a simple task of specifying each appender you need under the root logger, like so:
<root>
<level value="ALL" />
<appender-ref ref="DebugAppender" />
<appender-ref ref="ColoredConsoleAppender" />
</root>
You can use as many appenders as you want; however, know that for each appender you use you will accrue some runtime overhead.
Coming Up
You now have control over where your log messages persist. Next time, I'll describe how you can control the format your log messages as they are written out by each appender, including how to insert context information - such as the thread id, timestamp, or user identity - into the log message automagically.
Edit Notes
07.03.2008: Theo pointed out some inaccuracies in the way I was describing the maxSizeRollBackups and countDirection properties of the RollingFileAppender, which I've attempted to correct. Thanks, Theo!