Logging exceptions with NLog

A feature of my nice WPF application is to log exceptions that bring it down when running standalone. Of course this never happens but still, if such an event occurs I want to understand the cause. Why exceptions in plural? Because an exception might have an InnerException and this InnerException might have another InnerException and so forth.

I use NLog as logging framework. Nice and slick. Here is my really simple configuration:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File" fileName="log.txt" layout="${longdate}:${message} ${exception:format=message,stacktrace:separator=*}" />
<target name="ds" xsi:type="Debugger"/>
</targets>
<rules>
<logger name="*" writeTo="file" minLevel="Warn"/>
<logger name="*" writeTo="ds" />
</rules>
</nlog>

Basically I log exceptions to log.txt file (as well as pretty much everything to debugger’s output). The layout to write the exception is this: layout="${longdate}:${message} ${exception:format=message,stacktrace:separator=*}". It is a pretty much standard NLog’s format for exceptions. However, the problem with it is that it won’t log InnerExceptions at all.

Hence I need to use the following code:

private bool showingFatal;
private void Application_DispatcherUnhandledException(object sender, 
    DispatcherUnhandledExceptionEventArgs e)
{
    if (showingFatal)
    {
        e.Handled = true;
        return;
    }
    showingFatal = true;
    logger.Error(LogCategory.Engine, "Fatal exception start ********************** ");
    try
    {
        int level = 0;
        Exception ex = e.Exception;
        while (ex != null)
        {
            logger.Error(LogCategory.Engine, 
                string.Format("Fatal exception level {0}: ", level++), ex);
            ex = ex.InnerException;
        }
    }
    finally
    {
        logger.Error(LogCategory.Engine, "Fatal exception end ********************** ");
    }
    Shutdown();
}

I use the flag showingFatal to prevent duplicate entry into the method (not sure why it happens but it happens) and a loop to log all, really all, exceptions and callstacks there are.

This way I have a good information about the exception that caused the crash. If it happens someday…

Running NLog in WPF Browser Application and other partially trusted environments

NLog is a pretty slick logging library, no doubts about that. However if you try to use it from a partially trusted environment you are facing some problems. The solution is to fix two things in the sources and recompile them. Here is the recipe:

1. Open solution NLog.vs2005.sln in Visual Studio. If you have no special needs you’ll need to recompile just the project NLog.vs2005 – you can safely remove others from the solution.

2. Add [assembly: System.Security.AllowPartiallyTrustedCallers] line to AssemblyInfo.cs file. With this change you are allowing partially trusted callers. This might not be enough. See the next paragraph.

3. If you don’t provide explicit configuration then NLog will try to read from environmental variable and thus causing a SecurityException due to EnvironmentPermission request which is not granted by default. To avoid this you’ll have to comment a piece of code in the LogFactory.Configuration property:

if (_config == null)
{
    if (EnvironmentHelper.GetSafeEnvironmentVariable("NLOG_GLOBAL_CONFIG_FILE") != null)
    {
        string configFile = Environment.GetEnvironmentVariable("NLOG_GLOBAL_CONFIG_FILE");
        if (File.Exists(configFile))
        {
            InternalLogger.Debug("Attempting to load config from {0}", configFile);
            _config = new XmlLoggingConfiguration(configFile);
        }
        else
        {
            InternalLogger.Warn("NLog global config file pointed by NLOG_GLOBAL_CONFIG '{0}' doesn't exist.", configFile);
        }
    }
}

Since the code is commented no EnvironmentPermission will be thrown even if there is no explicit configuration provided. As a side effect you can’t rely on default configuration settings anymore but this shouldn’t be a big issue since you can’t read them in a default partially trusted environment anyway.

4. Compile in release configuration and there you go.

Don’t forget, most of the default logging targets won’t work due to the security permissions. But the ones your application has access to will.

Happy logging.