Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument Quartz.Net #5153

Open
TrymBeast opened this issue Feb 6, 2024 · 1 comment
Open

Instrument Quartz.Net #5153

TrymBeast opened this issue Feb 6, 2024 · 1 comment

Comments

@TrymBeast
Copy link

Are you requesting automatic instrumentation for a framework or library? Please describe.

  • Framework or library name : Quartz.NET
  • Library type: Job Scheduling
  • Library version: 3.x

Is your feature request related to a problem? Please describe.
The problem that I'm facing is that I see SQL commands that I can't correlate with the quartz.net jobs that are being executed.

Describe the solution you'd like
Being able to see the execution of the quartz.net jobs in the traces and correlate with sql queries and http requests being made by the jobs.

@robbiedhickey
Copy link

robbiedhickey commented Feb 6, 2024

This would be useful for our team as well. FWIW, here is a temporary solution that I am using to get some baseline telemetry.

  1. Create a job decorator to start a trace span and add any contextual metadata for the job
  /// <summary>
  /// A decorator to instrument job executions and track their runtime and other telemetry
  /// </summary>
  public class DatadogJobWrapper : IJob
  {
      private readonly IJob _innerJob;
      protected static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

      public DatadogJobWrapper(IJob innerJob)
      {
          _innerJob = innerJob;
      }

      public void Execute(JobExecutionContext context)
      {
          using (var scope = Tracer.Instance.StartActive("job"))
          {
              scope.Span.ResourceName = context.JobDetail.Name;

              if (context.NextFireTimeUtc.HasValue)
              {
                  scope.Span.SetTag("next_run_time", context.NextFireTimeUtc.Value.ToLocalTime().ToString());
              }

              if (context.PreviousFireTimeUtc.HasValue)
              {
                  scope.Span.SetTag("prev_run_time", context.PreviousFireTimeUtc.Value.ToLocalTime().ToString());
              }

              try
              {
                  // Execute the actual job
                  _innerJob.Execute(context);
              }
              catch (Exception ex)
              {
                  Log.Error($"{_innerJob.GetType().Name} job failed", ex);
                  scope.Span.SetException(ex);
              }
          }
      }
  }
  1. Update your job factory to wrap each instance with your new decorator:
 public class AutofacJobFactory : IJobFactory
 {
     private static readonly ILog Log = LoggerFactory.GetLogger<AutofacJobFactory>();
     private IComponentContext Context { get; set; }

     public AutofacJobFactory(IComponentContext context)
     {
         Context = context;
     }

     public IJob NewJob(TriggerFiredBundle bundle)
     {
         try
         {
             var job = (IJob)Context.Resolve(bundle.JobDetail.JobType);
             return new DatadogJobWrapper(job);
         }
         catch (Exception ex)
         {
             var type = bundle == null || bundle.JobDetail == null ? null : bundle.JobDetail.JobType;
             Log.Error(
                 string.Format("Failed to create IJob object of type '{0}'. ", type),
                 ex);
             throw;
         }
     }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants