appsignal 2.11.0-java → 2.11.4-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.semaphore/semaphore.yml +197 -0
  4. data/CHANGELOG.md +19 -0
  5. data/README.md +16 -1
  6. data/Rakefile +20 -11
  7. data/build_matrix.yml +13 -0
  8. data/ext/agent.yml +17 -25
  9. data/ext/appsignal_extension.c +1 -1
  10. data/ext/base.rb +12 -9
  11. data/gemfiles/no_dependencies.gemfile +7 -0
  12. data/gemfiles/resque-2.gemfile +0 -1
  13. data/gemfiles/webmachine.gemfile +1 -0
  14. data/lib/appsignal/cli/diagnose/utils.rb +8 -11
  15. data/lib/appsignal/cli/install.rb +5 -8
  16. data/lib/appsignal/helpers/instrumentation.rb +32 -0
  17. data/lib/appsignal/hooks.rb +1 -0
  18. data/lib/appsignal/hooks/action_mailer.rb +22 -0
  19. data/lib/appsignal/hooks/active_support_notifications.rb +72 -0
  20. data/lib/appsignal/hooks/shoryuken.rb +43 -4
  21. data/lib/appsignal/integrations/object.rb +4 -34
  22. data/lib/appsignal/integrations/object_ruby_19.rb +37 -0
  23. data/lib/appsignal/integrations/object_ruby_modern.rb +64 -0
  24. data/lib/appsignal/system.rb +4 -0
  25. data/lib/appsignal/transaction.rb +30 -2
  26. data/lib/appsignal/version.rb +1 -1
  27. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  28. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  29. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  30. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  31. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  32. data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
  33. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +151 -104
  34. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +4 -2
  35. data/spec/lib/appsignal/integrations/object_19_spec.rb +266 -0
  36. data/spec/lib/appsignal/integrations/object_spec.rb +29 -10
  37. data/spec/lib/appsignal/transaction_spec.rb +55 -0
  38. data/spec/lib/appsignal_spec.rb +30 -0
  39. data/spec/support/helpers/dependency_helper.rb +4 -0
  40. metadata +16 -3
@@ -60,7 +60,10 @@ describe Appsignal::Hooks::ResqueHook do
60
60
  "error" => nil,
61
61
  "namespace" => namespace,
62
62
  "metadata" => {},
63
- "sample_data" => { "tags" => { "queue" => queue } }
63
+ "sample_data" => {
64
+ "breadcrumbs" => [],
65
+ "tags" => { "queue" => queue }
66
+ }
64
67
  )
65
68
  expect(transaction_hash["events"].map { |e| e["name"] })
66
69
  .to eql(["perform.resque"])
@@ -84,7 +87,10 @@ describe Appsignal::Hooks::ResqueHook do
84
87
  },
85
88
  "namespace" => namespace,
86
89
  "metadata" => {},
87
- "sample_data" => { "tags" => { "queue" => queue } }
90
+ "sample_data" => {
91
+ "breadcrumbs" => [],
92
+ "tags" => { "queue" => queue }
93
+ }
88
94
  )
89
95
  end
90
96
  end
@@ -118,6 +124,7 @@ describe Appsignal::Hooks::ResqueHook do
118
124
  "metadata" => {},
119
125
  "sample_data" => {
120
126
  "tags" => { "queue" => queue },
127
+ "breadcrumbs" => [],
121
128
  "params" => [
122
129
  "foo",
123
130
  {
@@ -174,6 +181,7 @@ describe Appsignal::Hooks::ResqueHook do
174
181
  "namespace" => namespace,
175
182
  "metadata" => {},
176
183
  "sample_data" => {
184
+ "breadcrumbs" => [],
177
185
  "tags" => { "queue" => queue }
178
186
  # Params will be set by the ActiveJob integration
179
187
  }
@@ -1,55 +1,73 @@
1
1
  describe Appsignal::Hooks::ShoryukenMiddleware do
2
- let(:current_transaction) { background_job_transaction }
3
-
4
2
  class DemoShoryukenWorker
5
3
  end
6
4
 
5
+ let(:time) { "2010-01-01 10:01:00UTC" }
7
6
  let(:worker_instance) { DemoShoryukenWorker.new }
8
- let(:queue) { double }
9
- let(:sqs_msg) { double(:attributes => {}) }
7
+ let(:queue) { "some-funky-queue-name" }
8
+ let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
10
9
  let(:body) { {} }
11
-
12
- before do
13
- allow(Appsignal::Transaction).to receive(:current).and_return(current_transaction)
14
- start_agent
10
+ before(:context) { start_agent }
11
+ around { |example| keep_transactions { example.run } }
12
+
13
+ def perform_job(&block)
14
+ block ||= lambda {}
15
+ Timecop.freeze(Time.parse(time)) do
16
+ Appsignal::Hooks::ShoryukenMiddleware.new.call(
17
+ worker_instance,
18
+ queue,
19
+ sqs_msg,
20
+ body,
21
+ &block
22
+ )
23
+ end
15
24
  end
16
25
 
17
26
  context "with a performance call" do
18
- let(:queue) { "some-funky-queue-name" }
27
+ let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
19
28
  let(:sqs_msg) do
20
- double(:attributes => { "SentTimestamp" => Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 })
29
+ double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
21
30
  end
22
31
 
23
32
  context "with complex argument" do
24
- let(:body) do
25
- {
26
- :foo => "Foo",
27
- :bar => "Bar"
28
- }
29
- end
30
- after do
31
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
32
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
33
- # nothing
34
- end
35
- end
36
- end
33
+ let(:body) { { :foo => "Foo", :bar => "Bar" } }
37
34
 
38
35
  it "wraps the job in a transaction with the correct params" do
39
- expect(Appsignal).to receive(:monitor_transaction).with(
40
- "perform_job.shoryuken",
41
- :class => "DemoShoryukenWorker",
42
- :method => "perform",
43
- :metadata => {
44
- :queue => "some-funky-queue-name",
45
- "SentTimestamp" => 217_123_200_000
46
- },
47
- :params => {
48
- :foo => "Foo",
49
- :bar => "Bar"
50
- },
51
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
36
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
37
+ expect { perform_job }.to change { created_transactions.length }.by(1)
38
+
39
+ transaction = last_transaction
40
+ expect(transaction).to be_completed
41
+ transaction_hash = transaction.to_h
42
+ expect(transaction_hash).to include(
43
+ "action" => "DemoShoryukenWorker#perform",
44
+ "id" => kind_of(String), # AppSignal generated id
45
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
46
+ "error" => nil
52
47
  )
48
+ expect(transaction_hash["events"].first).to include(
49
+ "allocation_count" => kind_of(Integer),
50
+ "body" => "",
51
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
52
+ "child_allocation_count" => kind_of(Integer),
53
+ "child_duration" => kind_of(Float),
54
+ "child_gc_duration" => kind_of(Float),
55
+ "count" => 1,
56
+ "gc_duration" => kind_of(Float),
57
+ "start" => kind_of(Float),
58
+ "duration" => kind_of(Float),
59
+ "name" => "perform_job.shoryuken",
60
+ "title" => ""
61
+ )
62
+ expect(transaction_hash["sample_data"]).to include(
63
+ "params" => { "foo" => "Foo", "bar" => "Bar" },
64
+ "metadata" => {
65
+ "message_id" => "msg1",
66
+ "queue" => queue,
67
+ "SentTimestamp" => sent_timestamp
68
+ }
69
+ )
70
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
53
71
  end
54
72
 
55
73
  context "with parameter filtering" do
@@ -57,21 +75,16 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
57
75
  Appsignal.config = project_fixture_config("production")
58
76
  Appsignal.config[:filter_parameters] = ["foo"]
59
77
  end
78
+ after do
79
+ Appsignal.config[:filter_parameters] = []
80
+ end
60
81
 
61
82
  it "filters selected arguments" do
62
- expect(Appsignal).to receive(:monitor_transaction).with(
63
- "perform_job.shoryuken",
64
- :class => "DemoShoryukenWorker",
65
- :method => "perform",
66
- :metadata => {
67
- :queue => "some-funky-queue-name",
68
- "SentTimestamp" => 217_123_200_000
69
- },
70
- :params => {
71
- :foo => "[FILTERED]",
72
- :bar => "Bar"
73
- },
74
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
83
+ perform_job
84
+
85
+ transaction_hash = last_transaction.to_h
86
+ expect(transaction_hash["sample_data"]).to include(
87
+ "params" => { "foo" => "[FILTERED]", "bar" => "Bar" }
75
88
  )
76
89
  end
77
90
  end
@@ -81,23 +94,12 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
81
94
  let(:body) { "foo bar" }
82
95
 
83
96
  it "handles string arguments" do
84
- expect(Appsignal).to receive(:monitor_transaction).with(
85
- "perform_job.shoryuken",
86
- :class => "DemoShoryukenWorker",
87
- :method => "perform",
88
- :metadata => {
89
- :queue => "some-funky-queue-name",
90
- "SentTimestamp" => 217_123_200_000
91
- },
92
- :params => { :params => body },
93
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
94
- )
97
+ perform_job
95
98
 
96
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
97
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
98
- # nothing
99
- end
100
- end
99
+ transaction_hash = last_transaction.to_h
100
+ expect(transaction_hash["sample_data"]).to include(
101
+ "params" => { "params" => body }
102
+ )
101
103
  end
102
104
  end
103
105
 
@@ -105,58 +107,103 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
105
107
  let(:body) { 1 }
106
108
 
107
109
  it "handles primitive types as arguments" do
108
- expect(Appsignal).to receive(:monitor_transaction).with(
109
- "perform_job.shoryuken",
110
- :class => "DemoShoryukenWorker",
111
- :method => "perform",
112
- :metadata => {
113
- :queue => "some-funky-queue-name",
114
- "SentTimestamp" => 217_123_200_000
115
- },
116
- :params => { :params => body },
117
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
118
- )
110
+ perform_job
119
111
 
120
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
121
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
122
- # nothing
123
- end
124
- end
112
+ transaction_hash = last_transaction.to_h
113
+ expect(transaction_hash["sample_data"]).to include(
114
+ "params" => { "params" => body }
115
+ )
125
116
  end
126
117
  end
127
118
  end
128
119
 
129
120
  context "with exception" do
130
- let(:transaction) do
131
- Appsignal::Transaction.new(
132
- SecureRandom.uuid,
133
- Appsignal::Transaction::BACKGROUND_JOB,
134
- Appsignal::Transaction::GenericRequest.new({})
121
+ it "sets the exception on the transaction" do
122
+ expect do
123
+ expect do
124
+ perform_job { raise ExampleException, "error message" }
125
+ end.to raise_error(ExampleException)
126
+ end.to change { created_transactions.length }.by(1)
127
+
128
+ transaction = last_transaction
129
+ expect(transaction).to be_completed
130
+ transaction_hash = transaction.to_h
131
+ expect(transaction_hash).to include(
132
+ "action" => "DemoShoryukenWorker#perform",
133
+ "id" => kind_of(String), # AppSignal generated id
134
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
135
+ "error" => {
136
+ "name" => "ExampleException",
137
+ "message" => "error message",
138
+ "backtrace" => kind_of(String)
139
+ }
135
140
  )
136
141
  end
142
+ end
137
143
 
138
- before do
139
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
140
- expect(Appsignal::Transaction).to receive(:create)
141
- .with(
142
- kind_of(String),
143
- Appsignal::Transaction::BACKGROUND_JOB,
144
- kind_of(Appsignal::Transaction::GenericRequest)
145
- ).and_return(transaction)
144
+ context "with batched jobs" do
145
+ let(:sqs_msg) do
146
+ [
147
+ double(
148
+ :message_id => "msg2",
149
+ :attributes => { "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s }
150
+ ),
151
+ double(
152
+ :message_id => "msg1",
153
+ :attributes => { "SentTimestamp" => sent_timestamp.to_s }
154
+ )
155
+ ]
146
156
  end
147
-
148
- it "sets the exception on the transaction" do
149
- expect(transaction).to receive(:set_error).with(ExampleException)
157
+ let(:body) do
158
+ [
159
+ "foo bar",
160
+ { :id => "123", :foo => "Foo", :bar => "Bar" }
161
+ ]
150
162
  end
163
+ let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
151
164
 
152
- after do
165
+ it "creates a transaction for the batch" do
166
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
153
167
  expect do
154
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
155
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
156
- raise ExampleException
157
- end
158
- end
159
- end.to raise_error(ExampleException)
168
+ perform_job {}
169
+ end.to change { created_transactions.length }.by(1)
170
+
171
+ transaction = last_transaction
172
+ expect(transaction).to be_completed
173
+ transaction_hash = transaction.to_h
174
+ expect(transaction_hash).to include(
175
+ "action" => "DemoShoryukenWorker#perform",
176
+ "id" => kind_of(String), # AppSignal generated id
177
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
178
+ "error" => nil
179
+ )
180
+ expect(transaction_hash["events"].first).to include(
181
+ "allocation_count" => kind_of(Integer),
182
+ "body" => "",
183
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
184
+ "child_allocation_count" => kind_of(Integer),
185
+ "child_duration" => kind_of(Float),
186
+ "child_gc_duration" => kind_of(Float),
187
+ "count" => 1,
188
+ "gc_duration" => kind_of(Float),
189
+ "start" => kind_of(Float),
190
+ "duration" => kind_of(Float),
191
+ "name" => "perform_job.shoryuken",
192
+ "title" => ""
193
+ )
194
+ expect(transaction_hash["sample_data"]).to include(
195
+ "params" => {
196
+ "msg2" => "foo bar",
197
+ "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
198
+ },
199
+ "metadata" => {
200
+ "batch" => true,
201
+ "queue" => "some-funky-queue-name",
202
+ "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
203
+ }
204
+ )
205
+ # Queue time based on earliest/oldest timestamp from messages
206
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
160
207
  end
161
208
  end
162
209
  end
@@ -262,7 +262,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
262
262
  "sample_data" => {
263
263
  "environment" => {},
264
264
  "params" => expected_args,
265
- "tags" => {}
265
+ "tags" => {},
266
+ "breadcrumbs" => []
266
267
  }
267
268
  )
268
269
  expect_transaction_to_have_sidekiq_event(transaction_hash)
@@ -290,7 +291,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
290
291
  "sample_data" => {
291
292
  "environment" => {},
292
293
  "params" => expected_args,
293
- "tags" => {}
294
+ "tags" => {},
295
+ "breadcrumbs" => []
294
296
  }
295
297
  )
296
298
  # TODO: Not available in transaction.to_h yet.
@@ -0,0 +1,266 @@
1
+ require "appsignal/integrations/object"
2
+
3
+ def is_ruby_19
4
+ RUBY_VERSION < "2.0"
5
+ end
6
+
7
+ describe Object do
8
+ describe "#instrument_method" do
9
+ context "with instance method" do
10
+ let(:klass) do
11
+ Class.new do
12
+ def foo(param1, options = {})
13
+ [param1, options]
14
+ end
15
+ appsignal_instrument_method :foo
16
+ end
17
+ end
18
+ let(:instance) { klass.new }
19
+
20
+ def call_with_arguments
21
+ instance.foo(
22
+ "abc",
23
+ :foo => "bar"
24
+ )
25
+ end
26
+
27
+ context "when active" do
28
+ let(:transaction) { http_request_transaction }
29
+ before do
30
+ Appsignal.config = project_fixture_config
31
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
32
+ end
33
+ after { Appsignal.config = nil }
34
+
35
+ context "with anonymous class" do
36
+ it "instruments the method and calls it" do
37
+ expect(Appsignal.active?).to be_truthy
38
+ expect(transaction).to receive(:start_event)
39
+ expect(transaction).to receive(:finish_event).with \
40
+ "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
41
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
42
+ end
43
+ end
44
+
45
+ context "with named class" do
46
+ before do
47
+ class NamedClass
48
+ def foo
49
+ 1
50
+ end
51
+ appsignal_instrument_method :foo
52
+ end
53
+ end
54
+ after { Object.send(:remove_const, :NamedClass) }
55
+ let(:klass) { NamedClass }
56
+
57
+ it "instruments the method and calls it" do
58
+ expect(Appsignal.active?).to be_truthy
59
+ expect(transaction).to receive(:start_event)
60
+ expect(transaction).to receive(:finish_event).with \
61
+ "foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
62
+ expect(instance.foo).to eq(1)
63
+ end
64
+ end
65
+
66
+ context "with nested named class" do
67
+ before do
68
+ module MyModule
69
+ module NestedModule
70
+ class NamedClass
71
+ def bar
72
+ 2
73
+ end
74
+ appsignal_instrument_method :bar
75
+ end
76
+ end
77
+ end
78
+ end
79
+ after { Object.send(:remove_const, :MyModule) }
80
+ let(:klass) { MyModule::NestedModule::NamedClass }
81
+
82
+ it "instruments the method and calls it" do
83
+ expect(Appsignal.active?).to be_truthy
84
+ expect(transaction).to receive(:start_event)
85
+ expect(transaction).to receive(:finish_event).with \
86
+ "bar.NamedClass.NestedModule.MyModule.other", nil, nil,
87
+ Appsignal::EventFormatter::DEFAULT
88
+ expect(instance.bar).to eq(2)
89
+ end
90
+ end
91
+
92
+ context "with custom name" do
93
+ let(:klass) do
94
+ Class.new do
95
+ def foo
96
+ 1
97
+ end
98
+ appsignal_instrument_method :foo, :name => "my_method.group"
99
+ end
100
+ end
101
+
102
+ it "instruments with custom name" do
103
+ expect(Appsignal.active?).to be_truthy
104
+ expect(transaction).to receive(:start_event)
105
+ expect(transaction).to receive(:finish_event).with \
106
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
107
+ expect(instance.foo).to eq(1)
108
+ end
109
+ end
110
+
111
+ context "with a method given a block" do
112
+ let(:klass) do
113
+ Class.new do
114
+ def foo
115
+ yield
116
+ end
117
+ appsignal_instrument_method :foo
118
+ end
119
+ end
120
+
121
+ it "should yield the block" do
122
+ expect(instance.foo { 42 }).to eq(42)
123
+ end
124
+ end
125
+ end
126
+
127
+ context "when not active" do
128
+ let(:transaction) { Appsignal::Transaction.current }
129
+
130
+ it "does not instrument, but still calls the method" do
131
+ expect(Appsignal.active?).to be_falsy
132
+ expect(transaction).to_not receive(:start_event)
133
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
134
+ end
135
+ end
136
+ end
137
+
138
+ context "with class method" do
139
+ let(:klass) do
140
+ Class.new do
141
+ def self.bar(param1, options = {})
142
+ [param1, options]
143
+ end
144
+ appsignal_instrument_class_method :bar
145
+ end
146
+ end
147
+ def call_with_arguments
148
+ klass.bar(
149
+ "abc",
150
+ :foo => "bar"
151
+ )
152
+ end
153
+
154
+ context "when active" do
155
+ let(:transaction) { http_request_transaction }
156
+ before do
157
+ Appsignal.config = project_fixture_config
158
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once)
159
+ .and_return(transaction)
160
+ end
161
+ after { Appsignal.config = nil }
162
+
163
+ context "with anonymous class" do
164
+ it "instruments the method and calls it" do
165
+ expect(Appsignal.active?).to be_truthy
166
+ expect(transaction).to receive(:start_event)
167
+ expect(transaction).to receive(:finish_event).with \
168
+ "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
169
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
170
+ end
171
+ end
172
+
173
+ context "with named class" do
174
+ before do
175
+ class NamedClass
176
+ def self.bar
177
+ 2
178
+ end
179
+ appsignal_instrument_class_method :bar
180
+ end
181
+ end
182
+ after { Object.send(:remove_const, :NamedClass) }
183
+ let(:klass) { NamedClass }
184
+
185
+ it "instruments the method and calls it" do
186
+ expect(Appsignal.active?).to be_truthy
187
+ expect(transaction).to receive(:start_event)
188
+ expect(transaction).to receive(:finish_event).with \
189
+ "bar.class_method.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
190
+ expect(klass.bar).to eq(2)
191
+ end
192
+
193
+ context "with nested named class" do
194
+ before do
195
+ module MyModule
196
+ module NestedModule
197
+ class NamedClass
198
+ def self.bar
199
+ 2
200
+ end
201
+ appsignal_instrument_class_method :bar
202
+ end
203
+ end
204
+ end
205
+ end
206
+ after { Object.send(:remove_const, :MyModule) }
207
+ let(:klass) { MyModule::NestedModule::NamedClass }
208
+
209
+ it "instruments the method and calls it" do
210
+ expect(Appsignal.active?).to be_truthy
211
+ expect(transaction).to receive(:start_event)
212
+ expect(transaction).to receive(:finish_event).with \
213
+ "bar.class_method.NamedClass.NestedModule.MyModule.other", nil, nil,
214
+ Appsignal::EventFormatter::DEFAULT
215
+ expect(klass.bar).to eq(2)
216
+ end
217
+ end
218
+ end
219
+
220
+ context "with custom name" do
221
+ let(:klass) do
222
+ Class.new do
223
+ def self.bar
224
+ 2
225
+ end
226
+ appsignal_instrument_class_method :bar, :name => "my_method.group"
227
+ end
228
+ end
229
+
230
+ it "instruments with custom name" do
231
+ expect(Appsignal.active?).to be_truthy
232
+ expect(transaction).to receive(:start_event)
233
+ expect(transaction).to receive(:finish_event).with \
234
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
235
+ expect(klass.bar).to eq(2)
236
+ end
237
+ end
238
+
239
+ context "with a method given a block" do
240
+ let(:klass) do
241
+ Class.new do
242
+ def self.bar
243
+ yield
244
+ end
245
+ appsignal_instrument_class_method :bar
246
+ end
247
+ end
248
+
249
+ it "should yield the block" do
250
+ expect(klass.bar { 42 }).to eq(42)
251
+ end
252
+ end
253
+ end
254
+
255
+ context "when not active" do
256
+ let(:transaction) { Appsignal::Transaction.current }
257
+
258
+ it "does not instrument, but still call the method" do
259
+ expect(Appsignal.active?).to be_falsy
260
+ expect(transaction).to_not receive(:start_event)
261
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end