appsignal 2.11.0.beta.4-java → 2.11.2-java

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +254 -1
  3. data/CHANGELOG.md +27 -0
  4. data/README.md +20 -5
  5. data/Rakefile +27 -9
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +15 -2
  8. data/ext/Rakefile +2 -0
  9. data/ext/agent.yml +17 -25
  10. data/ext/appsignal_extension.c +1 -1
  11. data/ext/base.rb +19 -9
  12. data/ext/extconf.rb +2 -0
  13. data/gemfiles/no_dependencies.gemfile +7 -0
  14. data/gemfiles/resque-2.gemfile +0 -1
  15. data/gemfiles/webmachine.gemfile +1 -0
  16. data/lib/appsignal.rb +1 -0
  17. data/lib/appsignal/auth_check.rb +4 -2
  18. data/lib/appsignal/cli/diagnose.rb +1 -1
  19. data/lib/appsignal/cli/diagnose/utils.rb +8 -11
  20. data/lib/appsignal/cli/install.rb +5 -8
  21. data/lib/appsignal/config.rb +82 -17
  22. data/lib/appsignal/extension.rb +6 -5
  23. data/lib/appsignal/extension/jruby.rb +6 -5
  24. data/lib/appsignal/helpers/instrumentation.rb +32 -0
  25. data/lib/appsignal/hooks.rb +24 -0
  26. data/lib/appsignal/hooks/action_mailer.rb +22 -0
  27. data/lib/appsignal/hooks/active_job.rb +32 -9
  28. data/lib/appsignal/hooks/active_support_notifications.rb +72 -0
  29. data/lib/appsignal/hooks/puma.rb +0 -1
  30. data/lib/appsignal/hooks/sidekiq.rb +0 -1
  31. data/lib/appsignal/probes.rb +7 -0
  32. data/lib/appsignal/probes/puma.rb +1 -1
  33. data/lib/appsignal/probes/sidekiq.rb +3 -1
  34. data/lib/appsignal/transaction.rb +30 -2
  35. data/lib/appsignal/utils/deprecation_message.rb +1 -1
  36. data/lib/appsignal/version.rb +1 -1
  37. data/spec/lib/appsignal/auth_check_spec.rb +23 -0
  38. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  39. data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
  40. data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
  41. data/spec/lib/appsignal/config_spec.rb +39 -1
  42. data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
  43. data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
  44. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  45. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  46. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  47. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  48. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  49. data/spec/lib/appsignal/hooks/activejob_spec.rb +44 -1
  50. data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
  51. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +4 -2
  52. data/spec/lib/appsignal/hooks_spec.rb +57 -0
  53. data/spec/lib/appsignal/marker_spec.rb +1 -1
  54. data/spec/lib/appsignal/transaction_spec.rb +55 -0
  55. data/spec/lib/appsignal_spec.rb +30 -0
  56. data/spec/spec_helper.rb +5 -0
  57. data/spec/support/helpers/config_helpers.rb +3 -2
  58. data/spec/support/helpers/dependency_helper.rb +4 -0
  59. data/spec/support/helpers/transaction_helpers.rb +1 -1
  60. data/spec/support/testing.rb +19 -19
  61. metadata +17 -5
@@ -1,5 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'webmachine'
4
+ gem 'webrick'
4
5
 
5
6
  gemspec :path => '../'
@@ -339,6 +339,7 @@ require "appsignal/auth_check"
339
339
  require "appsignal/config"
340
340
  require "appsignal/event_formatter"
341
341
  require "appsignal/hooks"
342
+ require "appsignal/probes"
342
343
  require "appsignal/marker"
343
344
  require "appsignal/minutely"
344
345
  require "appsignal/garbage_collection_profiler"
@@ -25,8 +25,10 @@ module Appsignal
25
25
  def initialize(config, logger = nil)
26
26
  @config = config
27
27
  if logger # rubocop:disable Style/GuardClause
28
- warn "Deprecated: `logger` argument will be removed in the next " \
29
- "major version."
28
+ Appsignal::Utils::DeprecationMessage.message \
29
+ "`Appsignal::AuthCheck.new`'s `logger` argument will be removed " \
30
+ "in the next major version. Please configure the logger " \
31
+ "using `Appsignal.logger`."
30
32
  end
31
33
  end
32
34
 
@@ -371,7 +371,7 @@ module Appsignal
371
371
  end
372
372
 
373
373
  def print_installation_result_report(report)
374
- report = report.fetch("download", {})
374
+ report = report.fetch("result", {})
375
375
  puts " Installation result"
376
376
  puts " Status: #{report["status"]}"
377
377
  puts " Message: #{report["message"]}" if report["message"]
@@ -34,20 +34,17 @@ module Appsignal
34
34
  end
35
35
 
36
36
  def self.parse_yaml(contents)
37
- arguments = [contents]
38
37
  if YAML.respond_to? :safe_load
39
- method = :safe_load
40
- arguments << \
41
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
42
- # Use keyword params for Ruby 2.6 and up
43
- { :permitted_classes => [Time] }
44
- else
45
- [Time]
46
- end
38
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
39
+ # Use keyword params for Ruby 2.6 and up
40
+ YAML.safe_load(contents, :permitted_classes => [Time])
41
+ else
42
+ YAML.safe_load(contents, [Time])
43
+ end
47
44
  else
48
- method = :load
45
+ # Support for Ruby versions without YAML.safe_load
46
+ YAML.load(contents) # rubocop:disable Security/YAMLLoad
49
47
  end
50
- YAML.send(method, *arguments)
51
48
  end
52
49
  end
53
50
  end
@@ -278,14 +278,11 @@ module Appsignal
278
278
  "../../../resources/appsignal.yml.erb"
279
279
  )
280
280
  file_contents = File.read(filename)
281
- arguments = [file_contents]
282
- if ruby_2_6_or_up?
283
- arguments << { :trim_mode => "-" }
284
- else
285
- arguments << nil
286
- arguments << "-"
287
- end
288
- template = ERB.new(*arguments)
281
+ template = if ruby_2_6_or_up?
282
+ ERB.new(file_contents, :trim_mode => "-")
283
+ else
284
+ ERB.new(file_contents, nil, "-")
285
+ end
289
286
  config = template.result(OpenStruct.new(data).instance_eval { binding })
290
287
 
291
288
  FileUtils.mkdir_p(File.join(Dir.pwd, "config"))
@@ -81,6 +81,50 @@ module Appsignal
81
81
  "APPSIGNAL_TRANSACTION_DEBUG_MODE" => :transaction_debug_mode,
82
82
  "APP_REVISION" => :revision
83
83
  }.freeze
84
+ # @api private
85
+ ENV_STRING_KEYS = %w[
86
+ APPSIGNAL_APP_NAME
87
+ APPSIGNAL_CA_FILE_PATH
88
+ APPSIGNAL_DNS_SERVERS
89
+ APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH
90
+ APPSIGNAL_HOSTNAME
91
+ APPSIGNAL_HTTP_PROXY
92
+ APPSIGNAL_LOG
93
+ APPSIGNAL_LOG_PATH
94
+ APPSIGNAL_PUSH_API_ENDPOINT
95
+ APPSIGNAL_PUSH_API_KEY
96
+ APPSIGNAL_WORKING_DIRECTORY_PATH
97
+ APPSIGNAL_WORKING_DIR_PATH
98
+ APP_REVISION
99
+ ].freeze
100
+ # @api private
101
+ ENV_BOOLEAN_KEYS = %w[
102
+ APPSIGNAL_ACTIVE
103
+ APPSIGNAL_DEBUG
104
+ APPSIGNAL_ENABLE_ALLOCATION_TRACKING
105
+ APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING
106
+ APPSIGNAL_ENABLE_GC_INSTRUMENTATION
107
+ APPSIGNAL_ENABLE_HOST_METRICS
108
+ APPSIGNAL_ENABLE_MINUTELY_PROBES
109
+ APPSIGNAL_FILES_WORLD_ACCESSIBLE
110
+ APPSIGNAL_INSTRUMENT_NET_HTTP
111
+ APPSIGNAL_INSTRUMENT_REDIS
112
+ APPSIGNAL_INSTRUMENT_SEQUEL
113
+ APPSIGNAL_RUNNING_IN_CONTAINER
114
+ APPSIGNAL_SEND_ENVIRONMENT_METADATA
115
+ APPSIGNAL_SEND_PARAMS
116
+ APPSIGNAL_SKIP_SESSION_DATA
117
+ APPSIGNAL_TRANSACTION_DEBUG_MODE
118
+ ].freeze
119
+ # @api private
120
+ ENV_ARRAY_KEYS = %w[
121
+ APPSIGNAL_FILTER_PARAMETERS
122
+ APPSIGNAL_FILTER_SESSION_DATA
123
+ APPSIGNAL_IGNORE_ACTIONS
124
+ APPSIGNAL_IGNORE_ERRORS
125
+ APPSIGNAL_IGNORE_NAMESPACES
126
+ APPSIGNAL_REQUEST_HEADERS
127
+ ].freeze
84
128
 
85
129
  # Mapping of old and deprecated AppSignal configuration keys
86
130
  DEPRECATED_CONFIG_KEY_MAPPING = {
@@ -121,8 +165,41 @@ module Appsignal
121
165
  :initial_config, :file_config, :env_config
122
166
  attr_accessor :logger
123
167
 
124
- def initialize(root_path, env, initial_config = {}, logger = Appsignal.logger)
168
+ # Initialize a new configuration object for AppSignal.
169
+ #
170
+ # If this is manually initialized, and not by {Appsignal.start}, it needs
171
+ # to be assigned to the {Appsignal.config} attribute.
172
+ #
173
+ # @example
174
+ # require "appsignal"
175
+ # Appsignal.config = Appsignal::Config.new(
176
+ # app_path,
177
+ # "production"
178
+ # )
179
+ # Appsignal.start
180
+ #
181
+ # @param root_path [String] Root path of the app.
182
+ # @param env [String] The environment to load when AppSignal is started. It
183
+ # will look for an environment with this name in the `config/appsignal.yml`
184
+ # config file.
185
+ # @param initial_config [Hash<String, Object>] The initial configuration to
186
+ # use. This will be overwritten by the file config and environment
187
+ # variables config.
188
+ # @param logger [Logger] The logger to use for the AppSignal gem. This is
189
+ # used by the configuration class only. Default: {Appsignal.logger}. See
190
+ # also {Appsignal.start_logger}.
191
+ # @param config_file [String] Custom config file location. Default
192
+ # `config/appsignal.yml`.
193
+ #
194
+ # @see https://docs.appsignal.com/ruby/configuration/
195
+ # Configuration documentation
196
+ # @see https://docs.appsignal.com/ruby/configuration/load-order.html
197
+ # Configuration load order
198
+ # @see https://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html
199
+ # How to integrate AppSignal manually
200
+ def initialize(root_path, env, initial_config = {}, logger = Appsignal.logger, config_file = nil)
125
201
  @root_path = root_path
202
+ @config_file = config_file
126
203
  @logger = logger
127
204
  @valid = false
128
205
  @config_hash = Hash[DEFAULT_CONFIG]
@@ -288,7 +365,7 @@ module Appsignal
288
365
  " Skipping file config.\n" \
289
366
  "File: #{config_file.inspect}\n" \
290
367
  "#{e.class.name}: #{e}"
291
- $stderr.puts "appsignal: #{message}"
368
+ Kernel.warn "appsignal: #{message}"
292
369
  logger.error "#{message}\n#{e.backtrace.join("\n")}"
293
370
  nil
294
371
  end
@@ -325,33 +402,21 @@ module Appsignal
325
402
  config = {}
326
403
 
327
404
  # Configuration with string type
328
- %w[APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
329
- APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY
330
- APPSIGNAL_LOG APPSIGNAL_LOG_PATH APPSIGNAL_WORKING_DIR_PATH
331
- APPSIGNAL_HOSTNAME APPSIGNAL_CA_FILE_PATH APP_REVISION].each do |var|
405
+ ENV_STRING_KEYS.each do |var|
332
406
  env_var = ENV[var]
333
407
  next unless env_var
334
408
  config[ENV_TO_KEY_MAPPING[var]] = env_var
335
409
  end
336
410
 
337
411
  # Configuration with boolean type
338
- %w[APPSIGNAL_ACTIVE APPSIGNAL_DEBUG APPSIGNAL_INSTRUMENT_NET_HTTP
339
- APPSIGNAL_INSTRUMENT_REDIS APPSIGNAL_INSTRUMENT_SEQUEL
340
- APPSIGNAL_SKIP_SESSION_DATA APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING
341
- APPSIGNAL_ENABLE_ALLOCATION_TRACKING APPSIGNAL_ENABLE_GC_INSTRUMENTATION
342
- APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
343
- APPSIGNAL_SEND_ENVIRONMENT_METADATA APPSIGNAL_SEND_PARAMS
344
- APPSIGNAL_ENABLE_MINUTELY_PROBES APPSIGNAL_FILES_WORLD_ACCESSIBLE
345
- APPSIGNAL_TRANSACTION_DEBUG_MODE].each do |var|
412
+ ENV_BOOLEAN_KEYS.each do |var|
346
413
  env_var = ENV[var]
347
414
  next unless env_var
348
415
  config[ENV_TO_KEY_MAPPING[var]] = env_var.casecmp("true").zero?
349
416
  end
350
417
 
351
418
  # Configuration with array of strings type
352
- %w[APPSIGNAL_IGNORE_ACTIONS APPSIGNAL_IGNORE_ERRORS
353
- APPSIGNAL_IGNORE_NAMESPACES APPSIGNAL_FILTER_PARAMETERS
354
- APPSIGNAL_FILTER_SESSION_DATA APPSIGNAL_REQUEST_HEADERS].each do |var|
419
+ ENV_ARRAY_KEYS.each do |var|
355
420
  env_var = ENV[var]
356
421
  next unless env_var
357
422
  config[ENV_TO_KEY_MAPPING[var]] = env_var.split(",")
@@ -10,11 +10,12 @@ begin
10
10
  require "appsignal_extension"
11
11
  Appsignal.extension_loaded = true
12
12
  end
13
- rescue LoadError => err
14
- Appsignal.logger.error(
15
- "Failed to load extension (#{err}), please run `appsignal diagnose` " \
16
- "and email us at support@appsignal.com"
17
- )
13
+ rescue LoadError => error
14
+ error_message = "ERROR: AppSignal failed to load extension. " \
15
+ "Please run `appsignal diagnose` and email us at support@appsignal.com\n" \
16
+ "#{error.class}: #{error.message}"
17
+ Appsignal.logger.error(error_message)
18
+ Kernel.warn error_message
18
19
  Appsignal.extension_loaded = false
19
20
  end
20
21
 
@@ -179,11 +179,12 @@ module Appsignal
179
179
  :appsignal_string
180
180
 
181
181
  Appsignal.extension_loaded = true
182
- rescue LoadError => err
183
- Appsignal.logger.error(
184
- "Failed to load extension (#{err}), please email us at " \
185
- "support@appsignal.com"
186
- )
182
+ rescue LoadError => error
183
+ error_message = "ERROR: AppSignal failed to load extension. " \
184
+ "Please run `appsignal diagnose` and email us at support@appsignal.com\n" \
185
+ "#{error.class}: #{error.message}"
186
+ Appsignal.logger.error(error_message)
187
+ Kernel.warn error_message
187
188
  Appsignal.extension_loaded = false
188
189
  end
189
190
 
@@ -380,6 +380,38 @@ module Appsignal
380
380
  end
381
381
  alias :tag_job :tag_request
382
382
 
383
+ # Add breadcrumbs to the transaction.
384
+ #
385
+ # Breadcrumbs can be used to trace what path a user has taken
386
+ # before encounterin an error.
387
+ #
388
+ # Only the last 20 added breadcrumbs will be saved.
389
+ #
390
+ # @example
391
+ # Appsignal.add_breadcrumb("Navigation", "http://blablabla.com", "", { :response => 200 }, Time.now.utc)
392
+ # Appsignal.add_breadcrumb("Network", "[GET] http://blablabla.com", "", { :response => 500 })
393
+ # Appsignal.add_breadcrumb("UI", "closed modal(change_password)", "User closed modal without actions")
394
+ #
395
+ # @param category [String] category of breadcrumb
396
+ # e.g. "UI", "Network", "Navigation", "Console".
397
+ # @param action [String] name of breadcrumb
398
+ # e.g "The user clicked a button", "HTTP 500 from http://blablabla.com"
399
+ # @option message [String] optional message in string format
400
+ # @option metadata [Hash<String,String>] key/value metadata in <string, string> format
401
+ # @option time [Time] time of breadcrumb, should respond to `.to_i` defaults to `Time.now.utc`
402
+ # @return [void]
403
+ #
404
+ # @see Transaction#add_breadcrumb
405
+ # @see http://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html
406
+ # Breadcrumb reference
407
+ # @since 2.12.0
408
+ def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
409
+ return unless active?
410
+ transaction = Appsignal::Transaction.current
411
+ return false unless transaction
412
+ transaction.add_breadcrumb(category, action, message, metadata, time)
413
+ end
414
+
383
415
  # Instrument helper for AppSignal.
384
416
  #
385
417
  # For more help, read our custom instrumentation guide, listed under "See
@@ -69,10 +69,34 @@ module Appsignal
69
69
  text.size > 200 ? "#{text[0...197]}..." : text
70
70
  end
71
71
  end
72
+
73
+ # Alias Probes constants that have moved to their own module in version
74
+ # 2.11.0.
75
+ def self.const_missing(name)
76
+ case name
77
+ when :SidekiqProbe
78
+ callers = caller
79
+ Appsignal::Utils::DeprecationMessage.message \
80
+ "The constant Appsignal::Hooks::SidekiqProbe has been deprecated. " \
81
+ "Please update the constant name to Appsignal::Probes::SidekiqProbe " \
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
91
+ else
92
+ super
93
+ end
94
+ end
72
95
  end
73
96
  end
74
97
 
75
98
  require "appsignal/hooks/action_cable"
99
+ require "appsignal/hooks/action_mailer"
76
100
  require "appsignal/hooks/active_job"
77
101
  require "appsignal/hooks/active_support_notifications"
78
102
  require "appsignal/hooks/celluloid"
@@ -0,0 +1,22 @@
1
+ module Appsignal
2
+ class Hooks
3
+ class ActionMailerHook < Appsignal::Hooks::Hook
4
+ register :action_mailer
5
+
6
+ def dependencies_present?
7
+ defined?(::ActionMailer)
8
+ end
9
+
10
+ def install
11
+ ActiveSupport::Notifications
12
+ .subscribe("process.action_mailer") do |_, _, _, _, payload|
13
+ Appsignal.increment_counter(
14
+ :action_mailer_process,
15
+ 1,
16
+ :mailer => payload[:mailer], :action => payload[:action]
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -41,8 +41,6 @@ module Appsignal
41
41
  transaction.set_error(exception)
42
42
  raise exception
43
43
  ensure
44
- tags = ActiveJobHelpers.tags_for_job(job)
45
-
46
44
  if transaction
47
45
  transaction.params =
48
46
  Appsignal::Utils::HashSanitizer.sanitize(
@@ -50,7 +48,7 @@ module Appsignal
50
48
  Appsignal.config[:filter_parameters]
51
49
  )
52
50
 
53
- transaction_tags = tags.dup
51
+ transaction_tags = ActiveJobHelpers.transaction_tags_for(job)
54
52
  transaction_tags["active_job_id"] = job["job_id"]
55
53
  provider_job_id = job["provider_job_id"]
56
54
  if provider_job_id
@@ -71,12 +69,15 @@ module Appsignal
71
69
  end
72
70
  end
73
71
 
74
- if job_status
75
- ActiveJobHelpers.increment_counter "queue_job_count", 1,
76
- tags.merge(:status => job_status)
72
+ metrics = ActiveJobHelpers.metrics_for(job)
73
+ metrics.each do |(metric_name, tags)|
74
+ if job_status
75
+ ActiveJobHelpers.increment_counter metric_name, 1,
76
+ tags.merge(:status => job_status)
77
+ end
78
+ ActiveJobHelpers.increment_counter metric_name, 1,
79
+ tags.merge(:status => :processed)
77
80
  end
78
- ActiveJobHelpers.increment_counter "queue_job_count", 1,
79
- tags.merge(:status => :processed)
80
81
  end
81
82
  end
82
83
 
@@ -96,7 +97,29 @@ module Appsignal
96
97
  end
97
98
  end
98
99
 
99
- def self.tags_for_job(job)
100
+ # Returns an array of metrics with tags used to report the job metrics
101
+ #
102
+ # If job ONLY has a queue, it will return `queue_job_count` with tags.
103
+ # If job has a queue AND priority, it will ALSO return
104
+ # `queue_priority_job_count` with tags.
105
+ #
106
+ # @return [Array] Array of metrics with tags to report.
107
+ def self.metrics_for(job)
108
+ tags = { :queue => job["queue_name"] }
109
+ metrics = [["queue_job_count", tags]]
110
+
111
+ priority = job["priority"]
112
+ if priority
113
+ metrics << [
114
+ "queue_priority_job_count",
115
+ tags.merge(:priority => priority)
116
+ ]
117
+ end
118
+
119
+ metrics
120
+ end
121
+
122
+ def self.transaction_tags_for(job)
100
123
  tags = {}
101
124
  queue = job["queue_name"]
102
125
  tags[:queue] = queue if queue
@@ -23,6 +23,21 @@ module Appsignal
23
23
  end
24
24
  end
25
25
 
26
+ instrumenter = ::ActiveSupport::Notifications::Instrumenter
27
+
28
+ if instrumenter.method_defined?(:start) && instrumenter.method_defined?(:finish)
29
+ install_start_finish
30
+ else
31
+ install_instrument
32
+ end
33
+
34
+ # rubocop:disable Style/GuardClause
35
+ if instrumenter.method_defined?(:finish_with_state)
36
+ install_finish_with_state
37
+ end
38
+ end
39
+
40
+ def install_instrument
26
41
  ::ActiveSupport::Notifications::Instrumenter.class_eval do
27
42
  alias instrument_without_appsignal instrument
28
43
 
@@ -46,6 +61,63 @@ module Appsignal
46
61
  end
47
62
  end
48
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
120
+ end
49
121
  end
50
122
  end
51
123
  end