appsignal 2.9.17 → 2.10.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -6
  3. data/CHANGELOG.md +24 -0
  4. data/Rakefile +16 -2
  5. data/ext/agent.yml +19 -19
  6. data/lib/appsignal/cli.rb +9 -2
  7. data/lib/appsignal/cli/diagnose.rb +20 -19
  8. data/lib/appsignal/cli/helpers.rb +22 -10
  9. data/lib/appsignal/cli/install.rb +2 -1
  10. data/lib/appsignal/config.rb +23 -9
  11. data/lib/appsignal/event_formatter.rb +4 -4
  12. data/lib/appsignal/minutely.rb +4 -4
  13. data/lib/appsignal/rack/js_exception_catcher.rb +6 -0
  14. data/lib/appsignal/transaction.rb +1 -1
  15. data/lib/appsignal/version.rb +1 -1
  16. data/spec/lib/appsignal/cli/diagnose_spec.rb +54 -11
  17. data/spec/lib/appsignal/cli/helpers_spec.rb +11 -3
  18. data/spec/lib/appsignal/cli/install_spec.rb +30 -1
  19. data/spec/lib/appsignal/config_spec.rb +78 -7
  20. data/spec/lib/appsignal/hooks/action_cable_spec.rb +1 -5
  21. data/spec/lib/appsignal/hooks/rake_spec.rb +41 -39
  22. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -15
  23. data/spec/lib/appsignal/integrations/object_spec.rb +2 -2
  24. data/spec/lib/appsignal/integrations/que_spec.rb +26 -39
  25. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +108 -46
  26. data/spec/lib/appsignal/integrations/resque_spec.rb +40 -39
  27. data/spec/lib/appsignal/minutely_spec.rb +3 -3
  28. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +19 -5
  29. data/spec/lib/appsignal/transaction_spec.rb +4 -12
  30. data/spec/lib/appsignal_spec.rb +7 -8
  31. data/spec/spec_helper.rb +11 -11
  32. data/spec/support/fixtures/projects/broken/config/appsignal.yml +1 -0
  33. data/spec/support/helpers/cli_helpers.rb +15 -1
  34. data/spec/support/helpers/transaction_helpers.rb +53 -0
  35. data/spec/support/matchers/be_completed.rb +5 -0
  36. data/spec/support/matchers/have_colorized_text.rb +28 -0
  37. data/spec/support/testing.rb +113 -0
  38. metadata +10 -2
@@ -38,28 +38,15 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
38
38
  }
39
39
  end
40
40
  let(:plugin) { Appsignal::Hooks::SidekiqPlugin.new }
41
- let(:test_store) { {} }
42
41
  let(:log) { StringIO.new }
43
42
  before do
44
43
  start_agent
45
44
  Appsignal.logger = test_logger(log)
46
-
47
- # Stub calls to extension, because that would remove the transaction
48
- # from the extension.
49
- allow_any_instance_of(Appsignal::Extension::Transaction).to receive(:finish).and_return(true)
50
- allow_any_instance_of(Appsignal::Extension::Transaction).to receive(:complete)
51
-
52
- # Stub removal of current transaction from current thread so we can fetch
53
- # it later.
54
- expect(Appsignal::Transaction).to receive(:clear_current_transaction!) do
55
- transaction = Thread.current[:appsignal_transaction]
56
- test_store[:transaction] = transaction if transaction
57
- end
58
45
  end
46
+ around { |example| keep_transactions { example.run } }
59
47
  after :with_yaml_parse_error => false do
60
48
  expect(log_contents(log)).to_not contains_log(:warn, "Unable to load YAML")
61
49
  end
62
- after { clear_current_transaction! }
63
50
 
64
51
  shared_examples "sidekiq metadata" do
65
52
  describe "internal Sidekiq job values" do
@@ -513,7 +500,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
513
500
  end
514
501
 
515
502
  def transaction
516
- test_store[:transaction]
503
+ last_transaction
517
504
  end
518
505
 
519
506
  def expect_transaction_to_have_sidekiq_event(transaction_hash)
@@ -16,8 +16,8 @@ describe Object do
16
16
  context "when active" do
17
17
  let(:transaction) { http_request_transaction }
18
18
  before do
19
- expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
20
19
  Appsignal.config = project_fixture_config
20
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
21
21
  end
22
22
  after { Appsignal.config = nil }
23
23
 
@@ -137,9 +137,9 @@ describe Object do
137
137
  context "when active" do
138
138
  let(:transaction) { http_request_transaction }
139
139
  before do
140
+ Appsignal.config = project_fixture_config
140
141
  expect(Appsignal::Transaction).to receive(:current).at_least(:once)
141
142
  .and_return(transaction)
142
- Appsignal.config = project_fixture_config
143
143
  end
144
144
  after { Appsignal.config = nil }
145
145
 
@@ -37,47 +37,30 @@ if DependencyHelper.que_present?
37
37
  end
38
38
  end
39
39
  let(:instance) { job.new(job_attrs) }
40
- let(:transaction) do
41
- Appsignal::Transaction.new(
42
- SecureRandom.uuid,
43
- Appsignal::Transaction::BACKGROUND_JOB,
44
- Appsignal::Transaction::GenericRequest.new(env)
45
- )
46
- end
47
40
 
48
41
  before do
49
42
  allow(Que).to receive(:execute)
50
43
 
51
44
  start_agent
52
45
  expect(Appsignal.active?).to be_truthy
53
- transaction
54
-
55
- expect(Appsignal::Transaction).to receive(:create)
56
- .with(
57
- kind_of(String),
58
- Appsignal::Transaction::BACKGROUND_JOB,
59
- kind_of(Appsignal::Transaction::GenericRequest)
60
- ).and_return(transaction)
61
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
62
- expect(transaction.ext).to receive(:finish).and_return(true)
63
- expect(transaction.ext).to receive(:complete)
64
46
  end
65
-
66
- subject { transaction.to_h }
47
+ around { |example| keep_transactions { example.run } }
67
48
 
68
49
  context "success" do
69
50
  it "creates a transaction for a job" do
70
51
  expect do
71
52
  instance._run
72
- end.to_not raise_exception
53
+ end.to change { created_transactions.length }.by(1)
73
54
 
74
- expect(subject).to include(
55
+ expect(last_transaction).to be_completed
56
+ transaction_hash = last_transaction.to_h
57
+ expect(transaction_hash).to include(
75
58
  "action" => "MyQueJob#run",
76
59
  "id" => instance_of(String),
77
60
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
78
61
  )
79
- expect(subject["error"]).to be_nil
80
- expect(subject["events"].first).to include(
62
+ expect(transaction_hash["error"]).to be_nil
63
+ expect(transaction_hash["events"].first).to include(
81
64
  "allocation_count" => kind_of(Integer),
82
65
  "body" => "",
83
66
  "body_format" => Appsignal::EventFormatter::DEFAULT,
@@ -91,7 +74,7 @@ if DependencyHelper.que_present?
91
74
  "name" => "perform_job.que",
92
75
  "title" => ""
93
76
  )
94
- expect(subject["sample_data"]).to include(
77
+ expect(transaction_hash["sample_data"]).to include(
95
78
  "params" => %w[1 birds],
96
79
  "metadata" => {
97
80
  "attempts" => 0,
@@ -107,24 +90,28 @@ if DependencyHelper.que_present?
107
90
  context "with exception" do
108
91
  let(:error) { ExampleException.new("oh no!") }
109
92
 
110
- it "should report exceptions and re-raise them" do
93
+ it "reports exceptions and re-raise them" do
111
94
  allow(instance).to receive(:run).and_raise(error)
112
95
 
113
96
  expect do
114
- instance._run
115
- end.to raise_error(ExampleException)
116
-
117
- expect(subject).to include(
97
+ expect do
98
+ instance._run
99
+ end.to raise_error(ExampleException)
100
+ end.to change { created_transactions.length }.by(1)
101
+
102
+ expect(last_transaction).to be_completed
103
+ transaction_hash = last_transaction.to_h
104
+ expect(transaction_hash).to include(
118
105
  "action" => "MyQueJob#run",
119
106
  "id" => instance_of(String),
120
107
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
121
108
  )
122
- expect(subject["error"]).to include(
109
+ expect(transaction_hash["error"]).to include(
123
110
  "backtrace" => kind_of(String),
124
111
  "name" => error.class.name,
125
112
  "message" => error.message
126
113
  )
127
- expect(subject["sample_data"]).to include(
114
+ expect(transaction_hash["sample_data"]).to include(
128
115
  "params" => %w[1 birds],
129
116
  "metadata" => {
130
117
  "attempts" => 0,
@@ -140,24 +127,24 @@ if DependencyHelper.que_present?
140
127
  context "with error" do
141
128
  let(:error) { ExampleStandardError.new("oh no!") }
142
129
 
143
- it "should report errors and not re-raise them" do
130
+ it "reports errors and not re-raise them" do
144
131
  allow(instance).to receive(:run).and_raise(error)
145
132
 
146
- expect do
147
- instance._run
148
- end.to_not raise_error
133
+ expect { instance._run }.to change { created_transactions.length }.by(1)
149
134
 
150
- expect(subject).to include(
135
+ expect(last_transaction).to be_completed
136
+ transaction_hash = last_transaction.to_h
137
+ expect(transaction_hash).to include(
151
138
  "action" => "MyQueJob#run",
152
139
  "id" => instance_of(String),
153
140
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
154
141
  )
155
- expect(subject["error"]).to include(
142
+ expect(transaction_hash["error"]).to include(
156
143
  "backtrace" => kind_of(String),
157
144
  "name" => error.class.name,
158
145
  "message" => error.message
159
146
  )
160
- expect(subject["sample_data"]).to include(
147
+ expect(transaction_hash["sample_data"]).to include(
161
148
  "params" => %w[1 birds],
162
149
  "metadata" => {
163
150
  "attempts" => 0,
@@ -17,67 +17,129 @@ if DependencyHelper.resque_present? && DependencyHelper.active_job_present?
17
17
  end
18
18
  end
19
19
 
20
- it "wraps it in a transaction with the correct params" do
21
- expect(Appsignal).to receive(:monitor_single_transaction).with(
22
- "perform_job.resque",
23
- :class => "TestActiveJob",
24
- :method => "perform",
25
- :params => ["argument"],
26
- :metadata => {
27
- :id => kind_of(String),
28
- :queue => "default"
29
- }
30
- )
20
+ def perform
21
+ keep_transactions do
22
+ job.perform_now
23
+ end
31
24
  end
32
25
 
33
- context "with complex arguments" do
34
- let(:args) do
35
- {
36
- :foo => "Foo",
37
- :bar => "Bar"
38
- }
26
+ context "without error" do
27
+ it "creates a new transaction" do
28
+ expect { perform }.to change { created_transactions.length }.by(1)
29
+
30
+ expect(last_transaction.to_h).to include(
31
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
32
+ "action" => "TestActiveJob#perform",
33
+ "error" => nil,
34
+ "events" => [
35
+ hash_including(
36
+ "name" => "perform_job.resque",
37
+ "title" => "",
38
+ "body" => "",
39
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
40
+ "count" => 1,
41
+ "duration" => kind_of(Float)
42
+ )
43
+ ],
44
+ "sample_data" => hash_including(
45
+ "params" => ["argument"],
46
+ "metadata" => {
47
+ "id" => kind_of(String),
48
+ "queue" => "default"
49
+ }
50
+ )
51
+ )
39
52
  end
53
+ end
54
+
55
+ context "with error" do
56
+ let(:job) do
57
+ class BrokenTestActiveJob < ActiveJob::Base
58
+ include Appsignal::Integrations::ResqueActiveJobPlugin
40
59
 
41
- it "truncates large argument values" do
42
- expect(Appsignal).to receive(:monitor_single_transaction).with(
43
- "perform_job.resque",
44
- :class => "TestActiveJob",
45
- :method => "perform",
46
- :params => [
60
+ def perform(_)
61
+ raise ExampleException, "my error message"
62
+ end
63
+ end
64
+
65
+ BrokenTestActiveJob.new(args)
66
+ end
67
+
68
+ it "creates a new transaction with an error" do
69
+ expect do
70
+ expect { perform }.to raise_error(ExampleException, "my error message")
71
+ end.to change { created_transactions.length }.by(1)
72
+
73
+ expect(last_transaction.to_h).to include(
74
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
75
+ "action" => "BrokenTestActiveJob#perform",
76
+ "error" => {
77
+ "name" => "ExampleException",
78
+ "message" => "my error message",
79
+ "backtrace" => kind_of(String)
80
+ },
81
+ "sample_data" => hash_including(
82
+ "params" => ["argument"],
83
+ "metadata" => {
84
+ "id" => kind_of(String),
85
+ "queue" => "default"
86
+ }
87
+ )
88
+ )
89
+ end
90
+ end
91
+
92
+ context "with complex arguments" do
93
+ context "with too long values" do
94
+ let(:args) do
95
+ {
47
96
  :foo => "Foo",
48
- :bar => "Bar"
49
- ],
50
- :metadata => {
51
- :id => kind_of(String),
52
- :queue => "default"
97
+ :bar => "a" * 2001
53
98
  }
54
- )
99
+ end
100
+
101
+ it "truncates large argument values" do
102
+ perform
103
+ expect(last_transaction.to_h).to include(
104
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
105
+ "action" => "TestActiveJob#perform",
106
+ "error" => nil,
107
+ "sample_data" => hash_including(
108
+ "params" => ["foo" => "Foo", "bar" => "#{"a" * 2000}..."],
109
+ "metadata" => {
110
+ "id" => kind_of(String),
111
+ "queue" => "default"
112
+ }
113
+ )
114
+ )
115
+ end
55
116
  end
56
117
 
57
118
  context "with parameter filtering" do
58
- before do
59
- Appsignal.config = project_fixture_config("production")
60
- Appsignal.config[:filter_parameters] = ["foo"]
119
+ let(:args) do
120
+ {
121
+ :foo => "Foo",
122
+ :bar => "Bar"
123
+ }
61
124
  end
125
+ before { Appsignal.config[:filter_parameters] = ["foo"] }
62
126
 
63
127
  it "filters selected arguments" do
64
- expect(Appsignal).to receive(:monitor_single_transaction).with(
65
- "perform_job.resque",
66
- :class => "TestActiveJob",
67
- :method => "perform",
68
- :params => [
69
- :foo => "[FILTERED]",
70
- :bar => "Bar"
71
- ],
72
- :metadata => {
73
- :id => kind_of(String),
74
- :queue => "default"
75
- }
128
+ perform
129
+ expect(last_transaction.to_h).to include(
130
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
131
+ "action" => "TestActiveJob#perform",
132
+ "error" => nil,
133
+ "sample_data" => hash_including(
134
+ "params" => ["foo" => "[FILTERED]", "bar" => "Bar"],
135
+ "metadata" => {
136
+ "id" => kind_of(String),
137
+ "queue" => "default"
138
+ }
139
+ )
76
140
  )
77
141
  end
78
142
  end
79
143
  end
80
-
81
- after { job.perform_now }
82
144
  end
83
145
  end
@@ -18,65 +18,66 @@ if DependencyHelper.resque_present?
18
18
  extend Appsignal::Integrations::ResquePlugin
19
19
 
20
20
  def self.perform
21
- raise ExampleException
21
+ raise ExampleException, "my error message"
22
22
  end
23
23
  end
24
24
  end
25
25
 
26
26
  describe :around_perform_resque_plugin do
27
- let(:transaction) { Appsignal::Transaction.new("1", "background", {}, {}) }
28
27
  let(:job) { ::Resque::Job.new("default", "class" => "TestJob") }
29
- before do
30
- allow(transaction).to receive(:complete).and_return(true)
31
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
32
- expect(Appsignal).to receive(:stop)
33
- end
28
+ before { expect(Appsignal).to receive(:stop) }
34
29
 
35
30
  context "without exception" do
36
31
  it "creates a new transaction" do
37
- expect(Appsignal::Transaction).to receive(:create).and_return(transaction)
38
- end
32
+ expect do
33
+ keep_transactions { job.perform }
34
+ end.to change { created_transactions.length }.by(1)
39
35
 
40
- it "wraps it in a transaction with the correct params" do
41
- expect(Appsignal).to receive(:monitor_transaction).with(
42
- "perform_job.resque",
43
- :class => "TestJob",
44
- :method => "perform"
36
+ expect(last_transaction).to be_completed
37
+ expect(last_transaction.to_h).to include(
38
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
39
+ "action" => "TestJob#perform",
40
+ "error" => nil,
41
+ "events" => [
42
+ hash_including(
43
+ "name" => "perform_job.resque",
44
+ "title" => "",
45
+ "body" => "",
46
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
47
+ "count" => 1,
48
+ "duration" => kind_of(Float)
49
+ )
50
+ ]
45
51
  )
46
52
  end
47
-
48
- it "closes the transaction" do
49
- expect(transaction).to receive(:complete)
50
- end
51
-
52
- after { job.perform }
53
53
  end
54
54
 
55
55
  context "with exception" do
56
56
  let(:job) { ::Resque::Job.new("default", "class" => "BrokenTestJob") }
57
- let(:transaction) do
58
- Appsignal::Transaction.new(
59
- SecureRandom.uuid,
60
- Appsignal::Transaction::BACKGROUND_JOB,
61
- Appsignal::Transaction::GenericRequest.new({})
62
- )
63
- end
64
- before do
65
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
66
- expect(Appsignal::Transaction).to receive(:create)
67
- .with(
68
- kind_of(String),
69
- Appsignal::Transaction::BACKGROUND_JOB,
70
- kind_of(Appsignal::Transaction::GenericRequest)
71
- ).and_return(transaction)
57
+
58
+ def perform
59
+ keep_transactions do
60
+ expect do
61
+ job.perform
62
+ end.to raise_error(ExampleException, "my error message")
63
+ end
72
64
  end
73
65
 
74
66
  it "sets the exception on the transaction" do
75
- expect(transaction).to receive(:set_error).with(ExampleException)
76
- end
67
+ expect do
68
+ perform
69
+ end.to change { created_transactions.length }.by(1)
77
70
 
78
- after do
79
- expect { job.perform }.to raise_error(ExampleException)
71
+ expect(last_transaction).to be_completed
72
+ expect(last_transaction.to_h).to include(
73
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
74
+ "action" => "BrokenTestJob#perform",
75
+ "error" => {
76
+ "name" => "ExampleException",
77
+ "message" => "my error message",
78
+ "backtrace" => kind_of(String)
79
+ }
80
+ )
80
81
  end
81
82
  end
82
83
  end