1

I want to implement the AppDomain.FirstChanceException in my WPF app, for optionally logging every exception that occurs, handled or not. I don't wish to log these exceptions to the targets I have configured for NLog. Is is possible, around the time of calling Logger.Error (or whatever Logger method) to make NLog only log to one particular target?

ProfK
  • 49,207
  • 121
  • 399
  • 775
  • Probably I misunderstood your question, but do you need to change your NLog targets in the `AppDomain.FirstChanceException` handler? – Il Vic Mar 16 '15 at 17:03
  • Quite right, @IlVic, I want that one Logger method call, e.g. `Error`, inside `AppDomain.FirstChanceException`, to only log to file, not to DB, nor memory. – ProfK Mar 16 '15 at 17:57

1 Answers1

6

It's possible, but this is not a good idea because:

  1. It's not thread safe.
  2. All loggers of the specified factory will be reconfigured.

Configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets async="true">
    <target name="file1" xsi:type="File" fileName="${basedir}/log1.txt" />
    <target name="file2" xsi:type="File" fileName="${basedir}/log2.txt" />
  </targets>
  <rules>
    <logger name="*" minlevel="Trace" writeTo="file1,file2" />
  </rules>
</nlog>

Example of a global factory configuration:

var logger = LogManager.GetCurrentClassLogger();

logger.Error("Original Configuration");

var configOrig = LogManager.Configuration;
var configTemp = LogManager.Configuration.Reload();
var rule = configTemp.LoggingRules.First(r => r.NameMatches("*"));
var target = configTemp.FindTargetByName("file2");

rule.Targets.Remove(target);

LogManager.Configuration = configTemp;
logger.Error("Temporary Configuration");

LogManager.Configuration = configOrig;
logger.Error("Original Configuration");

Example of a user created factory configuration:

var factory = new LogFactory(LogManager.Configuration);
var logger = factory.GetCurrentClassLogger();

logger.Error("Original Configuration");

var config = factory.Configuration;
var rule = config.LoggingRules.First(r => r.NameMatches("*"));
var target = config.FindTargetByName("file2");

rule.Targets.Remove(target);
factory.ReconfigExistingLoggers();
logger.Error("Temporary Configuration");

rule.Targets.Add(target);
factory.ReconfigExistingLoggers();
logger.Error("Original Configuration");

log1.txt

2015-03-16 21:46:04.5685|ERROR|ConsoleApplication.Program|Original Configuration
2015-03-16 21:46:04.5865|ERROR|ConsoleApplication.Program|Temporary Configuration
2015-03-16 21:46:04.5865|ERROR|ConsoleApplication.Program|Original Configuration

log2.txt

2015-03-16 21:45:26.4238|ERROR|ConsoleApplication.Program|Original Configuration
2015-03-16 21:45:26.4588|ERROR|ConsoleApplication.Program|Original Configuration

Soulution

The best solution of the problem is to create two factories and one logger for each factory. The factories and loggers must be initialized before you subscribe to AppDomain.FirstChanceException. Otherwise, you will have problems with thread safety if you have async operations or other threads.

Yoh Deadfall
  • 2,711
  • 7
  • 28
  • 32
  • @Yoh, thanks, some nice detail. So, it looks like I will need two `GetLogger` methods in my factory, and use one logger specifically for where I want more detail, and the other logger elsewhere. In other words, I can't actually do anything at the time of using the logger, only when creating it? – ProfK Mar 17 '15 at 03:33
  • 1
    You need two factories and one logger for each factory. The first factory will use default configuration and the second - a modified configuration with some targets removed. – Yoh Deadfall Mar 17 '15 at 08:52