log4net PatternString

I’ve been using log4net since it was first available for Mono and barely know anything about it. To a certain extent you’ve got to love it because of that – it just works! The project is great, but development appears somewhat stagnant. Either way, I’ve never needed to know much more than basic configuration and how to get it monitored even though I’ve used it for daemons, client applications, Add-ins, etc.

Today however, I learned something! A while ago I needed to have the RollingFileAppender log to a varying location depending on Windows version (XP vs Vista/7). At the time I found posts showing me how to use variables within the “file” option like so:

<file value="${APPDATA}\Company\Product\logs\data.log" />

Not having looked at the log4net code this is either explicitly converted to a user’s roaming folder, or it is replaced as an environment variable. This works great for applications that should log per user, but what if I needed the “All Users” roaming folder on Windows? Neither ${COMMONAPPDATA} or ${ALLUSERAPPDATA} worked for me so I needed another option that would cut it.

Enter log4net.Util.PatternConverter

By extending this very simple class and implementing one method we can now do variable substitution. The syntax leaves a lot to be desired, but hey – it works!

<file type="log4net.Util.PatternString">
  <converter>
    <name value="sub" />
    <type value="SpecialFolderPathConverter, Assembly" />
  </converter>
  <conversionPattern value="%sub{ApplicationData}\Company\Product\logs\addin.log" />
</file>

With this setup now all I have to do is create the Assembly.SpecialFolderPathConverter class:

using System;
using System.IO;

namespace Assembly
{
    public class SpecialFolderPathConverter : log4net.Util.PatternConverter
    {
        protected override void Convert(TextWriter writer, object state)
        {
            // Option gets set to contents of {}
            if (String.IsNullOrEmpty(this.Option))
                return;

            try {
                Environment.SpecialFolder sf = (Environment.SpecialFolder)
                    Enum.Parse(typeof(Environment.SpecialFolder), this.Option, true);

                writer.Write(Environment.GetFolderPath(sf));
            } catch (Exception) {
            }
        }
    }
}

As you can probably see this class takes any string within the {} brackets following %sub and replaces it using Environment.GetFolderPath which is quite simple, and helpful.

Thank you log4net!

This entry was written by MET , posted on Wednesday December 23 2009at 03:12 pm , filed under programming and tagged , . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

Comments are closed.