0

I am working on a windows service and that service has five different jobs which runs at the same time as there is a scheduler which uses Quarts.Net and TopShelf library for scheduling these jobs at a specific time interval. The problem is with logging with each job, log4Net library is being used for logging each job to a separate file and there is a proper configuration for each logger in app.config file but still at run time logging is not done on correct log file, when all jobs running simultaneously. Below is the code sample for these jobs:

factory = new StdSchedulerFactory();
scheduler = factory.GetScheduler();

scheduler.JobFactory = new LoggerEnabledJobFactory() { AppLogger = LogManager.GetLogger("Job1") }; // in case of another job it will be "Job2"
 scheduler.ListenerManager.AddJobListener(new JobListener() { Name = "Job1Listener" });

IJobDetail jobDetails = JobBuilder.Create<ServiceJob1>().SetJobData(map).Build();

ITrigger trigger = TriggerBuilder.Create()
                                .WithSimpleSchedule(x =>
                                                        x.WithIntervalInSeconds(interval)
                                                        .RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires()
                                                    )
                                .StartNow()
                                .WithIdentity("Job1TriggerGroup")
                                .Build();

scheduler.ScheduleJob(jobDetails, trigger);
scheduler.Start();

Below is custom JobFactory code: (which was already there in code)

public interface IloggerEnabledJob : IJob
    {
        ILog AppLogger{ get; set; }
    }

    public class LoggerEnabledJobFactory : IJobFactory
    {
        public ILog AppLogger { get; set; }

        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            var job = Activator.CreateInstance(bundle.JobDetail.JobType);            
            ((IloggerEnabledJob)job).AppLogger = AppLogger ;

            return (IJob)job;
        }

        public void ReturnJob(IJob job)
        {
        }
    }

Below is the "ServiceJob1" class code:

public class ServiceJob1 : IloggerEnabledJob
{
   public ILog AppLogger { get; set; }

   public void Execute(IJobExecutionContext context)
        {
            try
            {
                // do something
            }
            catch (Exception ex)
            {
                Logger.Log(AppLogger, Logger.LogType.Error, "Exception : " + ex);
            }
        }
}

App.config :

    <log4net>
        <root>
          <level value="INFO"/>
          <appender-ref ref="Job1FileAppender"/>
          <appender-ref ref="Job2FileAppender"/>
         </root>

     <appender name="Job1FileAppender" type="log4net.Appender.RollingFileAppender">
          <file type="log4net.Util.PatternString">
            <conversionPattern value="\Logs\Job1\Job1_Log_%date{yyyy.MM.dd.HH.mm.ss}-%processid.txt"/>
          </file>
          <filter type="log4net.Filter.LoggerMatchFilter">
            <loggerToMatch value="Job1"/>
          </filter>
          <filter type="log4net.Filter.DenyAllFilter"/>
          <appendToFile value="true"/>
          <rollingStyle value="Size"/>
          <maxSizeRollBackups value="10"/>
          <maximumFileSize value="25MB"/>
          <staticLogFileName value="true"/>
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
          </layout>
        </appender>
    <appender name="Job2FileAppender" type="log4net.Appender.RollingFileAppender">
              <file type="log4net.Util.PatternString">
                <conversionPattern value="\Logs\Job2\Job2_Log_%date{yyyy.MM.dd.HH.mm.ss}-%processid.txt"/>
              </file>
              <filter type="log4net.Filter.LoggerMatchFilter">
                <loggerToMatch value="Job2"/>
              </filter>
              <filter type="log4net.Filter.DenyAllFilter"/>
              <appendToFile value="true"/>
              <rollingStyle value="Size"/>
              <maxSizeRollBackups value="10"/>
              <maximumFileSize value="25MB"/>
              <staticLogFileName value="true"/>
              <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/>
              </layout>
            </appender>

<logger name="Job1" additivity="true">
      <level value="ALL" />
      <appender-ref ref="Job1FileAppender" />
    </logger>
    <logger name="Job2" additivity="true">
      <level value="ALL" />
      <appender-ref ref="Job2FileAppender" />
    </logger>
    </log4net>

I need to log Job1 and Job2 logs to their respective log file even if all five jobs running simultaneously. Any help/suggestions would be appreciated!

techV
  • 935
  • 3
  • 23
  • 41
  • I would check this https://stackoverflow.com/questions/51345161/should-i-take-ilogger-iloggert-iloggerfactory-or-iloggerprovider-for-a-libra and look at CreateLogger() to ensure its created properly, if I am understanding this correctly you are just getting the default logger otherwise. – Netferret Feb 26 '20 at 17:05
  • @Netferret thanks will check this.For each job there is a separate class file like ServiceJob1 which has correct logger in GetLogger function as above. – techV Feb 27 '20 at 04:35

1 Answers1

1

It seems from your code that you may be passing the same LogManager.GetLogger("Job1") every time from your JobFactory.

I usually make my custom JobFactory that uses IoC container internally. If I've had to pass different loggers to different jobs, I would just configure those in IoC container setup and have the container figure it out for me.

If IoC container is not how you create jobs, then you could inject logger in NewJob method like this

public class CustomJobFactory : IJobFactory
{
    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var newJob = // Create job somehow
        ((ILoggerEnabledJob)newJob).AppLogger = LogManager.GetLogger(bundle.JobDetail.JobType.Name);

        return newJob;
    }

    public void ReturnJob(IJob job)
    {
        // ...
    }
}

Another option to consider is just use static logger in each job.

starteleport
  • 1,231
  • 2
  • 11
  • 21
  • thanks for your valuable post... yes I had already created a custom job factory but forgot to mention in above post. Edited my post now... – techV Feb 27 '20 at 03:44
  • 1
    The code of your factory says that you're taking AppLogger from outside the factory class. My proposal is to have the factory create the actual logger instance, like in my code snippet. The `bundle.JobDetail.JobType` contains `System.Type` of the job being created and there's already enough information to create logger. – starteleport Feb 27 '20 at 12:49