Logging the execution of a Hangfire RecurringJob in database?

I have set up hangfire successfully for my ASP.NET project, i.e. the 11 Hangfire tables are created in my database. I tried the following command inside the Application_Start() of my project’s Global.asax:

namespace myAPI
{
   public class WebApiApplication : System.Web.HttpApplication
   {
      protected void Application_Start(
      {
         System.Diagnostics.Debug.WriteLine("Recurring job will be set up.");

         RecurringJob.AddOrUpdate(
             "some-id", 
             () => System.Diagnostics.Debug.WriteLine("Job instance started at " +
                                                      DateTime.Now)),
             "*/2 * * * 1-5"); 
      }
   }
}

Sadly, inside Visual Studio’s window Output > Debug I only see Reccuring job will be set up. and nothing ever after. However, a SELECT * FROM [myContext].[HangFire].[Set] shows me

Key              Score      Value     ExpireAt
recurring-jobs  1579116240  some-id   NULL

So far so good, this means that the job is indeed set up.

But how do I log inside my DB each and each time when the RecurringJob is executed? Do I assume correctly that Hangfire does not do that out of the box and I have to log it myself within the arrow-function? Or is there a more elegant way?

Question on the side: Why don’t I see any output of System.Diagnostics.Debug.WriteLine within my recurring job?

References

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You can use SeriLog with Hangfire out of the box. Serilog comes with different sinks, e.g. Serilog.Sinks.MSSqlServer. You can configure it in startup.cs:

using Serilog;
using Serilog.Sinks.MSSqlServer;

Log.Logger = new LoggerConfiguration()
                 .WriteTo
                 .MSSqlServer(
                        connectionString: hangfireConnectionString,
                        tableName: "Logs",
                        autoCreateSqlTable: true
                    ).CreateLogger();
               // will display any issues with Serilog config. comment out in prod.
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));

GlobalConfiguration.Configuration
                   .UseSqlServerStorage(hangfireConnectionString)
                   .UseSerilogLogProvider();

After you schedule your job, you can log it with

Log.Information(string.Format("Hanfire Job Scheduled at {0}", DateTime.Now));

Method 2

Hangfire includes a concept of job filters (similar to ASP.NET MVC’s Action Filters). For your use case, you would define one that would write to your database (adjust based on your needs):

using Hangfire.Common;
using Hangfire.Server;

class LogCompletionAttribute : JobFilterAttribute, IServerFilter
{
    public void OnPerforming(PerformingContext filterContext)
    {
        // Code here if you care when the execution **has begun**
    }

    public void OnPerformed(PerformedContext context)
    {
        // Check that the job completed successfully
        if (!context.Canceled && context.Exception != null)
        {
            // Here you would write to your database.
            // Example with entity framework:
            using (var ctx = new YourDatabaseContext())
            {
                ctx.Something.Add(/**/);
                ctx.SaveChanges();
            }
        }
    }
}

And then apply the filter to the job method:

namespace myAPI
{
   public class WebApiApplication : System.Web.HttpApplication
   {
      protected void Application_Start(
      {
         System.Diagnostics.Debug.WriteLine("Recurring job will be set up.");

         RecurringJob.AddOrUpdate("some-id", () => MyJob(), "*/2 * * * 1-5"); 
      }

      [LogCompletion]
      public static void MyJob()
      {
          System.Diagnostics.Debug.WriteLine("Job instance started at " + DateTime.Now)
      }
   }
}

Docs: https://docs.hangfire.io/en/latest/extensibility/using-job-filters.html

Method 3

So the cron is set to fire At every 2nd minute on every day-of-week from Monday through Friday. I assume you are waiting for the job to execute and that it is in the right window of time.

Most of the references that I found on the web indicated that you can do.

RecurringJob.AddOrUpdate(() => Console.WriteLine("This job will execute once in every minute"), Cron.Minutely);

Maybe you have to line up the dots a bit better to write to the vs console.

There is also an admin portal that can be configured to see what is begin run and when.

I have the following setup.
Global.asax.cs

    protected void Application_Start()
    {
        HangfireJobsConfig.Register();
    }
    public class HangfireJobsConfig
    {
        public static void Register()
        {
            if (App1Config.RunHangfireService)
            {
                JobStorage.Current = new SqlServerStorage(App1Config.DefaultConnectionStringName.Split('=').Last());
                GlobalConfiguration.Configuration.UseConsole();
                RecurringJob.AddOrUpdate("RunJob1", () => RunJob1(null), Cron.MinuteInterval(App1Config.RunJob1Interval));
                RecurringJob.AddOrUpdate("RunJob2", () => RunJob2(null), Cron.MinuteInterval(App1Config.RunJob2Interval));
            }
        }

        [AutomaticRetry(Attempts = 0, Order = 1)]
        public static void RunJob1(PerformContext context)
        {
            //dostuff
        }

        [AutomaticRetry(Attempts = 0, Order = 2)]
        public static void RunJob2(PerformContext context)
        {
            //do stuff
        }
    }

Startup.cs

    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            ConfigureHangFire(app);
        }
        public void ConfigureHangFire(IAppBuilder app)
        {
            if (App1Config.RunHangfireService)
            {
                GlobalConfiguration.Configuration.UseSqlServerStorage(
                    AppiConfig.DefaultConnectionStringName.Split('=').Last());

                GlobalConfiguration.Configuration.UseConsole();

                app.UseHangfireServer();
                var options = new DashboardOptions
                {
                    AuthorizationFilters = new[]
                    {
                        new AuthorizationFilter { Roles = "Inventory" }         
                    }
                };
                app.UseHangfireDashboard("/hangfire", options);
            }
        }
    }

Method 4

The actual problem was a very trivial one, the initialization of the actual background server was missing BackgroundJobServer();. Here the fully functional code:

namespace myAPI 
{
  public class WebApiApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
       string connString = ConfigurationManager.ConnectionStrings["myContext"].ToString();
       Hangfire.GlobalConfiguration.Configuration.UseConsole();
       Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage(connString,
       new SqlServerStorageOptions {
             CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
             SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
             QueuePollInterval = TimeSpan.Zero,
             UseRecommendedIsolationLevel = true,
             UsePageLocksOnDequeue = true,
             DisableGlobalLocks = true
           });
        var bgndJS = new BackgroundJobServer(); // <--- this is essential!
        RecurringJob.AddOrUpdate("myRecurringJob", () => HangfireRecurringJob(), "*/2 * * * 1-5");
        System.Diagnostics.Debug.WriteLine("---> RecurringJob 'myHangfireJob' initated.");
    }

    public void HangfireRecurringJob() {
       System.Diagnostics.Debug.WriteLine("---> HangfireRecurringJob() executed at" + DateTime.Now);
       Console.Beep(); // <-- I was really happy to hear the beep
    }
  }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x