5

我正在尝试实现一个自定义 log4net 记录器/日志管理器,以便我可以添加一个新级别。我还希望目标表的架构(我使用的是 AdoNetAppender 和 SQL Server 2008)有一些可选字段;例如,我添加的关卡用于跟踪使用情况,其中一个字段需要是 Duration(告诉我们某件事花了多长时间)。但是,其他级别(INFO、DEBUG、FATAL 等)对于 Duration 没有任何值。因此,我需要能够插入到表中,而并非所有字段都具有值——最好不必重新实现所有标准级别的日志记录来为不相关的字段传递空值。理想情况下,log4net 会自动将 NULL 传递给它无法为特定日志记录事件找到的属性值。

我的自定义记录器中的代码如下:

public void Usage(TimeSpan? duration, DateTime? startTime, DateTime? endTime, string message)
    {
        if (IsUsageEnabled)
        {
            LoggingEvent loggingEvent = new LoggingEvent(GetType(), Logger.Repository, Logger.Name, _currentUsageLevel,
                message, null);
            if (startTime.HasValue)
            {
                loggingEvent.Properties["StartTime"] = startTime.Value;
            }
            else
            {
                loggingEvent.Properties["StartTime"] = DBNull.Value;
            }

            if (endTime.HasValue)
            {
                loggingEvent.Properties["EndTime"] = endTime.Value;
            }
            else
            {
                loggingEvent.Properties["EndTime"] = DBNull.Value;
            }

            if (duration.HasValue)
            {
                loggingEvent.Properties["Duration"] = duration.Value.TotalMilliseconds;
            }
            else
            {
                loggingEvent.Properties["Duration"] = DBNull.Value;
            }

            Logger.Log(loggingEvent);
        }
    }

(添加了对 DBNull.Value 的分配,希望这将有助于 log4net 在未传递值时传递空值,但我希望它们可以被删除)。

appender 配置如下:

<appender name="SQLAppender" type="log4net.Appender.AdoNetAppender">
  <bufferSize value="1" />
  <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  <connectionString value="my connection string (which actually gets replaced at runtime with a connection string retrieved from a config database)" />
  <commandText value="INSERT INTO MyTable ([Date],[Thread],[Level],[Logger],[Message],[Exception], [Login], [StartTime], [EndTime], [Duration]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @login, @start_time, @end_time, @duration)" />
  <parameter>
    <parameterName value="@log_date" />
    <dbType value="DateTime" />
    <layout type="log4net.Layout.RawTimeStampLayout" />
  </parameter>
  <parameter>
    <parameterName value="@thread" />
    <dbType value="String" />
    <size value="255" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%thread" />
    </layout>
  </parameter>
  <parameter>
    <parameterName value="@log_level" />
    <dbType value="String" />
    <size value="50" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%level" />
    </layout>
  </parameter>
  <parameter>
    <parameterName value="@logger" />
    <dbType value="String" />
    <size value="255" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%logger" />
    </layout>
  </parameter>
  <parameter>
    <parameterName value="@message" />
    <dbType value="String" />
    <size value="4000" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%message" />
    </layout>
  </parameter>
  <parameter>
    <parameterName value="@exception" />
    <dbType value="String" />
    <size value="4000" />
    <layout type="log4net.Layout.ExceptionLayout" />
  </parameter>
  <parameter>
    <parameterName value="@login" />
    <dbType value="String" />
    <size value="255" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%property{CurrentLogin}" />
    </layout>
  </parameter>
  <parameter>
    <parameterName value="@start_time" />
    <dbType value="DateTime" />
    <layout type="log4net.Layout.PatternLayout"
      value="%property{StartTime}" />
  </parameter>
  <parameter>
    <parameterName value="@end_time" />
    <dbType value="DateTime" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%property{EndTime}" />
    </layout>
  </parameter><parameter>
    <parameterName value="@duration" />
    <dbType value="Double" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%property{Duration}" />
    </layout>
  </parameter>
</appender>

当我尝试使用自定义级别记录某些内容时,它仅在我传入持续时间、开始时间和结束时间时才有效。对于其他任何事情(包括默认级别),它似乎都会默默地失败,在输出窗口中没有任何线索(尽管使用默认级别进行日志记录会引发异常,说明它无法将 String 转换为 DateTime 或 Double)。

任何人都可以看到为什么这会像这样默默地失败的任何理由?

4

1 回答 1

7

在跟踪了一些 log4net 源之后,我终于弄明白了。问题是我使用 PatternLayout 来呈现自定义参数。此布局强制null转换为 string "(null)",如果您尝试将其插入到datetime表中的可为空字段中,则会导致问题。解决方案很简单:不使用 PatternLayout 布局,而是使用 log4net.Layout.RawPropertyLayout。这将传递一个空属性作为null,这是你想要的。

于 2010-07-13T16:39:24.207 回答