diff --git a/lib/raven/context_filter.rb b/lib/raven/context_filter.rb new file mode 100644 index 000000000..ea67de89e --- /dev/null +++ b/lib/raven/context_filter.rb @@ -0,0 +1,39 @@ +module Raven + module ContextFilter + ACTIVEJOB_RESERVED_PREFIX = "_aj_".freeze + HAS_GLOBALID = const_defined?('GlobalID') + + extend self + + # Once an ActiveJob is queued, ActiveRecord references get serialized into + # some internal reserved keys, such as _aj_globalid. + # + # The problem is, if this job in turn gets queued back into ActiveJob with + # these magic reserved keys, ActiveJob will throw up and error. We want to + # capture these and mutate the keys so we can sanely report it. + def filter(context) + case context + when Array + context.map { |arg| filter(arg) } + when Hash + context.inject({}) do |hash, (key, value)| + next hash if key[0..3] == ACTIVEJOB_RESERVED_PREFIX + hash[key] = filter(value) + hash + end + else + format_globalid(context) + end + end + + private + + def format_globalid(context) + if HAS_GLOBALID && context.is_a?(GlobalID) + context.to_s + else + context + end + end + end +end diff --git a/lib/raven/integrations/delayed_job.rb b/lib/raven/integrations/delayed_job.rb index 74f6328f6..c99c88fe6 100644 --- a/lib/raven/integrations/delayed_job.rb +++ b/lib/raven/integrations/delayed_job.rb @@ -1,8 +1,12 @@ require 'delayed_job' +require 'raven/context_filter' + module Delayed module Plugins class Raven < ::Delayed::Plugin + include Raven::ContextFilter + callbacks do |lifecycle| lifecycle.around(:invoke_job) do |job, *args, &block| begin @@ -30,7 +34,7 @@ class Raven < ::Delayed::Plugin extra[:handler] = job.handler[0...1000] if job.handler if job.respond_to?('payload_object') && job.payload_object.respond_to?('job_data') - extra[:active_job] = job.payload_object.job_data + extra[:active_job] = Raven::ContextFilter.filter(job.payload_object.job_data) end ::Raven.capture_exception(exception, :logger => 'delayed_job', diff --git a/lib/raven/integrations/sidekiq.rb b/lib/raven/integrations/sidekiq.rb index 9470386ce..c2bc38921 100644 --- a/lib/raven/integrations/sidekiq.rb +++ b/lib/raven/integrations/sidekiq.rb @@ -1,6 +1,8 @@ require 'time' require 'sidekiq' +require 'raven/context_filter' + module Raven class SidekiqCleanupMiddleware def call(_worker, job, queue) @@ -13,11 +15,8 @@ def call(_worker, job, queue) end class SidekiqErrorHandler - ACTIVEJOB_RESERVED_PREFIX = "_aj_".freeze - HAS_GLOBALID = const_defined?('GlobalID') - def call(ex, context) - context = filter_context(context) + context = Raven::ContextFilter.filter(context) Raven.context.transaction.push transaction_from_context(context) Raven.capture_exception( ex, @@ -30,28 +29,6 @@ def call(ex, context) private - # Once an ActiveJob is queued, ActiveRecord references get serialized into - # some internal reserved keys, such as _aj_globalid. - # - # The problem is, if this job in turn gets queued back into ActiveJob with - # these magic reserved keys, ActiveJob will throw up and error. We want to - # capture these and mutate the keys so we can sanely report it. - def filter_context(context) - case context - when Array - context.map { |arg| filter_context(arg) } - when Hash - Hash[context.map { |key, value| filter_context_hash(key, value) }] - else - format_globalid(context) - end - end - - def filter_context_hash(key, value) - (key = key[3..-1]) if key [0..3] == ACTIVEJOB_RESERVED_PREFIX - [key, filter_context(value)] - end - # this will change in the future: # https://github.com/mperham/sidekiq/pull/3161 def transaction_from_context(context) @@ -66,14 +43,6 @@ def transaction_from_context(context) "Sidekiq" end end - - def format_globalid(context) - if HAS_GLOBALID && context.is_a?(GlobalID) - context.to_s - else - context - end - end end end diff --git a/spec/raven/context_filter_spec.rb b/spec/raven/context_filter_spec.rb new file mode 100644 index 000000000..6b0dcbc6b --- /dev/null +++ b/spec/raven/context_filter_spec.rb @@ -0,0 +1,40 @@ +require "spec_helper" + +require "raven/context_filter" + +RSpec.describe Raven::ContextFilter do + context "filters out ActiveJob keys from context" do + let(:context) do + { _aj_globalid: GlobalID.new("gid://app/model/id"), key: "value" } + end + let(:expected_context) do + { key: "value" } + end + + it "removes reserved keys" do + new_context = described_class.filter(context) + + expect(new_context).to eq(expected_context) + end + end + + context "filters out ActiveJob keys from nested context" do + let(:context) do + { + _aj_globalid: GlobalID.new("gid://app/model/id"), + arguments: { "key" => "value", "_aj_symbol_keys" => ["key"] } + } + end + let(:expected_context) do + { + arguments: { "key" => "value" } + } + end + + it "removes reserved keys" do + new_context = described_class.filter(context) + + expect(new_context).to eq(expected_context) + end + end +end diff --git a/spec/raven/integrations/sidekiq_spec.rb b/spec/raven/integrations/sidekiq_spec.rb index e356519f1..563b7ed0e 100644 --- a/spec/raven/integrations/sidekiq_spec.rb +++ b/spec/raven/integrations/sidekiq_spec.rb @@ -50,22 +50,6 @@ Raven::SidekiqErrorHandler.new.call(exception, context) end end - - it "filters out ActiveJob keys", :rails => true do - exception = build_exception - aj_context = context - aj_context["_aj_globalid"] = GlobalID.new('gid://app/model/id') - expected_context = aj_context.dup - expected_context.delete("_aj_globalid") - expected_context["_globalid"] = "gid://app/model/id" - expected_options = { - :message => exception.message, - :extra => { :sidekiq => expected_context } - } - expect(Raven).to receive(:capture_exception).with(exception, expected_options) - - Raven::SidekiqErrorHandler.new.call(exception, aj_context) - end end class HappyWorker