Friday, April 17, 2009

Logging in Spring.NET with log4net

Wherein real progress is made...

I've mentioned that Spring.NET is a large project, with more than just the IoC container that I was babbling about. It also has log4net built in. At first blush, this seemed like a good thing. Then I got to the documentation.

The docs say use log4net syntax....
But they are working on a Common Logging...
Which they got going in 2006...
Okay, at least there's a "Common.Logging.dll" in the spring folder...
Hmmm, well the example that guy gives doesn't help much, where's the better docs? The example uses an external file - what if I don't want to do that? Is that INLINE?
Another chunk of doc sends us to the Common Infrastructure Libraries for .NET site...
The sum of whose libraries is logging. Heh.
Hm. Okee dokee. Does Common.Logging.dll include log4net?

Only one way to find out, I guess...
Added it's section name (the example from up there, and the corresponding configSections entry, to the app.config...nuthin. I'd expect to see something from Spring, at least.
Changed the logging level to "DEBUG" for Spring...still nuthin.
Okay, maybe some code is in order. How do I initialize this? Do I use the log4net syntax?
Back to the...oh crap...*sigh*...documentation.

Aha! Totally different. Yea, I could've started here, huh? But I didn't.

There's a few different adapters supported, including NLog, and EntLib. BTW, Microsoft's Enterprise Library is pretty nice - it has a lot of the functionality of Spring/Spring.NET, but isn't as complete. If you like a lot of what I'm talking about, but want an MS alternative, that would be it.

In fact, it might be more correct to re-title this post as "Logging in Spring.NET with log4net".

Log4NetLoggerFactoryAdapter seems to be what I'm looking for...
There's a lot of references to calling XmlConfigurator.Configure(). Is this from Spring.net?
Apparently, in the common/logging/factoryAdapter node, the configType of INLINE means "do it like you were using log4net, only you need this whole other section, too".

In goes a log4net section, then.

BLAMMO! no worky.
{"Could not load file or assembly 'Common.Logging.Log4Net' or one of its dependencies. The system cannot find the file specified.":"Common.Logging.Log4Net"}

Okay, that ain't there. It's hiding in the spring.net distribution under lib\net\2.0, along with some other dlls I'll probably want some day. I need to get the source code and add it to the project, but this should do for now.

Added Common.Logging.Log4Net and log4net as references in the project, and that got rid of the errors, and I got a chunk of output. Cool.

To summarize (don't you wish I had already?):

Using INLINE in the app config tells Common Logging to call XmlConfigurator.Configure(), which is (apparently - I forget how to use it) log4net initializer. To get it all working, you have to create a "common" section, as well as a "log4net" section. If I understand the history of all this correctly, it was to simplify dealing with multiple versions of log4net, with the bonus of being able to plug in other logging providers.

I'm thinking it might be simpler just to use log4net directly. I don't anticipate having that particular problem. I'll try it out, first, though.

To get it to work:

Reference log4net, Common.Logging, and Common.Logging.Log4Net in your project.
Then update your app.config to look something like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="solr" type="org.apache.solr.SolrSharp.Configuration.SolrConfigurationSection, SolrSharp" allowLocation="true" allowDefinition="Everywhere" />
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>

</configSections>

<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>

<!-- Set default logging level to DEBUG -->
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>

<!-- Set logging for Spring to INFO. Logger names in Spring correspond to the namespace -->
<logger name="Spring">
<level value="DEBUG" />
</logger>
</log4net>

<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter,Common.Logging.Log4Net">
<!-- choices are INLINE, FILE, FILE-WATCH, EXTERNAL-->
<!-- otherwise BasicConfigurer.Configure is used -->
<!-- log4net configuration file is specified with key configFile-->
<arg key="configType" value="INLINE" />
</factoryAdapter>
</logging>
</common>

<spring>

<context>
<resource uri="config://spring/objects"/>
</context>
<objects>
</objects>
</spring>


</configuration>


Great - it's logging all of Spring.net's stuff. How do developers use it? Easy, once you have the references.

using Common.Logging;
...
ILog log = LogManager.GetLogger( "somelog" );
log.Error( "this is not an error!" );


Too simple, once it's set up.

No comments:

Post a Comment