根据方法参数使用不同的日志文件

本文关键字:日志 文件 方法 参数 | 更新日期: 2023-09-27 18:19:15

我有一个函数:processData(string taskName)我想生成多个日志文件,其中包含taskName参数在他们的路径使用NLog

例如:

processData("task1")应该只记录到C:'log_task1.txt

processData("task2")应该只记录到C:'log_task2.txt

processData("task3")应该只记录到C:'log_task3.txt

等等

注意:我事先不知道参数的值。这只是一个例子

根据方法参数使用不同的日志文件

在NLog配置中,为每个日志文件创建不同的规则:

<targets>
    <target name="logfile1"
        xsi:type="File"
        fileName="C:'log_task1.log" />
    <target name="logfile2"
        xsi:type="File"
        fileName="C:'log_task2.log" />
    <target name="logfile3"
        xsi:type="File"
        fileName="C:'log_task3.log" />
</targets>
<rules>
    <logger name="task1"
        minlevel="Trace"
        writeTo="logfile1" />
    <logger name="task2"
        minlevel="Trace"
        writeTo="logfile2" />
    <logger name="task3"
        minlevel="Trace"
        writeTo="logfile3" />
</rules>

然后,在你的方法中,使用相应的NLog记录器:

public void ProcessData(string param)
{
    var logger = NLog.LogManager.GetLogger(param);
}

如果你事先不知道参数的值,你可以在运行时以编程方式创建NLog配置(但不要忘记只允许白名单参数值,以防止攻击者在选择的位置覆盖现有文件)。这里描述了如何通过编程方式配置NLog:

NLog Configuration API

代码应该是这样的(只是写下来没有测试):

public void AddLogTarget(string param)
{
    if (NLog.LogManager.Configuration.FindTargetByName(param) == null)
    {
        var target = new FileTarget
        {
            Name = param,
            FileName = @"C:'" + param + ".log"
        };
        NLog.LogManager.Configuration.AddTarget(param, target);
        NLog.LogManager.Configuration.LoggingRules.Add(new LoggingRule(param, LogLevel.Debug, target));
    }
}

有一个选项可能有效,但是使用它需要您付出一些努力。

NLog能够通过大多数LayoutRenderers命名日志文件。在您的情况下,您可以这样配置您的File目标:

<target name="FileTarget" xsi:type="File" fileName="${gdc:item=Task}.log" layout=${longdate} | ${logger} | ${level} | ${message}" />

然后,在执行任务时,可以根据任务名称设置GlobalDiagnosticContext:

void processData(String taskName)
{
   GlobalDiagnosticContext.Set("Task", taskName);
   log.Info("Hello from processData.  This should be in {0}.log", taskName);
   GlobalDiagnosticContext.Remove("Task");
}

这将导致所有被写入的日志消息被写入所需的日志文件。

如果您的应用程序是单线程的,这可能工作得很好,所以您不必担心同时执行processData之间的冲突。

如果您正在使用线程,您可能会以同样的方式使用ThreadDiagnosticContext获得良好的结果。

另一个想法是手动创建LogEventInfo对象并使用Log方法自己记录它们。像这样:

var LogEventInfo le = new LogEventInfo(LogLevel.Info, logger.Name, "Hello!");
le.Properties["Task"] = taskName;
logger.Log(le);

在这种情况下,你将使用EventContext LayoutRenderer来构建日志文件名:

<target name="FileTarget" xsi:type="File" fileName="${event-context:item=Task}.log" layout=${longdate} | ${logger} | ${level} | ${message}" />

你可以包装NLog记录器,或者你可以写一个扩展方法,这样你就不必在每个日志站点都建立一个LogEventInfo对象。

例如:

public static class NLogExtensions
{
  public static void Log(this Logger logger, string task, LogLevel level, string message)
  {
    var LogEventInfo le = new LogEventInfo(level, logger.Name, message);
    le.Properties["Task"] = task;
    logger.Log(typeof(NLogExtensions), le);
  }
}

使用Type作为第一个参数的Log签名是为了保证呼叫局点信息的正常记录。

查看我的回答,这里有一个关于通过扩展方法"包装"NLog的简短讨论:

NLog可以通过c#扩展方法保存callsite信息吗?