我怎样才能让 log4net 为 Parallel.Invoke 中的每个操作创建一个单独的日志文件?

How can I have log4net create a separate log file for each Action in a Parallel.Invoke?

我有一系列 Action,我想使用 Parallel.Invoke 并行执行。但是对于每个 Action 我想要一个由 log4net 创建的单独的日志文件。在我的 log4net 配置文件中,我添加了这个:

<file type="log4net.Util.PatternString" value="3S2M3_%property{UniqueIdentifier}.log" />

如果只有一个操作可执行,则此操作正常:日志文件具有正确的文件名。

但是,如果要执行多个 Action,所有日志条目最终都会在同一个日志文件中。

创建 Action 的代码是这样的:

    Dim lstActions(4) As Action
    Dim iCount As Integer = 0

    For iCount = 0 To 4
        Dim sUniqueIdentifier As String = iCount.ToString("D4")

        Dim aOrderTask As Action = Sub()
           log4net.LogicalThreadContext.Properties("UniqueIdentifier") = sUniqueIdentifier

       ' Some process that takes some time to complete
                                   End Sub
        lstActions(iCount) = aOrderTask
    Next

    Parallel.Invoke(lstActions)

创建的日志文件正在使用分配给 UniqueIdentifier 属性 的值之一。我无法弄清楚如何选择这个值:它并不总是第一个或最后一个。在上面的示例中,只创建了文件 3S2M3_0004.log,它包含所有日志条目。

我已经尝试了 LogicalThreadContext 和 ThreadContext,但没有区别。

log4net 在加载配置时分配日志文件名,因此您需要在运行时设置自定义名称。

另一件需要注意的事情是 log4net 在记录器之间共享 appender,因此要同时记录到不同的文件,您将需要以编程方式分配 appender,并且您将必须在该过程中执行此操作,而不是在外面。

(如果你真的不想在代码中设置附加程序属性,你可以在配置中定义一个,从层次结构中获取它,并为每个任务克隆它。无论哪种方式,任务的每个实例需要它自己的记录器和一个独特的附加程序)

所以,给定这样的配置:

<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="false">
   <root>
     <level value="DEBUG" />
   </root>

   <!-- more config -->

   <logger name="EmptyLogger" additivity="false">
   </logger>
   <!-- additivity="false" specifies not to load any appenders defined on the root logger -->
</log4net>

然后你可以有这样的代码:

Private Shared Sub ProcessThatTakesSomeTimeToComplete()
    Dim sUniqueIdentifier As New String(Guid.NewGuid().ToString("N").Take(4).ToArray())

    ' Get a uniquely-named empty logger containing no appenders
    Dim log = LogManager.GetLogger("EmptyLogger." & sUniqueIdentifier)

    ' Create an appender manually, setting the path as required
    Dim sPath As New String(Path.Combine("c:\temp", sUniqueIdentifier & ".log"))

    Dim appender = New FileAppender() With { _
         .Layout = New PatternLayout("%date [%thread] %message"), _
         .File = sPath, _
         .AppendToFile = True _
    }

    appender.ActivateOptions()

    ' Add the appender to the logger
    DirectCast(log.Logger, Logger).AddAppender(appender)

    ' And off we go.
    log.Debug("Hello from thread " + Thread.CurrentThread.ManagedThreadId)

    Thread.Sleep(100)
End Sub

进口:

Imports System.IO
Imports System.Threading
Imports log4net
Imports log4net.Appender
Imports log4net.Layout
Imports log4net.Repository.Hierarchy