Log4net, ADONetAppender и хранимая процедура: установить точность и масштаб?

Сочетание того, что я предполагаю, состоит в том, что мелочи мешают моему приложению вести журнал базы данных, как я и ожидаю.

  • Текстовая команда работает. И то и другое INSERT а также EXEC команды работают и данные попадают в базу данных
  • если я раскомментирую ExecutionTime Параметр перестает работать.Я знаю, что кто-то работал в какой-то момент, но я не могу понять, почему раскомментирование параметра, а тем более его добавление, приводит к тому, что Appender молча завершается ошибкой.
    • Обнаружена ошибка теперь, когда отладка работает. Перечислено ниже и обновлено с добавлением точности и масштаба.
  • Я пытаюсь включить внутреннюю отладку, и она, кажется, не хочет работать. Нашел проблему. Переместил разделы конфигурации в нужное место (не в log4net).
  • Если я переключу commandType на StoredProcedure перестает работать, даже если текстовая версия работает
    • Все еще проблема. Пробую разные вещи, но не повезло

SQL:

DROP TABLE [LOG];
GO
CREATE TABLE [dbo].[LOG] (
     [Id] [int] IDENTITY(1,1) NOT NULL
    ,[Origin] [varchar](55) null
    ,[LogDate] [datetime] NOT NULL
    ,[Thread] [varchar](32) NOT NULL
    ,[Context] [varchar](10) NOT NULL
    ,[Level] [varchar](10) NOT NULL
    ,[Logger] [varchar](255) NOT NULL
    ,[Message] [varchar](4000) Not NULL
    ,[MethodName] [varchar](200) NULL
    ,[Parameters] [varchar](4000) NULL
    ,[Exception] [varchar](4000) NULL
    ,[ExecutionTime] [decimal](14, 4) NULL
)
GO

DROP PROCEDURE InsertLog
GO
CREATE PROCEDURE [dbo].[InsertLog]
     @LogDate DateTime
    ,@Thread varchar(32)
    ,@Context varchar(10)
    ,@Level varchar(10)
    ,@Logger varchar(255)
    ,@Message varchar(4000)
    ,@MethodName varchar(200) = null
    ,@Parameters varchar(4000) = null
    ,@Exception varchar(4000) = null
    ,@ExecutionTime decimal(14,4) = null
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[LOG] ([Origin],[LogDate],[Thread],[Level],[Logger],[Message],[MethodName],[Parameters],[Exception],[Context]) 
    VALUES ('InsertLog',@LogDate, @Thread, @Level, @Logger, @Message, @MethodName, @Parameters, @Exception, @Context)
END
GO

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
        ...
    </configSections>
    <connectionStrings>...</connectionStrings>
    <applicationSettings>...</applicationSettings>
    <log4net configSource="Log4Net.config" />
<appSettings>
    <add key="log4net.Internal.Debug" value="true" />
</appSettings>
<system.diagnostics>
    <trace autoflush="true">
        <listeners>
            <add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" initializeData=".\log4net.log" />
        </listeners>
    </trace>
</system.diagnostics>
</configuration>

log4net.config

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
    <root>
        <level value="ALL" />
        <!-- ColoredConsoleAppender works, so It's not listed below -->
        <appender-ref ref="ColoredConsoleAppender"/>
        <appender-ref ref="ADONetAppender" />
        <appender-ref ref="OutputDebugStringAppender" />
        <appender-ref ref="TraceAppender" />
    </root>
    <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
        <threshold value="ALL"/>
        <bufferSize value="1" />
        <lossy value="false"/>
        <param name="UseTransactions" value="False" />
        <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
        <connectionString value="..." />
        <!--<commandText value="
            INSERT INTO [Log] ([Origin],[LogDate],[Thread],[Context],[Level],[Logger],[Message],[MethodName],[Parameters],[Exception])
            VALUES ('ADONetAppender',@log_date,@thread,@Context,@Level,@Logger,@Message,@MethodName,@Parameters,@Exception);"
        />-->
        <commandText value="
            exec InsertLog @log_date,@thread,@Context,@Level,@Logger,@Message,@MethodName,@Parameters,@Exception;"
        />
        <!--<commandType value="StoredProcedure" />-->
        <!--<commandText value="InsertLog" />-->
        <parameter name="LogDate">
            <parameterName value="@log_date"/>
            <dbType value="DateTime"/>
            <layout type="log4net.Layout.RawTimeStampLayout"/>
        </parameter>
                    ...
        <!-- This causes appender to fail silently. -->
        <parameter name="ExecutionTime">
            <parameterName value="@ExecutionTime"/>
            <dbType value="Decimal"/>
            <precision value="14"/>
            <scale value="4"/>
            <layout type="log4net.Layout.PatternLayout" value="%property{execution_time}" />
        </parameter>
    </appender>
    <appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender">
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date %-5level %logger (%file:%line) - %message%newline" />
        </layout>
    </appender>
    <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date %-5level - %message%newline" />
        </layout>
    </appender>
</log4net>

Обновление 1 Теперь, когда у меня работает Trace... он жалуется, что команде DB необходимо явно установить точность и масштаб. Не уверен, как это сделать, так как ни один из примеров, которые я видел, не имеет этого набора

log4net: ОШИБКА [AdoNetAppender] ErrorCode: GenericFailure. Не удалось подготовить команду базы данных [ exec InsertLog @log_date,@thread,@Context,@Level,@Logger,@Message,@MethodName,@Parameters,@Exception; ] System.InvalidOperationException: метод SqlCommand.Prepare требует, чтобы параметры типа 'Decimal' имели явно заданные Precision и Scale. в System.Data.SqlClient.SqlParameter.Prepare(SqlCommand cmd) в System.Data.SqlClient.SqlCommand.Prepare() в log4net.Appender.AdoNetAppender.InitializeDatabaseCommand()

Обновление 2 С добавлением Precision/Scale ошибка меняется. Теперь жалуется:

log4net: ОШИБКА [AdoNetAppender] ErrorCode: GenericFailure. Ошибка в DoAppend System.FormatException: не удалось преобразовать значение параметра из строки в десятичную. ---> System.FormatException: входная строка была в неправильном формате.

2 ответа

Есть два параметра, которые являются частью AdoNetAppenderParameter класс вы можете добавить в свой ExecutionTime параметр в файле XML, Precision а также Scale:

<parameter name="ExecutionTime">
    <parameterName value="@ExecutionTime"/>
    <dbType value="Decimal"/>
    <precision value="14"/>
    <scale value="4"/>
    <layout type="log4net.Layout.PatternLayout" value="%property{execution_time}" />
</parameter>
  • Включил отладку и нашел жалобу на точность и масштаб
  • Далее были значения NULL. Они превращаются в буквальные (null) и он задыхается от перевода этого в десятичную.
  • Преобразовал Log4net.config в STRING и добавил логику преобразования в хранимую процедуру
  • Преобразовал ExecutionTime в Int, поскольку код, который я использую, находится в миллисекундах.

SQL:

    DROP TABLE [LOG];
    GO
    CREATE TABLE [dbo].[LOG] (
         [Id] [int] IDENTITY(1,1) NOT NULL
        ,[LogDate] [datetime] NOT NULL
        ,[Thread] [varchar](32) NOT NULL
        ,[Level] [varchar](10) NOT NULL
        ,[Logger] [varchar](255) NOT NULL
        ,[Message] [varchar](4000) NOT NULL
        ,[Exception] [varchar](4000) NULL
        ,[Context] [varchar](10) NULL
        ,[MethodName] [varchar](200) NULL
        ,[Parameters] [varchar](4000) NULL
        ,[ExecutionTime] int NULL
        --,[Origin] [varchar](55) NOT NULL
        --,[ExecutionRaw] [varchar](4000) NULL
    )
    GO

    DROP PROCEDURE InsertLog
    GO
    CREATE PROCEDURE [dbo].[InsertLog]
         @LogDate DateTime
        ,@Thread varchar(32)
        ,@Level varchar(10)
        ,@Logger varchar(255)
        ,@Message varchar(4000)
        ,@Exception varchar(4000) = null
        ,@Context varchar(10) = null
        ,@MethodName varchar(200) = null
        ,@Parameters varchar(4000) = null
        ,@ExecutionTime varchar(32) = null
    AS
    BEGIN
        SET NOCOUNT ON;
        --DECLARE @ETChar varchar(32); set @ETChar = @ExecutionTime;

        if @Exception     = '' set @Exception = null;
        if @Context       = '(null)' set @Context = null;
        if @MethodName    = '(null)' set @MethodName = null;
        if @Parameters    = '(null)' set @Parameters = null;        
        if @ExecutionTime = '(null)' set @ExecutionTime = null;

        DECLARE @ETInt int;          set @ETInt  = convert(int, @ExecutionTime);

        INSERT INTO [dbo].[LOG] (
             [LogDate]
            ,[Thread]
            ,[Level]
            ,[Logger]
            ,[Message]
            ,[Exception]
            ,[Context]
            ,[MethodName]
            ,[Parameters]
            ,[ExecutionTime]
            --,[Origin]
            --,[ExecutionRaw]
            )
        VALUES (
             @LogDate
            ,@Thread
            ,@Level
            ,@Logger
            ,@Message
            ,@Exception
            ,@Context
            ,@MethodName
            ,@Parameters
            ,@ETInt
            --,'InsertLog'
            --,@ETChar
            )

        SET NOCOUNT OFF;
    END
    GO

    DROP INDEX [IX_LEVEL] ON [LOG]
    GO
    CREATE NONCLUSTERED INDEX [IX_Level] ON [dbo].[LOG] ( [Level] ASC ); 
    GO

Log4Net.config:

<?xml version="1.0" encoding="utf-8"?>

<log4net>
  <root>
    <level value="DEBUG" />
    <appender-ref ref="ADONetAppender" />
  </root>
  <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
    <threshold value="ALL" />
    <bufferSize value="1" />
    <lossy value="false" />
    <param name="UseTransactions" value="False" />
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <connectionString value="..." />
    <commandType value="StoredProcedure" />
    <commandText value="InsertLog" />
    <parameter name="LogDate">
      <parameterName value="@LogDate" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter name="Thread">
      <parameterName value="@Thread" />
      <dbType value="String" />
      <size value="32" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%thread" />
      </layout>
    </parameter>
    <parameter name="Level">
      <parameterName value="@Level" />
      <dbType value="String" />
      <size value="10" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%level" />
      </layout>
    </parameter>
    <parameter name="Logger">
      <parameterName value="@Logger" />
      <dbType value="String" />
      <size value="255" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%logger" />
      </layout>
    </parameter>
    <parameter name="Message">
      <parameterName value="@Message" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message" />
      </layout>
    </parameter>
    <parameter name="Exception">
      <parameterName value="@Exception" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.ExceptionLayout" />
    </parameter>
    <parameter name="Context">
      <parameterName value="@Context" />
      <dbType value="String" />
      <size value="10" />
      <layout type="log4net.Layout.PatternLayout" value="%x" />
    </parameter>
    <parameter name="MethodName">
      <parameterName value="@MethodName" />
      <dbType value="String" />
      <size value="200" />
      <layout type="log4net.Layout.PatternLayout" value="%property{method_name}" />
    </parameter>
    <parameter name="Parameters">
      <parameterName value="@Parameters" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.PatternLayout" value="%property{properties}" />
    </parameter>
    <parameter name="ExecutionTime">
      <parameterName value="@ExecutionTime" />
      <dbType value="String" />
      <size value="32" />
      <layout type="log4net.Layout.PatternLayout" value="%property{execution_time}" />
    </parameter>
  </appender>
</log4net>
Другие вопросы по тегам