Reputation: 5827
I am using NLog in a .NET application. Currently, my config section in my App.config looks like this:
<nlog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="assemblyLogFile" xsi:type="File" fileName="${basedir}/logs/${shortdate}/assembly.log" createDirs="true" keepFileOpen="true" encoding="utf-8" layout="${longdate} ${message}${exception:format=ToString}"></target>
<target name="appLogFile" xsi:type="File" fileName="${basedir}/logs/app.log" createDirs="true" keepFileOpen="true" encoding="utf-8" layout="${longdate} ${message}${exception:format=ToString}"></target>
</targets>
<rules>
<logger name="Assembly" minlevel="Trace" writeTo="assemblyLogFile" />
<logger name="*" minlevel="Trace" writeTo="appLogFile" />
</rules>
</nlog>
My app is dynamically loading the assemblies. I would like to log each assemblies logs to their own file. In essence, I would like to update the filename
attribute on the target
element to something like this:
fileName="${basedir}/logs/${shortdate}/assembly.[Name].log"
In this pattern, "[Name]" is defined in the code. My question is, is there a way to programmatically pass a variable to a target via NLog? If so, how?
Upvotes: 4
Views: 3719
Reputation: 78518
If you want this to be completely dynamic, I think you have two approaches.
1) Create a custom log factory and wrapper for NLog.ILogger
that keeps track of and injects the assembly name into the NLog logEvent. This means all assemblies must use your logger factory, and not NLog directly.
2) Create a NLog extension that lets you access the assembly name as a layout variable, derived from the logger name. This works if you use the default LogManager.GetCurrentClassLogger()
in all the assemblies.
Here is a naive caching renderer that uses reflection to find the correct assembly. You could probably make this more efficient by scanning assemblies when you load them - or maybe just do string wrangling on the logger name.
[NLog.LayoutRenderers.LayoutRenderer("assemblyName")]
public class AssemblyNameLayoutRenderer : NLog.LayoutRenderers.LayoutRenderer
{
static ConcurrentDictionary<string, string> loggerNameToAssemblyNameCache = new ConcurrentDictionary<string, string>();
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
var fullClassName = logEvent.LoggerName;
var assemblyName = FindAssemblyNameFromClassName(fullClassName);
if (!string.IsNullOrEmpty(assemblyName))
builder.Append(assemblyName);
}
private string FindAssemblyNameFromClassName(string fullClassName)
{
return loggerNameToAssemblyNameCache.GetOrAdd(fullClassName, cl =>
{
var klass = (
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
where t.FullName == fullClassName
select t).FirstOrDefault();
return klass?.Assembly.GetName().Name;
});
}
}
And then use this in the config file like this:
<extensions>
<add assembly="AssemblyContainingCustomRenderer" />
</extensions>
<targets>
<target xsi:type="FilteringWrapper" name="assemblyLogFile" condition="'${assemblyName}' != ''">
<target name="realAssemblyLogFile" xsi:type="File" fileName="logs/${shortdate}/assembly.${assemblyName}.log" createDirs="true" keepFileOpen="true" encoding="utf-8" layout="${longdate} ${message}${exception:format=ToString}" /
</target>
</targets>
Upvotes: 2
Reputation: 19847
If the assembly being loaded creates the logger using NLog.LogManager.GetLogger("assemblyName")
, then you can do this (Works best with NLog ver. 4.5 or higher):
<nlog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="assemblyLogFile" xsi:type="File" fileName="${basedir}/logs/${shortdate}/${logger}.log" createDirs="true" keepFileOpen="true" encoding="utf-8" layout="${longdate} ${message}${exception:format=ToString}"></target>
<target name="appLogFile" xsi:type="File" fileName="${basedir}/logs/app.log" createDirs="true" keepFileOpen="true" encoding="utf-8" layout="${longdate} ${message}${exception:format=ToString}"></target>
</targets>
<rules>
<logger name="AssemblyName" minlevel="Trace" writeTo="assemblyLogFile" />
<logger name="*" minlevel="Trace" writeTo="appLogFile" />
</rules>
</nlog>
Upvotes: 0