【发布时间】:2011-06-28 15:47:37
【问题描述】:
请注意,我在 SO(讨论 log4net 的线程安全)上看到了这些问题,我怀疑他们回答了我的问题,但我还是要问:
Log4Net FileAppender not thread safe?
最近我编写了一个用于日志记录的 WCF 服务。这个想法与Clog(或look for Clog under Calcium)非常相似。基本上,我已经实现了一个用于 Silverlight 客户端(Silverlight 类库)的日志记录 API。日志 API 或多或少是 Common.Logging for .NET API 的克隆,我们在应用程序的其他地方使用它。 API 的实现将所有日志消息转发到 WCF 日志服务,该服务本身是根据 Common.Logging 实现的。
在查看 Clog 时,我发现 Log4NetStrategy 类中的以下代码让我觉得有点奇怪:
/// <summary>
/// All Write log calls are done asynchronously because the DanielVaughan.Logging.Log
/// uses the AppPool to dispatch calls to this method. Therefore we need to ensure
/// that the call to Log is threadsafe. That is, if an appender such as FileAppender is used,
/// then we need to ensure it is called from no more than one thread at a time.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="loggingEvent">The logging event.</param>
[MethodImpl(MethodImplOptions.Synchronized)]
static void WriteThreadSafe(ILogger logger, LoggingEvent loggingEvent)
{
logger.Log(loggingEvent);
}
Log4NetStrategy(以及 NLog 和 EnterpriseLibrary 的策略)实现了一个接口,该接口具有类似于 Write(ILogEntry logEntry) 的方法。 ILogEntry 本质上是 Clog 日志记录服务从客户端接收的 DTO。提取来自 logEntry 的信息并用于创建 log4net LoggingEvent。还会根据 ILogEntry DTO 中的记录器名称检索适当的 log4net 记录器。 log4net LoggingEvent 创建后,它和记录器被发送到上面的WriteThreadSafe 方法并通过log4net 记录。 NLog 和 EnterpriesLibrary 的实现类似。
在伪代码中,Clog 日志记录服务上的“Write”方法如下所示:
// Write on the logging service
// Send the input log entry to each configured strategy.
public void Write(ILogEntry logEntry)
{
foreach (ILoggingStrategy ls in loggingStrategies)
{
ls.Write(logEntry);
}
}
// Write on the Log4NetStrategy
// Convert the logEntry to log4net form and log with log4net
public void Write(ILogEntry logEntry)
{
log4net.LoggingEvent le = ConvertToLoggingEvent(logEntry);
ILog logger = log4net.GetLogger(logEntry.Logger);
WriteThreadSafe(logger, le);
}
所以这是我的问题... 通常 log4net(和 NLog 以及我认为 EnterpriseLibrary)被认为是多线程兼容的。也就是说,公共 API 的用户可以简单地调用 log.Info、log.Log 等,而不用担心在多线程环境中运行。日志框架应该注意确保日志调用(以及日志调用中的任何处理)是线程安全的。
如果日志框架是多线程兼容的,那么就是使用
[MethodImpl(MethodImplOptions.Synchronized]
属性真的需要吗?似乎这会(或可能)通过强制同步处理所有日志消息而导致瓶颈,即使底层日志框架应该能够在多线程环境中处理日志。
就我的日志服务(可能是多线程的)而言,似乎没有必要像这样同步调用。似乎我应该能够从服务调用中获取我的日志输入,构建适当的日志结构(基于底层日志框架),然后记录它。如果我的日志服务是多线程的,它应该“正常工作”,因为底层的日志框架应该支持它(多线程)。
这有意义吗?日志调用的显式同步(至少对于 log4net 和 NLog)真的有必要吗?
【问题讨论】:
-
像 ILogger 这样的接口类型不可能对线程安全做出强有力的保证。除非它具有 IsThreadSafe 属性。
-
我想我更具体地询问 log4net 和 NLog。如果在上面的示例中,日志记录“策略”是根据 log4net 和/或 NLog 实现的(因此 Clog 是一种抽象,可以配置为使用 log4net 或 NLog - 或两者)并且如果 log4net 和 NLog 都是线程安全(正如他们的网站所说的那样),明确强制“记录器”一次只能由一个线程写入有什么好处?这似乎相当于用这样的锁“保护”每个日志记录调用(在任何使用 log4net 或 NLog 的代码中):
-
lock(globalLoggerLocker) { logger.Info("Hello from protected logger");并且没有人这样做。在给定的框架(如 log4net)中,某些组件可能不是线程安全的(根据某些文档 log4net 的 FileAppender 不是线程安全的),但至少 log4net 和 NLog 宣传为线程安全的,因此它们必须处理使用所需的任何同步非线程安全组件。
标签: .net multithreading logging log4net nlog