Repository
The second layer is responsible for maintaining the organization of loggers.
By organization, I am talking about the logical structure of the loggers inside of
the framework. Before the current version of log4net, the framework only supported the hierarchical
organization. As we discussed earlier, this hierarchical nature is an implementation of
the repository and is defined in the log4net.Repository.Hierarchy namespace.
To implement a Repository, it is necessary to implement the log4net.Repository.ILoggerRepository
interface. But instead of directly implementing this interface, another
class, log4net.Repository.LoggerRepositorySkeleton, is provided to work as the base class; e.g., the hierarchical repository is implemented by the log4net.Repository.Hierarchy.Hierarchy class.
If you are a normal developer only using the log4net framework instead of extending it, then you would probably not use any of these Repository classes in your code.
Instead, you would use the LogManager class, as described earlier, to automatically
manage the repositories and the loggers.
Appender
Any good logging framework should be able to generate output for multiple destinations,
such as outputting the trace statements to the console or serializing it into a log file.
log4net is a perfect match for this requirement. It uses a component called
Appender to define this output medium. As the name suggests, these components append themselves
to the Logger component and relay the output to an output stream. You can append multiple appenders to a single logger.
There are several appenders provided by the log4net framework; the
complete list of appenders provided by the log4net framework can be found here.
With all of these appenders provided, there is not much need for writing
your own, but if you wish to, you can start by inheriting the log4net.Appender.AppenderSkeleton
class, which works as an adapter between your class and the IAppender interface.
Appender Filters
An Appender defaults to pass all logging events to the Layout. Appender Filters
can be used to select events by different criteria. There are several filters
defined under the log4net.Filter namespace. By using a filter, you can either
filter a range of level values, or filter out any log message with a
particular string. We'll see filters in action later in our example. More information
about filters is provided in the API documentation.
Layout
The Layout component is used to display the final formatted output to the user.
The output can be shown in multiple formats, depending upon the layout we are
using. It can be linear or an XML file. The layout component works with an appender.
There is a list of different layouts in the API documentation. You cannot use
multiple layouts with an appender. To create your own layout, you need to inherit
the log4net.Layout.LayoutSkeleton class, which implements the ILayout interface.
Using
log4net in Your Application
Before you start logging your application, you need to heat up the log4net
engine. Technically, this means that you need to configure the three components
that we discussed earlier. There are two different methods by which you can
specify the configuration: you can either define them in a separate configuration
file, or you can place them inside of your code, configuring it programmatically.
The first method is always recommended, for the following reasons.
- You can change the settings without recompiling the source files.
- You can change the settings even when your application is running. This is very important in web application and remote application scenarios.
Considering the importance of the first method, we'll see it first.
Using a Configuration File
The configuration settings required are put into either of the following files:
- In the application config file (AssemblyName.config or web.config).
- Into your own file. The filename could be anything you like, or it could be name of the assembly with a different extension concatenated onto it (such as AppName.exe.xyz).
The log4net framework looks for the configuration file in the file path relative to the application's base directory defined by the AppDomain.CurrentDomain.BaseDirectory
property. The only thing that the log4net framework searches for inside of the configuration
file is the <log4net> tag. A complete sample configuration file is shown
below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,
log4net-net-1.0"
/>
</configSections>
<log4net>
<logger name="testApp.Logging">
<level value="DEBUG"/>
</logger>
<root>
<level value="WARN" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
<appender name="LogFileAppender"
type="log4net.Appender.FileAppender" >
<param name="File" value="log-file.txt" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="Header" value="[Header]\r\n"/>
<param name="Footer" value="[Footer]\r\n"/>
<param name="ConversionPattern"
value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n"
/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="WARN" />
</filter>
</appender>
<appender name="ConsoleAppender"
type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern"
value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n"
/>
</layout>
</appender>
</log4net>
</configuration>
You can copy the above file to use in any application, but it
is always better to know what constitutes the configuration file. The <section>
entry inside of the <configSection> tag is only necessary if you are using
the application's configuration file. Otherwise, only the text inside of the
<log4net> tag is required. It is not a requirement to maintain the sequence
of individual tags; I am only putting it this way to maintain the flow of describing
things. Taking each tag individually, we start with the <logger> element.
<Logger>
<logger name="testApp.Logging">
<level value="DEBUG"/>
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
</logger>
The <logger> element defines the settings for an individual logger. Then
by calling LogManager.GetLogger(...), you can retrieve the same logger by the
name. You can also define the appenders to use with that logger through the
<appender-ref> tag. <appender-ref> defines a reference to an appender which is actually defined anywhere else.
<root>
<root>
<level value="WARN" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="ConsoleAppender" />
</root>
The <root> tag is next to the logger tag. All loggers in the hierarchy
are children of the root logger; therefore the framework uses the properties
defined here if there are no loggers explicitly defined in the configuration
file. After knowing this, we can also tell that the <logger> tag that
we see above is not necessary. Inside of the <root> tag, the default values
are defined. Both the level value and the appender list can be put in
here. The default value of LEVEL, if not defined anywhere else, is set to DEBUG. Obviously,
the individual setting for a logger in the <logger> tag overrides the
settings in the root tag for that particular logger. In the case of an appender,
the <logger> tag will inherit all of the appenders defined by its ancestor.
This default behavior can be changed by explicitly setting the additivity
attribute for the <logger> tag to false.
<logger name="testApp.Logging" additivity="false">
</logger>
This attribute is set to true by default. The <root> tag is not necessary,
but recommended.
<appender>
<appender name="LogFileAppender"
type="log4net.Appender.FileAppender" >
<param name="File" value="log-file.txt" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="Header" value="[Header]\r\n" />
<param name="Footer" value="[Footer]\r\n"/>
<param name="ConversionPattern"
value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n"
/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="DEBUG" />
<param name="LevelMax" value="WARN" />
</filter>
</appender>
The appenders listed either in the <root> tag or in the individual <logger>
tag are defined individually using the <appender> tag. The basic format
of the <appender> tag is defined above. It uses the appender name and
maps it to the class that defines that appender. Other important things to
see here are the tags inside of the <appender> element. The <param>
tag varies with different appenders. Here, to use the FileAppender, you need
a file name that you can define as a parameter. To complete the picture, a Layout
is defined inside of the <appender> tag. The layout is declared in its own
<layout> tag. The <layout> element defines the layout type (PatternLayout
in the example) and the parameters that are required by that layout
(as in the pattern string used by the PatternLayout class).
The Header and Footer tags provide the text to print before and after a logging
session. The details of configuring each appender are further described in the
documentation here,
where you can see individual appender section as examples.
The last thing is the <filter> tag in the Appender element. It defines
the filter to apply to a specific Appender. In this example, we are applying
the LevelRangeFilter, which extracts only those messages that fall between the
levels defined between the LevelMin and LevelMax parameters. Similarly,
other tags are defined, as appropriate. Multiple filters can be applied to an
appender, which then work in a pipeline in the sequence in which they are ordered. Other
filters and information on using them can be found in the log4net SDK
documents.
These are the necessary elements that we needed to initialize the log4net
framework for our application. Now that we have created the configuration file,
it's time to link to it from our application.
By default, every standalone executable assembly defines its own configuration
settings. The log4net framework uses the log4net.Config.DOMConfiguratorAttribute
on the assembly level to set the configuration file. There are three properties
for this attribute.
ConfigFile: The property is only used if we are defining the<log4net>tag into our own configuration file.ConfigFileExtension: If we are using the application compiled assembly with a different extension, then we need to define the extension here.Watch(Boolean): This is the property by which thelog4netsystem decides whether to watch the file for runtime changes or not. If the value istrue, then theFileSystemWatcherclass is used to monitor the file for change, rename, and delete notifications.
[assembly:log4net.Config.DOMConfigurator(ConfigFile="filename",
ConfigFileExtension="ext",Watch=true/false)]
The log4net framework will consider the application's configuration file if
you do not define either the ConfigFile or ConfigFileExtension attribute. These attributes are mutually exclusive. We also need to keep it in
mind that the DOMConfigurator attribute is necessary and can be defined
as follows, with no parameters:
[assembly: log4net.Config.DOMConfigurator()]
There is another technique that saves you from having to use the attributes. It uses
the DOMConfigurator class inside of the code to load the configuration file provided in the parameter. This method takes a FileInfo object instead of a file name.
This method has the same effect as loading the file through the attribute, as
shown previously.
log4net.Config.DOMConfigurator.Configure(
new FileInfo("TestLogger.Exe.Config"));
There is another method, ConfigureAndWatch(..), in the DOMConfigurator class,
to configure the framework to watch the file for any changes.
The above step concludes everything related to configuration. Next, the following two steps are required in our code to use the logger.
- Create a new logger or get the logger you already
created. It uses the setting defined in the configuration file. If this particular
logger is not defined in the configuration file, then the framework uses the
logger's hierarchy to gather different parameters from its ancestors and, lastly,
from the root logger.
Log4net.ILog log = log4net.LogManager.GetLogger("logger-name"); - Use the log object to call any of the logger
methods. You can also check the level of the logger through the
IsXXXEnabledBoolean variables before calling the methods to boost performance.if (log.IsDebugEnabled) log.Debug("message"); if (log.IsInfoEnabled) log.Info("message); //….

