Home > Software engineering >  How to use NLog with async factory method configuration pattern
How to use NLog with async factory method configuration pattern

Time:11-18

Versions

  • Nlog 5.0.4
  • .NET 6.0 application

Originally I configured NLog with an XML config file which worked fine, however there was a secret string in the file so I wanted to move to programmatic configuration so I could retrieve the secret string securely in C# and not have it in plaintext in the file.

I was able to get the programmatic configuration to work, but the secret string requires an async method to be retrieved so it cannot be in the Logger constructor. I then took the config code and the async call out of the Logger constructor and made a factory method.

My problem is: I cannot figure out how to get Logger in each class with a private constructor factory method instead of a public constructor due to my configuration requiring an async call.

Here is my current NLog setup:

public class MyLogger : Logger 
{
    // Private constructor
    private MyLogger(LoggingConfiguration config)
    {
      // Apply config           
      LogManager.Configuration = config;
    }

    // FactoryMethod
    public async static Task<MyLogger> BuildMyLogger()
    {
      LoggingConfiguration config = new LoggingConfiguration();

      var secret = await CallToGetSecretString();

        DatabaseTarget databaseTarget = new DatabaseTarget("database")
        {
          ConnectionString = secret,
          CommandType = System.Data.CommandType.StoredProcedure,
          CommandText = "[StoredProcedure]",
          Parameters = {
          new DatabaseParameterInfo("@logged", "${date}")
          // Shortened...
          }
        };
          
        config.AddRuleForAllLevels(databaseTarget);

        return new MyLogger(config);
    }

    // Other code here...
}

Previously my setup was:

public class MyLogger : Logger
{
  public MyLogger()
  {
    LogManager.Configuration = new XmlLoggingConfiguration("NLog/MyNlogConfig.config");
  }
}

This call worked for the previous way, but throws an error when using the new config

private readonly static MyLogger logger = (MyLogger)LogManager.GetCurrentClassLogger(typeof(MyLogger));
System.TypeInitializationException: 'The type initializer for 'App.Logic.MyClass' threw an exception.'
Inner Exception
InvalidCastException: Unable to cast object of type 'NLog.Logger' to type 'App.Nlog.MyLogger'.

I also tried to call the factory method from a class, but that is not possible due to it being async

private readonly static MyLogger logger = await MyLogger.BuildMyLogger();

CS1992  The 'await' operator can only be used when contained within a method or lambda expression marked with the 'async' modifier

Thank you for your time and suggestions on this issue.

CodePudding user response:

You should only attempt to load NLog-configuration once, and not every time creating a Logger-object. The new code probably fails because MyLogger no longer has a valid default-constructor.

Think you should focus on creating initial NLog-config at application-startup, and assign that to LogManager.Configuration as early as possible.

Note you can also access application-secrets from NLog.config by using NLog Context. Ex. ${gdc:item=SecretKey} and NLog.GlobalDiagnosticsContext.Set("SecretKey","SomeSecretValue");

  • Related