0
votes

I have been trying to programmatically flip the NLog async switch on and off for a long time. I am getting nowhere. I have code that appears to do what I want, but after making the changes, I get no logging at all. Here's what I've found so far:

When the NLog config file contains async=true, then at runtime, I will have two targets (LogManager.Configuration.AllTargets) for each target in the config file. One is the original target, renamed with "_wrapped" at the end. The other is the original (now renamed) target wrapped in an AsyncTargetWrapper, and given the name of my original target.

Before doing anything (to turn on or off async), I first clear all of the LoggingRules.

To turn off async, I get the wrapped target from the AsyncTargetWrapper, rename it to the original name, remove the wrapper target (which seems to remove the original too), then re-add the original target (which has now been given its original name again). Then I create a LoggingRule and set the appropriate logging levels. Finally, I call LogManager.ReconfigExistingLoggers(). At this point, something is broken, as I will get no logging whatsoever.

I follow a similar process when turning async on, except this time, I rename the original target, create an AsyncTargetWrapper for it, then add the new wrapper target. Again, I create a LoggingRule for the new target and set the logging levels. At this point, something is broken, as I will get no logging whatsoever.

The code is below:

LogManager.Configuration.LoggingRules.Clear();

if (async) // turning async ON
{
    foreach (var target in LogManager.Configuration.AllTargets)
    {
        if (!(target is AsyncTargetWrapper))
        {
            string targetName = target.Name;
            // rename the synchronous target to indicate that it is going to be wrapped by another target (an asynchronous wrapper)
            target.Name = $"{target.Name}_wrapped";

            // create an asynchronous target wrpper and give it the name of the synchronous target's original (non-wrapped) name
            AsyncTargetWrapper asyncTarget = new AsyncTargetWrapper(target) { Name = targetName };

            LogManager.Configuration.AddTarget(asyncTarget);

            LoggingRule asyncRule = new LoggingRule("*", asyncTarget);
            if (_minLevel != null && _maxLevel != null)
            {
                asyncRule.EnableLoggingForLevels(_minLevel, _maxLevel);
            }
            else
            {
                foreach (var level in LogLevel.AllLevels.Except(new List<LogLevel>() { LogLevel.Off }))
                {
                    if (Logger.IsEnabled(level)) asyncRule.EnableLoggingForLevel(level);
                }
            }
        }
    }
}
else // turning async OFF
{
    foreach (var target in LogManager.Configuration.AllTargets)
    {
        if (target is AsyncTargetWrapper)
        {
            // get the wrapped (synchronous) target from the wrapper
            Target syncTarget = ((AsyncTargetWrapper)target).WrappedTarget;

            // rename the synchronous target (remove "_wrapped" from the end of its name)
            syncTarget.Name = target.Name.Replace("_wrapped", "");

            // remove the wrapper target
            LogManager.Configuration.RemoveTarget(target.Name);

            LogManager.Configuration.AddTarget(syncTarget);

            // create a rule for the wrapped (synchronous) target
            LoggingRule syncRule = new LoggingRule("*", syncTarget);

            // set the logging level for the synchronous target
            if (_minLevel != null && _maxLevel != null)
            {
                syncRule.EnableLoggingForLevels(_minLevel, _maxLevel);
            }
            else
            {
                foreach (var level in LogLevel.AllLevels.Except(new List<LogLevel>() { LogLevel.Off }))
                {
                    if (Logger.IsEnabled(level)) syncRule.EnableLoggingForLevel(level);
                }
            }
        }
    }
}

LogManager.Configuration.Reload();
LogManager.ReconfigExistingLoggers();

I don't think those last two lines are needed, but I tried them because nothing else was working.

There must be a correct way to flip the async flag, but I don't know what it is. Can anyone tell me how to do it?

1

1 Answers

1
votes

I found the problem. I had a working implementation in the past, so this was all very confusing for me. There were problems with my old implementation, so I decided to refactor it, but made a mistake in the new implementation. I left out a critical line of code! New logging rules must be added to the configuration after they are created:

LogManager.Configuration.LoggingRules.Add(syncRule);

Now it works exactly as I want it to. Whew!