如何在关闭 Log4net 调试时捕获 Log4net Appender 错误

How to capture Log4net Appender errors when Log4net Debugging turned off

我正在使用 Log4net。我最近决定在我的配置文件中添加一个跟踪侦听器,以捕获 Log4net 本身生成的任何错误。 (这 Log4net FAQ 包含一个示例配置。)

(我说的是 Log4net 由于无法完成其工作而生成自己的错误消息的情况。我不是在谈论将其配置为记录 应用程序 错误。)

在测试这个时,我在测试项目中弄乱了我的 Log4net 配置,以强制出现 Log4netADO.Net Appender 错误,以验证它们是否被 Trace 正确处理侦听器设置。

我的发现表明,即使 Log4net 调试已关闭,内部 Log4net 错误也会写入跟踪侦听器。 这就是我想要的.

这是我通过调整 app.config 中的 Log4net.Layout 的类型名称以引用不存在的类型而生成的示例错误。这是跟踪侦听器文件中的唯一条目,这正是我想要的。

log4net:ERROR Failed to find type [log4net.Layout.RawTimeStampLayoutXX]
System.TypeLoadException: Could not load type [log4net.Layout.RawTimeStampLayoutXX]. Tried assembly [log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a] and all loaded assemblies
  at log4net.Util.SystemInfo.GetTypeFromString(Assembly relativeAssembly, String typeName, Boolean throwOnError, Boolean ignoreCase)
  at log4net.Util.SystemInfo.GetTypeFromString(String typeName, Boolean throwOnError, Boolean ignoreCase)
  at log4net.Repository.Hierarchy.XmlHierarchyConfigurator.CreateObjectFromXml(XmlElement element, Type defaultTargetType, Type typeConstraint)
log4net:ERROR Failed to create object to set param: layout

但是,我还发现,如果错误发生在外部系统中(例如,在下面的示例中,SQL 服务器错误),错误 只会出现在如果 Log4net 调试打开 ,则跟踪侦听器文件。这意味着它被大量内部管理信息所包围。

log4net:ERROR [AdoNetAppender] ErrorCode: GenericFailure. Exception while writing to database
System.Data.SqlClient.SqlException (0x80131904): Must declare the scalar variable "@log_date".
  at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
  at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
  at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
  at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
  at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption)
  at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
  at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
  at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
  at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
  at log4net.Appender.AdoNetAppender.SendBuffer(IDbTransaction dbTran, LoggingEvent[] events)
  at log4net.Appender.AdoNetAppender.SendBuffer(LoggingEvent[] events)
ClientConnectionId:8ddb2758-4adc-495e-af8b-c16e0acc937a
Error Number:137,State:2,Class:15

我不想运行一直打开调试,只是为了捕获这种错误。这个应用程序 运行 每天大约运行 70 次,每次 运行 生成 25KB 的调试跟踪侦听器输出,仅来自内部管理。我不想在数兆字节的噪音中艰难前行才能找到一个信号。

所以,我的问题:

  1. 是否可以在跟踪侦听器中捕获对 Appender 致命的错误,而无需打开 Log4net 调试?
  2. 这是设计使然的行为吗?

我找到了解决办法。一个漂亮、优雅的。

Log4net 中的所有 Appender class 都有一个名为 ErrorHandler 的 public 属性。默认情况下,这被设置为一个名为 OnlyOnceErrorHandler 的 class 实例,它会将错误记录到调试提要(如果启用),然后停止报告错误。

但是你可以通过配置覆盖它:

  <log4net>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <file value="Logs\logfile.txt" />
      <rollingStyle value="Date" />
      <datePattern value="yyyyMMdd" />
      <maxSizeRollBackups value="-1" />
      <maximumFileSize value="50GB" />
      <errorHandler type="MyTraceLoggingErrorHandler, MyAssembly" />

ErrorHandler实现了非常简单的接口IErrorHandler,可以做任何你想让它做的事情。

在我的例子中,我只是让它将收到的消息和异常写入 TraceListener:

Trace.WriteLine(message);
Trace.WriteLine(errorCode);
Trace.WriteLine(e.Message);
Trace.WriteLine(e.StackTrace);

这解决了我的问题。