Writing only the topmost element of the ThreadContext stack

We are using Log4net ThreadContext.Stacks and mostly work well. My problem occurs if there were multiple ThreadContext.Stacks ["key"]. Push (...).

With a simple ConversionPattern:

<param name="ConversionPattern value="... topProp=%properties{key} ..."/>

      

I see log entries like:

... topProp=first second third ...

      

I would really like to see only the last pressed value, not all values. I was hoping that something like the following could be added in my appender / layout / ConversionPattern:

<param name="ConversionPattern value="... topProp=%properties{key}{1} ..."/>

      

but that won't work. I can kill it by assuming / requiring all values ​​to be the same length (e.g. 5) and doing:

<param name="ConversionPattern value="... topProp=%5.5properties{key} ..."/>

      

But it's not really attractive. Any ideas?

Thank!

[Edit to add a very simple example]

using System;
using System.IO;
using log4net;
using log4net.Config;

namespace ThreadLocalExample {
class Program {
    private const string PropJobId = "Example:JobId";

    static void Main() {
        XmlConfigurator.Configure(new FileInfo("log4net.cfg"));
        var log = LogManager.GetLogger(typeof(Program));

        ThreadContext.Stacks[PropJobId].Push("Old");

        log.Debug("Enter using");
        using (ThreadContext.Stacks[PropJobId].Push("New")) {
            log.Debug("stuff");
        }
        log.Debug("Out of using");

        log.Debug("done.");
        Console.ReadKey();
    }
  }
}

      

With log4net config:

<appender name="Console" type="log4net.Appender.ConsoleAppender">
    <threshold value="ALL" />
    <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="[jobId=%P{Example:JobId}]: %m%n" />
    </layout>
</appender>

      

Outputs:

[jobId=Old]: Enter using
[jobId=Old New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.

      

But I would like:

[jobId=Old]: Enter using
[jobId=New]: stuff
[jobId=Old]: Out of using
[jobId=Old]: done.

      

+3


source to share


2 answers


I had the same problem and it was not just about "bad formatting" because I was using a database application (AdoNetAppender) that expects integers. Thus, after combining all the summed values, the result is no longer an integer. Consider an application like this:

<appender name="DbAppender" type="log4net.Appender.AdoNetAppender">
...
<commandText value="INSERT INTO Log ([Id]) VALUES (@Id)" />
<parameter>
    <parameterName value="@Id" />
    <dbType value="Int32" />
    <layout type="log4net.Layout.PatternLayout" value="%P{someId}" />
</parameter>

      

This appender accepts no log message where "someId" gets stacked twice or more - no logs in the database ...

So, to solve this problem, I ditched stacks and went back to flat properties. I have coded a little extension:

    public static class Log4NetExt {
        public static IDisposable ThreadContextPush(string key, object value) {
            object oldVal = ThreadContext.Properties[key];
            ThreadContext.Properties[key] = value;
            var topMostCleaner = new DispCleaner();
            topMostCleaner.EvDispose += () => {
                // Pop = restore old value
                ThreadContext.Properties[key] = oldVal;
            };
            return topMostCleaner;
        }

        private class DispCleaner : IDisposable {
            public event Action EvDispose;

            public void Dispose() {
                if (EvDispose != null) EvDispose();
            }
        }
    }

      



And now instead of:

using (ThreadContext.Stacks[PropJobId].Push("New")) {

      

records:

using (Log4NetExt.ThreadContextPush(PropJobId, "New")) {

      

and it works fine;)

(This shortcode doesn't follow all the best practices for creating disposable objects, removing event handlers and all that stuff, but it's short and I think it's safe in this simple case.)

+1


source


I did this with Filip's solution modified as

 public static class Log4NetExt
    {
        public static IDisposable ThreadContextSet(string key, object value)
        {
            //object oldVal = ThreadContext.Properties[key];
            ThreadContext.Properties[key] = value;
            var topMostCleaner = new DispCleaner();
            topMostCleaner.EvDispose += () => {
                // Pop = restore old value
                //ThreadContext.Properties[key] = oldVal;
                ThreadContext.Properties[key] = null;
            };
            return topMostCleaner;
        }

        private class DispCleaner : IDisposable
        {
            public event Action EvDispose;

            public void Dispose()
            {
                if (EvDispose != null)
                {
                    EvDispose();
                }
            }
        }
    }

      

And used it like



using (Log4NetExt.ThreadContextSet ("ContextId", "Feed")) using (Log4NetExt.ThreadContextPush ("ContextValue", objFeed.FeedId)) {

}

0


source







All Articles