appsignal 2.9.18.beta.1 → 2.10.6.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -6
  3. data/.semaphore/semaphore.yml +964 -0
  4. data/CHANGELOG.md +48 -1
  5. data/README.md +1 -1
  6. data/Rakefile +82 -23
  7. data/appsignal.gemspec +5 -2
  8. data/build_matrix.yml +100 -52
  9. data/ext/agent.yml +19 -19
  10. data/gemfiles/capistrano2.gemfile +0 -5
  11. data/gemfiles/capistrano3.gemfile +0 -5
  12. data/gemfiles/grape.gemfile +0 -5
  13. data/gemfiles/no_dependencies.gemfile +0 -5
  14. data/gemfiles/padrino.gemfile +0 -6
  15. data/gemfiles/que.gemfile +0 -5
  16. data/gemfiles/que_beta.gemfile +0 -5
  17. data/gemfiles/rails-3.2.gemfile +0 -5
  18. data/gemfiles/rails-4.0.gemfile +0 -5
  19. data/gemfiles/rails-4.1.gemfile +0 -5
  20. data/gemfiles/rails-4.2.gemfile +0 -5
  21. data/gemfiles/resque.gemfile +0 -6
  22. data/lib/appsignal.rb +10 -7
  23. data/lib/appsignal/cli.rb +9 -2
  24. data/lib/appsignal/cli/diagnose.rb +21 -20
  25. data/lib/appsignal/cli/helpers.rb +22 -10
  26. data/lib/appsignal/cli/install.rb +2 -1
  27. data/lib/appsignal/cli/notify_of_deploy.rb +1 -1
  28. data/lib/appsignal/config.rb +23 -9
  29. data/lib/appsignal/event_formatter.rb +5 -6
  30. data/lib/appsignal/hooks/net_http.rb +13 -8
  31. data/lib/appsignal/integrations/grape.rb +2 -1
  32. data/lib/appsignal/integrations/net_http.rb +16 -0
  33. data/lib/appsignal/logger.rb +22 -0
  34. data/lib/appsignal/minutely.rb +5 -6
  35. data/lib/appsignal/rack/js_exception_catcher.rb +5 -0
  36. data/lib/appsignal/system.rb +6 -0
  37. data/lib/appsignal/transaction.rb +8 -3
  38. data/lib/appsignal/transmitter.rb +0 -1
  39. data/lib/appsignal/utils/deprecation_message.rb +2 -2
  40. data/lib/appsignal/version.rb +1 -1
  41. data/resources/cacert.pem +1404 -1804
  42. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  43. data/spec/lib/appsignal/cli/diagnose_spec.rb +54 -11
  44. data/spec/lib/appsignal/cli/helpers_spec.rb +11 -3
  45. data/spec/lib/appsignal/cli/install_spec.rb +30 -1
  46. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +4 -2
  47. data/spec/lib/appsignal/config_spec.rb +85 -14
  48. data/spec/lib/appsignal/event_formatter_spec.rb +5 -4
  49. data/spec/lib/appsignal/hooks/action_cable_spec.rb +1 -5
  50. data/spec/lib/appsignal/hooks/rake_spec.rb +41 -39
  51. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -15
  52. data/spec/lib/appsignal/integrations/grape_spec.rb +10 -0
  53. data/spec/lib/appsignal/integrations/object_spec.rb +2 -2
  54. data/spec/lib/appsignal/integrations/que_spec.rb +26 -39
  55. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +108 -46
  56. data/spec/lib/appsignal/integrations/resque_spec.rb +40 -39
  57. data/spec/lib/appsignal/logger_spec.rb +25 -0
  58. data/spec/lib/appsignal/minutely_spec.rb +7 -7
  59. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +23 -5
  60. data/spec/lib/appsignal/system_spec.rb +36 -0
  61. data/spec/lib/appsignal/transaction_spec.rb +37 -26
  62. data/spec/lib/appsignal/transmitter_spec.rb +3 -1
  63. data/spec/lib/appsignal_spec.rb +13 -10
  64. data/spec/spec_helper.rb +26 -12
  65. data/spec/support/fixtures/projects/broken/config/appsignal.yml +1 -0
  66. data/spec/support/helpers/cli_helpers.rb +15 -1
  67. data/spec/support/helpers/log_helpers.rb +13 -6
  68. data/spec/support/helpers/transaction_helpers.rb +53 -0
  69. data/spec/support/helpers/wait_for_helper.rb +2 -2
  70. data/spec/support/matchers/be_completed.rb +5 -0
  71. data/spec/support/matchers/have_colorized_text.rb +28 -0
  72. data/spec/support/testing.rb +113 -0
  73. data/support/check_versions +22 -0
  74. data/support/install_deps +9 -4
  75. metadata +32 -19
  76. data/.travis.yml +0 -149
@@ -54,16 +54,12 @@ describe Appsignal::Hooks::ActionCableHook do
54
54
  .with(transaction_id, Appsignal::Transaction::ACTION_CABLE, kind_of(ActionDispatch::Request))
55
55
  .and_return(transaction)
56
56
  allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
57
- # Make sure sample data is added
58
- expect(transaction.ext).to receive(:finish).and_return(true)
59
- # Stub complete call, stops it from being cleared in the extension
60
- # And allows us to call `#to_h` on it after it's been completed.
61
- expect(transaction.ext).to receive(:complete)
62
57
 
63
58
  # Stub transmit call for subscribe/unsubscribe tests
64
59
  allow(connection).to receive(:websocket)
65
60
  .and_return(instance_double("ActionCable::Connection::WebSocket", :transmit => nil))
66
61
  end
62
+ around { |example| keep_transactions { example.run } }
67
63
 
68
64
  describe "#perform_action" do
69
65
  it "creates a transaction for an action" do
@@ -4,69 +4,71 @@ describe Appsignal::Hooks::RakeHook do
4
4
  let(:task) { Rake::Task.new("task:name", Rake::Application.new) }
5
5
  let(:arguments) { Rake::TaskArguments.new(["foo"], ["bar"]) }
6
6
  let(:generic_request) { Appsignal::Transaction::GenericRequest.new({}) }
7
- before(:context) do
8
- Appsignal.config = project_fixture_config
9
- expect(Appsignal.active?).to be_truthy
10
- Appsignal::Hooks.load_hooks
11
- end
7
+ before(:context) { start_agent }
12
8
 
13
9
  describe "#execute" do
14
10
  context "without error" do
11
+ before { expect(Appsignal).to_not receive(:stop) }
12
+
13
+ def perform
14
+ task.execute(arguments)
15
+ end
16
+
15
17
  it "creates no transaction" do
16
- expect(Appsignal::Transaction).to_not receive(:create)
18
+ expect(Appsignal::Transaction).to_not receive(:new)
19
+ expect { perform }.to_not(change { created_transactions })
17
20
  end
18
21
 
19
22
  it "calls the original task" do
20
- expect(task).to receive(:execute_without_appsignal).with("foo")
23
+ expect(task).to receive(:execute_without_appsignal).with(arguments)
24
+ perform
21
25
  end
22
-
23
- after { task.execute("foo") }
24
26
  end
25
27
 
26
28
  context "with error" do
27
29
  let(:error) { ExampleException }
28
- let(:transaction) { background_job_transaction }
29
30
  before do
30
- task.enhance { raise error }
31
-
32
- expect(Appsignal::Transaction).to receive(:create).with(
33
- kind_of(String),
34
- Appsignal::Transaction::BACKGROUND_JOB,
35
- kind_of(Appsignal::Transaction::GenericRequest)
36
- ).and_return(transaction)
31
+ task.enhance { raise error, "my error message" }
32
+ # We don't call `and_call_original` here as we don't want AppSignal to
33
+ # stop and start for every spec.
34
+ expect(Appsignal).to receive(:stop).with("rake")
37
35
  end
38
36
 
39
- it "sets the action" do
40
- expect(transaction).to receive(:set_action).with("task:name")
41
- end
42
-
43
- it "sets the error" do
44
- expect(transaction).to receive(:set_error).with(error)
37
+ def perform
38
+ keep_transactions do
39
+ expect { task.execute(arguments) }.to raise_error(error)
40
+ end
45
41
  end
46
42
 
47
- it "completes the transaction and stops" do
48
- expect(transaction).to receive(:complete).ordered
49
- expect(Appsignal).to receive(:stop).with("rake").ordered
50
- end
43
+ it "creates a background job transaction" do
44
+ perform
51
45
 
52
- it "adds the task arguments to the request" do
53
- expect(Appsignal::Transaction::GenericRequest).to receive(:new)
54
- .with(:params => { :foo => "bar" })
55
- .and_return(generic_request)
46
+ expect(last_transaction).to be_completed
47
+ expect(last_transaction.to_h).to include(
48
+ "id" => kind_of(String),
49
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
50
+ "action" => "task:name",
51
+ "error" => {
52
+ "name" => "ExampleException",
53
+ "message" => "my error message",
54
+ "backtrace" => kind_of(String)
55
+ },
56
+ "sample_data" => hash_including(
57
+ "params" => { "foo" => "bar" }
58
+ )
59
+ )
56
60
  end
57
61
 
58
62
  context "when first argument is not a `Rake::TaskArguments`" do
59
63
  let(:arguments) { nil }
60
64
 
61
- it "adds the first argument regardless" do
62
- expect(Appsignal::Transaction::GenericRequest).to receive(:new)
63
- .with(:params => nil)
64
- .and_return(generic_request)
65
- end
66
- end
65
+ it "does not add the params to the transaction" do
66
+ perform
67
67
 
68
- after do
69
- expect { task.execute(arguments) }.to raise_error ExampleException
68
+ expect(last_transaction.to_h).to include(
69
+ "sample_data" => hash_excluding("params")
70
+ )
71
+ end
70
72
  end
71
73
  end
72
74
  end
@@ -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)
@@ -93,6 +93,16 @@ if DependencyHelper.grape_present?
93
93
  expect(transaction).to receive(:set_error).with(kind_of(ExampleException))
94
94
  end
95
95
 
96
+ context "with env['grape.skip_appsignal_error'] = true" do
97
+ before do
98
+ env["grape.skip_appsignal_error"] = true
99
+ end
100
+
101
+ it "does not add the error" do
102
+ expect(transaction).to_not receive(:set_error)
103
+ end
104
+ end
105
+
96
106
  after do
97
107
  expect { middleware.call(env) }.to raise_error ExampleException
98
108
  end
@@ -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