如何在运行时拦截和替换 Quartz.NET 的默认日志记录?

How to intercept and replace Quartz.NET's default logging at runtime?

为了消除人为错误并避免维护谁知道多少 App.config 文件的单调乏味,我决定将我所有 Quartz.NET 作业的 Log4Net 配置设置为运行时,完全在代码中。不允许 App.config 个文件。我已经成功地获得了一个独立的控制台应用程序来执行此操作,如 here.

所示

但是,调度程序本身是另一回事。由于某种原因,它没有使用我指定的配置:

Dim oProperties As New NameValueCollection

' ... Set Quartz.NET properties here

BasicConfigurator.Configure(New Logger.DbAppender)

With New StdSchedulerFactory(oProperties)
  Manager.Scheduler = .GetScheduler
End With

我 运行 它作为 TopShelf 服务,我希望 Scheduler 的日志与每个 IJob 的日志到达相同的目的地。在这里,Quartz.NET 顽固地继续将其输出发送到 TopShelf 的调试控制台 window (STDOUT)。

这个构造在控制台应用程序中工作(上面链接);为什么它不在这里工作?

好的,知道了。 (没多久!)

出于某种原因,如果缺少必需的 App.config 条目,TopShelf/Quartz.NET 组合坚持抑制日志输出。

在这种情况下,由于它是我们正在处理的中央调度程序,而不是过多的作业,我会硬着头皮继续并为此使用一个 App.config 文件。

所以根本问题还是没有解决;这只是暂时的解决方法。如果您知道答案,我仍然很高兴收到您的来信。谢谢

好的,知道了。 (这次是真的。)

说明:在 TopShelf/Quartz.NET 组合下的 运行 时 Common.Logging 中会在 .config 文件的情况下抑制记录。出于某种原因,它始终拒绝识别 运行 时间加载的 Appender,即使服务中的第一行代码配置了日志记录。仍然是个谜,但并不重要。 (至少对我来说不是,有了这个最新发现——YMMV。)

唯一被抑制的日志记录是在 Common.Logging 库本身的构建和拆卸之后的日志记录;服务本身发生的所有事情仍然会被记录下来。

我能够使用下面的代码让它工作(当然,结合上面链接的代码)。

正如我所说,YMMV。

[服务]

Public Module Main
  Sub Main()
    Try
      BasicConfigurator.Configure(New Logger.DbAppender("ConnectionString"))
      Logger = LogManager.GetLogger(GetType(Main))

      Environment.ExitCode = HostFactory.Run(Sub(Configurator)
                                               Configurator.Service(Of Manager)(Sub(Service)
                                                                                  Service.ConstructUsing(Function(Factory) As ServiceControl
                                                                                                           Return New Manager
                                                                                                         End Function)

                                                                                  Service.WhenStarted(Function(Manager, HostControl) As Boolean
                                                                                                        Return Manager.StartService(HostControl)
                                                                                                      End Function)

                                                                                  Service.WhenStopped(Function(Manager, HostControl) As Boolean
                                                                                                        Return Manager.StopService(HostControl)
                                                                                                      End Function)
                                                                                End Sub)

                                               Configurator.SetDescription(ServiceInfo.Description)
                                               Configurator.SetServiceName(ServiceInfo.Product)
                                               Configurator.SetDisplayName(ServiceInfo.Title)
                                               Configurator.StartAutomatically()
                                               Configurator.RunAsLocalSystem()
                                             End Sub)

    Catch ex As Exception
      EventLog.WriteEntry(ServiceInfo.Product, ex.Message, EventLogEntryType.Error, 1, 1, ex.ToBytes)

    End Try
  End Sub



  Public Property Logger As ILog
End Module

[DbAppender](更新了 w/add 接受连接字符串的构造函数)

Public Class DbAppender
  Inherits AdoNetAppender

  Public Sub New()
    Me.New(String.Empty)
  End Sub



  Public Sub New(ConnectionString As String)
    If Trim(ConnectionString) <> String.Empty Then
      MyBase.ConnectionString = ConnectionString
    End If

    MyBase.CommandText = Me.CommandText
    MyBase.BufferSize = 1

    Me.Parameters.ForEach(Sub(Parameter As DbParameter)
                            MyBase.AddParameter(Parameter)
                          End Sub)

    Me.ActivateOptions()
  End Sub



  Protected Overrides Function CreateConnection(ConnectionType As Type, ConnectionString As String) As IDbConnection
    Return MyBase.CreateConnection(GetType(System.Data.SqlClient.SqlConnection), MyBase.ConnectionString)
  End Function



  Private Overloads ReadOnly Property CommandText As String
    Get
      Dim _
        sColumns,
        sValues As String

      sColumns = Join(Me.Parameters.Select(Function(P As DbParameter) P.DbColumn).ToArray, ",")
      sValues = Join(Me.Parameters.Select(Function(P As DbParameter) P.ParameterName).ToArray, ",")

      Return COMMAND_TEXT.ToFormat(sColumns, sValues)
    End Get
  End Property



  Private ReadOnly Property Parameters As List(Of DbParameter)
    Get
      Parameters = New List(Of DbParameter)
      Parameters.Add(Me.Date)
      Parameters.Add(Me.Thread)
      Parameters.Add(Me.Level)
      Parameters.Add(Me.Source)
      Parameters.Add(Me.Message)
      Parameters.Add(Me.Exception)
    End Get
  End Property



  Private ReadOnly Property [Date] As DbParameter
    Get
      Return New DbParameter("Date", New DbPatternLayout(PATTERN_DATE), DbType.Date, 0)
    End Get
  End Property



  Private ReadOnly Property Thread As DbParameter
    Get
      Return New DbParameter("Thread", New DbPatternLayout(PATTERN_THREAD), DbType.String, 255)
    End Get
  End Property



  Private ReadOnly Property Level As DbParameter
    Get
      Return New DbParameter("Level", New DbPatternLayout(PATTERN_LEVEL), DbType.String, 50)
    End Get
  End Property



  Private ReadOnly Property Source As DbParameter
    Get
      Return New DbParameter("Source", New DbPatternLayout(PATTERN_SOURCE), DbType.String, 255)
    End Get
  End Property



  Private ReadOnly Property Message As DbParameter
    Get
      Return New DbParameter("Message", New DbPatternLayout(PATTERN_MESSAGE), DbType.String, 4000)
    End Get
  End Property



  Private ReadOnly Property Exception As DbParameter
    Get
      Return New DbParameter("Exception", New DbExceptionLayout)
    End Get
  End Property



  Private Const PATTERN_MESSAGE As String = "%message"
  Private Const PATTERN_THREAD As String = "%thread"
  Private Const PATTERN_SOURCE As String = "%logger.%M()"
  Private Const PATTERN_LEVEL As String = "%level"
  Private Const PATTERN_DATE As String = "%date{yyyy-MM-dd HH:mm:ss.fff}"
  Private Const COMMAND_TEXT As String = "INSERT INTO Log ({0}) VALUES ({1})"
  '======================================================================================
  ' Available patterns:
  ' http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
  '======================================================================================
End Class