appsignal 2.11.0.beta.5-java → 2.11.3-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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +197 -0
  3. data/CHANGELOG.md +18 -0
  4. data/README.md +16 -1
  5. data/Rakefile +11 -5
  6. data/build_matrix.yml +13 -0
  7. data/ext/agent.yml +17 -25
  8. data/ext/appsignal_extension.c +1 -1
  9. data/ext/base.rb +12 -9
  10. data/gemfiles/no_dependencies.gemfile +7 -0
  11. data/gemfiles/resque-2.gemfile +0 -1
  12. data/gemfiles/webmachine.gemfile +1 -0
  13. data/lib/appsignal/cli/diagnose/utils.rb +8 -11
  14. data/lib/appsignal/cli/install.rb +5 -8
  15. data/lib/appsignal/config.rb +47 -15
  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/transaction.rb +30 -2
  22. data/lib/appsignal/version.rb +1 -1
  23. data/spec/lib/appsignal/config_spec.rb +18 -1
  24. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  25. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  26. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  27. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  28. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  29. data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
  30. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +151 -104
  31. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +4 -2
  32. data/spec/lib/appsignal/transaction_spec.rb +55 -0
  33. data/spec/lib/appsignal_spec.rb +30 -0
  34. data/spec/support/helpers/dependency_helper.rb +4 -0
  35. metadata +14 -5
@@ -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.
@@ -356,6 +356,57 @@ describe Appsignal::Transaction do
356
356
  end
357
357
  end
358
358
 
359
+ describe "#add_breadcrumb" do
360
+ context "when over the limit" do
361
+ before do
362
+ 22.times do |i|
363
+ transaction.add_breadcrumb(
364
+ "network",
365
+ "GET http://localhost",
366
+ "User made external network request",
367
+ { :code => i + 1 },
368
+ Time.parse("10-10-2010 10:00:00 UTC")
369
+ )
370
+ end
371
+ transaction.sample_data
372
+ end
373
+
374
+ it "stores last <LIMIT> breadcrumbs on the transaction" do
375
+ expect(transaction.to_h["sample_data"]["breadcrumbs"].length).to eql(20)
376
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][0]).to eq(
377
+ "action" => "GET http://localhost",
378
+ "category" => "network",
379
+ "message" => "User made external network request",
380
+ "metadata" => { "code" => 3 },
381
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
382
+ )
383
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][19]).to eq(
384
+ "action" => "GET http://localhost",
385
+ "category" => "network",
386
+ "message" => "User made external network request",
387
+ "metadata" => { "code" => 22 },
388
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
389
+ )
390
+ end
391
+ end
392
+
393
+ context "with defaults" do
394
+ it "stores breadcrumb with defaults on transaction" do
395
+ timeframe_start = Time.now.utc.to_i
396
+ transaction.add_breadcrumb("user_action", "clicked HOME")
397
+ transaction.sample_data
398
+ timeframe_end = Time.now.utc.to_i
399
+
400
+ breadcrumb = transaction.to_h["sample_data"]["breadcrumbs"][0]
401
+ expect(breadcrumb["category"]).to eq("user_action")
402
+ expect(breadcrumb["action"]).to eq("clicked HOME")
403
+ expect(breadcrumb["message"]).to eq("")
404
+ expect(breadcrumb["time"]).to be_between(timeframe_start, timeframe_end)
405
+ expect(breadcrumb["metadata"]).to eq({})
406
+ end
407
+ end
408
+ end
409
+
359
410
  describe "#set_action" do
360
411
  context "when the action is set" do
361
412
  it "updates the action name on the transaction" do
@@ -649,6 +700,10 @@ describe Appsignal::Transaction do
649
700
  "tags",
650
701
  Appsignal::Utils::Data.generate({})
651
702
  ).once
703
+ expect(transaction.ext).to receive(:set_sample_data).with(
704
+ "breadcrumbs",
705
+ Appsignal::Utils::Data.generate([])
706
+ ).once
652
707
 
653
708
  transaction.sample_data
654
709
  end
@@ -491,6 +491,36 @@ describe Appsignal do
491
491
  end
492
492
  end
493
493
 
494
+ describe ".add_breadcrumb" do
495
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
496
+
497
+ context "with transaction" do
498
+ let(:transaction) { double }
499
+ it "should call add_breadcrumb on transaction" do
500
+ expect(transaction).to receive(:add_breadcrumb)
501
+ .with("Network", "http", "User made network request", { :response => 200 }, fixed_time)
502
+ end
503
+
504
+ after do
505
+ Appsignal.add_breadcrumb(
506
+ "Network",
507
+ "http",
508
+ "User made network request",
509
+ { :response => 200 },
510
+ fixed_time
511
+ )
512
+ end
513
+ end
514
+
515
+ context "without transaction" do
516
+ let(:transaction) { nil }
517
+
518
+ it "should not call add_breadcrumb on transaction" do
519
+ expect(Appsignal.add_breadcrumb("Network", "http")).to be_falsy
520
+ end
521
+ end
522
+ end
523
+
494
524
  describe "custom stats" do
495
525
  let(:tags) { { :foo => "bar" } }
496
526