appsignal 3.4.4 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +1 -1
  4. data/.semaphore/semaphore.yml +683 -52
  5. data/CHANGELOG.md +353 -4
  6. data/README.md +3 -0
  7. data/Rakefile +4 -2
  8. data/appsignal.gemspec +1 -1
  9. data/build_matrix.yml +27 -13
  10. data/ext/Rakefile +8 -1
  11. data/ext/agent.rb +27 -27
  12. data/ext/appsignal_extension.c +0 -24
  13. data/ext/base.rb +5 -2
  14. data/gemfiles/dry-monitor.gemfile +5 -0
  15. data/gemfiles/rails-7.1.gemfile +7 -0
  16. data/gemfiles/redis-4.gemfile +5 -0
  17. data/gemfiles/redis-5.gemfile +6 -0
  18. data/lib/appsignal/auth_check.rb +1 -1
  19. data/lib/appsignal/cli/diagnose/paths.rb +33 -10
  20. data/lib/appsignal/cli/diagnose.rb +15 -1
  21. data/lib/appsignal/config.rb +72 -7
  22. data/lib/appsignal/demo.rb +1 -1
  23. data/lib/appsignal/environment.rb +24 -13
  24. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -1
  25. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +18 -0
  26. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +1 -1
  27. data/lib/appsignal/event_formatter.rb +2 -2
  28. data/lib/appsignal/extension/jruby.rb +4 -17
  29. data/lib/appsignal/extension.rb +1 -1
  30. data/lib/appsignal/heartbeat.rb +71 -0
  31. data/lib/appsignal/helpers/instrumentation.rb +10 -10
  32. data/lib/appsignal/helpers/metrics.rb +15 -13
  33. data/lib/appsignal/hooks/active_job.rb +9 -1
  34. data/lib/appsignal/hooks/active_support_notifications.rb +18 -9
  35. data/lib/appsignal/hooks/dry_monitor.rb +20 -0
  36. data/lib/appsignal/hooks/redis.rb +1 -0
  37. data/lib/appsignal/hooks/redis_client.rb +28 -0
  38. data/lib/appsignal/hooks.rb +4 -2
  39. data/lib/appsignal/integrations/active_support_notifications.rb +26 -0
  40. data/lib/appsignal/integrations/dry_monitor.rb +22 -0
  41. data/lib/appsignal/integrations/hanami.rb +1 -1
  42. data/lib/appsignal/integrations/padrino.rb +1 -1
  43. data/lib/appsignal/integrations/railtie.rb +28 -6
  44. data/lib/appsignal/integrations/redis_client.rb +20 -0
  45. data/lib/appsignal/integrations/sidekiq.rb +2 -2
  46. data/lib/appsignal/integrations/sinatra.rb +1 -1
  47. data/lib/appsignal/logger.rb +7 -5
  48. data/lib/appsignal/minutely.rb +4 -4
  49. data/lib/appsignal/probes/gvl.rb +1 -1
  50. data/lib/appsignal/probes/helpers.rb +1 -1
  51. data/lib/appsignal/probes/mri.rb +1 -1
  52. data/lib/appsignal/probes/sidekiq.rb +10 -8
  53. data/lib/appsignal/rack/generic_instrumentation.rb +1 -1
  54. data/lib/appsignal/rack/rails_instrumentation.rb +2 -2
  55. data/lib/appsignal/rack/sinatra_instrumentation.rb +5 -4
  56. data/lib/appsignal/rack/streaming_listener.rb +1 -1
  57. data/lib/appsignal/span.rb +2 -2
  58. data/lib/appsignal/transaction.rb +69 -14
  59. data/lib/appsignal/utils/deprecation_message.rb +2 -2
  60. data/lib/appsignal/utils/hash_sanitizer.rb +21 -9
  61. data/lib/appsignal/version.rb +1 -1
  62. data/lib/appsignal.rb +38 -31
  63. data/lib/puma/plugin/appsignal.rb +1 -1
  64. data/resources/cacert.pem +321 -159
  65. data/spec/lib/appsignal/capistrano2_spec.rb +2 -2
  66. data/spec/lib/appsignal/capistrano3_spec.rb +2 -2
  67. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +11 -0
  68. data/spec/lib/appsignal/cli/diagnose_spec.rb +70 -13
  69. data/spec/lib/appsignal/config_spec.rb +75 -18
  70. data/spec/lib/appsignal/environment_spec.rb +3 -3
  71. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +1 -1
  72. data/spec/lib/appsignal/event_formatter/rom/sql_formatter_spec.rb +22 -0
  73. data/spec/lib/appsignal/heartbeat_spec.rb +89 -0
  74. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +6 -0
  75. data/spec/lib/appsignal/hooks/activejob_spec.rb +26 -1
  76. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +104 -0
  77. data/spec/lib/appsignal/hooks/redis_client_spec.rb +238 -0
  78. data/spec/lib/appsignal/hooks/redis_spec.rb +98 -76
  79. data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
  80. data/spec/lib/appsignal/hooks_spec.rb +5 -5
  81. data/spec/lib/appsignal/integrations/railtie_spec.rb +128 -59
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +20 -15
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
  84. data/spec/lib/appsignal/minutely_spec.rb +2 -2
  85. data/spec/lib/appsignal/probes/sidekiq_spec.rb +29 -6
  86. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +1 -1
  87. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +163 -71
  88. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +1 -0
  89. data/spec/lib/appsignal/transaction_spec.rb +139 -10
  90. data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +42 -4
  91. data/spec/lib/appsignal_spec.rb +63 -61
  92. data/spec/lib/puma/appsignal_spec.rb +1 -1
  93. data/spec/spec_helper.rb +7 -7
  94. data/spec/support/fixtures/projects/valid/config/appsignal.yml +3 -3
  95. data/spec/support/helpers/config_helpers.rb +6 -2
  96. data/spec/support/helpers/dependency_helper.rb +13 -1
  97. data/spec/support/helpers/log_helpers.rb +2 -2
  98. data/spec/support/helpers/rails_helper.rb +28 -0
  99. data/spec/support/matchers/have_colorized_text.rb +1 -1
  100. metadata +19 -5
  101. data/ext/._appsignal-agent +0 -0
@@ -10,9 +10,9 @@ module Appsignal
10
10
  #
11
11
  # The value of the environment metadata is given as a block that captures
12
12
  # errors that might be raised while fetching the value. It will not
13
- # re-raise errors, but instead log them using the {Appsignal.logger}. This
14
- # ensures AppSignal will not cause an error in the application when
15
- # collecting this metadata.
13
+ # re-raise errors, but instead log them using the
14
+ # {Appsignal.internal_logger}. This ensures AppSignal will not cause an
15
+ # error in the application when collecting this metadata.
16
16
  #
17
17
  # @example Reporting a key and value
18
18
  # Appsignal::Environment.report("ruby_version") { RUBY_VERSION }
@@ -34,8 +34,8 @@ module Appsignal
34
34
  when String
35
35
  key
36
36
  else
37
- Appsignal.logger.error "Unable to report on environment metadata: " \
38
- "Unsupported value type for #{key.inspect}"
37
+ Appsignal.internal_logger.error "Unable to report on environment " \
38
+ "metadata: Unsupported value type for #{key.inspect}"
39
39
  return
40
40
  end
41
41
 
@@ -43,7 +43,7 @@ module Appsignal
43
43
  begin
44
44
  yield
45
45
  rescue => e
46
- Appsignal.logger.error \
46
+ Appsignal.internal_logger.error \
47
47
  "Unable to report on environment metadata #{key.inspect}:\n" \
48
48
  "#{e.class}: #{e}"
49
49
  return
@@ -56,26 +56,35 @@ module Appsignal
56
56
  when String
57
57
  yielded_value
58
58
  else
59
- Appsignal.logger.error "Unable to report on environment metadata " \
60
- "#{key.inspect}: Unsupported value type for " \
59
+ Appsignal.internal_logger.error "Unable to report on environment " \
60
+ "metadata #{key.inspect}: Unsupported value type for " \
61
61
  "#{yielded_value.inspect}"
62
62
  return
63
63
  end
64
64
 
65
65
  Appsignal::Extension.set_environment_metadata(key, value)
66
66
  rescue => e
67
- Appsignal.logger.error "Unable to report on environment metadata:\n" \
68
- "#{e.class}: #{e}"
67
+ Appsignal.internal_logger.error "Unable to report on environment " \
68
+ "metadata:\n#{e.class}: #{e}"
69
69
  end
70
70
 
71
71
  # @see report_supported_gems
72
72
  SUPPORTED_GEMS = %w[
73
73
  actioncable
74
+ actionmailer
74
75
  activejob
76
+ activerecord
75
77
  capistrano
76
78
  celluloid
77
79
  data_mapper
78
80
  delayed_job
81
+ dry-monitor
82
+ elasticsearch
83
+ excon
84
+ faraday
85
+ gvltools
86
+ hanami
87
+ hiredis
79
88
  mongo_ruby_driver
80
89
  padrino
81
90
  passenger
@@ -85,7 +94,9 @@ module Appsignal
85
94
  rails
86
95
  rake
87
96
  redis
97
+ redis-client
88
98
  resque
99
+ rom
89
100
  sequel
90
101
  shoryuken
91
102
  sidekiq
@@ -114,15 +125,15 @@ module Appsignal
114
125
  report("ruby_#{gem_name}_version") { gem_spec.version.to_s }
115
126
  end
116
127
  rescue => e
117
- Appsignal.logger.error "Unable to report supported gems:\n" \
128
+ Appsignal.internal_logger.error "Unable to report supported gems:\n" \
118
129
  "#{e.class}: #{e}"
119
130
  end
120
131
 
121
132
  def self.report_enabled(feature)
122
133
  Appsignal::Environment.report("ruby_#{feature}_enabled") { true }
123
134
  rescue => e
124
- Appsignal.logger.error "Unable to report integration enabled:\n" \
125
- "#{e.class}: #{e}"
135
+ Appsignal.internal_logger.error "Unable to report integration " \
136
+ "enabled:\n#{e.class}: #{e}"
126
137
  end
127
138
  end
128
139
  end
@@ -10,7 +10,7 @@ module Appsignal
10
10
  attr_reader :root_path
11
11
 
12
12
  def initialize
13
- @root_path = "#{Rails.root}/".freeze
13
+ @root_path = "#{Rails.root}/"
14
14
  end
15
15
 
16
16
  def format(payload)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class EventFormatter
5
+ module Rom
6
+ class SqlFormatter
7
+ def format(payload)
8
+ ["query.#{payload[:name]}", payload[:query], SQL_BODY_FORMAT]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Appsignal::EventFormatter.register(
16
+ "sql.dry",
17
+ Appsignal::EventFormatter::Rom::SqlFormatter
18
+ )
@@ -4,7 +4,7 @@ module Appsignal
4
4
  class EventFormatter
5
5
  # @api private
6
6
  module Sequel
7
- # Compatability with the sequel-rails gem.
7
+ # Compatibility with the sequel-rails gem.
8
8
  # The sequel-rails gem adds its own ActiveSupport::Notifications events
9
9
  # that conflict with our own sequel instrumentor. Without this event
10
10
  # formatter the sequel-rails events are recorded without the SQL query
@@ -73,7 +73,7 @@ module Appsignal
73
73
  end
74
74
 
75
75
  def logger
76
- Appsignal.logger
76
+ Appsignal.internal_logger
77
77
  end
78
78
  end
79
79
 
@@ -84,6 +84,6 @@ module Appsignal
84
84
  end
85
85
  end
86
86
 
87
- Dir.glob(File.expand_path("event_formatter/**/*.rb", __dir__)).each do |file|
87
+ Dir.glob(File.expand_path("event_formatter/**/*.rb", __dir__)).sort.each do |file|
88
88
  require file
89
89
  end
@@ -71,12 +71,6 @@ module Appsignal
71
71
  attach_function :appsignal_set_gauge,
72
72
  [:appsignal_string, :double, :pointer],
73
73
  :void
74
- attach_function :appsignal_set_host_gauge,
75
- [:appsignal_string, :double],
76
- :void
77
- attach_function :appsignal_set_process_gauge,
78
- [:appsignal_string, :double],
79
- :void
80
74
  attach_function :appsignal_increment_counter,
81
75
  [:appsignal_string, :double, :pointer],
82
76
  :void
@@ -248,14 +242,15 @@ module Appsignal
248
242
  [:pointer],
249
243
  :appsignal_string
250
244
 
251
- Appsignal.extension_loaded = true
245
+ Appsignal.extension_loaded = true if Appsignal.respond_to? :extension_loaded=
252
246
  rescue LoadError => error
253
247
  error_message = "ERROR: AppSignal failed to load extension. " \
254
248
  "Please run `appsignal diagnose` and email us at support@appsignal.com\n" \
255
249
  "#{error.class}: #{error.message}"
256
- Appsignal.logger.error(error_message)
250
+ Appsignal.internal_logger.error(error_message) if Appsignal.respond_to? :internal_logger
257
251
  Kernel.warn error_message
258
- Appsignal.extension_loaded = false
252
+ Appsignal.extension_loaded = false if Appsignal.respond_to? :extension_loaded=
253
+ raise error if ENV["_APPSIGNAL_EXTENSION_INSTALL"] == "true"
259
254
  end
260
255
 
261
256
  def start
@@ -319,14 +314,6 @@ module Appsignal
319
314
  appsignal_set_gauge(make_appsignal_string(key), value, tags.pointer)
320
315
  end
321
316
 
322
- def set_host_gauge(key, value)
323
- appsignal_set_host_gauge(make_appsignal_string(key), value)
324
- end
325
-
326
- def set_process_gauge(key, value)
327
- appsignal_set_process_gauge(make_appsignal_string(key), value)
328
- end
329
-
330
317
  def increment_counter(key, value, tags)
331
318
  appsignal_increment_counter(make_appsignal_string(key), value, tags.pointer)
332
319
  end
@@ -12,7 +12,7 @@ rescue LoadError => error
12
12
  error_message = "ERROR: AppSignal failed to load extension. " \
13
13
  "Please run `appsignal diagnose` and email us at support@appsignal.com\n" \
14
14
  "#{error.class}: #{error.message}"
15
- Appsignal.logger.error(error_message)
15
+ Appsignal.internal_logger.error(error_message)
16
16
  Kernel.warn error_message
17
17
  Appsignal.extension_loaded = false
18
18
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class Heartbeat
5
+ class << self
6
+ def transmitter
7
+ @transmitter ||= Appsignal::Transmitter.new(
8
+ "#{Appsignal.config[:logging_endpoint]}/heartbeats/json"
9
+ )
10
+ end
11
+ end
12
+
13
+ attr_reader :name, :id
14
+
15
+ def initialize(name:)
16
+ @name = name
17
+ @id = SecureRandom.hex(8)
18
+ end
19
+
20
+ def start
21
+ transmit_event("start")
22
+ end
23
+
24
+ def finish
25
+ transmit_event("finish")
26
+ end
27
+
28
+ private
29
+
30
+ def event(kind)
31
+ {
32
+ :name => name,
33
+ :id => @id,
34
+ :kind => kind,
35
+ :timestamp => Time.now.utc.to_i
36
+ }
37
+ end
38
+
39
+ def transmit_event(kind)
40
+ unless Appsignal.active?
41
+ Appsignal.internal_logger.debug("AppSignal not active, not transmitting heartbeat event")
42
+ return
43
+ end
44
+
45
+ response = self.class.transmitter.transmit(event(kind))
46
+
47
+ if response.code.to_i >= 200 && response.code.to_i < 300
48
+ Appsignal.internal_logger.trace("Transmitted heartbeat `#{name}` (#{id}) #{kind} event")
49
+ else
50
+ Appsignal.internal_logger.error(
51
+ "Failed to transmit heartbeat event: status code was #{response.code}"
52
+ )
53
+ end
54
+ rescue => e
55
+ Appsignal.internal_logger.error("Failed to transmit heartbeat event: #{e}")
56
+ end
57
+ end
58
+
59
+ def self.heartbeat(name)
60
+ heartbeat = Appsignal::Heartbeat.new(:name => name)
61
+ output = nil
62
+
63
+ if block_given?
64
+ heartbeat.start
65
+ output = yield
66
+ end
67
+
68
+ heartbeat.finish
69
+ output
70
+ end
71
+ end
@@ -7,8 +7,8 @@ module Appsignal
7
7
 
8
8
  # Creates an AppSignal transaction for the given block.
9
9
  #
10
- # If AppSignal is not {.active?} it will still execute the block, but not
11
- # create a transaction for it.
10
+ # If AppSignal is not {Appsignal.active?} it will still execute the
11
+ # block, but not create a transaction for it.
12
12
  #
13
13
  # A event is created for this transaction with the name given in the
14
14
  # `name` argument. The event name must start with either `perform_job` or
@@ -68,9 +68,9 @@ module Appsignal
68
68
  namespace = Appsignal::Transaction::HTTP_REQUEST
69
69
  request = ::Rack::Request.new(env)
70
70
  else
71
- logger.error "Unrecognized name '#{name}': names must start with " \
72
- "either 'perform_job' (for jobs and tasks) or 'process_action' " \
73
- "(for HTTP requests)"
71
+ internal_logger.error "Unrecognized name '#{name}': names must " \
72
+ "start with either 'perform_job' (for jobs and tasks) or " \
73
+ "'process_action' (for HTTP requests)"
74
74
  return yield
75
75
  end
76
76
 
@@ -112,7 +112,7 @@ module Appsignal
112
112
  # transaction. Does not add the error to the current transaction.
113
113
  #
114
114
  # Make sure that AppSignal is integrated in your application beforehand.
115
- # AppSignal won't record errors unless {Config#active?} is `true`.
115
+ # AppSignal won't record errors unless {Appsignal.active?} is `true`.
116
116
  #
117
117
  # @example
118
118
  # # my_app.rb
@@ -228,8 +228,8 @@ module Appsignal
228
228
  return unless active?
229
229
 
230
230
  unless error.is_a?(Exception)
231
- logger.error "Appsignal.send_error: Cannot send error. The given " \
232
- "value is not an exception: #{error.inspect}"
231
+ internal_logger.error "Appsignal.send_error: Cannot send error. " \
232
+ "The given value is not an exception: #{error.inspect}"
233
233
  return
234
234
  end
235
235
  transaction = Appsignal::Transaction.new(
@@ -319,8 +319,8 @@ module Appsignal
319
319
  "Appsignal.set_error called on location: #{call_location}"
320
320
  end
321
321
  unless exception.is_a?(Exception)
322
- logger.error "Appsignal.set_error: Cannot set error. The given " \
323
- "value is not an exception: #{exception.inspect}"
322
+ internal_logger.error "Appsignal.set_error: Cannot set error. " \
323
+ "The given value is not an exception: #{exception.inspect}"
324
324
  return
325
325
  end
326
326
  return if !active? || !Appsignal::Transaction.current?
@@ -10,22 +10,24 @@ module Appsignal
10
10
  Appsignal::Utils::Data.generate(tags)
11
11
  )
12
12
  rescue RangeError
13
- Appsignal.logger
13
+ Appsignal.internal_logger
14
14
  .warn("Gauge value #{value} for key '#{key}' is too big")
15
15
  end
16
16
 
17
- def set_host_gauge(key, value)
18
- Appsignal::Extension.set_host_gauge(key.to_s, value.to_f)
19
- rescue RangeError
20
- Appsignal.logger
21
- .warn("Host gauge value #{value} for key '#{key}' is too big")
17
+ def set_host_gauge(_key, _value)
18
+ Appsignal::Utils::DeprecationMessage.message \
19
+ "The `set_host_gauge` method has been deprecated. " \
20
+ "Calling this method has no effect. " \
21
+ "Please remove method call in the following file to remove " \
22
+ "this message.\n#{caller.first}"
22
23
  end
23
24
 
24
- def set_process_gauge(key, value)
25
- Appsignal::Extension.set_process_gauge(key.to_s, value.to_f)
26
- rescue RangeError
27
- Appsignal.logger
28
- .warn("Process gauge value #{value} for key '#{key}' is too big")
25
+ def set_process_gauge(_key, _value)
26
+ Appsignal::Utils::DeprecationMessage.message \
27
+ "The `set_process_gauge` method has been deprecated. " \
28
+ "Calling this method has no effect. " \
29
+ "Please remove method call in the following file to remove " \
30
+ "this message.\n#{caller.first}"
29
31
  end
30
32
 
31
33
  def increment_counter(key, value = 1.0, tags = {})
@@ -35,7 +37,7 @@ module Appsignal
35
37
  Appsignal::Utils::Data.generate(tags)
36
38
  )
37
39
  rescue RangeError
38
- Appsignal.logger
40
+ Appsignal.internal_logger
39
41
  .warn("Counter value #{value} for key '#{key}' is too big")
40
42
  end
41
43
 
@@ -46,7 +48,7 @@ module Appsignal
46
48
  Appsignal::Utils::Data.generate(tags)
47
49
  )
48
50
  rescue RangeError
49
- Appsignal.logger
51
+ Appsignal.internal_logger
50
52
  .warn("Distribution value #{value} for key '#{key}' is too big")
51
53
  end
52
54
  end
@@ -56,7 +56,7 @@ module Appsignal
56
56
  super
57
57
  rescue Exception => exception # rubocop:disable Lint/RescueException
58
58
  job_status = :failed
59
- transaction.set_error(exception)
59
+ transaction_set_error(transaction, exception)
60
60
  raise exception
61
61
  ensure
62
62
  if transaction
@@ -82,6 +82,14 @@ module Appsignal
82
82
  tags.merge(:status => :processed)
83
83
  end
84
84
  end
85
+
86
+ private
87
+
88
+ def transaction_set_error(transaction, exception)
89
+ return if Appsignal.config[:activejob_report_errors] == "none"
90
+
91
+ transaction.set_error(exception)
92
+ end
85
93
  end
86
94
 
87
95
  module ActiveJobHelpers
@@ -22,21 +22,30 @@ module Appsignal
22
22
  end
23
23
 
24
24
  require "appsignal/integrations/active_support_notifications"
25
- instrumenter = ::ActiveSupport::Notifications::Instrumenter
26
25
  parent_integration_module = Appsignal::Integrations::ActiveSupportNotificationsIntegration
27
- if instrumenter.method_defined?(:start) && instrumenter.method_defined?(:finish)
28
- install_module(parent_integration_module::StartFinishIntegration)
26
+
27
+ if defined?(::ActiveSupport::Notifications::Fanout::Handle)
28
+ install_module(
29
+ parent_integration_module::StartFinishHandlerIntegration,
30
+ ::ActiveSupport::Notifications::Fanout::Handle
31
+ )
29
32
  else
30
- install_module(parent_integration_module::InstrumentIntegration)
31
- end
33
+ instrumenter = ::ActiveSupport::Notifications::Instrumenter
32
34
 
33
- return unless instrumenter.method_defined?(:finish_with_state)
35
+ if instrumenter.method_defined?(:start) && instrumenter.method_defined?(:finish)
36
+ install_module(parent_integration_module::StartFinishIntegration, instrumenter)
37
+ else
38
+ install_module(parent_integration_module::InstrumentIntegration, instrumenter)
39
+ end
34
40
 
35
- install_module(parent_integration_module::FinishStateIntegration)
41
+ return unless instrumenter.method_defined?(:finish_with_state)
42
+
43
+ install_module(parent_integration_module::FinishStateIntegration, instrumenter)
44
+ end
36
45
  end
37
46
 
38
- def install_module(mod)
39
- ::ActiveSupport::Notifications::Instrumenter.send(:prepend, mod)
47
+ def install_module(mod, instrumenter)
48
+ instrumenter.send(:prepend, mod)
40
49
  end
41
50
  end
42
51
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class Hooks
5
+ # @api private
6
+ class DryMonitorHook < Appsignal::Hooks::Hook
7
+ register :dry_monitor
8
+
9
+ def dependencies_present?
10
+ defined?(::Dry::Monitor::Notifications)
11
+ end
12
+
13
+ def install
14
+ require "appsignal/integrations/dry_monitor"
15
+
16
+ ::Dry::Monitor::Notifications.prepend(Appsignal::Integrations::DryMonitorIntegration)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -8,6 +8,7 @@ module Appsignal
8
8
 
9
9
  def dependencies_present?
10
10
  defined?(::Redis) &&
11
+ !defined?(::RedisClient) &&
11
12
  Appsignal.config &&
12
13
  Appsignal.config[:instrument_redis]
13
14
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class Hooks
5
+ # @api private
6
+ class RedisClientHook < Appsignal::Hooks::Hook
7
+ register :redis_client
8
+
9
+ def dependencies_present?
10
+ defined?(::RedisClient) &&
11
+ Gem::Version.new(::RedisClient::VERSION) >= Gem::Version.new("0.14.0") &&
12
+ Appsignal.config &&
13
+ Appsignal.config[:instrument_redis]
14
+ end
15
+
16
+ def install
17
+ require "appsignal/integrations/redis_client"
18
+ ::RedisClient::RubyConnection.prepend Appsignal::Integrations::RedisClientIntegration
19
+ Appsignal::Environment.report_enabled("redis")
20
+
21
+ return unless defined?(::RedisClient::HiredisConnection)
22
+
23
+ ::RedisClient::HiredisConnection.prepend Appsignal::Integrations::RedisClientIntegration
24
+ Appsignal::Environment.report_enabled("hiredis")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -32,12 +32,12 @@ module Appsignal
32
32
  return unless dependencies_present?
33
33
  return if installed?
34
34
 
35
- Appsignal.logger.debug("Installing #{name} hook")
35
+ Appsignal.internal_logger.debug("Installing #{name} hook")
36
36
  begin
37
37
  install
38
38
  @installed = true
39
39
  rescue => ex
40
- logger = Appsignal.logger
40
+ logger = Appsignal.internal_logger
41
41
  logger.error("Error while installing #{name} hook: #{ex}")
42
42
  logger.debug ex.backtrace.join("\n")
43
43
  end
@@ -95,6 +95,7 @@ require "appsignal/hooks/active_support_notifications"
95
95
  require "appsignal/hooks/celluloid"
96
96
  require "appsignal/hooks/delayed_job"
97
97
  require "appsignal/hooks/gvl"
98
+ require "appsignal/hooks/dry_monitor"
98
99
  require "appsignal/hooks/http"
99
100
  require "appsignal/hooks/mri"
100
101
  require "appsignal/hooks/net_http"
@@ -102,6 +103,7 @@ require "appsignal/hooks/passenger"
102
103
  require "appsignal/hooks/puma"
103
104
  require "appsignal/hooks/rake"
104
105
  require "appsignal/hooks/redis"
106
+ require "appsignal/hooks/redis_client"
105
107
  require "appsignal/hooks/resque"
106
108
  require "appsignal/hooks/sequel"
107
109
  require "appsignal/hooks/shoryuken"
@@ -54,6 +54,32 @@ module Appsignal
54
54
  end
55
55
  end
56
56
 
57
+ module StartFinishHandlerIntegration
58
+ def start
59
+ instrument_this = @name[0] != ActiveSupportNotificationsIntegration::BANG
60
+
61
+ Appsignal::Transaction.current.start_event if instrument_this
62
+ super
63
+ end
64
+
65
+ def finish_with_values(name, id, payload = {})
66
+ # Events that start with a bang are internal to Rails
67
+ instrument_this = name[0] != ActiveSupportNotificationsIntegration::BANG
68
+
69
+ if instrument_this
70
+ title, body, body_format = Appsignal::EventFormatter.format(name, payload)
71
+ Appsignal::Transaction.current.finish_event(
72
+ name.to_s,
73
+ title,
74
+ body,
75
+ body_format
76
+ )
77
+ end
78
+
79
+ super
80
+ end
81
+ end
82
+
57
83
  module FinishStateIntegration
58
84
  def finish_with_state(listeners_state, name, payload = {})
59
85
  # Events that start with a bang are internal to Rails
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ module DryMonitorIntegration
6
+ def instrument(event_id, payload = {}, &block)
7
+ Appsignal::Transaction.current.start_event
8
+
9
+ super
10
+ ensure
11
+ title, body, body_format = Appsignal::EventFormatter.format("#{event_id}.dry", payload)
12
+
13
+ Appsignal::Transaction.current.finish_event(
14
+ title || event_id.to_s,
15
+ title,
16
+ body,
17
+ body_format
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -6,7 +6,7 @@ module Appsignal
6
6
  module Integrations
7
7
  module HanamiPlugin
8
8
  def self.init
9
- Appsignal.logger.debug("Loading Hanami integration")
9
+ Appsignal.internal_logger.debug("Loading Hanami integration")
10
10
 
11
11
  hanami_app_config = ::Hanami.app.config
12
12
  Appsignal.config = Appsignal::Config.new(
@@ -7,7 +7,7 @@ module Appsignal
7
7
  # @api private
8
8
  module PadrinoPlugin
9
9
  def self.init
10
- Appsignal.logger.debug("Loading Padrino (#{Padrino::VERSION}) integration")
10
+ Appsignal.internal_logger.debug("Loading Padrino (#{Padrino::VERSION}) integration")
11
11
 
12
12
  root = Padrino.mounted_root
13
13
  Appsignal.config = Appsignal::Config.new(root, Padrino.env)