Declare log4net variables globally and use them across all class files c#

Viewed 1295

I have a class library written in c#. In this library we have used log4net to do the logging.
The class library has 2-3 class files.This class library is being used by one windows service also written in c#.

Now the log4net has been implemented in the following manner.
1. I have added reference from nuget for log4net.
2. In each class file , added this on top :

private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

3. Wanted some extra details to be printed along with the default values , so added some parameteres in log4net.config file as below :

<parameterName value="@Parameter1"/>
<parameterName value="@Parameter2"/>

and in layout pattern :

<layout type="log4net.Layout.PatternLayout">
     <conversionPattern value="%date{dd/MM/yy HH:mm:ss} [%thread] %level  :: Parameter1 = %property{Parameter1} :: Parameter2 = %property{Parameter2}:: %logger : %message%newline"   />
</layout>

And added this line in every method in ClassA , where logging is being done:

log4net.GlobalContext.Properties["Parameter1"] = parameter1;
log4net.GlobalContext.Properties["Parameter2"] = parameter2;

Now the problem I'm facing here is, say I have 2 class files in my class library named Class1 and Class2.And parameter1 and parameter2 are public variables in ClassA. As mentioned in this question, I'm not able to access paramter1 and parameter2 in ClassB. So when the logs of classB methods are printed, parameter1 and parameter2 are always null.

So is there a way I can set parameters globally once and whenever the logs are printed it uses the same variables.

My code is similar to below :

public ClassA {
    public string parameter1 { get; set; } = "ABC"
    public string parameter2 { get; set; } = "XYZ"

    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    public void SomeMethod(){
        log4net.GlobalContext.Properties["Parameter1"] = parameter1;
        log4net.GlobalContext.Properties["Parameter2"] = parameter2;
        ClassB b = new ClassB();
        log.info("In Some Method");
        b.AnotherMethod();
        LogMessage("After AnotherMethod");  
    }   
}

public class ClassB(){
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    public void AnotherMethod(){        
        log.info("Inside AnotherMethod");       
    }
}

Expected Output :

30/10/18 21:57:11 [5] INFO  :: Parameter1 = 'ABC' :: Parameter2 = 'XYZ' :: ClassA : In Some Method
30/10/18 21:57:11 [5] INFO  :: Parameter1 = 'ABC' :: Parameter2 = 'XYZ' :: ClassB : Inside AnotherMethod
30/10/18 21:57:11 [5] INFO  :: Parameter1 = 'ABC' :: Parameter2 = 'XYZ' :: ClassA : After AnotherMethod

Actual Output :

30/10/18 21:57:11 [5] INFO  :: Parameter1 = 'ABC' :: Parameter2 = 'XYZ' :: ClassA : In Some Method
30/10/18 21:57:11 [5] INFO  :: Parameter1 = (null) :: Parameter2 = (null) :: ClassB : Inside AnotherMethod
30/10/18 21:57:11 [5] INFO  :: Parameter1 = 'ABC' :: Parameter2 = 'XYZ' :: ClassA : After AnotherMethod

How can I make parameter 1 and 2 to print everytime or declare it globally.So once I set, it has to be used by all the log4net messages.

Any help in this regard is highly appreciated.

3 Answers

This line in your code:

log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

You end up with 2 different loggers and so the values you set for the logger in ClassA is not the same as the logger in ClassB. Try changing those two lines to something like:

log4net.LogManager.GetLogger("MyLogger");

And it should work.

If you take a read through the documentation you will see that when you call this method that uses the type name to load the logger "The full name of type will be used as the name of the logger to retrieve." Since you have two different types in your code you end up with two different loggers. You need to either use a common name or a common type to get the same logger instance.

Your other option would be to set the value on the settings file, but I am guessing that won't work for you as they are probably values that you need to set at run time.

If you set properties in log4net.GlobalContext, they should be available to all loggers. I'm not sure where or why you've added parameterName elements to your log4net.config file, but you shouldn't need them.

Typically you would set log4net.GlobalContext properties once at application startup. If you want properties only for the duration of a method, you could consider using log4Net.ThreadContext.

This dotnetfiddle is similar to your code and gives the result you say you want.

Related