开发者

log4net ADO.NET Appender works on Dev Fabric, but fails silently on Azure

开发者 https://www.devze.com 2022-12-31 10:12 出处:网络
I\'m using log4net appender ADO.NET to log messages of Azure Worker Role to the SQL Azure instance (default diagnostics just does not fit). For some reason, while running the worker in dev fabric, log

I'm using log4net appender ADO.NET to log messages of Azure Worker Role to the SQL Azure instance (default diagnostics just does not fit). For some reason, while running the worker in dev fabric, logging works. Yet, when the instance is deployed to the cloud (with exactly the same config) errors do not get logged.

Configuration happens in the code using this file:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <renderer renderingClass="{ExceptionRenderer}" renderedClass="System.Exception" />
  <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
    <layout type="log4net.Layout.PatternLayout">
      <!--<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />-->
      <conversionPattern value="%message%newline" />
    </layout>
  </appender>
  <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
    <bufferSize value="3" />
    <connectionType value="{ConnectionType}" />
    <connectionString value="{ConnectionString}" />
    <commandText value="INSERT INTO Salescast_Log ([Date],[Thread],[Version],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, '{Version}',@log_level, @logger, @message, @exception)" />
    <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 val开发者_运维问答ue="@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="{ExceptionLayoutType}" />
    </parameter>
    <filter type="log4net.Filter.LevelRangeFilter">
      <levelMin value="ERROR" />
      <levelMax value="FATAL" />
    </filter>
  </appender>
  <root>
    <level value="DEBUG" />
    <appender-ref ref="TraceAppender" />
    <appender-ref ref="AdoNetAppender" />
  </root>
</log4net>

log4net is initialized with the proper values, when the Autofac IoC environment is initalized (per role startup). The code looks like this:

static ILogProvider BuildProvider(IComponentContext context)
{
  var connection = context
    .Resolve<IProvideBusSettings>()
    .GetString("SqlConnection")
    .ExposeException("Failed to get SQL string for logging");

  var xml = Properties.Resources.Logging
    .Replace("{ConnectionType}", typeof(SqlConnection).AssemblyQualifiedName)
    .Replace("{ConnectionString}", connection)
    .Replace("{ExceptionLayoutType}", typeof(LoggingTrimmedExceptionLayout).AssemblyQualifiedName)
    .Replace("{ExceptionRenderer}", typeof(LoggingExceptionRenderer).AssemblyQualifiedName)
    .Replace("{Version}", SystemDescriptor.Default.Version.ToString());

  var doc = new XmlDocument();
  doc.LoadXml(xml);
  XmlConfigurator.Configure(doc.DocumentElement);
  return new LoggingProvider();
}

Default Azure OS is used. SQL connections are obviously valid.

Could anybody, please, think of reason, why log4net logs errors from the dev fabric, but fails to do that from the azure OS, using exactly the same service configuration files?


I just ran into this and spent the better part of the day trying to figure it out. It came down to the fact that SQL Azure requires clustered indexes on your tables. The example SQL code provided by log4net to create the Log table does not have a clustered index, which is a requirement for SQL Azure. Adding any data at all to the table will fail unless it has a clustered index (as of the time this is written).

Try doing a manual insert statement using SQL Server Management Studio while connected to SQL Azure and it will tell you straight away if this is the problem. If so, run the following SQL to add a clustered index on the table (assuming you used the SQL direct from log4net) and then try again.

CREATE UNIQUE CLUSTERED INDEX PK_Log ON [Log]
  ([Id])
GO


I may have a lead into this issue. I enabled internal logging for log4net as described here. The logging would work for a while and would then stop. The causing error in the log4net log was:

log4net:ERROR [log4netDbAppender] Failed in DoAppend System.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An established connection was aborted by the software in your host machine.) at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() at System.Data.SqlClient.TdsParserStateObject.WriteSni() at System.Data.SqlClient.TdsParserStateObject.ExecuteFlush() at System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at log4net.Appender.AdoNetAppender.SendBuffer(IDbTransaction dbTran, LoggingEvent[] events) at log4net.Appender.AdoNetAppender.SendBuffer(LoggingEvent[] events)
at log4net.Appender.BufferingAppenderSkeleton.Append(LoggingEvent loggingEvent) at log4net.Appender.AppenderSkeleton.DoAppend(LoggingEvent loggingEvent)

It seems like log4net stops trying after encountering this error. This article describes a way to deal with this type of exception in general and this could be used to extend AdoNetAppender.

EDIT What may help is setting AdoNetAppender.ReconnectOnError=true, it is false by default.


Could it be an issue with your SQL Azure firewall rules?

0

精彩评论

暂无评论...
验证码 换一张
取 消