NLog - 文件目标文件名的参数

NLog - Parameter for File Target FileName

我在 .NET 应用程序中使用 NLog。目前,我的 App.config 中的配置部分如下所示:

<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>

我的应用正在动态加载程序集。我想将每个程序集日志记录到它们自己的文件中。本质上,我想将 target 元素上的 filename 属性更新为如下内容:

fileName="${basedir}/logs/${shortdate}/assembly.[Name].log"

在此模式中,代码中定义了“[Name]”。我的问题是,有没有办法通过 NLog 以编程方式将变量传递给目标?如果可以,怎么做?

如果正在加载的程序集使用 NLog.LogManager.GetLogger("assemblyName") 创建记录器,那么您可以这样做(最适合 NLog 版本 4.5 或更高版本):

<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>

如果你想让它完全动态,我想你有两种方法。

1) 为 NLog.ILogger 创建自定义日志工厂和包装器,跟踪程序集名称并将其注入 NLog logEvent。这意味着所有程序集都必须使用您的记录器工厂,而不是直接使用 NLog。

2) 创建一个 NLog 扩展,允许您将程序集名称作为布局变量访问,派生自记录器名称。如果您在所有程序集中使用默认值 LogManager.GetCurrentClassLogger(),这将起作用。

这是一个简单的缓存渲染器,它使用反射来找到正确的程序集。您可以通过在加载程序集时扫描程序集来提高效率 - 或者可能只是对记录器名称进行字符串整理。

[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;
        });
    }
}

然后像这样在配置文件中使用它:

<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>