appsignal 3.0.0.beta.1 → 3.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -2,9 +2,29 @@
2
2
 
3
3
  # 3.0.0
4
4
  - Drop Ruby 1.9 support. PR #683, #682, #688, #694
5
+ - Require Ruby 2.0 or newer for gem. PR #701
5
6
  - Use Module.prepend for all gem integrations. Fixes #603 in combination with
6
7
  other gems that provide instrumentation for gems. PR #683
7
8
  - Remove deprecated integrations, classes, methods and arguments. PR #685
9
+ - Deprecate `set_error` and `send_error` error helpers `tags` and `namespace`
10
+ arguments. PR #702
11
+ - Add Sidekiq error handler. Report more Sidekiq errors that happen around job
12
+ execution. PR #699
13
+
14
+ # 2.11.9
15
+ - Fix and simplify Ruby method delegation for object method instrumentation in
16
+ the different Ruby versions. PR #706
17
+
18
+ # 2.11.8
19
+ - Mark minutely probe thread as fork-safe by @pixeltrix. PR #704
20
+
21
+ # 2.11.7
22
+ - Fix ActionCable integration in test environment using `stub_connection`.
23
+ PR #705
24
+
25
+ # 2.11.6
26
+ - Prepend Sidekiq middleware to wrap all Sidekiq middleware. Catches more
27
+ errors and provide more complete performance measurements. PR #698
8
28
 
9
29
  # 2.11.5
10
30
  - Add more detailed logging to finish_event calls when the event is unknown, so
data/appsignal.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem| # rubocop:disable Metrics/BlockLength
21
21
  gem.name = "appsignal"
22
22
  gem.require_paths = %w[lib ext]
23
23
  gem.version = Appsignal::VERSION
24
- gem.required_ruby_version = ">= 1.9"
24
+ gem.required_ruby_version = ">= 2.0"
25
25
  # Default extension installer. Overridden by JRuby gemspec as defined in
26
26
  # `Rakefile`.
27
27
  gem.extensions = %w[ext/extconf.rb]
data/build_matrix.yml CHANGED
@@ -58,7 +58,7 @@ semaphore: # Default `.semaphore/semaphore.yml` contents
58
58
  - name: Validate CI setup
59
59
  env_vars:
60
60
  - name: RUBY_VERSION
61
- value: 2.6.5
61
+ value: 2.6.6
62
62
  - name: GEMSET
63
63
  value: no_dependencies
64
64
  - name: BUNDLE_GEMFILE
@@ -72,7 +72,7 @@ semaphore: # Default `.semaphore/semaphore.yml` contents
72
72
  - name: RuboCop
73
73
  env_vars:
74
74
  - name: RUBY_VERSION
75
- value: 2.6.5
75
+ value: 2.6.6
76
76
  - name: GEMSET
77
77
  value: no_dependencies
78
78
  - name: BUNDLE_GEMFILE
@@ -107,12 +107,12 @@ matrix:
107
107
  gems: "none"
108
108
  - ruby: "2.3.8"
109
109
  gems: "none"
110
- - ruby: "2.4.9"
110
+ - ruby: "2.4.10"
111
111
  gems: "none"
112
- - ruby: "2.5.7"
112
+ - ruby: "2.5.8"
113
113
  gems: "minimal"
114
- - ruby: "2.6.5"
115
- - ruby: "2.7.1"
114
+ - ruby: "2.6.6"
115
+ - ruby: "2.7.2"
116
116
  - ruby: "3.0.0"
117
117
  - ruby: "jruby-9.1.17.0"
118
118
  gems: "minimal"
@@ -134,15 +134,15 @@ matrix:
134
134
  bundler: "1.17.3"
135
135
  exclude:
136
136
  ruby:
137
- - "2.6.5"
138
- - "2.7.1"
137
+ - "2.6.6"
138
+ - "2.7.2"
139
139
  - "3.0.0"
140
140
  - gem: "rails-4.2"
141
141
  bundler: "1.17.3"
142
142
  exclude:
143
143
  ruby:
144
- - "2.6.5"
145
- - "2.7.1"
144
+ - "2.6.6"
145
+ - "2.7.2"
146
146
  - "3.0.0"
147
147
  - gem: "rails-5.0"
148
148
  exclude:
@@ -166,7 +166,7 @@ matrix:
166
166
  - "2.1.10"
167
167
  - "2.2.10"
168
168
  - "2.3.8"
169
- - "2.4.9"
169
+ - "2.4.10"
170
170
  - "jruby-9.1.17.0"
171
171
  - gem: "resque-1"
172
172
  bundler: "1.17.3"
data/lib/appsignal.rb CHANGED
@@ -4,9 +4,9 @@ require "json"
4
4
  require "securerandom"
5
5
 
6
6
  require "appsignal/logger"
7
+ require "appsignal/utils/deprecation_message"
7
8
  require "appsignal/helpers/instrumentation"
8
9
  require "appsignal/helpers/metrics"
9
- require "appsignal/utils/deprecation_message"
10
10
 
11
11
  # AppSignal for Ruby gem's main module.
12
12
  #
@@ -18,7 +18,6 @@ module Appsignal
18
18
  class << self
19
19
  include Helpers::Instrumentation
20
20
  include Helpers::Metrics
21
- include Utils::DeprecationMessage
22
21
 
23
22
  # Accessor for the AppSignal configuration.
24
23
  # Return the current AppSignal configuration.
@@ -3,6 +3,8 @@
3
3
  module Appsignal
4
4
  module Helpers
5
5
  module Instrumentation # rubocop:disable Metrics/ModuleLength
6
+ include Appsignal::Utils::DeprecationMessage
7
+
6
8
  # Creates an AppSignal transaction for the given block.
7
9
  #
8
10
  # If AppSignal is not {.active?} it will still execute the block, but not
@@ -139,7 +141,10 @@ module Appsignal
139
141
  )
140
142
  yield
141
143
  rescue Exception => error # rubocop:disable Lint/RescueException
142
- send_error(error, tags, namespace)
144
+ send_error(error) do |transaction|
145
+ transaction.set_tags(tags) if tags
146
+ transaction.set_namespace(namespace) if namespace
147
+ end
143
148
  raise error
144
149
  end
145
150
  alias :listen_for_exception :listen_for_error
@@ -164,7 +169,7 @@ module Appsignal
164
169
  # Appsignal.send_error(e)
165
170
  # end
166
171
  #
167
- # @example Send an exception with tags
172
+ # @example Send an exception with tags. Deprecated method.
168
173
  # begin
169
174
  # raise "oh no!"
170
175
  # rescue => e
@@ -172,17 +177,20 @@ module Appsignal
172
177
  # end
173
178
  #
174
179
  # @example Add more metadata to transaction
175
- # Appsignal.send_error(e, :key => "value") do |transaction|
180
+ # Appsignal.send_error(e) do |transaction|
176
181
  # transaction.params(:search_query => params[:search_query])
177
182
  # transaction.set_action("my_action_name")
183
+ # transaction.set_tags(:key => "value")
178
184
  # transaction.set_namespace("my_namespace")
179
185
  # end
180
186
  #
181
187
  # @param error [Exception] The error to send to AppSignal.
182
188
  # @param tags [Hash{String, Symbol => String, Symbol, Integer}]
183
189
  # Additional tags to add to the error. See also {.tag_request}.
190
+ # This parameter is deprecated. Use the block argument instead.
184
191
  # @param namespace [String] The namespace in which the error occurred.
185
192
  # See also {.set_namespace}.
193
+ # This parameter is deprecated. Use the block argument instead.
186
194
  # @yield [transaction] yields block to allow modification of the
187
195
  # transaction before it's send.
188
196
  # @yieldparam transaction [Transaction] yields the AppSignal transaction
@@ -197,9 +205,30 @@ module Appsignal
197
205
  def send_error(
198
206
  error,
199
207
  tags = nil,
200
- namespace = Appsignal::Transaction::HTTP_REQUEST
208
+ namespace = nil
201
209
  )
210
+ if tags
211
+ call_location = caller(1..1).first
212
+ deprecation_message \
213
+ "The tags argument for `Appsignal.send_error` is deprecated. " \
214
+ "Please use the block method to set tags instead.\n\n" \
215
+ " Appsignal.send_error(error) do |transaction|\n" \
216
+ " transaction.set_tags(#{tags})\n" \
217
+ " end\n\n" \
218
+ "Appsignal.send_error called on location: #{call_location}"
219
+ end
220
+ if namespace
221
+ call_location = caller(1..1).first
222
+ deprecation_message \
223
+ "The namespace argument for `Appsignal.send_error` is deprecated. " \
224
+ "Please use the block method to set the namespace instead.\n\n" \
225
+ " Appsignal.send_error(error) do |transaction|\n" \
226
+ " transaction.namespace(#{namespace.inspect})\n" \
227
+ " end\n\n" \
228
+ "Appsignal.send_error called on location: #{call_location}"
229
+ end
202
230
  return unless active?
231
+
203
232
  unless error.is_a?(Exception)
204
233
  logger.error "Appsignal.send_error: Cannot send error. The given " \
205
234
  "value is not an exception: #{error.inspect}"
@@ -207,7 +236,7 @@ module Appsignal
207
236
  end
208
237
  transaction = Appsignal::Transaction.new(
209
238
  SecureRandom.uuid,
210
- namespace,
239
+ namespace || Appsignal::Transaction::HTTP_REQUEST,
211
240
  Appsignal::Transaction::GenericRequest.new({})
212
241
  )
213
242
  transaction.set_tags(tags) if tags
@@ -244,12 +273,26 @@ module Appsignal
244
273
  # end
245
274
  # end
246
275
  #
276
+ # @example Add more metadata to transaction
277
+ # Appsignal.set_error(e) do |transaction|
278
+ # transaction.params(:search_query => params[:search_query])
279
+ # transaction.set_action("my_action_name")
280
+ # transaction.set_tags(:key => "value")
281
+ # transaction.set_namespace("my_namespace")
282
+ # end
283
+ #
247
284
  # @param exception [Exception] The error to add to the current
248
285
  # transaction.
249
286
  # @param tags [Hash{String, Symbol => String, Symbol, Integer}]
250
287
  # Additional tags to add to the error. See also {.tag_request}.
288
+ # This parameter is deprecated. Use the block argument instead.
251
289
  # @param namespace [String] The namespace in which the error occurred.
252
290
  # See also {.set_namespace}.
291
+ # This parameter is deprecated. Use the block argument instead.
292
+ # @yield [transaction] yields block to allow modification of the
293
+ # transaction.
294
+ # @yieldparam transaction [Transaction] yields the AppSignal transaction
295
+ # used to store the error.
253
296
  # @return [void]
254
297
  #
255
298
  # @see Transaction#set_error
@@ -257,6 +300,26 @@ module Appsignal
257
300
  # Exception handling guide
258
301
  # @since 0.6.6
259
302
  def set_error(exception, tags = nil, namespace = nil)
303
+ if tags
304
+ call_location = caller(1..1).first
305
+ deprecation_message \
306
+ "The tags argument for `Appsignal.set_error` is deprecated. " \
307
+ "Please use the block method to set tags instead.\n\n" \
308
+ " Appsignal.set_error(error) do |transaction|\n" \
309
+ " transaction.set_tags(#{tags})\n" \
310
+ " end\n\n" \
311
+ "Appsignal.set_error called on location: #{call_location}"
312
+ end
313
+ if namespace
314
+ call_location = caller(1..1).first
315
+ deprecation_message \
316
+ "The namespace argument for `Appsignal.set_error` is deprecated. " \
317
+ "Please use the block method to set the namespace instead.\n\n" \
318
+ " Appsignal.set_error(error) do |transaction|\n" \
319
+ " transaction.namespace(#{namespace.inspect})\n" \
320
+ " end\n\n" \
321
+ "Appsignal.set_error called on location: #{call_location}"
322
+ end
260
323
  unless exception.is_a?(Exception)
261
324
  logger.error "Appsignal.set_error: Cannot set error. The given " \
262
325
  "value is not an exception: #{exception.inspect}"
@@ -267,6 +330,7 @@ module Appsignal
267
330
  transaction.set_error(exception)
268
331
  transaction.set_tags(tags) if tags
269
332
  transaction.set_namespace(namespace) if namespace
333
+ yield transaction if block_given?
270
334
  end
271
335
  alias :set_exception :set_error
272
336
  alias :add_exception :set_error
@@ -69,6 +69,22 @@ module Appsignal
69
69
  text.size > 200 ? "#{text[0...197]}..." : text
70
70
  end
71
71
  end
72
+
73
+ # Alias integration constants that have moved to their own module.
74
+ def self.const_missing(name)
75
+ case name
76
+ when :SidekiqPlugin
77
+ require "appsignal/integrations/sidekiq"
78
+ callers = caller
79
+ Appsignal::Utils::DeprecationMessage.message \
80
+ "The constant Appsignal::Hooks::SidekiqPlugin has been deprecated. " \
81
+ "Please update the constant name to Appsignal::Integrations::SidekiqMiddleware " \
82
+ "in the following file to remove this message.\n#{callers.first}"
83
+ Appsignal::Integrations::SidekiqMiddleware
84
+ else
85
+ super
86
+ end
87
+ end
72
88
  end
73
89
  end
74
90
 
@@ -25,7 +25,11 @@ module Appsignal
25
25
  def install_callbacks
26
26
  ActionCable::Channel::Base.set_callback :subscribe, :around, :prepend => true do |channel, inner|
27
27
  # The request is only the original websocket request
28
- env = channel.connection.env
28
+ connection = channel.connection
29
+ # #env is not available on the Rails ConnectionStub class used in the
30
+ # Rails app test suite. If we call `#env` it causes an error to occur
31
+ # in apps' test suites.
32
+ env = connection.respond_to?(:env) ? connection.env : {}
29
33
  request = ActionDispatch::Request.new(env)
30
34
  env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
31
35
  request.request_id || SecureRandom.uuid
@@ -53,7 +57,11 @@ module Appsignal
53
57
 
54
58
  ActionCable::Channel::Base.set_callback :unsubscribe, :around, :prepend => true do |channel, inner|
55
59
  # The request is only the original websocket request
56
- env = channel.connection.env
60
+ connection = channel.connection
61
+ # #env is not available on the Rails ConnectionStub class used in the
62
+ # Rails app test suite. If we call `#env` it causes an error to occur
63
+ # in apps' test suites.
64
+ env = connection.respond_to?(:env) ? connection.env : {}
57
65
  request = ActionDispatch::Request.new(env)
58
66
  env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
59
67
  request.request_id || SecureRandom.uuid
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "yaml"
4
-
5
3
  module Appsignal
6
4
  class Hooks
7
5
  class SidekiqHook < Appsignal::Hooks::Hook
@@ -12,153 +10,22 @@ module Appsignal
12
10
  end
13
11
 
14
12
  def install
13
+ require "appsignal/integrations/sidekiq"
15
14
  Appsignal::Minutely.probes.register :sidekiq, Appsignal::Probes::SidekiqProbe
16
15
 
17
16
  ::Sidekiq.configure_server do |config|
18
- config.server_middleware do |chain|
19
- chain.add Appsignal::Hooks::SidekiqPlugin
20
- end
21
- end
22
- end
23
- end
24
-
25
- # @api private
26
- class SidekiqPlugin # rubocop:disable Metrics/ClassLength
27
- include Appsignal::Hooks::Helpers
28
-
29
- EXCLUDED_JOB_KEYS = %w[
30
- args backtrace class created_at enqueued_at error_backtrace error_class
31
- error_message failed_at jid retried_at retry wrapped
32
- ].freeze
33
-
34
- def call(_worker, item, _queue)
35
- job_status = nil
36
- transaction = Appsignal::Transaction.create(
37
- item["jid"],
38
- Appsignal::Transaction::BACKGROUND_JOB,
39
- Appsignal::Transaction::GenericRequest.new(
40
- :queue_start => item["enqueued_at"]
41
- )
42
- )
43
-
44
- Appsignal.instrument "perform_job.sidekiq" do
45
- begin
46
- yield
47
- rescue Exception => exception # rubocop:disable Lint/RescueException
48
- job_status = :failed
49
- transaction.set_error(exception)
50
- raise exception
51
- end
52
- end
53
- ensure
54
- if transaction
55
- transaction.set_action_if_nil(formatted_action_name(item))
56
-
57
- params = filtered_arguments(item)
58
- transaction.params = params if params
59
-
60
- formatted_metadata(item).each do |key, value|
61
- transaction.set_metadata key, value
62
- end
63
- transaction.set_http_or_background_queue_start
64
- Appsignal::Transaction.complete_current!
65
- queue = item["queue"] || "unknown"
66
- if job_status
67
- increment_counter "queue_job_count", 1,
68
- :queue => queue,
69
- :status => job_status
70
- end
71
- increment_counter "queue_job_count", 1,
72
- :queue => queue,
73
- :status => :processed
74
- end
75
- end
76
-
77
- private
78
-
79
- def increment_counter(key, value, tags = {})
80
- Appsignal.increment_counter "sidekiq_#{key}", value, tags
81
- end
17
+ config.error_handlers << \
18
+ Appsignal::Integrations::SidekiqErrorHandler.new
82
19
 
83
- def formatted_action_name(job)
84
- sidekiq_action_name = parse_action_name(job)
85
- return unless sidekiq_action_name
86
-
87
- complete_action = sidekiq_action_name =~ /\.|#/
88
- return sidekiq_action_name if complete_action
89
-
90
- "#{sidekiq_action_name}#perform"
91
- end
92
-
93
- def filtered_arguments(job)
94
- arguments = parse_arguments(job)
95
- return unless arguments
96
-
97
- Appsignal::Utils::HashSanitizer.sanitize(
98
- arguments,
99
- Appsignal.config[:filter_parameters]
100
- )
101
- end
102
-
103
- def formatted_metadata(item)
104
- {}.tap do |hash|
105
- (item || {}).each do |key, value|
106
- next if EXCLUDED_JOB_KEYS.include?(key)
107
-
108
- hash[key] = truncate(string_or_inspect(value))
109
- end
110
- end
111
- end
112
-
113
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L316-L334
114
- def parse_action_name(job)
115
- args = job.fetch("args", [])
116
- job_class = job["class"]
117
- case job_class
118
- when "Sidekiq::Extensions::DelayedModel"
119
- safe_load(args[0], job_class) do |target, method, _|
120
- "#{target.class}##{method}"
121
- end
122
- when /\ASidekiq::Extensions::Delayed/
123
- safe_load(args[0], job_class) do |target, method, _|
124
- "#{target}.#{method}"
125
- end
126
- else
127
- job_class
128
- end
129
- end
130
-
131
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L336-L358
132
- def parse_arguments(job)
133
- args = job.fetch("args", [])
134
- case job["class"]
135
- when /\ASidekiq::Extensions::Delayed/
136
- safe_load(args[0], args) do |_, _, arg|
137
- arg
138
- end
139
- when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
140
- nil # Set in the ActiveJob integration
141
- else
142
- # Sidekiq Enterprise argument encryption.
143
- # More information: https://github.com/mperham/sidekiq/wiki/Ent-Encryption
144
- if job["encrypt".freeze]
145
- # No point in showing 150+ bytes of random garbage
146
- args[-1] = "[encrypted data]".freeze
20
+ config.server_middleware do |chain|
21
+ if chain.respond_to? :prepend
22
+ chain.prepend Appsignal::Integrations::SidekiqMiddleware
23
+ else
24
+ chain.add Appsignal::Integrations::SidekiqMiddleware
25
+ end
147
26
  end
148
- args
149
27
  end
150
28
  end
151
-
152
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L403-L412
153
- def safe_load(content, default)
154
- yield(*YAML.load(content))
155
- rescue => error
156
- # Sidekiq issue #1761: in dev mode, it's possible to have jobs enqueued
157
- # which haven't been loaded into memory yet so the YAML can't be
158
- # loaded.
159
- Appsignal.logger.warn "Unable to load YAML: #{error.message}"
160
- default
161
- end
162
29
  end
163
30
  end
164
31
  end