views:

29

answers:

1

Is it possible to create a log4net custom PatternLayoutConverter that allows an "index" value to be configured? I know about the "property" conversion string that allows you to write code like this:

ThreadContext.Properties["ID"] = yourID;

And specify like this:

%property{ID} 

That the value should be included in the output.

What if the values I want to log are in some other "dictionary"? I suppose that I could write some logic to copy those values from the dictionary to one of the log4net contexts and then just use the built in %property token. What if I want log4net to log the values directly from my own "dictionary" based on an index value specified in the config file?

Can I write my own PatternLayoutConverter that would allow me to configure something like this:

%myproperty{ID}

And then pull the corresponding "ID" value from my own "dictionary"?

For anyone that is interested, it is pretty easy to do the same thing with NLog:

  [LayoutRenderer("MyGDC")]
  class GdcLayoutRenderer : LayoutRenderer
  {
    [RequiredParameter]
    [DefaultParameter]
    public string Item { get; set; }

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
      string msg = GDC.Get(this.Item);
      builder.Append(msg);
    }

    protected override int GetEstimatedBufferSize(LogEventInfo logEvent)
    {
      return 10;
    }
  }

And configured like this:

Tell NLog about any assemblies with extensions:

  <extensions>
    <add assembly="NLog.Extensions"/>
  </extensions>

Use the "indexed" property in a layout:

  <layout="${longdate} | ${MyGDC:item=name} | ${message}"/>

In this example I am actually using NLog's GDC object as my "dictionary", but I am demonstrating how I was able to write my own "indexable" LayoutRenderer (more or less equivalent to log4net's PatternLayoutConverter) to access a value indexed by a value in the config file.

[EDIT] I got the answer that I wanted. I have included the code for my sample PatternLayoutRenderer here. In my test, I have a static dictionary in my main form class where I could store "application settings". I have created a PatternLayoutConverter that can accept a key as a parameter so that the converter can lookup the correct value in the dictionary. I might be able to achieve the same functionality using the log4net (or NLog) context objects, but in our application we might have some settings or session info that the application will hold for other purposes and we want to be able to add that to the logging output. Since it will already be in a lookup structure, it would be nice to be able to reference the data directly rather than having to explicitly copy it to the log4net (or NLog context).

Anyway, here is the code:

namespace Log4NetTest
{
  class KeyLookupPatternConverter : PatternLayoutConverter
  {
    protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent)
    {
      //Use the value in Option as a key into the "application settings" stored on the main form.
      string setting;
      if (Form1.AppSettings.TryGetValue(Option, out setting))
      {
        writer.Write(setting);
      }
    }
  }
}

Layout configuration:

  //Log the "sessionid" and "userid" values from our "application settings" object
  <layout type="log4net.Layout.PatternLayout">
    <param name="ConversionPattern" value="%d [%t] %-5p [session = %KLPC{sessionid}] [user = %KLPC{userid}] %m%n"/>
    <converter>
      <name value="KLPC" />
      <type value="Log4NetTest.KeyLookupPatternConverter" />
    </converter>
  </layout>
+1  A: 

I did not try it but this should work. In log4net you can pass an option string to a pattern converter like this:

%converterName{converterOptions}

The date pattern converter for instance can be used like this:

%date{HH:mm:ss,fff}

This means you can write your pattern converter the way you suggested. A simple example for such a converter can be found here.

In the Convert method you can access the the property string with the property 'Option' (defined in the PatternConverter class) and use the thread context to get the desired entry from the dictionary. You can also implement the IOptionHandler interface if your options consist of more then just the dictionary key: This way you can parse the options upon activation of the log4net configuration.

Stefan Egli
Thanks! That did exactly what I wanted to do. I will add my test LayoutPatternConverter to my question for reference in case anyone else is interested.
wageoghe