2

I have a service that loads a dll into a seperate appdomain(the appdomain is needed because the dll is loading a dynamically generated assembly and needs to be able to unload them)

How can I copy the nlog configuration so that the new appdomain uses the same settings?

One additional complication is that I setup logging parameters using the GlobalDiagnosticsContext at the beginning of the program. Is there any option besides having to set them over again in each appdomain?

static void Main()
{
    // Setup NLog variables
    GlobalDiagnosticsContext.Set("ConnectionString", @"...");
    GlobalDiagnosticsContext.Set("ApplicationName", @"appName");

    // ... loads appdomain and does logging from the new appdomain

This is my config file:

<?xml version="1.0" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <variable name="TextErrorLayout" value ="${gdc:item=ApplicationName} ${date:format=yyyy-MM-dd HH\:mm\:ss.fff}${newline}
Level: ${level}${newline}
Description: ${message}${newline}
Machine: ${machinename}${newline}
User: ${windows-identity}${newline}
Process: ${processname}${newline}
WorkingDir: ${basedir}${newline}
Exception: ${exception:format=tostring}${newline}
DetailedMessage: ${event-context:item=Details}${newline}"/>

  <targets async="true">
    <target name="LogMill" xsi:type="FallbackGroup">
      <target xsi:type="Database"
              connectionString="${gdc:item=ConnectionString}"
              commandText="exec dbo.Usp_Log_CreateWithExtended @applicationName, @logLevel, @entryDate, @description, @machineName, @userName, @assembly, @workingDirectory, @exception, @detailedMessage">
        <dbProvider>mssql</dbProvider>
        <parameter name="@applicationName" layout="${gdc:item=ApplicationName}"/>
        <parameter name="@logLevel" layout="${level}"/>
        <parameter name="@entryDate" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff}"/>
        <parameter name="@description" layout="${message}"/>
        <parameter name="@machineName" layout="${machinename}"/>
        <parameter name="@userName" layout="${windows-identity}"/>
        <parameter name="@assembly" layout="${processname}"/>
        <parameter name="@workingDirectory" layout="${basedir}"/>
        <parameter name="@exception" layout="${exception:format=tostring}"/>
        <parameter name="@detailedMessage" layout="${event-context:item=Details}"/>
      </target>
      <target xsi:type="File" fileName="LogMill-FailSafe.log" layout="${TextErrorLayout}"/>
    </target>

    <target name="EmailDevelopers" xsi:type="Mail"
            smtpServer="smtp.local"
            from="errors@email.com"
            to="email@email.com"
            subject="${gdc:item=ApplicationName} ${level} Error: ${message}"
            layout="${TextErrorLayout}"/>

    <target name="Console" xsi:type="ColoredConsole" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff} ${message} ${exception:format=tostring}"/>
  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="LogMill" />
    <logger name="*" minlevel="Error" writeTo="EmailDevelopers" />
  </rules>
</nlog>
BrandonAGr
  • 5,827
  • 5
  • 47
  • 72

2 Answers2

2

I don't know if there is a more automatic way to get each AppDomain to use the same config information, but you might be able to use the technique in this post:

Most useful NLog configurations

To set the configuration programmatically via XML. For each AppDomain you load, you could read the NLog config file and then set the XML on NLog in the new AppDomain.

One possible solution for getting your global values to the new AppDomain would be to store them using CallContext.LogicalSetData. The values will flow to the new AppDomain. Write a custom LayoutRenderer to get the values using CallContext.LogicalGetData. The LayoutRenderer is pretty easy to write.

See this post for an example of how to write a custom LayoutRenderer that looks up a value based on a key (just like the GDC). For the internals, just use CallContext.LogicalGetData to retrieve the value:

Custom log4net property PatternLayoutConverter (with index)

See this blog post by Jeffrey Richter about using the CallContext:

http://www.wintellect.com/CS/blogs/jeffreyr/archive/2010/09/27/logical-call-context-flowing-data-across-threads-appdomains-and-processes.aspx

Community
  • 1
  • 1
wageoghe
  • 27,390
  • 13
  • 88
  • 116
1

I just did something very similar, and I found that the static member NLog.LogManager.Configuration in each child AppDomain started out having the same value as that member in the parent AppDomain.. I'm not sure if it ended up re-reading the values from the app.config or if the static members are copied from the parent AppDomain directly.

Also, I ended up using CallContext to filter all log messages (regardless of logger name) matching a certain criteria (eg, the motivating request ID which caused the program to run), and group them to an in-memory log target.

gap
  • 2,766
  • 2
  • 28
  • 37