Search code examples
formseventspropertieslog4netnon-modal

log4net: How to distinguish between different forms on the same UI thread?


is there a way (NDC, Properties, ...?) to have a name/id per form that is included in all log4net messages, so I can distinguish between the forms in all log messages?

I have many service methods etc. that are used in all my forms, and I'd like to see e.g. that a service was called as a result of user input in what form (think multiple nonmodal similar forms (same class), running in the same UI thread, containing a button, and in the button's Click-Event, a service method is called. Inside the service method, there are logging calls. In the log messages, I'd like to have a property containing the information of in exactly which form instance the button was clicked in).

I don't want to modify ALL logging calls. The examples in the web for log contexts / NDC all only talk about multiple clients / asp.net requests / etc., not multiple forms in 1 thread.

Thanks, Tim


Solution

  • To do this, set the properties in the form's Activated event to what you want to log:

    private void Form1_Activated(object sender, System.EventArgs e)
    {
        // for example
        log4net.GlobalContext.Properties["Name"] = this.GetType().Name;
        log4net.GlobalContext.Properties["Id"] = this.Id;
    }
    

    The in your logging configuration, you can reference the properties in the PatternLayout for each appender:

    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%property{Name} : %property{Id} : [%level]- %message%newline" />
    </layout>
    

    Edit: to preserve multiple values, use a stack, as in this unit test which outputs:

    Now in TestClass1 Now in TestClass2

    using log4net.Appender;
    using log4net.Config;
    using log4net.Core;
    using log4net.Layout;
    using NUnit.Framework;
    
    namespace log4net.Tests
    {
        [TestFixture] // A NUnit test
        public class log4net_Stacks
        {
            [SetUp]
            public void Setup()
            {
                ConsoleAppender ca = new ConsoleAppender
                {
                    Layout = new PatternLayout("%property{demo}"),
                    Threshold = Level.All
                };
    
                ca.ActivateOptions();
                BasicConfigurator.Configure(ca);
            }
    
            [Test]
            public void Stacks_Demo()
            {
                new TestClass1().Method1();
                LogManager.GetLogger("logger").Debug("");
                ThreadContext.Stacks["demo"].Clear();
            }
    
            private abstract class BaseTestClass
            {
                protected static void AddToStack(string message)
                {
                    ThreadContext.Stacks["demo"].Push(message);
                }
            }
    
            private class TestClass1 : BaseTestClass
            {
                public void Method1()
                {
                    AddToStack("Now in " + GetType().Name);
                    var tc2 = new TestClass2();
                    tc2.Method2();
                }
            }
    
            private class TestClass2 : BaseTestClass
            {
                public void Method2()
                {
                    AddToStack("Now in " + GetType().Name);
                }
            }    
        }
    }