2

I have the following scenario,

  • A Windows Service running a self hosted Quartz Scheduler service.
  • I have a class library with a number of classes that are 'jobs' that will run when triggered by the Quartz Scheduler Service.
  • I have NLog configured in each 'job' class such that it logs the activity of that job to a specific folder and file. This gives me a logical separation of all logging for each 'job' and I can turn logging on and off for each individual 'job' at run time using an xml configuration file.
  • I have configured the Quartz Scheduler to log the start-up information into a scheduler log.

This is all working without any problems.

What I would like to do now is to log the default information output of the Quartz Scheduler into another separate NLog log but I cannot figure out how to 'pipe' this into NLog, from the common logging framework used by Quartz. All of my logging configuration is done programmatically, so that I can switch logging on and off for each individual 'job' log at run time.

Here is a cut down version of my NLog config-

?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true">
  <targets  async="True" xsi:type="AsyncWrapper" queueLimit="20000">
<!--Scheduler Logging Section-->
    <target name="sInfo" xsi:type="File" 
            fileName="${basedir}/logs/Scheduler/SchedulerInfolog.txt" 
            layout="[${date:format=dd-MM-yyy HH\:mm\:ss}] ${message}"
            keepFileOpen="false"
            archiveFileName="${basedir}/logs/archive/Scheduler/SchedulerInfoLog{#}.txt"
            archiveEvery="Month"
            archiveNumbering="Rolling"
            maxArchiveFiles="10"
            archiveAboveSize="10485760"/>
    </targets>
    <rules>
    </rules>
  </nlog>

I have this declared in the scheduler class,

Private Shared Logger As NLog.Logger = LogManager.GetLogger("Scheduler")
Private xlc As LoggingConfiguration = LogManager.Configuration
Private sInfoRule As New LoggingRule
Private sTraceRule As New LoggingRule
Private sErrorRule As New LoggingRule
Private sfileTarget As New FileTarget

In the Quartz Scheduler start up process I call a method that runs this code,

If infoEnabled Then
  sInfoRule = New LoggingRule("Scheduler", LogLevel.Info,   xlc.FindTargetByName("sInfo"))
  xlc.LoggingRules.Add(sInfoRule)
  sInfoRule.DisableLoggingForLevel(LogLevel.Fatal)
  sInfoRule.DisableLoggingForLevel(LogLevel.Error)
  sInfoRule.DisableLoggingForLevel(LogLevel.Warn)
End If

LogManager.Configuration = xlc
LogManager.ReconfigExistingLoggers()

I am configuring the Quartz Scheduler like this,

Dim properties = New NameValueCollection() 
properties("quartz.scheduler.instanceName") = "SRCTaskScheduler" 

properties("quartz.threadPool.type") = "Quartz.Simpl.SimpleThreadPool, Quartz"

properties("quartz.threadPool.threadCount") = "20"

properties("quartz.threadPool.threadPriority")
= "Normal" 

properties("quartz.plugin.jobInitializer.type") = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz"

properties("quartz.plugin.jobInitializer.fileNames") = path & "ScheduledTasks.xml"

properties("quartz.plugin.jobInitializer.failOnFileNotFound")
= "true"

properties("quartz.plugin.jobInitializer.scanInterval") = "60" 

properties("quartz.plugin.triggerHistory.type") = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz"

properties("quartz.plugin.triggerHistory.triggerFiredMessage") = "Trigger
[{1}.{0}] fired job [{6}.{5}] scheduled at: [{2:dd/MM/yyyy HH:mm:ss]}, next scheduled at: [{3:dd/MM/yyyy HH:mm:ss}]"

properties("quartz.plugin.triggerHistory.triggerCompleteMessage") = "Trigger [{1}.{0}] completed firing job [{6}.{5}] with resulting trigger
instruction code: {9}. Next scheduled at: {3:dd/MM/yyyy HH:mm:ss}"

properties("quartz.plugin.triggerHistory.triggerMisfiredMessage") = "Trigger [{1}.{0}] misfired job [{6}.{5}]. Should have fired at: {3:dd/MM/yyyy HH:mm:ss}"

properties("quartz.plugin.jobHistory.type")
= "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz"

properties("quartz.plugin.jobHistory.jobToBeFiredMessage") = "Job [{1}.{0}] to be fired by trigger [{4}.{3}] at: [{5:dd/MM/yyyy HH:mm:ss}] with re-fire: {7}"

properties("quartz.plugin.jobHistory.jobSuccessMessage")
= "Job [{1}.{0}] execution complete, next Schedule at: [{6:dd/MM/yyyy HH:mm:ss}] and reports: [{8}] "

properties("quartz.plugin.jobHistory.jobFailedMessage") = "Job [{1}.{0}] execution failed with exception: [{8}]"

properties("quartz.plugin.jobHistory.jobWasVetoedMessage")
= "Job [{1}.{0}] was vetoed. It was to be fired by trigger [{4}.{3}] at: [{2:dd-MM-yyyy HH:mm:ss.SSS}]"

properties("quartz.plugin.ShutdownHook.type") = "Quartz.Plugin.Management.ShutdownHookPlugin, Quartz"

properties("quartz.plugin.ShutdownHook.CleanShutdown")
= "false" 

Dim sf As ISchedulerFactory = New StdSchedulerFactory(properties) 
_scheduler = sf.GetScheduler()

I can then write to that particular log file like this,

Logger.Trace("[Scheduler configuration has completed.]")
Logger.Info("[Starting Scheduler System.]")

This may seem strange but the reason behind all of this is that once a job completes I write the next trigger time for that job in that particular job log, but if I change the trigger time in the scheduler, I have no record anywhere of that change, it just looks like the trigger for that job did not fire on time - I would ideally like to just log the Quartz Scheduler output as it reads in the new schedule, but I guess that is a leap too far.

My plan B, if this is not possible is to have a job configured to run every 60 seconds or so and to log the current scheduler settings, that would work, but I am like a dog with a bone on this and would like to see if it is possible to get Plan A to work, I just don't have the knowledge and skills to get the job done.

theB
  • 6,450
  • 1
  • 28
  • 38
Dave Rob
  • 21
  • 4

1 Answers1

0

Here is my NLog in my Quartz.Net POC (Proof of Concept).

And what I noticed was the lack of < rules > in your config.

Could that be it?

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="Nlog.log">
    <targets>

        <target name="logfileTargetName" xsi:type="File"   layout="${longdate}|${level}|${callsite}|${logger}|${threadid}|${windows-identity:domain=false}__${message} ${exception:format=message,stacktrace:separator=*"
                fileName="MyNLogLogFile.txt" />

        <target name="consoleTargetName" xsi:type="Console" />

        <target xsi:type="EventLog"
             name="eventTargetName"
             layout="${message}"
             source="MyCompany.Apps.QuartzPOC.ConsoleAppOne"
             eventId="555"
             log="Application"
                     />             
    </targets>
    <rules>

        <logger name="*" minlevel="Info" writeTo="logfileTargetName"/>
        <logger name="*" minlevel="Info" appendTo="consoleTargetName"/>
        <logger name="*" minLevel="Error" writeTo="eventTargetName" />
    </rules>
</nlog>
granadaCoder
  • 26,328
  • 10
  • 113
  • 146
  • Thank you for taking a look at this question, I take your point about the lack of rules in the configuration. – Dave Rob Dec 16 '14 at 16:32
  • The reason behind this is that I wanted to be able to enable/disable logging dynamically, without the need to restart my Windows service and after much experimentation and gnashing of teeth, I settled on defining the rules withing the code itself and adding the rules when the logging enabled flag is set to true, and removing the rules when the flag is set to false. This might be a sledge hammer and nut situation possibly, but it works. So the rules are there, just defined in the code, not in the config. – Dave Rob Dec 16 '14 at 16:41
  • Can you share how you configure your Quartz to use target? C# maybe? Or it is bind by "source"? – Valentyn Vynogradskiy Dec 17 '14 at 10:08