appsignal 2.11.0.beta.3-java → 2.11.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +57 -1
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +11 -5
  5. data/Rakefile +27 -9
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +2 -2
  8. data/ext/Rakefile +2 -0
  9. data/ext/agent.yml +17 -25
  10. data/ext/appsignal_extension.c +1 -1
  11. data/ext/base.rb +7 -0
  12. data/ext/extconf.rb +2 -0
  13. data/lib/appsignal.rb +1 -0
  14. data/lib/appsignal/auth_check.rb +4 -2
  15. data/lib/appsignal/cli/diagnose.rb +1 -1
  16. data/lib/appsignal/config.rb +82 -17
  17. data/lib/appsignal/extension.rb +6 -5
  18. data/lib/appsignal/extension/jruby.rb +6 -5
  19. data/lib/appsignal/hooks.rb +24 -0
  20. data/lib/appsignal/hooks/action_mailer.rb +22 -0
  21. data/lib/appsignal/hooks/active_job.rb +41 -12
  22. data/lib/appsignal/hooks/active_support_notifications.rb +72 -0
  23. data/lib/appsignal/hooks/puma.rb +0 -1
  24. data/lib/appsignal/hooks/sidekiq.rb +1 -2
  25. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
  26. data/lib/appsignal/probes.rb +7 -0
  27. data/lib/appsignal/probes/puma.rb +1 -1
  28. data/lib/appsignal/probes/sidekiq.rb +3 -1
  29. data/lib/appsignal/utils/deprecation_message.rb +1 -1
  30. data/lib/appsignal/version.rb +1 -1
  31. data/spec/lib/appsignal/auth_check_spec.rb +23 -0
  32. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  33. data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
  34. data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
  35. data/spec/lib/appsignal/config_spec.rb +39 -1
  36. data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
  37. data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
  38. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  39. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  40. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  41. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  42. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  43. data/spec/lib/appsignal/hooks/activejob_spec.rb +82 -12
  44. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +3 -14
  45. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +7 -5
  46. data/spec/lib/appsignal/hooks_spec.rb +57 -0
  47. data/spec/lib/appsignal/marker_spec.rb +1 -1
  48. data/spec/spec_helper.rb +5 -0
  49. data/spec/support/helpers/config_helpers.rb +3 -2
  50. data/spec/support/helpers/dependency_helper.rb +4 -0
  51. data/spec/support/helpers/transaction_helpers.rb +1 -1
  52. data/spec/support/testing.rb +19 -19
  53. metadata +21 -9
@@ -110,7 +110,10 @@ if DependencyHelper.active_job_present?
110
110
  "metadata" => {},
111
111
  "sample_data" => hash_including(
112
112
  "params" => [],
113
- "tags" => { "queue" => queue }
113
+ "tags" => {
114
+ "active_job_id" => kind_of(String),
115
+ "queue" => queue
116
+ }
114
117
  )
115
118
  )
116
119
  events = transaction_hash["events"]
@@ -130,7 +133,7 @@ if DependencyHelper.active_job_present?
130
133
  transaction_hash = transaction.to_h
131
134
  expect(transaction_hash).to include(
132
135
  "sample_data" => hash_including(
133
- "tags" => { "queue" => "custom_queue" }
136
+ "tags" => hash_including("queue" => "custom_queue")
134
137
  )
135
138
  )
136
139
  end
@@ -151,9 +154,11 @@ if DependencyHelper.active_job_present?
151
154
  end
152
155
 
153
156
  it "reports the priority as tag on the transaction" do
154
- tags = { :priority => 10, :queue => queue }
157
+ tags = { :queue => queue }
155
158
  expect(Appsignal).to receive(:increment_counter)
156
159
  .with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
160
+ expect(Appsignal).to receive(:increment_counter)
161
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
157
162
 
158
163
  perform_job(ActiveJobPriorityTestJob)
159
164
 
@@ -161,7 +166,7 @@ if DependencyHelper.active_job_present?
161
166
  transaction_hash = transaction.to_h
162
167
  expect(transaction_hash).to include(
163
168
  "sample_data" => hash_including(
164
- "tags" => { "queue" => queue, "priority" => 10 }
169
+ "tags" => hash_including("queue" => queue, "priority" => 10)
165
170
  )
166
171
  )
167
172
  end
@@ -194,7 +199,10 @@ if DependencyHelper.active_job_present?
194
199
  "metadata" => {},
195
200
  "sample_data" => hash_including(
196
201
  "params" => [],
197
- "tags" => { "queue" => queue }
202
+ "tags" => {
203
+ "active_job_id" => kind_of(String),
204
+ "queue" => queue
205
+ }
198
206
  )
199
207
  )
200
208
  events = transaction_hash["events"]
@@ -202,6 +210,47 @@ if DependencyHelper.active_job_present?
202
210
  .map { |event| event["name"] }
203
211
  expect(events).to eq(["perform_start.active_job", "perform.active_job"])
204
212
  end
213
+
214
+ if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
215
+ context "with priority" do
216
+ before do
217
+ class ActiveJobErrorPriorityTestJob < ActiveJob::Base
218
+ queue_with_priority 10
219
+
220
+ def perform(*_args)
221
+ raise "uh oh"
222
+ end
223
+ end
224
+ end
225
+ after do
226
+ Object.send(:remove_const, :ActiveJobErrorPriorityTestJob)
227
+ end
228
+
229
+ it "reports the priority as tag on the transaction" do
230
+ tags = { :queue => queue }
231
+ expect(Appsignal).to receive(:increment_counter)
232
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
233
+ expect(Appsignal).to receive(:increment_counter)
234
+ .with("active_job_queue_job_count", 1, tags.merge(:status => :failed))
235
+ expect(Appsignal).to receive(:increment_counter)
236
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
237
+ expect(Appsignal).to receive(:increment_counter)
238
+ .with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :failed))
239
+
240
+ expect do
241
+ perform_job(ActiveJobErrorPriorityTestJob)
242
+ end.to raise_error(RuntimeError, "uh oh")
243
+
244
+ transaction = last_transaction
245
+ transaction_hash = transaction.to_h
246
+ expect(transaction_hash).to include(
247
+ "sample_data" => hash_including(
248
+ "tags" => hash_including("queue" => queue, "priority" => 10)
249
+ )
250
+ )
251
+ end
252
+ end
253
+ end
205
254
  end
206
255
 
207
256
  context "when wrapped in another transaction" do
@@ -227,7 +276,10 @@ if DependencyHelper.active_job_present?
227
276
  "metadata" => {},
228
277
  "sample_data" => hash_including(
229
278
  "params" => [],
230
- "tags" => { "queue" => queue }
279
+ "tags" => {
280
+ "active_job_id" => kind_of(String),
281
+ "queue" => queue
282
+ }
231
283
  )
232
284
  )
233
285
  events = transaction_hash["events"]
@@ -363,7 +415,10 @@ if DependencyHelper.active_job_present?
363
415
  "action" => "ActionMailerTestJob#welcome",
364
416
  "sample_data" => hash_including(
365
417
  "params" => ["ActionMailerTestJob", "welcome", "deliver_now"],
366
- "tags" => { "queue" => "mailers" }
418
+ "tags" => {
419
+ "active_job_id" => kind_of(String),
420
+ "queue" => "mailers"
421
+ }
367
422
  )
368
423
  )
369
424
  end
@@ -379,7 +434,10 @@ if DependencyHelper.active_job_present?
379
434
  "action" => "ActionMailerTestJob#welcome",
380
435
  "sample_data" => hash_including(
381
436
  "params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + method_expected_args,
382
- "tags" => { "queue" => "mailers" }
437
+ "tags" => {
438
+ "active_job_id" => kind_of(String),
439
+ "queue" => "mailers"
440
+ }
383
441
  )
384
442
  )
385
443
  end
@@ -396,7 +454,10 @@ if DependencyHelper.active_job_present?
396
454
  "action" => "ActionMailerTestJob#welcome",
397
455
  "sample_data" => hash_including(
398
456
  "params" => ["ActionMailerTestJob", "welcome", "deliver_now", parameterized_expected_args],
399
- "tags" => { "queue" => "mailers" }
457
+ "tags" => {
458
+ "active_job_id" => kind_of(String),
459
+ "queue" => "mailers"
460
+ }
400
461
  )
401
462
  )
402
463
  end
@@ -434,7 +495,10 @@ if DependencyHelper.active_job_present?
434
495
  "deliver_now",
435
496
  { active_job_internal_key => ["args"], "args" => [] }
436
497
  ],
437
- "tags" => { "queue" => "mailers" }
498
+ "tags" => {
499
+ "active_job_id" => kind_of(String),
500
+ "queue" => "mailers"
501
+ }
438
502
  )
439
503
  )
440
504
  end
@@ -457,7 +521,10 @@ if DependencyHelper.active_job_present?
457
521
  "args" => method_expected_args
458
522
  }
459
523
  ],
460
- "tags" => { "queue" => "mailers" }
524
+ "tags" => {
525
+ "active_job_id" => kind_of(String),
526
+ "queue" => "mailers"
527
+ }
461
528
  )
462
529
  )
463
530
  end
@@ -482,7 +549,10 @@ if DependencyHelper.active_job_present?
482
549
  "params" => parameterized_expected_args
483
550
  }
484
551
  ],
485
- "tags" => { "queue" => "mailers" }
552
+ "tags" => {
553
+ "active_job_id" => kind_of(String),
554
+ "queue" => "mailers"
555
+ }
486
556
  )
487
557
  )
488
558
  end
@@ -227,25 +227,14 @@ describe Appsignal::Hooks::DelayedJobHook do
227
227
  end
228
228
  end
229
229
 
230
- context "without job name" do
231
- let(:job_data) do
232
- { :name => "", :payload_object => payload_object }
233
- end
234
-
235
- it "wraps it in a transaction using the class method job name" do
236
- perform
237
- expect(last_transaction.to_h["action"]).to eql("unknown")
238
- end
239
- end
240
-
241
- context "with invalid job name" do
230
+ context "with only job class name" do
242
231
  let(:job_data) do
243
232
  { :name => "Banana", :payload_object => payload_object }
244
233
  end
245
234
 
246
- it "wraps it in a transaction using the class method job name" do
235
+ it "appends #perform to the class name" do
247
236
  perform
248
- expect(last_transaction.to_h["action"]).to eql("unknown")
237
+ expect(last_transaction.to_h["action"]).to eql("Banana#perform")
249
238
  end
250
239
  end
251
240
 
@@ -76,9 +76,10 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
76
76
  ]
77
77
  end
78
78
  let(:job_class) { "TestClass" }
79
+ let(:jid) { "b4a577edbccf1d805744efa9" }
79
80
  let(:item) do
80
81
  {
81
- "jid" => "b4a577edbccf1d805744efa9",
82
+ "jid" => jid,
82
83
  "class" => job_class,
83
84
  "retry_count" => 0,
84
85
  "queue" => "default",
@@ -151,7 +152,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
151
152
  context "when using the Sidekiq delayed extension" do
152
153
  let(:item) do
153
154
  {
154
- "jid" => "efb140489485999d32b5504c",
155
+ "jid" => jid,
155
156
  "class" => "Sidekiq::Extensions::DelayedClass",
156
157
  "queue" => "default",
157
158
  "args" => [
@@ -191,7 +192,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
191
192
  context "when using the Sidekiq ActiveRecord instance delayed extension" do
192
193
  let(:item) do
193
194
  {
194
- "jid" => "efb140489485999d32b5504c",
195
+ "jid" => jid,
195
196
  "class" => "Sidekiq::Extensions::DelayedModel",
196
197
  "queue" => "default",
197
198
  "args" => [
@@ -243,7 +244,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
243
244
 
244
245
  transaction_hash = transaction.to_h
245
246
  expect(transaction_hash).to include(
246
- "id" => kind_of(String),
247
+ "id" => jid,
247
248
  "action" => "TestClass#perform",
248
249
  "error" => {
249
250
  "name" => "ExampleException",
@@ -277,7 +278,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
277
278
 
278
279
  transaction_hash = transaction.to_h
279
280
  expect(transaction_hash).to include(
280
- "id" => kind_of(String),
281
+ "id" => jid,
281
282
  "action" => "TestClass#perform",
282
283
  "error" => nil,
283
284
  "metadata" => {
@@ -361,6 +362,7 @@ if DependencyHelper.active_job_present?
361
362
  end
362
363
  let(:expected_tags) do
363
364
  {}.tap do |hash|
365
+ hash["active_job_id"] = kind_of(String)
364
366
  if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
365
367
  hash["provider_job_id"] = kind_of(String)
366
368
  end
@@ -78,6 +78,63 @@ describe Appsignal::Hooks do
78
78
  expect(Appsignal::Hooks.hooks[:mock_error_hook].installed?).to be_falsy
79
79
  Appsignal::Hooks.hooks.delete(:mock_error_hook)
80
80
  end
81
+
82
+ describe "missing constants" do
83
+ let(:err_stream) { std_stream }
84
+ let(:stderr) { err_stream.read }
85
+ let(:log_stream) { std_stream }
86
+ let(:log) { log_contents(log_stream) }
87
+ before do
88
+ Appsignal.logger = test_logger(log_stream)
89
+ end
90
+
91
+ def call_constant(&block)
92
+ capture_std_streams(std_stream, err_stream, &block)
93
+ end
94
+
95
+ describe "SidekiqProbe" do
96
+ it "logs a deprecation message and returns the new constant" do
97
+ constant = call_constant { Appsignal::Hooks::SidekiqProbe }
98
+
99
+ expect(constant).to eql(Appsignal::Probes::SidekiqProbe)
100
+ expect(constant.name).to eql("Appsignal::Probes::SidekiqProbe")
101
+
102
+ deprecation_message =
103
+ "The constant Appsignal::Hooks::SidekiqProbe has been deprecated. " \
104
+ "Please update the constant name to Appsignal::Probes::SidekiqProbe " \
105
+ "in the following file to remove this message.\n#{__FILE__}:"
106
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
107
+ expect(log).to contains_log :warn, deprecation_message
108
+ end
109
+ end
110
+
111
+ describe "PumaProbe" do
112
+ it "logs a deprecation message and returns the new constant" do
113
+ constant = call_constant { Appsignal::Hooks::PumaProbe }
114
+
115
+ expect(constant).to eql(Appsignal::Probes::PumaProbe)
116
+ expect(constant.name).to eql("Appsignal::Probes::PumaProbe")
117
+
118
+ deprecation_message =
119
+ "The constant Appsignal::Hooks::PumaProbe has been deprecated. " \
120
+ "Please update the constant name to Appsignal::Probes::PumaProbe " \
121
+ "in the following file to remove this message.\n#{__FILE__}:"
122
+ expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
123
+ expect(log).to contains_log :warn, deprecation_message
124
+ end
125
+ end
126
+
127
+ describe "other constant" do
128
+ it "raises a NameError like Ruby normally does" do
129
+ expect do
130
+ call_constant { Appsignal::Hooks::Unknown }
131
+ end.to raise_error(NameError)
132
+
133
+ expect(stderr).to be_empty
134
+ expect(log).to be_empty
135
+ end
136
+ end
137
+ end
81
138
  end
82
139
 
83
140
  describe Appsignal::Hooks::Helpers do
@@ -4,7 +4,7 @@ describe Appsignal::Marker do
4
4
  described_class.new(
5
5
  {
6
6
  :revision => "503ce0923ed177a3ce000005",
7
- :repository => "master",
7
+ :repository => "main",
8
8
  :user => "batman",
9
9
  :rails_env => "production"
10
10
  },
@@ -65,6 +65,10 @@ RSpec.configure do |config|
65
65
 
66
66
  config.example_status_persistence_file_path = "spec/examples.txt"
67
67
  config.fail_if_no_examples = true
68
+ config.filter_run_excluding(
69
+ :extension_installation_failure => true,
70
+ :jruby => !DependencyHelper.running_jruby?
71
+ )
68
72
  config.mock_with :rspec do |mocks|
69
73
  mocks.syntax = :expect
70
74
  end
@@ -111,6 +115,7 @@ RSpec.configure do |config|
111
115
  # in the diagnose task, so add it manually to the list of to-be cleaned up
112
116
  # keys.
113
117
  env_keys << "_APPSIGNAL_DIAGNOSE"
118
+ env_keys << "_TEST_APPSIGNAL_EXTENSION_FAILURE"
114
119
  env_keys.each do |key|
115
120
  # First set the ENV var to an empty string and then delete the key from
116
121
  # the env. We set the env var to an empty string first as JRuby doesn't
@@ -5,12 +5,13 @@ module ConfigHelpers
5
5
  )
6
6
  end
7
7
 
8
- def project_fixture_config(env = "production", initial_config = {}, logger = Appsignal.logger)
8
+ def project_fixture_config(env = "production", initial_config = {}, logger = Appsignal.logger, config_file = nil)
9
9
  Appsignal::Config.new(
10
10
  project_fixture_path,
11
11
  env,
12
12
  initial_config,
13
- logger
13
+ logger,
14
+ config_file
14
15
  )
15
16
  end
16
17
 
@@ -37,6 +37,10 @@ module DependencyHelper
37
37
  dependency_present? "actioncable"
38
38
  end
39
39
 
40
+ def action_mailer_present?
41
+ dependency_present? "actionmailer"
42
+ end
43
+
40
44
  def active_job_present?
41
45
  dependency_present? "activejob"
42
46
  end
@@ -46,7 +46,7 @@ module TransactionHelpers
46
46
 
47
47
  # Set current transaction manually.
48
48
  # Cleared by {clear_current_transaction!}
49
- def set_current_transaction(transaction) # rubocop:disable Style/AccessorMethodName
49
+ def set_current_transaction(transaction) # rubocop:disable Naming/AccessorMethodName
50
50
  Thread.current[:appsignal_transaction] = transaction
51
51
  end
52
52
 
@@ -38,25 +38,9 @@ module Appsignal
38
38
  end
39
39
  end
40
40
 
41
- class Transaction
42
- class << self
43
- alias original_new new
44
-
45
- # Override the {Appsignal::Transaction.new} method so we can track which
46
- # transactions are created on the {Appsignal::Testing.transactions} list.
47
- #
48
- # @see TransactionHelpers#last_transaction
49
- def new(*args)
50
- transaction = original_new(*args)
51
- Appsignal::Testing.transactions << transaction
52
- transaction
53
- end
54
- end
55
- end
56
-
57
41
  class Extension
58
42
  class Transaction
59
- alias original_finish finish
43
+ alias original_finish finish if method_defined? :finish
60
44
 
61
45
  # Override default {Extension::Transaction#finish} behavior to always
62
46
  # return true, which tells the transaction to add its sample data (unless
@@ -72,7 +56,7 @@ module Appsignal
72
56
  return_value
73
57
  end
74
58
 
75
- alias original_complete complete
59
+ alias original_complete complete if method_defined? :complete
76
60
 
77
61
  # Override default {Extension::Transaction#complete} behavior to
78
62
  # store the transaction JSON before the transaction is completed
@@ -94,7 +78,7 @@ module Appsignal
94
78
  @completed || false
95
79
  end
96
80
 
97
- alias original_to_json to_json
81
+ alias original_to_json to_json if method_defined? :to_json
98
82
 
99
83
  # Override default {Extension::Transaction#to_json} behavior to
100
84
  # return the stored the transaction JSON when the transaction was
@@ -111,3 +95,19 @@ module Appsignal
111
95
  end
112
96
  end
113
97
  end
98
+
99
+ module AppsignalTest
100
+ module Transaction
101
+ # Override the {Appsignal::Transaction.new} method so we can track which
102
+ # transactions are created on the {Appsignal::Testing.transactions} list.
103
+ #
104
+ # @see TransactionHelpers#last_transaction
105
+ def new(*_args)
106
+ transaction = super
107
+ Appsignal::Testing.transactions << transaction
108
+ transaction
109
+ end
110
+ end
111
+ end
112
+
113
+ Appsignal::Transaction.extend(AppsignalTest::Transaction)