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

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.
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