appsignal 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +63 -21
  3. data/.rubocop_todo.yml +68 -54
  4. data/.semaphore/semaphore.yml +11 -11
  5. data/CHANGELOG.md +37 -0
  6. data/Rakefile +15 -99
  7. data/appsignal.gemspec +3 -4
  8. data/bin/appsignal +4 -2
  9. data/build_matrix.yml +4 -4
  10. data/ext/._appsignal-agent +0 -0
  11. data/ext/Rakefile +22 -21
  12. data/ext/agent.rb +2 -0
  13. data/ext/base.rb +14 -17
  14. data/ext/extconf.rb +4 -1
  15. data/lib/appsignal/auth_check.rb +3 -3
  16. data/lib/appsignal/capistrano.rb +1 -1
  17. data/lib/appsignal/cli/demo.rb +5 -2
  18. data/lib/appsignal/cli/diagnose/paths.rb +4 -1
  19. data/lib/appsignal/cli/diagnose/utils.rb +7 -3
  20. data/lib/appsignal/cli/diagnose.rb +7 -5
  21. data/lib/appsignal/cli/helpers.rb +1 -4
  22. data/lib/appsignal/cli/install.rb +4 -10
  23. data/lib/appsignal/cli.rb +3 -2
  24. data/lib/appsignal/config.rb +105 -102
  25. data/lib/appsignal/demo.rb +2 -1
  26. data/lib/appsignal/environment.rb +2 -0
  27. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +2 -1
  28. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +13 -13
  29. data/lib/appsignal/event_formatter.rb +5 -4
  30. data/lib/appsignal/extension/jruby.rb +11 -9
  31. data/lib/appsignal/extension.rb +1 -1
  32. data/lib/appsignal/helpers/instrumentation.rb +50 -35
  33. data/lib/appsignal/hooks/action_cable.rb +6 -4
  34. data/lib/appsignal/hooks/action_mailer.rb +2 -0
  35. data/lib/appsignal/hooks/active_job.rb +11 -10
  36. data/lib/appsignal/hooks/active_support_notifications.rb +3 -4
  37. data/lib/appsignal/hooks/data_mapper.rb +1 -1
  38. data/lib/appsignal/hooks/gvl.rb +3 -0
  39. data/lib/appsignal/hooks/http.rb +1 -1
  40. data/lib/appsignal/hooks/mri.rb +2 -0
  41. data/lib/appsignal/hooks/net_http.rb +1 -1
  42. data/lib/appsignal/hooks/que.rb +1 -1
  43. data/lib/appsignal/hooks/rake.rb +1 -1
  44. data/lib/appsignal/hooks/redis.rb +1 -1
  45. data/lib/appsignal/hooks/resque.rb +1 -1
  46. data/lib/appsignal/hooks/shoryuken.rb +2 -4
  47. data/lib/appsignal/hooks/sidekiq.rb +1 -1
  48. data/lib/appsignal/hooks/unicorn.rb +2 -2
  49. data/lib/appsignal/hooks/webmachine.rb +1 -1
  50. data/lib/appsignal/hooks.rb +2 -2
  51. data/lib/appsignal/integrations/active_support_notifications.rb +1 -1
  52. data/lib/appsignal/integrations/capistrano/appsignal.cap +6 -3
  53. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +5 -4
  54. data/lib/appsignal/integrations/delayed_job_plugin.rb +3 -5
  55. data/lib/appsignal/integrations/grape.rb +1 -1
  56. data/lib/appsignal/integrations/hanami.rb +1 -1
  57. data/lib/appsignal/integrations/object.rb +2 -3
  58. data/lib/appsignal/integrations/padrino.rb +2 -4
  59. data/lib/appsignal/integrations/que.rb +6 -6
  60. data/lib/appsignal/integrations/railtie.rb +72 -0
  61. data/lib/appsignal/integrations/sidekiq.rb +9 -11
  62. data/lib/appsignal/integrations/sinatra.rb +1 -3
  63. data/lib/appsignal/integrations/webmachine.rb +4 -6
  64. data/lib/appsignal/logger.rb +31 -6
  65. data/lib/appsignal/marker.rb +4 -5
  66. data/lib/appsignal/minutely.rb +7 -7
  67. data/lib/appsignal/probes/gvl.rb +9 -4
  68. data/lib/appsignal/probes/helpers.rb +4 -6
  69. data/lib/appsignal/probes/mri.rb +7 -5
  70. data/lib/appsignal/probes/sidekiq.rb +3 -0
  71. data/lib/appsignal/probes.rb +2 -0
  72. data/lib/appsignal/rack/generic_instrumentation.rb +1 -5
  73. data/lib/appsignal/rack/sinatra_instrumentation.rb +3 -5
  74. data/lib/appsignal/rack/streaming_listener.rb +11 -13
  75. data/lib/appsignal/span.rb +5 -5
  76. data/lib/appsignal/system.rb +10 -11
  77. data/lib/appsignal/transaction.rb +49 -25
  78. data/lib/appsignal/transmitter.rb +4 -2
  79. data/lib/appsignal/utils/deprecation_message.rb +2 -0
  80. data/lib/appsignal/utils/hash_sanitizer.rb +1 -1
  81. data/lib/appsignal/utils/integration_logger.rb +5 -3
  82. data/lib/appsignal/utils/json.rb +1 -1
  83. data/lib/appsignal/utils/query_params_sanitizer.rb +1 -1
  84. data/lib/appsignal/version.rb +1 -1
  85. data/lib/appsignal.rb +5 -4
  86. data/lib/puma/plugin/appsignal.rb +16 -18
  87. data/script/lint_git +1 -1
  88. data/spec/lib/appsignal/capistrano2_spec.rb +6 -3
  89. data/spec/lib/appsignal/capistrano3_spec.rb +6 -3
  90. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +1 -3
  91. data/spec/lib/appsignal/cli/diagnose_spec.rb +33 -30
  92. data/spec/lib/appsignal/cli/install_spec.rb +5 -6
  93. data/spec/lib/appsignal/cli_spec.rb +1 -1
  94. data/spec/lib/appsignal/config_spec.rb +43 -37
  95. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +11 -5
  96. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +4 -4
  97. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +1 -4
  98. data/spec/lib/appsignal/event_formatter_spec.rb +11 -9
  99. data/spec/lib/appsignal/hooks/action_cable_spec.rb +5 -2
  100. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +2 -1
  101. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +1 -1
  102. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +1 -1
  103. data/spec/lib/appsignal/hooks/activejob_spec.rb +21 -12
  104. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +1 -0
  105. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +12 -12
  106. data/spec/lib/appsignal/hooks/excon_spec.rb +2 -2
  107. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +3 -1
  108. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +4 -2
  109. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -1
  110. data/spec/lib/appsignal/hooks_spec.rb +5 -4
  111. data/spec/lib/appsignal/integrations/grape_spec.rb +8 -4
  112. data/spec/lib/appsignal/integrations/hanami_spec.rb +16 -8
  113. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +2 -4
  114. data/spec/lib/appsignal/integrations/object_spec.rb +6 -1
  115. data/spec/lib/appsignal/integrations/padrino_spec.rb +4 -2
  116. data/spec/lib/appsignal/integrations/railtie_spec.rb +213 -6
  117. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +54 -41
  118. data/spec/lib/appsignal/logger_spec.rb +20 -4
  119. data/spec/lib/appsignal/marker_spec.rb +2 -2
  120. data/spec/lib/appsignal/minutely_spec.rb +3 -3
  121. data/spec/lib/appsignal/probes/gvl_spec.rb +60 -12
  122. data/spec/lib/appsignal/probes/mri_spec.rb +7 -4
  123. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +2 -1
  124. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +2 -1
  125. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +10 -5
  126. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +7 -5
  127. data/spec/lib/appsignal/transaction_spec.rb +20 -13
  128. data/spec/lib/appsignal/utils/data_spec.rb +10 -1
  129. data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +11 -11
  130. data/spec/lib/appsignal/utils/json_spec.rb +4 -2
  131. data/spec/lib/appsignal_spec.rb +49 -35
  132. data/spec/lib/puma/appsignal_spec.rb +9 -11
  133. data/spec/spec_helper.rb +14 -2
  134. data/spec/support/fixtures/projects/valid/config/appsignal.yml +1 -1
  135. data/spec/support/helpers/config_helpers.rb +2 -1
  136. data/spec/support/helpers/dependency_helper.rb +1 -9
  137. data/spec/support/helpers/std_streams_helper.rb +1 -3
  138. data/spec/support/helpers/wait_for_helper.rb +2 -3
  139. data/spec/support/mocks/appsignal_mock.rb +1 -1
  140. data/spec/support/mocks/fake_gvl_tools.rb +2 -10
  141. data/spec/support/testing.rb +4 -3
  142. metadata +9 -135
@@ -153,7 +153,8 @@ if DependencyHelper.grape_present?
153
153
  end
154
154
 
155
155
  it "sets non-unique route_param path" do
156
- expect(transaction).to receive(:set_action_if_nil).with("GET::GrapeExample::Api#/users/:id/")
156
+ expect(transaction).to receive(:set_action_if_nil)
157
+ .with("GET::GrapeExample::Api#/users/:id/")
157
158
  expect(transaction).to receive(:set_metadata).with("path", "/users/:id/")
158
159
  expect(transaction).to receive(:set_metadata).with("method", "GET")
159
160
  end
@@ -177,7 +178,8 @@ if DependencyHelper.grape_present?
177
178
  end
178
179
 
179
180
  it "sets namespaced path" do
180
- expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
181
+ expect(transaction).to receive(:set_action_if_nil)
182
+ .with("POST::GrapeExample::Api#/v1/beta/ping")
181
183
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
182
184
  expect(transaction).to receive(:set_metadata).with("method", "POST")
183
185
  end
@@ -199,7 +201,8 @@ if DependencyHelper.grape_present?
199
201
  end
200
202
 
201
203
  it "sets namespaced path" do
202
- expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
204
+ expect(transaction).to receive(:set_action_if_nil)
205
+ .with("POST::GrapeExample::Api#/v1/beta/ping")
203
206
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
204
207
  expect(transaction).to receive(:set_metadata).with("method", "POST")
205
208
  end
@@ -220,7 +223,8 @@ if DependencyHelper.grape_present?
220
223
  end
221
224
 
222
225
  it "sets namespaced path" do
223
- expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
226
+ expect(transaction).to receive(:set_action_if_nil)
227
+ .with("POST::GrapeExample::Api#/v1/beta/ping")
224
228
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
225
229
  expect(transaction).to receive(:set_metadata).with("method", "POST")
226
230
  end
@@ -20,14 +20,16 @@ if DependencyHelper.hanami2_present?
20
20
  end
21
21
 
22
22
  it "prepends the integration to Hanami" do
23
- expect(::Hanami::Action).to receive(:send).with(:prepend, Appsignal::Integrations::HanamiIntegration)
23
+ expect(::Hanami::Action).to receive(:prepend)
24
+ .with(Appsignal::Integrations::HanamiIntegration)
24
25
  end
25
26
 
26
27
  context "when not active" do
27
28
  before { allow(Appsignal).to receive(:active?).and_return(false) }
28
29
 
29
30
  it "does not prepend the integration" do
30
- expect(::Hanami::Action).to_not receive(:send).with(:prepend, Appsignal::Integrations::HanamiIntegration)
31
+ expect(::Hanami::Action).to_not receive(:prepend)
32
+ .with(Appsignal::Integrations::HanamiIntegration)
31
33
  end
32
34
  end
33
35
 
@@ -71,17 +73,22 @@ if DependencyHelper.hanami2_present?
71
73
  end
72
74
 
73
75
  it "sets the action name" do
74
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil).with("HanamiApp::Actions::Books::Index")
76
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil)
77
+ .with("HanamiApp::Actions::Books::Index")
75
78
  end
76
79
 
77
80
  it "sets the metadata" do
78
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).with("status", "200")
79
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).with("path", "/books")
80
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).with("method", "GET")
81
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
82
+ .with("status", "200")
83
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
84
+ .with("path", "/books")
85
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
86
+ .with("method", "GET")
81
87
  end
82
88
 
83
89
  it "sets the queue start" do
84
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_http_or_background_queue_start)
90
+ expect_any_instance_of(Appsignal::Transaction)
91
+ .to receive(:set_http_or_background_queue_start)
85
92
  end
86
93
 
87
94
  context "with error", :error => true do
@@ -92,7 +99,8 @@ if DependencyHelper.hanami2_present?
92
99
  end
93
100
 
94
101
  it "sets the status to 500" do
95
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).with("status", "500")
102
+ expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata)
103
+ .with("status", "500")
96
104
  expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).twice
97
105
  end
98
106
  end
@@ -19,11 +19,8 @@ describe Appsignal::Hooks::MongoMonitorSubscriber do
19
19
  it "should sanitize command" do
20
20
  # TODO: additional curly brackets required for issue
21
21
  # https://github.com/rspec/rspec-mocks/issues/1460
22
- # rubocop:disable Style/BracesAroundHashParameters
23
22
  expect(Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter)
24
23
  .to receive(:format).with("find", { "foo" => "bar" })
25
- # rubocop:enable Style/BracesAroundHashParameters
26
-
27
24
  subscriber.started(event)
28
25
  end
29
26
 
@@ -107,7 +104,8 @@ describe Appsignal::Hooks::MongoMonitorSubscriber do
107
104
 
108
105
  context "without transaction" do
109
106
  before do
110
- allow(Appsignal::Transaction).to receive(:current).and_return(Appsignal::Transaction::NilTransaction.new)
107
+ allow(Appsignal::Transaction).to receive(:current)
108
+ .and_return(Appsignal::Transaction::NilTransaction.new)
111
109
  end
112
110
 
113
111
  it "should not attempt to start an event" do
@@ -25,7 +25,8 @@ describe Object do
25
25
  let(:transaction) { http_request_transaction }
26
26
  before do
27
27
  Appsignal.config = project_fixture_config
28
- expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
28
+ expect(Appsignal::Transaction).to receive(:current)
29
+ .at_least(:once).and_return(transaction)
29
30
  expect(Appsignal.active?).to be_truthy
30
31
  end
31
32
  after { Appsignal.config = nil }
@@ -43,9 +44,11 @@ describe Object do
43
44
  end
44
45
  appsignal_instrument_method :positional_arguments_splat
45
46
 
47
+ # rubocop:disable Naming/MethodParameterName
46
48
  def keyword_arguments(a: nil, b: nil)
47
49
  [a, b]
48
50
  end
51
+ # rubocop:enable Naming/MethodParameterName
49
52
  appsignal_instrument_method :keyword_arguments
50
53
 
51
54
  def keyword_arguments_splat(**kwargs)
@@ -213,9 +216,11 @@ describe Object do
213
216
  end
214
217
  appsignal_instrument_class_method :positional_arguments_splat
215
218
 
219
+ # rubocop:disable Naming/MethodParameterName
216
220
  def self.keyword_arguments(a: nil, b: nil)
217
221
  [a, b]
218
222
  end
223
+ # rubocop:enable Naming/MethodParameterName
219
224
  appsignal_instrument_class_method :keyword_arguments
220
225
 
221
226
  def self.keyword_arguments_splat(**kwargs)
@@ -283,7 +283,8 @@ if DependencyHelper.padrino_present?
283
283
 
284
284
  it "sets the action with the app name, controller name and action name" do
285
285
  expect_a_transaction_to_be_created
286
- expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:my_controller#index")
286
+ expect(transaction).to receive(:set_action_if_nil)
287
+ .with("PadrinoTestApp:my_controller#index")
287
288
  end
288
289
  end
289
290
 
@@ -295,7 +296,8 @@ if DependencyHelper.padrino_present?
295
296
 
296
297
  it "sets the action with the app name, controller name and action path" do
297
298
  expect_a_transaction_to_be_created
298
- expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:/my_controller#index")
299
+ expect(transaction).to receive(:set_action_if_nil)
300
+ .with("PadrinoTestApp:/my_controller#index")
299
301
  end
300
302
  end
301
303
  end
@@ -1,4 +1,6 @@
1
1
  if DependencyHelper.rails_present?
2
+ require "action_mailer"
3
+
2
4
  describe Appsignal::Integrations::Railtie do
3
5
  context "after initializing the app" do
4
6
  it "should call initialize_appsignal" do
@@ -10,11 +12,7 @@ if DependencyHelper.rails_present?
10
12
  end
11
13
 
12
14
  describe "#initialize_appsignal" do
13
- let(:app) { MyApp::Application }
14
- before do
15
- allow(app.middleware).to receive(:insert_before)
16
- allow(app.middleware).to receive(:insert_after)
17
- end
15
+ let(:app) { MyApp::Application.new }
18
16
 
19
17
  describe ".logger" do
20
18
  before { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
@@ -58,6 +56,52 @@ if DependencyHelper.rails_present?
58
56
  expect(config.env).to eq "env_test"
59
57
  end
60
58
  end
59
+
60
+ if Rails.respond_to?(:error)
61
+ context "when Rails listens to `error`" do
62
+ class ErrorReporterMock
63
+ attr_reader :subscribers
64
+
65
+ def initialize
66
+ @subscribers = []
67
+ end
68
+
69
+ def subscribe(subscriber)
70
+ @subscribers << subscriber
71
+ end
72
+ end
73
+
74
+ let(:error_reporter) { ErrorReporterMock.new }
75
+ before do
76
+ allow(Rails).to receive(:error).and_return(error_reporter)
77
+ end
78
+
79
+ context "when enable_rails_error_reporter is enabled" do
80
+ it "subscribes to the error reporter" do
81
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
82
+
83
+ expect(error_reporter.subscribers)
84
+ .to eq([Appsignal::Integrations::RailsErrorReporterSubscriber])
85
+ end
86
+ end
87
+
88
+ context "when enable_rails_error_reporter is disabled" do
89
+ it "does not subscribe to the error reporter" do
90
+ ENV["APPSIGNAL_ENABLE_RAILS_ERROR_REPORTER"] = "false"
91
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
92
+
93
+ expect(error_reporter.subscribers)
94
+ .to_not eq([Appsignal::Integrations::RailsErrorReporterSubscriber])
95
+ end
96
+ end
97
+ end
98
+ else
99
+ context "when Rails does not listen to `error`" do
100
+ it "does not error trying to subscribe to the error reporter" do
101
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
102
+ end
103
+ end
104
+ end
61
105
  end
62
106
 
63
107
  describe ".initial_config" do
@@ -75,9 +119,172 @@ if DependencyHelper.rails_present?
75
119
  ActionDispatch::DebugExceptions,
76
120
  Appsignal::Rack::RailsInstrumentation
77
121
  )
122
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
78
123
  end
124
+ end
125
+
126
+ if Rails.respond_to?(:error)
127
+ describe "Rails error reporter" do
128
+ before do
129
+ Appsignal::Integrations::Railtie.initialize_appsignal(app)
130
+ start_agent
131
+ end
132
+ around { |example| keep_transactions { example.run } }
133
+
134
+ context "when error is not handled (reraises the error)" do
135
+ it "does nothing" do
136
+ expect do
137
+ Rails.error.record { raise ExampleStandardError }
138
+ end.to raise_error(ExampleStandardError)
139
+
140
+ expect(created_transactions).to be_empty
141
+ end
142
+ end
79
143
 
80
- after { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
144
+ context "when error is handled (not reraised)" do
145
+ context "when a transaction is active" do
146
+ it "duplicates the transaction namespace, action and tags" do
147
+ current_transaction = http_request_transaction
148
+ current_transaction.set_namespace "custom"
149
+ current_transaction.set_action "CustomAction"
150
+ current_transaction.set_tags(
151
+ :duplicated_tag => "duplicated value"
152
+ )
153
+
154
+ with_current_transaction current_transaction do
155
+ Rails.error.handle { raise ExampleStandardError }
156
+
157
+ transaction = last_transaction
158
+ transaction_hash = transaction.to_h
159
+ expect(transaction_hash).to include(
160
+ "action" => "CustomAction",
161
+ "namespace" => "custom",
162
+ "error" => {
163
+ "name" => "ExampleStandardError",
164
+ "message" => "ExampleStandardError",
165
+ "backtrace" => kind_of(String)
166
+ },
167
+ "sample_data" => hash_including(
168
+ "tags" => {
169
+ "duplicated_tag" => "duplicated value",
170
+ "severity" => "warning"
171
+ }
172
+ )
173
+ )
174
+ end
175
+ end
176
+
177
+ it "overwrites duplicated tags with tags from context" do
178
+ current_transaction = http_request_transaction
179
+ current_transaction.set_tags(:tag1 => "duplicated value")
180
+
181
+ with_current_transaction current_transaction do
182
+ given_context = { :tag1 => "value1", :tag2 => "value2" }
183
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
184
+
185
+ transaction = last_transaction
186
+ transaction_hash = transaction.to_h
187
+ expect(transaction_hash).to include(
188
+ "sample_data" => hash_including(
189
+ "tags" => {
190
+ "tag1" => "value1",
191
+ "tag2" => "value2",
192
+ "severity" => "warning"
193
+ }
194
+ )
195
+ )
196
+ end
197
+ end
198
+
199
+ it "overwrites duplicated namespace and action with custom from context" do
200
+ current_transaction = http_request_transaction
201
+ current_transaction.set_namespace "custom"
202
+ current_transaction.set_action "CustomAction"
203
+
204
+ with_current_transaction current_transaction do
205
+ given_context = {
206
+ :appsignal => { :namespace => "context", :action => "ContextAction" }
207
+ }
208
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
209
+
210
+ transaction = last_transaction
211
+ transaction_hash = transaction.to_h
212
+ expect(transaction_hash).to include(
213
+ "namespace" => "context",
214
+ "action" => "ContextAction"
215
+ )
216
+ end
217
+ end
218
+ end
219
+
220
+ context "when no transaction is active" do
221
+ class ExampleRailsControllerMock
222
+ def action_name
223
+ "index"
224
+ end
225
+ end
226
+
227
+ class ExampleRailsJobMock
228
+ end
229
+
230
+ class ExampleRailsMailerMock < ActionMailer::MailDeliveryJob
231
+ def arguments
232
+ ["ExampleRailsMailerMock", "send_mail"]
233
+ end
234
+ end
235
+
236
+ before do
237
+ clear_current_transaction!
238
+ end
239
+
240
+ it "fetches the action from the controller in the context" do
241
+ # The controller key is set by Rails when raised in a controller
242
+ given_context = { :controller => ExampleRailsControllerMock.new }
243
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
244
+
245
+ transaction = last_transaction
246
+ transaction_hash = transaction.to_h
247
+ expect(transaction_hash).to include(
248
+ "action" => "ExampleRailsControllerMock#index"
249
+ )
250
+ end
251
+
252
+ it "sets no action if no execution context is present" do
253
+ # The controller key is set by Rails when raised in a controller
254
+ Rails.error.handle { raise ExampleStandardError }
255
+
256
+ transaction = last_transaction
257
+ transaction_hash = transaction.to_h
258
+ expect(transaction_hash).to include(
259
+ "action" => nil
260
+ )
261
+ end
262
+ end
263
+
264
+ it "sets the error context as tags" do
265
+ given_context = {
266
+ :controller => ExampleRailsControllerMock.new, # Not set as tag
267
+ :job => ExampleRailsJobMock.new, # Not set as tag
268
+ :appsignal => { :something => "not used" }, # Not set as tag
269
+ :tag1 => "value1",
270
+ :tag2 => "value2"
271
+ }
272
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
273
+
274
+ transaction = last_transaction
275
+ transaction_hash = transaction.to_h
276
+ expect(transaction_hash).to include(
277
+ "sample_data" => hash_including(
278
+ "tags" => {
279
+ "tag1" => "value1",
280
+ "tag2" => "value2",
281
+ "severity" => "warning"
282
+ }
283
+ )
284
+ )
285
+ end
286
+ end
287
+ end
81
288
  end
82
289
  end
83
290
  end
@@ -10,11 +10,9 @@ describe Appsignal::Integrations::SidekiqErrorHandler do
10
10
 
11
11
  context "without a current transction" do
12
12
  let(:exception) do
13
- begin
14
- raise ExampleStandardError, "uh oh"
15
- rescue => error
16
- error
17
- end
13
+ raise ExampleStandardError, "uh oh"
14
+ rescue => error
15
+ error
18
16
  end
19
17
  let(:job_context) do
20
18
  {
@@ -74,14 +72,14 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
74
72
  let(:jid) { "b4a577edbccf1d805744efa9" }
75
73
  let(:item) do
76
74
  {
77
- "jid" => jid,
78
- "class" => job_class,
75
+ "jid" => jid,
76
+ "class" => job_class,
79
77
  "retry_count" => 0,
80
- "queue" => "default",
81
- "created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
78
+ "queue" => "default",
79
+ "created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
82
80
  "enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
83
- "args" => given_args,
84
- "extra" => "data"
81
+ "args" => given_args,
82
+ "extra" => "data"
85
83
  }
86
84
  end
87
85
  let(:plugin) { Appsignal::Integrations::SidekiqMiddleware.new }
@@ -230,13 +228,10 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
230
228
  it "creates a transaction and adds the error" do
231
229
  # TODO: additional curly brackets required for issue
232
230
  # https://github.com/rspec/rspec-mocks/issues/1460
233
- # rubocop:disable Style/BracesAroundHashParameters
234
231
  expect(Appsignal).to receive(:increment_counter)
235
232
  .with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :failed })
236
233
  expect(Appsignal).to receive(:increment_counter)
237
234
  .with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :processed })
238
- # rubocop:enable Style/BracesAroundHashParameters
239
-
240
235
  expect do
241
236
  perform_job { raise error, "uh oh" }
242
237
  end.to raise_error(error)
@@ -269,15 +264,40 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
269
264
  end
270
265
  end
271
266
 
267
+ if DependencyHelper.rails7_present?
268
+ context "with Rails error reporter" do
269
+ it "reports the worker name as the action, copies the namespace and tags" do
270
+ Appsignal::Integrations::Railtie.initialize_appsignal(MyApp::Application.new)
271
+ Appsignal.config = project_fixture_config("production")
272
+ perform_job do
273
+ Appsignal.tag_job("test_tag" => "value")
274
+ Rails.error.handle do
275
+ raise error, "uh oh"
276
+ end
277
+ end
278
+
279
+ expect(created_transactions.count).to eq(2)
280
+ expected_transaction = {
281
+ "namespace" => "background_job",
282
+ "action" => "TestClass#perform",
283
+ "sample_data" => hash_including(
284
+ "tags" => hash_including("test_tag" => "value")
285
+ )
286
+ }
287
+ sidekiq_transaction = created_transactions.first.to_h
288
+ error_reporter_transaction = created_transactions.last.to_h
289
+ expect(sidekiq_transaction).to include(expected_transaction)
290
+ expect(error_reporter_transaction).to include(expected_transaction)
291
+ end
292
+ end
293
+ end
294
+
272
295
  context "without an error" do
273
296
  it "creates a transaction with events" do
274
297
  # TODO: additional curly brackets required for issue
275
298
  # https://github.com/rspec/rspec-mocks/issues/1460
276
- # rubocop:disable Style/BracesAroundHashParameters
277
299
  expect(Appsignal).to receive(:increment_counter)
278
300
  .with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :processed })
279
- # rubocop:enable Style/BracesAroundHashParameters
280
-
281
301
  perform_job
282
302
 
283
303
  transaction_hash = transaction.to_h
@@ -309,18 +329,14 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
309
329
 
310
330
  def perform_job
311
331
  Timecop.freeze(Time.parse("2001-01-01 10:01:00UTC")) do
312
- begin
313
- exception = nil
314
- plugin.call(worker, item, queue) do
315
- yield if block_given?
316
- end
317
- rescue Exception => exception # rubocop:disable Lint/RescueException
318
- raise exception
319
- ensure
320
- if exception
321
- Appsignal::Integrations::SidekiqErrorHandler.new.call(exception, :job => item)
322
- end
332
+ exception = nil
333
+ plugin.call(worker, item, queue) do
334
+ yield if block_given?
323
335
  end
336
+ rescue Exception => exception # rubocop:disable Lint/RescueException
337
+ raise exception
338
+ ensure
339
+ Appsignal::Integrations::SidekiqErrorHandler.new.call(exception, :job => item) if exception
324
340
  end
325
341
  end
326
342
 
@@ -516,7 +532,8 @@ if DependencyHelper.active_job_present?
516
532
  expect(transaction_hash).to include(
517
533
  "action" => "ActionMailerSidekiqTestJob#welcome",
518
534
  "sample_data" => hash_including(
519
- "params" => ["ActionMailerSidekiqTestJob", "welcome", "deliver_now"] + expected_wrapped_args
535
+ "params" => ["ActionMailerSidekiqTestJob", "welcome",
536
+ "deliver_now"] + expected_wrapped_args
520
537
  )
521
538
  )
522
539
  end
@@ -524,18 +541,14 @@ if DependencyHelper.active_job_present?
524
541
 
525
542
  def perform_sidekiq
526
543
  Timecop.freeze(time) do
527
- begin
528
- yield
529
- # Combined with Sidekiq::Testing.fake! and drain_all we get a
530
- # enqueue_at in the job data.
531
- Sidekiq::Worker.drain_all
532
- rescue Exception => exception # rubocop:disable Lint/RescueException
533
- raise exception
534
- ensure
535
- if exception
536
- Appsignal::Integrations::SidekiqErrorHandler.new.call(exception, {})
537
- end
538
- end
544
+ yield
545
+ # Combined with Sidekiq::Testing.fake! and drain_all we get a
546
+ # enqueue_at in the job data.
547
+ Sidekiq::Worker.drain_all
548
+ rescue Exception => exception # rubocop:disable Lint/RescueException
549
+ raise exception
550
+ ensure
551
+ Appsignal::Integrations::SidekiqErrorHandler.new.call(exception, {}) if exception
539
552
  end
540
553
  end
541
554
 
@@ -60,13 +60,31 @@ describe Appsignal::Logger do
60
60
  end
61
61
 
62
62
  it "should log with a level, message and group" do
63
- expect(Appsignal::Extension).to receive(:log)
64
- .with("other_group", 3, 0, "formatted: 'Log message'", instance_of(Appsignal::Extension::Data))
63
+ expect(Appsignal::Extension).to receive(:log).with(
64
+ "other_group",
65
+ 3,
66
+ 0,
67
+ "formatted: 'Log message'",
68
+ instance_of(Appsignal::Extension::Data)
69
+ )
65
70
  logger.add(::Logger::INFO, "Log message", "other_group")
66
71
  end
67
72
  end
68
73
  end
69
74
 
75
+ describe "#silence" do
76
+ it "calls the given block" do
77
+ num = 1
78
+
79
+ logger.silence do
80
+ num += 1
81
+ end
82
+
83
+ expect(num).to eq(2)
84
+ expect(Appsignal::Extension).not_to receive(:log)
85
+ end
86
+ end
87
+
70
88
  [
71
89
  ["debug", 2, ::Logger::INFO],
72
90
  ["info", 3, ::Logger::WARN],
@@ -76,11 +94,9 @@ describe Appsignal::Logger do
76
94
  ].each do |method|
77
95
  describe "##{method[0]}" do
78
96
  it "should log with a message" do
79
- # rubocop:disable Style/BracesAroundHashParameters
80
97
  expect(Appsignal::Utils::Data).to receive(:generate)
81
98
  .with({ :attribute => "value" })
82
99
  .and_call_original
83
- # rubocop:enable Style/BracesAroundHashParameters
84
100
  expect(Appsignal::Extension).to receive(:log)
85
101
  .with("group", method[1], 0, "Log message", instance_of(Appsignal::Extension::Data))
86
102
 
@@ -41,8 +41,8 @@ describe Appsignal::Marker do
41
41
  run
42
42
  expect(output).to include \
43
43
  "Notifying AppSignal of deploy with: revision: 503ce0923ed177a3ce000005, user: batman",
44
- "Something went wrong while trying to notify AppSignal: 500 at "\
45
- "#{config[:endpoint]}/1/markers"
44
+ "Something went wrong while trying to notify AppSignal: 500 at " \
45
+ "#{config[:endpoint]}/1/markers"
46
46
  expect(output).to_not include \
47
47
  "AppSignal has been notified of this deploy!"
48
48
  end
@@ -133,7 +133,7 @@ describe Appsignal::Minutely do
133
133
  "oh no initialize!"
134
134
  )
135
135
  # Start of the error backtrace as debug log
136
- expect(log).to contains_log :debug, File.expand_path("../../../../", __FILE__)
136
+ expect(log).to contains_log :debug, File.expand_path("../../..", __dir__)
137
137
  end
138
138
  end
139
139
  end
@@ -166,7 +166,7 @@ describe Appsignal::Minutely do
166
166
  expect(log).to contains_log(:debug, "Gathering minutely metrics with 'my_probe' probe")
167
167
  expect(log).to contains_log(:debug, "Gathering minutely metrics with 'broken_probe' probe")
168
168
  expect(log).to contains_log(:error, "Error in minutely probe 'broken_probe': oh no!")
169
- gem_path = File.expand_path("../../../../", __FILE__) # Start of backtrace
169
+ gem_path = File.expand_path("../../..", __dir__) # Start of backtrace
170
170
  expect(log).to contains_log(:debug, gem_path)
171
171
  end
172
172
  end
@@ -191,7 +191,7 @@ describe Appsignal::Minutely do
191
191
  # Fetch old thread
192
192
  thread = Appsignal::Minutely.instance_variable_get(:@thread)
193
193
  Appsignal::Minutely.start
194
- thread && thread.join # Wait for old thread to exit
194
+ thread&.join # Wait for old thread to exit
195
195
  end.to_not(change { alive_thread_counter.call })
196
196
  end
197
197
  end