我对你的问题并没有真正的答案,但我确实有一些观察和一些问题:
根据您的代码,您似乎想为每个线程创建一个记录器,并且希望将该记录器记录到以某些传入的 id 值命名的文件中。因此,id 为“abc”的记录器将记录到“x:\abc.log”,“def”将记录到“x:\def.log”,依此类推。我怀疑您可以通过 NLog 配置而不是通过编程来完成此操作。我不知道它是否会更好,或者 NLog 是否会遇到与您相同的问题。
我的第一印象是您正在做很多工作:为每个线程创建一个文件目标,为每个线程创建一个新规则,获取一个新的记录器实例等,您可能不需要做这些工作来完成您想要的事情去完成。
我知道 NLog 允许至少基于一些 NLog LayoutRenderer 动态命名输出文件。例如,我知道这是有效的:
fileName="${level}.log"
并会给你这样的文件名:
Trace.log
Debug.log
Info.log
Warn.log
Error.log
Fatal.log
因此,例如,您似乎可以使用这样的模式根据线程 ID 创建输出文件:
fileName="${threadid}.log"
如果您最终拥有线程 101 和 102,那么您将拥有两个日志文件:101.log 和 102.log。
在你的例子中,你想根据你自己的 id 来命名文件。您可以将 id 存储在 MappedDiagnosticContext (这是一个字典,允许您存储线程本地名称-值对),然后在您的模式中引用它。
您的文件名模式将如下所示:
fileName="${mdc:myid}.log"
因此,在您的代码中您可以这样做:
public class ThreadManager
{
//Get one logger per type.
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
protected override void OnDoWork(DoWorkEventArgs e)
{
// Set the desired id into the thread context
NLog.MappedDiagnosticsContext.Set("myid", myRandomID);
logger.Info("Hello from thread {0}, myid {1}", Thread.CurrentThread.ManagedThreadId, myRandomID);
base.OnDoWork(e);
//Clear out the random id when the thread work is finished.
NLog.MappedDiagnosticsContext.Remove("myid");
}
}
像这样的东西应该允许您的 ThreadManager 类有一个名为“ThreadManager”的记录器。每次记录消息时,它都会在 Info 调用中记录格式化字符串。如果记录器配置为记录到文件目标(在配置文件中制定一条规则,将“*.ThreadManager”发送到文件目标,其文件名布局如下所示:
fileName="${basedir}/${mdc:myid}.log"
记录消息时,NLog 将根据 fileName 布局的值确定文件名应该是什么(即,它在记录时应用格式化标记)。如果该文件存在,则消息将写入其中。如果该文件尚不存在,则会创建该文件并将消息记录到该文件中。
如果每个线程都有一个随机 ID,如“aaaaaaaaaaaa”、“aaaaaaaaaaab”、“aaaaaaaaaaac”,那么您应该获得如下日志文件:
aaaaaaaaaaaa.log
aaaaaaaaaaab.log
aaaaaaaaaaac.log
等等。
如果您可以这样做,那么您的生活应该会更简单,因为您不必进行 NLog 的所有编程配置(创建规则和文件目标)。您可以让 NLog 负责创建输出文件名。
我不确定这是否会比你所做的更好。或者,即使确实如此,您可能确实需要从更大的角度考虑您正在做的事情。测试它是否有效应该很容易(即您可以根据 MappedDiagnosticContext 中的值命名输出文件)。如果它适用于此,那么您可以在创建数千个线程的情况下尝试它。
UPDATE:
这是一些示例代码:
使用该程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Threading;
using System.Threading.Tasks;
namespace NLogMultiFileTest
{
class Program
{
public static Logger logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
int totalThreads = 50;
TaskCreationOptions tco = TaskCreationOptions.None;
Task task = null;
logger.Info("Enter Main");
Task[] allTasks = new Task[totalThreads];
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
task = Task.Factory.StartNew(() =>
{
MDC.Set("id", "_" + ii.ToString() + "_");
logger.Info("Enter delegate. i = {0}", ii);
logger.Info("Hello! from delegate. i = {0}", ii);
logger.Info("Exit delegate. i = {0}", ii);
MDC.Remove("id");
});
allTasks[i] = task;
}
logger.Info("Wait on tasks");
Task.WaitAll(allTasks);
logger.Info("Tasks finished");
logger.Info("Exit Main");
}
}
}
这个 NLog.config 文件:
<?xml version="1.0" encoding="utf-8" ?>
<!--
This file needs to be put in the application directory. Make sure to set
'Copy to Output Directory' option in Visual Studio.
-->
<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" layout="${longdate} | ${processid} | ${threadid} | ${logger} | ${level} | id=${mdc:id} | ${message}" fileName="${basedir}/log_${mdc:item=id}.txt" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file" />
</rules>
</nlog>
我能够为委托的每次执行获取一个日志文件。日志文件以 MDC (MappedDiagnosticContext) 中存储的“id”命名。
因此,当我运行示例程序时,我得到了 50 个日志文件,每个日志文件都有三行“Enter...”、“Hello...”、“Exit...”。每个文件都有命名log__X_.txt
其中 X 是捕获的计数器 (ii) 的值,所以我有 log_0.txt,日志_1.txt,日志_1.txt等,log_49。TXT。每个日志文件仅包含与委托的一次执行相关的日志消息。
这与您想要做的类似吗?我的示例程序使用任务而不是线程,因为我之前已经编写过它。我认为该技术应该足够容易地适应你正在做的事情。
您也可以使用相同的 NLog.config 文件以这种方式(为委托的每次执行获取一个新的记录器):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Threading;
using System.Threading.Tasks;
namespace NLogMultiFileTest
{
class Program
{
public static Logger logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
int totalThreads = 50;
TaskCreationOptions tco = TaskCreationOptions.None;
Task task = null;
logger.Info("Enter Main");
Task[] allTasks = new Task[totalThreads];
for (int i = 0; i < totalThreads; i++)
{
int ii = i;
task = Task.Factory.StartNew(() =>
{
Logger innerLogger = LogManager.GetLogger(ii.ToString());
MDC.Set("id", "_" + ii.ToString() + "_");
innerLogger.Info("Enter delegate. i = {0}", ii);
innerLogger.Info("Hello! from delegate. i = {0}", ii);
innerLogger.Info("Exit delegate. i = {0}", ii);
MDC.Remove("id");
});
allTasks[i] = task;
}
logger.Info("Wait on tasks");
Task.WaitAll(allTasks);
logger.Info("Tasks finished");
logger.Info("Exit Main");
}
}
}