Skip to content

Commit 11fe4cc

Browse files
authored
Merge pull request #555 from nateberkopec/better-transaction-names
Sidekiq: identify culprit + drop Sidekiq 2
2 parents b413d68 + 1411ecd commit 11fe4cc

File tree

6 files changed

+108
-41
lines changed

6 files changed

+108
-41
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ gemspec
44

55
group :test do
66
gem "rack"
7+
gem "sidekiq"
78
end
89

910
group :development do

gemfiles/rails32.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
source "http://rubygems.org"
22

33
gem "rails", ["~> 3.2.6", "!= 3.2.22.1"]
4+
gem "sidekiq", "3.2.1"
45

56
gemspec :path => "../"

gemfiles/rails42.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ source "http://rubygems.org"
22

33
gem "rails", "~> 4.2.0"
44
gem "mime-types", "< 3.0.0"
5+
gem "sidekiq", "3.2.1"
56

67
gemspec :path => "../"

gemfiles/rails5.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
source "http://rubygems.org"
22

33
gem "rails", "5.0.0"
4+
gem "sidekiq"
45

56
gemspec :path => "../"

lib/raven/integrations/sidekiq.rb

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,62 @@
33

44
module Raven
55
class Sidekiq
6-
def call(_worker, msg, _queue)
7-
started_at = Time.now
8-
yield
9-
rescue Exception => ex
10-
Raven.capture_exception(ex, :extra => { :sidekiq => msg },
11-
:time_spent => Time.now - started_at)
12-
raise
6+
ACTIVEJOB_RESERVED_PREFIX = "_aj_".freeze
7+
8+
def call(ex, context)
9+
context = filter_context(context)
10+
Raven.capture_exception(
11+
ex,
12+
:message => ex.message,
13+
:extra => { :sidekiq => context },
14+
:culprit => culprit_from_context(context)
15+
)
1316
ensure
1417
Context.clear!
1518
BreadcrumbBuffer.clear!
1619
end
17-
end
18-
end
1920

20-
if Sidekiq::VERSION < '3'
21-
# old behavior
22-
::Sidekiq.configure_server do |config|
23-
config.server_middleware do |chain|
24-
chain.add ::Raven::Sidekiq
21+
private
22+
23+
# Once an ActiveJob is queued, ActiveRecord references get serialized into
24+
# some internal reserved keys, such as _aj_globalid.
25+
#
26+
# The problem is, if this job in turn gets queued back into ActiveJob with
27+
# these magic reserved keys, ActiveJob will throw up and error. We want to
28+
# capture these and mutate the keys so we can sanely report it.
29+
def filter_context(context)
30+
case context
31+
when Array
32+
context.map { |arg| filter_context(arg) }
33+
when Hash
34+
Hash[context.map { |key, value| filter_context_hash(key, value) }]
35+
else
36+
context
37+
end
2538
end
26-
end
27-
else
28-
Sidekiq.configure_server do |config|
29-
config.error_handlers << proc do |ex, context|
30-
Raven.capture_exception(ex, :extra => {
31-
:sidekiq => filter_context(context)
32-
})
39+
40+
def filter_context_hash(key, value)
41+
(key = key[3..-1]) if key [0..3] == ACTIVEJOB_RESERVED_PREFIX
42+
[key, filter_context(value)]
3343
end
34-
end
35-
end
3644

37-
def filter_context(context)
38-
case context
39-
when Array
40-
context.map { |arg| filter_context(arg) }
41-
when Hash
42-
Hash[context.map { |key, value| filter_context_hash(key, value) }]
43-
else
44-
context
45+
# this will change in the future:
46+
# https://github.com/mperham/sidekiq/pull/3161
47+
def culprit_from_context(context)
48+
classname = (context["class"] || (context["job"] && context["job"]["class"]))
49+
if classname
50+
"Sidekiq/#{classname}"
51+
elsif context["event"]
52+
"Sidekiq/#{context['event']}"
53+
else
54+
"Sidekiq"
55+
end
56+
end
4557
end
4658
end
4759

48-
def filter_context_hash(key, value)
49-
# Strip any `_aj` prefixes from keys.
50-
# These keys come from an internal serialized object from ActiveJob.
51-
# Internally, there are a subset of keys that ActiveJob references, but
52-
# these are declared as private, and I don't think it's wise
53-
# to keep chasing what this list is. But they all use a common prefix, so
54-
# we want to strip this becuase ActiveJob will complain.
55-
# e.g.: _aj_globalid -> _globalid
56-
(key = key[3..-1]) if key [0..3] == "_aj_"
57-
[key, filter_context(value)]
60+
if Sidekiq::VERSION > '3'
61+
Sidekiq.configure_server do |config|
62+
config.error_handlers << Raven::Sidekiq.new
63+
end
5864
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
require 'spec_helper'
2+
require 'raven/integrations/sidekiq'
3+
4+
describe Raven::Sidekiq do
5+
before(:all) do
6+
Raven.configure do |config|
7+
config.dsn = 'dummy://notaserver'
8+
end
9+
end
10+
11+
let(:context) do
12+
{
13+
"args" => [true, true],
14+
"class" => "HardWorker",
15+
"created_at" => 1_474_922_824.910579,
16+
"enqueued_at" => 1_474_922_824.910665,
17+
"error_class" => "RuntimeError",
18+
"error_message" => "a wild exception appeared",
19+
"failed_at" => 1_474_922_825.158953,
20+
"jid" => "701ed9cfa51c84a763d56bc4",
21+
"queue" => "default",
22+
"retry" => true,
23+
"retry_count" => 0
24+
}
25+
end
26+
27+
it "should capture exceptions based on Sidekiq context" do
28+
exception = build_exception
29+
expected_options = {
30+
:message => exception.message,
31+
:extra => { :sidekiq => context },
32+
:culprit => "Sidekiq/HardWorker"
33+
}
34+
35+
expect(Raven).to receive(:capture_exception).with(exception, expected_options)
36+
37+
subject.call(exception, context)
38+
end
39+
40+
it "filters out ActiveJob keys" do
41+
exception = build_exception
42+
aj_context = context
43+
aj_context["_aj_globalid"] = "oh noes"
44+
expected_context = aj_context
45+
expected_context.delete("_aj_globalid")
46+
expected_context["_globalid"] = "oh noes"
47+
expected_options = {
48+
:message => exception.message,
49+
:extra => { :sidekiq => expected_context },
50+
:culprit => "Sidekiq/HardWorker"
51+
}
52+
53+
expect(Raven).to receive(:capture_exception).with(exception, expected_options)
54+
55+
subject.call(exception, aj_context)
56+
end
57+
end

0 commit comments

Comments
 (0)