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>