Class: Sentry::DelayedJob::Plugin

Inherits:
Delayed::Plugin
  • Object
show all
Defined in:
lib/sentry/delayed_job/plugin.rb

Constant Summary collapse

DELAYED_JOB_CONTEXT_KEY =

need to symbolize strings as keyword arguments in Ruby 2.4~2.6

:"Delayed-Job"
ACTIVE_JOB_CONTEXT_KEY =
:"Active-Job"
OP_NAME =
"queue.delayed_job"
SPAN_ORIGIN =
"auto.queue.delayed_job"

Class Method Summary collapse

Class Method Details

.capture_exception(exception, job) ⇒ Object



83
84
85
# File 'lib/sentry/delayed_job/plugin.rb', line 83

def self.capture_exception(exception, job)
  Sentry::DelayedJob.capture_exception(exception, hint: { background: false }) if report?(job)
end

.compute_job_class(payload_object) ⇒ Object



74
75
76
77
78
79
80
81
# File 'lib/sentry/delayed_job/plugin.rb', line 74

def self.compute_job_class(payload_object)
  if payload_object.is_a?(Delayed::PerformableMethod)
    klass = payload_object.object.is_a?(Class) ? payload_object.object.name : payload_object.object.class.name
    "#{klass}##{payload_object.method_name}"
  else
    payload_object.class.name
  end
end

.extract_trace_data(job) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/sentry/delayed_job/plugin.rb', line 127

def self.extract_trace_data(job)
  payload_object = job.payload_object
  return nil unless payload_object.is_a?(Delayed::PerformableMethod)

  target_payload = payload_object.args.find { |a| a.is_a?(Hash) && a.key?(:sentry) }
  return nil unless target_payload
  payload_object.args.delete(target_payload)
  target_payload[:sentry]
end

.finish_transaction(transaction, status) ⇒ Object



108
109
110
111
112
113
# File 'lib/sentry/delayed_job/plugin.rb', line 108

def self.finish_transaction(transaction, status)
  return unless transaction

  transaction.set_http_status(status)
  transaction.finish
end

.generate_contexts(job) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sentry/delayed_job/plugin.rb', line 46

def self.generate_contexts(job)
  context = {}

  context[DELAYED_JOB_CONTEXT_KEY] = {
    id: job.id.to_s,
    priority: job.priority,
    attempts: job.attempts,
    run_at: job.run_at,
    locked_at: job.locked_at,
    locked_by: job.locked_by,
    queue: job.queue,
    created_at: job.created_at,
    last_error: job.last_error&.byteslice(0..1000),
    handler: job.handler&.byteslice(0..1000),
    job_class: compute_job_class(job.payload_object)
  }

  if job.payload_object.respond_to?(:job_data)
    context[ACTIVE_JOB_CONTEXT_KEY] = {}

    job.payload_object.job_data.each do |key, value|
      context[ACTIVE_JOB_CONTEXT_KEY][key.to_sym] = value
    end
  end

  context
end

.inject_trace_data(job) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/sentry/delayed_job/plugin.rb', line 115

def self.inject_trace_data(job)
  # active job style is handled in the sentry-rails/active_job extension more generally
  # if someone enqueues manually with some other job class, we cannot make assumptions unfortunately
  payload_object = job.payload_object
  return unless payload_object.is_a?(Delayed::PerformableMethod)

  # we will add the trace data to args and remove it again
  # this is hacky but it's the only reliable way to survive the YAML serialization/deserialization
  payload_object.args << { sentry: Sentry.get_trace_propagation_headers }
  job.payload_object = payload_object
end

.report?(job) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
90
91
92
93
94
# File 'lib/sentry/delayed_job/plugin.rb', line 87

def self.report?(job)
  return true unless Sentry.configuration.delayed_job.report_after_job_retries

  # We use the predecessor because the job's attempts haven't been increased to the new
  # count at this point.
  max_attempts = job&.max_attempts&.pred || Delayed::Worker.max_attempts.pred
  job.attempts >= max_attempts
end

.start_transaction(scope, env, contexts) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/sentry/delayed_job/plugin.rb', line 96

def self.start_transaction(scope, env, contexts)
  options = {
    name: scope.transaction_name,
    source: scope.transaction_source,
    op: OP_NAME,
    origin: SPAN_ORIGIN
  }

  transaction = Sentry.continue_trace(env, **options)
  Sentry.start_transaction(transaction: transaction, custom_sampling_context: contexts, **options)
end