简体   繁体   中英

Initialize log4Net as early as possible with NUnit

I'm wondering what is the best way to initialize log4Net in a NUnit project. Of course I want to call the init code (ie. XmlConfigurator.Configure() ) as soon as I can to get as many early log output as I can. But since my project is run through NUnit, I have little control on its entry point.

According to NUnit documentation, it should call some constructors first, then a method marked with the [SetUp] attribute in a class marked with [TestFixtureSetup] .

So, First, I created a static helper class which I can call several times without trouble.

  public static class LoggingFacility
  {
    private static bool _loggerIsUp = false;

    public static void InitLogger()
    {
      if (_loggerIsUp == false)
        XmlConfigurator.ConfigureAndWatch(f);

      _loggerIsUp = true;
    }
  }

Then, I made all my [TestFixtureSetup] inherit a single one that does pretty much nothing else than calling LoggingFacility.initLogger() . But that still leaves all constructors that are run earlier, in an order I can only assume random. And moreover, it will probably do some static initializations before I am even able to execute some code.

In fact, as I can see in my log, the first 4 seconds or so of execution are completely unrecorded.

Does it mean I will have to call my InitLogger() in every constructor and forbid the use of any static initializer? That's tough job!

Does somebody know a magic trick with this?

For single initialization point you should use class marked with [SetUpFixture] attribute and method marked with [SetUp] , for example:

[SetUpFixture]
public class TestsInitializer
{
    [SetUp]
    public void InitializeLogger()
    {
        LoggingFacility.InitLogger();
    }
}

Now, this method ( [SetUp] InitializeLogger ) will run before any test is run, same as one marked with [TearDown] will run once all tests are run. But here's the catch - what does any and all mean in this context? Tests from classes declared in the same namespace as class marked with [SetUpFixture] .

For example, assuming hierarchy like this:

- Tests
--- Business
----- TestsInitializer.cs // SetUpFixture class
----- FirstBusinessTests.cs
----- SecondBusinesTests.cs
--- ComplexLogic
----- VeryComplexLogicTests.cs

First and SecondBusinessTests will run after SetUp from TestsInitializer , however VeryComplexLogicTests might run in random order.

According to linked documentation , if you declare SetUpFixture class outside of any namespace, setup and teardown will apply for entire assembly:

Only one SetUpFixture should be created in a given namespace. A SetUpFixture outside of any namespace provides SetUp and TearDown for the entire assembly.

A work mate provided me with the following workaround, that does the job:

In all my classes that require logging, I had the following logger initialization

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

I simply changed it to a singleton initializer

private static readonly ILog Log = LoggingFacility.GetLoggerWithInit(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

/*** ... ***/

public static class LoggingFacility
{
  private static bool _loggerIsUp = false;

  public static ILog GetLoggerWithInit(Type declaringType)
  {
    if (_loggerIsUp == false)
      XmlConfigurator.Configure(_log4NetCfgFile);
    _loggerIsUp = true;
    return LogManager.GetLogger(declaringType);
  }
}

Because I have this code in every class, this static initializer has to be called very early by NUnit wile instantiating my test classes.

Next step is to make that thread safe :(

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM