如何在运行时拦截和替换 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
为了消除人为错误并避免维护谁知道多少 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