appsignal 2.11.8-java → 3.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/.rubocop_todo.yml +1 -1
  4. data/.semaphore/semaphore.yml +88 -111
  5. data/CHANGELOG.md +24 -0
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +11 -15
  8. data/lib/appsignal.rb +2 -29
  9. data/lib/appsignal/auth_check.rb +2 -8
  10. data/lib/appsignal/cli.rb +1 -23
  11. data/lib/appsignal/config.rb +1 -25
  12. data/lib/appsignal/event_formatter.rb +0 -25
  13. data/lib/appsignal/helpers/instrumentation.rb +69 -5
  14. data/lib/appsignal/hooks.rb +6 -13
  15. data/lib/appsignal/hooks/action_cable.rb +3 -34
  16. data/lib/appsignal/hooks/active_support_notifications.rb +7 -86
  17. data/lib/appsignal/hooks/celluloid.rb +5 -9
  18. data/lib/appsignal/hooks/net_http.rb +2 -12
  19. data/lib/appsignal/hooks/puma.rb +3 -5
  20. data/lib/appsignal/hooks/que.rb +1 -1
  21. data/lib/appsignal/hooks/rake.rb +2 -24
  22. data/lib/appsignal/hooks/redis.rb +2 -13
  23. data/lib/appsignal/hooks/resque.rb +2 -43
  24. data/lib/appsignal/hooks/sidekiq.rb +6 -143
  25. data/lib/appsignal/hooks/unicorn.rb +3 -24
  26. data/lib/appsignal/hooks/webmachine.rb +1 -7
  27. data/lib/appsignal/integrations/action_cable.rb +34 -0
  28. data/lib/appsignal/integrations/active_support_notifications.rb +77 -0
  29. data/lib/appsignal/integrations/net_http.rb +16 -0
  30. data/lib/appsignal/integrations/object.rb +39 -4
  31. data/lib/appsignal/integrations/padrino.rb +5 -7
  32. data/lib/appsignal/integrations/que.rb +26 -33
  33. data/lib/appsignal/integrations/railtie.rb +1 -4
  34. data/lib/appsignal/integrations/rake.rb +26 -2
  35. data/lib/appsignal/integrations/redis.rb +17 -0
  36. data/lib/appsignal/integrations/resque.rb +39 -10
  37. data/lib/appsignal/integrations/sidekiq.rb +171 -0
  38. data/lib/appsignal/integrations/unicorn.rb +28 -0
  39. data/lib/appsignal/integrations/webmachine.rb +22 -24
  40. data/lib/appsignal/minutely.rb +0 -12
  41. data/lib/appsignal/version.rb +1 -1
  42. data/spec/lib/appsignal/auth_check_spec.rb +1 -24
  43. data/spec/lib/appsignal/cli_spec.rb +1 -1
  44. data/spec/lib/appsignal/config_spec.rb +2 -66
  45. data/spec/lib/appsignal/event_formatter_spec.rb +0 -37
  46. data/spec/lib/appsignal/hooks/celluloid_spec.rb +6 -1
  47. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -2
  48. data/spec/lib/appsignal/hooks/redis_spec.rb +50 -15
  49. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +12 -464
  50. data/spec/lib/appsignal/hooks/unicorn_spec.rb +14 -3
  51. data/spec/lib/appsignal/hooks/webmachine_spec.rb +2 -13
  52. data/spec/lib/appsignal/hooks_spec.rb +6 -22
  53. data/spec/lib/appsignal/integrations/object_spec.rb +91 -8
  54. data/spec/lib/appsignal/integrations/padrino_spec.rb +2 -3
  55. data/spec/lib/appsignal/integrations/railtie_spec.rb +0 -45
  56. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +524 -0
  57. data/spec/lib/appsignal/integrations/webmachine_spec.rb +26 -8
  58. data/spec/lib/appsignal/minutely_spec.rb +0 -19
  59. data/spec/lib/appsignal/transaction_spec.rb +1 -14
  60. data/spec/lib/appsignal/transmitter_spec.rb +1 -1
  61. data/spec/lib/appsignal_spec.rb +162 -116
  62. data/spec/spec_helper.rb +1 -15
  63. metadata +11 -21
  64. data/lib/appsignal/cli/notify_of_deploy.rb +0 -131
  65. data/lib/appsignal/integrations/object_ruby_19.rb +0 -37
  66. data/lib/appsignal/integrations/object_ruby_modern.rb +0 -64
  67. data/lib/appsignal/integrations/resque_active_job.rb +0 -19
  68. data/lib/appsignal/js_exception_transaction.rb +0 -56
  69. data/lib/appsignal/rack/js_exception_catcher.rb +0 -80
  70. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +0 -180
  71. data/spec/lib/appsignal/integrations/object_19_spec.rb +0 -266
  72. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +0 -28
  73. data/spec/lib/appsignal/integrations/resque_spec.rb +0 -28
  74. data/spec/lib/appsignal/js_exception_transaction_spec.rb +0 -128
  75. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +0 -170
@@ -70,24 +70,17 @@ module Appsignal
70
70
  end
71
71
  end
72
72
 
73
- # Alias Probes constants that have moved to their own module in version
74
- # 2.11.0.
73
+ # Alias integration constants that have moved to their own module.
75
74
  def self.const_missing(name)
76
75
  case name
77
- when :SidekiqProbe
76
+ when :SidekiqPlugin
77
+ require "appsignal/integrations/sidekiq"
78
78
  callers = caller
79
79
  Appsignal::Utils::DeprecationMessage.message \
80
- "The constant Appsignal::Hooks::SidekiqProbe has been deprecated. " \
81
- "Please update the constant name to Appsignal::Probes::SidekiqProbe " \
80
+ "The constant Appsignal::Hooks::SidekiqPlugin has been deprecated. " \
81
+ "Please update the constant name to Appsignal::Integrations::SidekiqMiddleware " \
82
82
  "in the following file to remove this message.\n#{callers.first}"
83
- Appsignal::Probes::SidekiqProbe
84
- when :PumaProbe
85
- callers = caller
86
- Appsignal::Utils::DeprecationMessage.message \
87
- "The constant Appsignal::Hooks::PumaProbe has been deprecated. " \
88
- "Please update the constant name to Appsignal::Probes::PumaProbe " \
89
- "in the following file to remove this message.\n#{callers.first}"
90
- Appsignal::Probes::PumaProbe
83
+ Appsignal::Integrations::SidekiqMiddleware
91
84
  else
92
85
  super
93
86
  end
@@ -14,45 +14,14 @@ module Appsignal
14
14
  end
15
15
 
16
16
  def install
17
- patch_perform_action
17
+ require "appsignal/integrations/action_cable"
18
+ ActionCable::Channel::Base.send(:prepend, Appsignal::Integrations::ActionCableIntegration)
19
+
18
20
  install_callbacks
19
21
  end
20
22
 
21
23
  private
22
24
 
23
- def patch_perform_action
24
- ActionCable::Channel::Base.class_eval do
25
- alias_method :original_perform_action, :perform_action
26
-
27
- def perform_action(*args, &block)
28
- # The request is only the original websocket request
29
- env = connection.env
30
- request = ActionDispatch::Request.new(env)
31
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
32
- request.request_id || SecureRandom.uuid
33
-
34
- transaction = Appsignal::Transaction.create(
35
- env[Appsignal::Hooks::ActionCableHook::REQUEST_ID],
36
- Appsignal::Transaction::ACTION_CABLE,
37
- request
38
- )
39
-
40
- begin
41
- original_perform_action(*args, &block)
42
- rescue Exception => exception # rubocop:disable Lint/RescueException
43
- transaction.set_error(exception)
44
- raise exception
45
- ensure
46
- transaction.params = args.first
47
- transaction.set_action_if_nil("#{self.class}##{args.first["action"]}")
48
- transaction.set_metadata("path", request.path)
49
- transaction.set_metadata("method", "websocket")
50
- Appsignal::Transaction.complete_current!
51
- end
52
- end
53
- end
54
- end
55
-
56
25
  def install_callbacks
57
26
  ActionCable::Channel::Base.set_callback :subscribe, :around, :prepend => true do |channel, inner|
58
27
  # The request is only the original websocket request
@@ -6,8 +6,6 @@ module Appsignal
6
6
  class ActiveSupportNotificationsHook < Appsignal::Hooks::Hook
7
7
  register :active_support_notifications
8
8
 
9
- BANG = "!".freeze
10
-
11
9
  def dependencies_present?
12
10
  defined?(::ActiveSupport::Notifications::Instrumenter)
13
11
  end
@@ -23,100 +21,23 @@ module Appsignal
23
21
  end
24
22
  end
25
23
 
24
+ require "appsignal/integrations/active_support_notifications"
26
25
  instrumenter = ::ActiveSupport::Notifications::Instrumenter
27
-
26
+ parent_integration_module = Appsignal::Integrations::ActiveSupportNotificationsIntegration
28
27
  if instrumenter.method_defined?(:start) && instrumenter.method_defined?(:finish)
29
- install_start_finish
28
+ install_module(parent_integration_module::StartFinishIntegration)
30
29
  else
31
- install_instrument
30
+ install_module(parent_integration_module::InstrumentIntegration)
32
31
  end
33
32
 
34
33
  # rubocop:disable Style/GuardClause
35
34
  if instrumenter.method_defined?(:finish_with_state)
36
- install_finish_with_state
35
+ install_module(parent_integration_module::FinishStateIntegration)
37
36
  end
38
37
  end
39
38
 
40
- def install_instrument
41
- ::ActiveSupport::Notifications::Instrumenter.class_eval do
42
- alias instrument_without_appsignal instrument
43
-
44
- def instrument(name, payload = {}, &block)
45
- # Events that start with a bang are internal to Rails
46
- instrument_this = name[0] != BANG
47
-
48
- Appsignal::Transaction.current.start_event if instrument_this
49
-
50
- instrument_without_appsignal(name, payload, &block)
51
- ensure
52
- if instrument_this
53
- title, body, body_format = Appsignal::EventFormatter.format(name, payload)
54
- Appsignal::Transaction.current.finish_event(
55
- name.to_s,
56
- title,
57
- body,
58
- body_format
59
- )
60
- end
61
- end
62
- end
63
- end
64
-
65
- def install_start_finish
66
- ::ActiveSupport::Notifications::Instrumenter.class_eval do
67
- alias start_without_appsignal start
68
-
69
- def start(name, payload = {})
70
- # Events that start with a bang are internal to Rails
71
- instrument_this = name[0] != BANG
72
-
73
- Appsignal::Transaction.current.start_event if instrument_this
74
-
75
- start_without_appsignal(name, payload)
76
- end
77
-
78
- alias finish_without_appsignal finish
79
-
80
- def finish(name, payload = {})
81
- # Events that start with a bang are internal to Rails
82
- instrument_this = name[0] != BANG
83
-
84
- if instrument_this
85
- title, body, body_format = Appsignal::EventFormatter.format(name, payload)
86
- Appsignal::Transaction.current.finish_event(
87
- name.to_s,
88
- title,
89
- body,
90
- body_format
91
- )
92
- end
93
-
94
- finish_without_appsignal(name, payload)
95
- end
96
- end
97
- end
98
-
99
- def install_finish_with_state
100
- ::ActiveSupport::Notifications::Instrumenter.class_eval do
101
- alias finish_with_state_without_appsignal finish_with_state
102
-
103
- def finish_with_state(listeners_state, name, payload = {})
104
- # Events that start with a bang are internal to Rails
105
- instrument_this = name[0] != BANG
106
-
107
- if instrument_this
108
- title, body, body_format = Appsignal::EventFormatter.format(name, payload)
109
- Appsignal::Transaction.current.finish_event(
110
- name.to_s,
111
- title,
112
- body,
113
- body_format
114
- )
115
- end
116
-
117
- finish_with_state_without_appsignal(listeners_state, name, payload)
118
- end
119
- end
39
+ def install_module(mod)
40
+ ::ActiveSupport::Notifications::Instrumenter.send(:prepend, mod)
120
41
  end
121
42
  end
122
43
  end
@@ -16,16 +16,12 @@ module Appsignal
16
16
  # down Celluloid so we're sure our thread does not aggravate this situation.
17
17
  # This way we also make sure any outstanding transactions get flushed.
18
18
 
19
- ::Celluloid.class_eval do
20
- class << self
21
- alias shutdown_without_appsignal shutdown
22
-
23
- def shutdown
24
- Appsignal.stop("celluloid")
25
- shutdown_without_appsignal
26
- end
19
+ Celluloid.singleton_class.send(:prepend, Module.new do
20
+ def shutdown
21
+ Appsignal.stop("celluloid")
22
+ super
27
23
  end
28
- end
24
+ end)
29
25
  end
30
26
  end
31
27
  end
@@ -13,18 +13,8 @@ module Appsignal
13
13
  end
14
14
 
15
15
  def install
16
- Net::HTTP.class_eval do
17
- alias request_without_appsignal request
18
-
19
- def request(request, body = nil, &block)
20
- Appsignal.instrument(
21
- "request.net_http",
22
- "#{request.method} #{use_ssl? ? "https" : "http"}://#{request["host"] || address}"
23
- ) do
24
- request_without_appsignal(request, body, &block)
25
- end
26
- end
27
- end
16
+ require "appsignal/integrations/net_http"
17
+ Net::HTTP.send(:prepend, Appsignal::Integrations::NetHttpIntegration)
28
18
 
29
19
  Appsignal::Environment.report_enabled("net_http")
30
20
  end
@@ -29,14 +29,12 @@ module Appsignal
29
29
 
30
30
  return unless defined?(::Puma::Cluster)
31
31
  # For clustered mode with multiple workers
32
- ::Puma::Cluster.class_eval do
33
- alias stop_workers_without_appsignal stop_workers
34
-
32
+ ::Puma::Cluster.send(:prepend, Module.new do
35
33
  def stop_workers
36
34
  Appsignal.stop("puma cluster")
37
- stop_workers_without_appsignal
35
+ super
38
36
  end
39
- end
37
+ end)
40
38
  end
41
39
  end
42
40
  end
@@ -12,7 +12,7 @@ module Appsignal
12
12
 
13
13
  def install
14
14
  require "appsignal/integrations/que"
15
- ::Que::Job.send(:include, Appsignal::Integrations::QuePlugin)
15
+ ::Que::Job.send(:prepend, Appsignal::Integrations::QuePlugin)
16
16
 
17
17
  ::Que.error_notifier = proc do |error, _job|
18
18
  Appsignal::Transaction.current.set_error(error)
@@ -11,30 +11,8 @@ module Appsignal
11
11
  end
12
12
 
13
13
  def install
14
- ::Rake::Task.class_eval do
15
- alias :execute_without_appsignal :execute
16
-
17
- def execute(*args)
18
- execute_without_appsignal(*args)
19
- rescue Exception => error # rubocop:disable Lint/RescueException
20
- # Format given arguments and cast to hash if possible
21
- params, _ = args
22
- params = params.to_hash if params.respond_to?(:to_hash)
23
-
24
- transaction = Appsignal::Transaction.create(
25
- SecureRandom.uuid,
26
- Appsignal::Transaction::BACKGROUND_JOB,
27
- Appsignal::Transaction::GenericRequest.new(
28
- :params => params
29
- )
30
- )
31
- transaction.set_action(name)
32
- transaction.set_error(error)
33
- transaction.complete
34
- Appsignal.stop("rake")
35
- raise error
36
- end
37
- end
14
+ require "appsignal/integrations/rake"
15
+ ::Rake::Task.send(:prepend, Appsignal::Integrations::RakeIntegration)
38
16
  end
39
17
  end
40
18
  end
@@ -13,19 +13,8 @@ module Appsignal
13
13
  end
14
14
 
15
15
  def install
16
- ::Redis::Client.class_eval do
17
- alias process_without_appsignal process
18
-
19
- def process(commands, &block)
20
- sanitized_commands = commands.map do |command, *args|
21
- "#{command}#{" ?" * args.size}"
22
- end.join("\n")
23
-
24
- Appsignal.instrument "query.redis", id, sanitized_commands do
25
- process_without_appsignal(commands, &block)
26
- end
27
- end
28
- end
16
+ require "appsignal/integrations/redis"
17
+ ::Redis::Client.send(:prepend, Appsignal::Integrations::RedisIntegration)
29
18
 
30
19
  Appsignal::Environment.report_enabled("redis")
31
20
  end
@@ -11,49 +11,8 @@ module Appsignal
11
11
  end
12
12
 
13
13
  def install
14
- Resque::Job.class_eval do
15
- alias_method :perform_without_appsignal, :perform
16
-
17
- def perform
18
- transaction = Appsignal::Transaction.create(
19
- SecureRandom.uuid,
20
- Appsignal::Transaction::BACKGROUND_JOB,
21
- Appsignal::Transaction::GenericRequest.new({})
22
- )
23
-
24
- Appsignal.instrument "perform.resque" do
25
- perform_without_appsignal
26
- end
27
- rescue Exception => exception # rubocop:disable Lint/RescueException
28
- transaction.set_error(exception)
29
- raise exception
30
- ensure
31
- if transaction
32
- transaction.set_action_if_nil("#{payload["class"]}#perform")
33
- args =
34
- Appsignal::Utils::HashSanitizer.sanitize(
35
- ResqueHelpers.arguments(payload),
36
- Appsignal.config[:filter_parameters]
37
- )
38
- transaction.params = args if args
39
- transaction.set_tags("queue" => queue)
40
-
41
- Appsignal::Transaction.complete_current!
42
- end
43
- Appsignal.stop("resque")
44
- end
45
- end
46
- end
47
-
48
- class ResqueHelpers
49
- def self.arguments(payload)
50
- case payload["class"]
51
- when "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper"
52
- nil # Set in the ActiveJob integration
53
- else
54
- payload["args"]
55
- end
56
- end
14
+ require "appsignal/integrations/resque"
15
+ Resque::Job.send(:prepend, Appsignal::Integrations::ResqueIntegration)
57
16
  end
58
17
  end
59
18
  end
@@ -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,157 +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|
17
+ config.error_handlers << \
18
+ Appsignal::Integrations::SidekiqErrorHandler.new
19
+
18
20
  config.server_middleware do |chain|
19
21
  if chain.respond_to? :prepend
20
- chain.prepend Appsignal::Hooks::SidekiqPlugin
22
+ chain.prepend Appsignal::Integrations::SidekiqMiddleware
21
23
  else
22
- chain.add Appsignal::Hooks::SidekiqPlugin
24
+ chain.add Appsignal::Integrations::SidekiqMiddleware
23
25
  end
24
26
  end
25
27
  end
26
28
  end
27
29
  end
28
-
29
- # @api private
30
- class SidekiqPlugin # rubocop:disable Metrics/ClassLength
31
- include Appsignal::Hooks::Helpers
32
-
33
- EXCLUDED_JOB_KEYS = %w[
34
- args backtrace class created_at enqueued_at error_backtrace error_class
35
- error_message failed_at jid retried_at retry wrapped
36
- ].freeze
37
-
38
- def call(_worker, item, _queue)
39
- job_status = nil
40
- transaction = Appsignal::Transaction.create(
41
- item["jid"],
42
- Appsignal::Transaction::BACKGROUND_JOB,
43
- Appsignal::Transaction::GenericRequest.new(
44
- :queue_start => item["enqueued_at"]
45
- )
46
- )
47
-
48
- Appsignal.instrument "perform_job.sidekiq" do
49
- begin
50
- yield
51
- rescue Exception => exception # rubocop:disable Lint/RescueException
52
- job_status = :failed
53
- transaction.set_error(exception)
54
- raise exception
55
- end
56
- end
57
- ensure
58
- if transaction
59
- transaction.set_action_if_nil(formatted_action_name(item))
60
-
61
- params = filtered_arguments(item)
62
- transaction.params = params if params
63
-
64
- formatted_metadata(item).each do |key, value|
65
- transaction.set_metadata key, value
66
- end
67
- transaction.set_http_or_background_queue_start
68
- Appsignal::Transaction.complete_current!
69
- queue = item["queue"] || "unknown"
70
- if job_status
71
- increment_counter "queue_job_count", 1,
72
- :queue => queue,
73
- :status => job_status
74
- end
75
- increment_counter "queue_job_count", 1,
76
- :queue => queue,
77
- :status => :processed
78
- end
79
- end
80
-
81
- private
82
-
83
- def increment_counter(key, value, tags = {})
84
- Appsignal.increment_counter "sidekiq_#{key}", value, tags
85
- end
86
-
87
- def formatted_action_name(job)
88
- sidekiq_action_name = parse_action_name(job)
89
- return unless sidekiq_action_name
90
-
91
- complete_action = sidekiq_action_name =~ /\.|#/
92
- return sidekiq_action_name if complete_action
93
-
94
- "#{sidekiq_action_name}#perform"
95
- end
96
-
97
- def filtered_arguments(job)
98
- arguments = parse_arguments(job)
99
- return unless arguments
100
-
101
- Appsignal::Utils::HashSanitizer.sanitize(
102
- arguments,
103
- Appsignal.config[:filter_parameters]
104
- )
105
- end
106
-
107
- def formatted_metadata(item)
108
- {}.tap do |hash|
109
- (item || {}).each do |key, value|
110
- next if EXCLUDED_JOB_KEYS.include?(key)
111
-
112
- hash[key] = truncate(string_or_inspect(value))
113
- end
114
- end
115
- end
116
-
117
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L316-L334
118
- def parse_action_name(job)
119
- args = job.fetch("args", [])
120
- job_class = job["class"]
121
- case job_class
122
- when "Sidekiq::Extensions::DelayedModel"
123
- safe_load(args[0], job_class) do |target, method, _|
124
- "#{target.class}##{method}"
125
- end
126
- when /\ASidekiq::Extensions::Delayed/
127
- safe_load(args[0], job_class) do |target, method, _|
128
- "#{target}.#{method}"
129
- end
130
- else
131
- job_class
132
- end
133
- end
134
-
135
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L336-L358
136
- def parse_arguments(job)
137
- args = job.fetch("args", [])
138
- case job["class"]
139
- when /\ASidekiq::Extensions::Delayed/
140
- safe_load(args[0], args) do |_, _, arg|
141
- arg
142
- end
143
- when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
144
- nil # Set in the ActiveJob integration
145
- else
146
- # Sidekiq Enterprise argument encryption.
147
- # More information: https://github.com/mperham/sidekiq/wiki/Ent-Encryption
148
- if job["encrypt".freeze]
149
- # No point in showing 150+ bytes of random garbage
150
- args[-1] = "[encrypted data]".freeze
151
- end
152
- args
153
- end
154
- end
155
-
156
- # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L403-L412
157
- def safe_load(content, default)
158
- yield(*YAML.load(content))
159
- rescue => error
160
- # Sidekiq issue #1761: in dev mode, it's possible to have jobs enqueued
161
- # which haven't been loaded into memory yet so the YAML can't be
162
- # loaded.
163
- Appsignal.logger.warn "Unable to load YAML: #{error.message}"
164
- default
165
- end
166
- end
167
30
  end
168
31
  end