appsignal 4.2.0 → 4.5.17

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +443 -0
  3. data/README.md +0 -3
  4. data/Rakefile +1 -1
  5. data/appsignal.gemspec +2 -1
  6. data/build_matrix.yml +33 -0
  7. data/ext/agent.rb +27 -27
  8. data/ext/appsignal_extension.c +90 -73
  9. data/ext/base.rb +3 -1
  10. data/lib/appsignal/check_in/event.rb +55 -0
  11. data/lib/appsignal/check_in/scheduler.rb +8 -2
  12. data/lib/appsignal/check_in.rb +9 -8
  13. data/lib/appsignal/config.rb +36 -15
  14. data/lib/appsignal/custom_marker.rb +72 -0
  15. data/lib/appsignal/environment.rb +1 -0
  16. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +4 -6
  17. data/lib/appsignal/event_formatter/view_component/render_formatter.rb +4 -6
  18. data/lib/appsignal/helpers/instrumentation.rb +5 -0
  19. data/lib/appsignal/hooks/active_job.rb +25 -5
  20. data/lib/appsignal/hooks/at_exit.rb +18 -4
  21. data/lib/appsignal/hooks/ownership.rb +44 -0
  22. data/lib/appsignal/hooks.rb +1 -0
  23. data/lib/appsignal/integrations/capistrano/appsignal.cap +4 -8
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +8 -11
  25. data/lib/appsignal/integrations/http.rb +2 -1
  26. data/lib/appsignal/integrations/ownership.rb +51 -0
  27. data/lib/appsignal/integrations/rake.rb +14 -2
  28. data/lib/appsignal/integrations/sidekiq.rb +14 -3
  29. data/lib/appsignal/internal_errors.rb +19 -0
  30. data/lib/appsignal/logger.rb +121 -69
  31. data/lib/appsignal/marker.rb +1 -1
  32. data/lib/appsignal/probes/sidekiq.rb +5 -1
  33. data/lib/appsignal/rack/body_wrapper.rb +1 -1
  34. data/lib/appsignal/rack/event_handler.rb +7 -5
  35. data/lib/appsignal/transaction.rb +91 -13
  36. data/lib/appsignal/version.rb +1 -1
  37. data/lib/appsignal.rb +82 -11
  38. data/resources/cacert.pem +164 -87
  39. metadata +8 -4
@@ -39,8 +39,10 @@ module Appsignal
39
39
  ENV.fetch("APPSIGNAL_APP_ENV", nil),
40
40
  ENV.fetch("RAILS_ENV", nil),
41
41
  ENV.fetch("RACK_ENV", nil)
42
- ].compact.each do |env|
43
- return env if env
42
+ ].compact.each do |env_value|
43
+ value = env_value.to_s.strip
44
+ next if value.empty?
45
+ return value if value
44
46
  end
45
47
 
46
48
  loader_defaults.reverse.each do |loader_defaults|
@@ -91,6 +93,7 @@ module Appsignal
91
93
  :ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
92
94
  :dns_servers => [],
93
95
  :enable_allocation_tracking => true,
96
+ :enable_at_exit_hook => "on_error",
94
97
  :enable_at_exit_reporter => true,
95
98
  :enable_host_metrics => true,
96
99
  :enable_minutely_probes => true,
@@ -111,10 +114,12 @@ module Appsignal
111
114
  :ignore_namespaces => [],
112
115
  :instrument_http_rb => true,
113
116
  :instrument_net_http => true,
117
+ :instrument_ownership => true,
114
118
  :instrument_redis => true,
115
119
  :instrument_sequel => true,
116
120
  :log => "file",
117
121
  :logging_endpoint => "https://appsignal-endpoint.net",
122
+ :ownership_set_namespace => false,
118
123
  :request_headers => %w[
119
124
  HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
120
125
  HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_CONNECTION
@@ -149,6 +154,7 @@ module Appsignal
149
154
  :name => "APPSIGNAL_APP_NAME",
150
155
  :bind_address => "APPSIGNAL_BIND_ADDRESS",
151
156
  :ca_file_path => "APPSIGNAL_CA_FILE_PATH",
157
+ :enable_at_exit_hook => "APPSIGNAL_ENABLE_AT_EXIT_HOOK",
152
158
  :hostname => "APPSIGNAL_HOSTNAME",
153
159
  :host_role => "APPSIGNAL_HOST_ROLE",
154
160
  :http_proxy => "APPSIGNAL_HTTP_PROXY",
@@ -160,6 +166,7 @@ module Appsignal
160
166
  :push_api_key => "APPSIGNAL_PUSH_API_KEY",
161
167
  :sidekiq_report_errors => "APPSIGNAL_SIDEKIQ_REPORT_ERRORS",
162
168
  :statsd_port => "APPSIGNAL_STATSD_PORT",
169
+ :nginx_port => "APPSIGNAL_NGINX_PORT",
163
170
  :working_directory_path => "APPSIGNAL_WORKING_DIRECTORY_PATH",
164
171
  :revision => "APP_REVISION"
165
172
  }.freeze
@@ -181,8 +188,10 @@ module Appsignal
181
188
  :files_world_accessible => "APPSIGNAL_FILES_WORLD_ACCESSIBLE",
182
189
  :instrument_http_rb => "APPSIGNAL_INSTRUMENT_HTTP_RB",
183
190
  :instrument_net_http => "APPSIGNAL_INSTRUMENT_NET_HTTP",
191
+ :instrument_ownership => "APPSIGNAL_INSTRUMENT_OWNERSHIP",
184
192
  :instrument_redis => "APPSIGNAL_INSTRUMENT_REDIS",
185
193
  :instrument_sequel => "APPSIGNAL_INSTRUMENT_SEQUEL",
194
+ :ownership_set_namespace => "APPSIGNAL_OWNERSHIP_SET_NAMESPACE",
186
195
  :running_in_container => "APPSIGNAL_RUNNING_IN_CONTAINER",
187
196
  :send_environment_metadata => "APPSIGNAL_SEND_ENVIRONMENT_METADATA",
188
197
  :send_params => "APPSIGNAL_SEND_PARAMS",
@@ -242,8 +251,8 @@ module Appsignal
242
251
  )
243
252
  @load_yaml_file = load_yaml_file
244
253
  @root_path = root_path.to_s
245
- @config_file_error = false
246
- @config_file = config_file
254
+ @yml_config_file_error = false
255
+ @yml_config_file = yml_config_file
247
256
  @valid = false
248
257
 
249
258
  @env = env.to_s
@@ -385,8 +394,12 @@ module Appsignal
385
394
  @valid
386
395
  end
387
396
 
397
+ def active_for_env?
398
+ config_hash[:active]
399
+ end
400
+
388
401
  def active?
389
- @valid && config_hash[:active]
402
+ valid? && active_for_env?
390
403
  end
391
404
 
392
405
  # @api private
@@ -424,6 +437,7 @@ module Appsignal
424
437
  ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"] = config_hash[:running_in_container].to_s
425
438
  ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = config_hash[:send_environment_metadata].to_s
426
439
  ENV["_APPSIGNAL_STATSD_PORT"] = config_hash[:statsd_port].to_s
440
+ ENV["_APPSIGNAL_NGINX_PORT"] = config_hash[:nginx_port].to_s
427
441
  if config_hash[:working_directory_path]
428
442
  ENV["_APPSIGNAL_WORKING_DIRECTORY_PATH"] = config_hash[:working_directory_path]
429
443
  end
@@ -436,12 +450,16 @@ module Appsignal
436
450
  merge(options)
437
451
  end
438
452
 
453
+ # Apply any overrides for invalid settings.
439
454
  # @api private
440
- def validate
441
- # Apply any overrides for invalid settings.
455
+ def apply_overrides
442
456
  @override_config = determine_overrides
443
457
  merge(override_config)
458
+ end
444
459
 
460
+ # @return [void]
461
+ # @api private
462
+ def validate
445
463
  # Strip path from endpoint so we're backwards compatible with
446
464
  # earlier versions of the gem.
447
465
  # TODO: Move to its own method, maybe in `#[]=`?
@@ -466,6 +484,7 @@ module Appsignal
466
484
  # of the Ruby app.
467
485
  #
468
486
  # @api private
487
+ # @return [void]
469
488
  # @since 4.0.0
470
489
  def freeze
471
490
  super
@@ -475,9 +494,9 @@ module Appsignal
475
494
 
476
495
  # @api private
477
496
  def yml_config_file?
478
- return false unless config_file
497
+ return false unless yml_config_file
479
498
 
480
- File.exist?(config_file)
499
+ File.exist?(yml_config_file)
481
500
  end
482
501
 
483
502
  private
@@ -486,8 +505,8 @@ module Appsignal
486
505
  Appsignal.internal_logger
487
506
  end
488
507
 
489
- def config_file
490
- @config_file ||=
508
+ def yml_config_file
509
+ @yml_config_file ||=
491
510
  root_path.nil? ? nil : File.join(root_path, "config", "appsignal.yml")
492
511
  end
493
512
 
@@ -499,6 +518,8 @@ module Appsignal
499
518
  # environment variable is present and not empty.
500
519
  env_push_api_key = ENV["APPSIGNAL_PUSH_API_KEY"] || ""
501
520
  hash[:active] = true unless env_push_api_key.strip.empty?
521
+
522
+ hash[:enable_at_exit_hook] = "always" if Appsignal::Extension.running_in_container?
502
523
  end
503
524
  end
504
525
 
@@ -506,7 +527,7 @@ module Appsignal
506
527
  return unless yml_config_file?
507
528
 
508
529
  read_options = YAML::VERSION >= "4.0.0" ? { :aliases => true } : {}
509
- configurations = YAML.load(ERB.new(File.read(config_file)).result, **read_options)
530
+ configurations = YAML.load(ERB.new(File.read(yml_config_file)).result, **read_options)
510
531
  config_for_this_env = configurations[env]
511
532
  if config_for_this_env
512
533
  config_for_this_env.transform_keys(&:to_sym)
@@ -515,10 +536,10 @@ module Appsignal
515
536
  nil
516
537
  end
517
538
  rescue => e
518
- @config_file_error = true
539
+ @yml_config_file_error = true
519
540
  message = "An error occurred while loading the AppSignal config file. " \
520
541
  "Not starting AppSignal.\n" \
521
- "File: #{config_file.inspect}\n" \
542
+ "File: #{yml_config_file.inspect}\n" \
522
543
  "#{e.class.name}: #{e}"
523
544
  Kernel.warn "appsignal: #{message}"
524
545
  logger.error "#{message}\n#{e.backtrace.join("\n")}"
@@ -570,7 +591,7 @@ module Appsignal
570
591
  # If an error was detected during config file reading/parsing and the new
571
592
  # behavior is enabled to not start AppSignal on incomplete config, do not
572
593
  # start AppSignal.
573
- config[:active] = false if @config_file_error
594
+ config[:active] = false if @yml_config_file_error
574
595
 
575
596
  if config_hash[:activejob_report_errors] == "discard" &&
576
597
  !Appsignal::Hooks::ActiveJobHook.version_7_1_or_higher?
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # Custom markers are used on AppSignal.com to indicate events in an
5
+ # application, to give additional context on graph timelines.
6
+ #
7
+ # This helper class will send a request to the AppSignal public endpoint to
8
+ # create a Custom marker for the application on AppSignal.com.
9
+ #
10
+ # @see https://docs.appsignal.com/api/public-endpoint/custom-markers.html
11
+ # Public Endpoint API markers endpoint documentation
12
+ # @see https://docs.appsignal.com/appsignal/terminology.html#markers
13
+ # Terminology: Markers
14
+ class CustomMarker
15
+ # @param icon [String] icon to use for the marker, like an emoji.
16
+ # @param message [String] name of the user that is creating the
17
+ # marker.
18
+ # @param created_at [Time/String] A Ruby time object or a valid ISO8601
19
+ # timestamp.
20
+ # @return [Boolean]
21
+ def self.report(
22
+ icon: nil,
23
+ message: nil,
24
+ created_at: nil
25
+ )
26
+ new(
27
+ {
28
+ :icon => icon,
29
+ :message => message,
30
+ :created_at => created_at.respond_to?(:iso8601) ? created_at.iso8601 : created_at
31
+ }.compact
32
+ ).transmit
33
+ end
34
+
35
+ # @api private
36
+ def initialize(marker_data)
37
+ @marker_data = marker_data
38
+ end
39
+
40
+ # @api private
41
+ def transmit
42
+ unless Appsignal.config
43
+ Appsignal.internal_logger.warn(
44
+ "Did not transmit custom marker: no AppSignal config loaded"
45
+ )
46
+ return false
47
+ end
48
+
49
+ transmitter = Transmitter.new(
50
+ "#{Appsignal.config[:logging_endpoint]}/markers",
51
+ Appsignal.config
52
+ )
53
+ response = transmitter.transmit(@marker_data)
54
+
55
+ if (200...300).include?(response.code.to_i)
56
+ Appsignal.internal_logger.info("Transmitted custom marker")
57
+ true
58
+ else
59
+ Appsignal.internal_logger.error(
60
+ "Failed to transmit custom marker: #{response.code} status code"
61
+ )
62
+ false
63
+ end
64
+ rescue => e
65
+ Appsignal.internal_logger.error(
66
+ "Failed to transmit custom marker: #{e.class}: #{e.message}\n" \
67
+ "#{e.backtrace}"
68
+ )
69
+ false
70
+ end
71
+ end
72
+ end
@@ -90,6 +90,7 @@ module Appsignal
90
90
  hanami
91
91
  hiredis
92
92
  mongo_ruby_driver
93
+ ownership
93
94
  padrino
94
95
  passenger
95
96
  puma
@@ -7,17 +7,15 @@ module Appsignal
7
7
  class RenderFormatter
8
8
  BLANK = ""
9
9
 
10
- attr_reader :root_path
11
-
12
- def initialize
13
- @root_path = "#{Rails.root}/"
14
- end
15
-
16
10
  def format(payload)
17
11
  return nil unless payload[:identifier]
18
12
 
19
13
  [payload[:identifier].sub(root_path, BLANK), nil]
20
14
  end
15
+
16
+ def root_path
17
+ @root_path ||= "#{Rails.root}/"
18
+ end
21
19
  end
22
20
  end
23
21
  end
@@ -7,14 +7,12 @@ module Appsignal
7
7
  class RenderFormatter
8
8
  BLANK = ""
9
9
 
10
- attr_reader :root_path
11
-
12
- def initialize
13
- @root_path = "#{Rails.root}/"
10
+ def format(payload)
11
+ [payload[:name], payload[:identifier].sub(root_path, BLANK)]
14
12
  end
15
13
 
16
- def format(payload)
17
- [payload[:name], payload[:identifier].sub(@root_path, BLANK)]
14
+ def root_path
15
+ @root_path ||= "#{Rails.root}/"
18
16
  end
19
17
  end
20
18
  end
@@ -154,6 +154,11 @@ module Appsignal
154
154
  #
155
155
  # @see monitor
156
156
  def monitor_and_stop(action:, namespace: nil, &block)
157
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
158
+ "The `Appsignal.monitor_and_stop` helper is deprecated. " \
159
+ "Use the `Appsignal.monitor` along with our `enable_at_exit_hook` " \
160
+ "option instead."
161
+
157
162
  monitor(:namespace => namespace, :action => action, &block)
158
163
  ensure
159
164
  Appsignal.stop("monitor_and_stop")
@@ -42,7 +42,16 @@ module Appsignal
42
42
  end
43
43
 
44
44
  module ActiveJobClassInstrumentation
45
- def execute(job)
45
+ def execute(job) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
46
+ enqueued_at = job["enqueued_at"]
47
+ queue_start = Time.parse(enqueued_at) if enqueued_at
48
+ queue_time =
49
+ if queue_start
50
+ time_now = Time.now.utc
51
+ # Calculate queue time and store it as milliseconds
52
+ (time_now - queue_start) * 1_000
53
+ end
54
+
46
55
  job_status = nil
47
56
  has_wrapper_transaction = Appsignal::Transaction.current?
48
57
  transaction =
@@ -73,10 +82,8 @@ module Appsignal
73
82
  raise exception
74
83
  ensure
75
84
  if transaction
76
- enqueued_at = job["enqueued_at"]
77
- if enqueued_at # Present in Rails 6 and up
78
- transaction.set_queue_start((Time.parse(enqueued_at).to_f * 1_000).to_i)
79
- end
85
+ # Present in Rails 6 and up
86
+ transaction.set_queue_start((queue_start.to_f * 1_000).to_i) if queue_start
80
87
 
81
88
  unless has_wrapper_transaction
82
89
  # Only complete transaction if ActiveJob is not wrapped in
@@ -94,6 +101,15 @@ module Appsignal
94
101
  ActiveJobHelpers.increment_counter metric_name, 1,
95
102
  tags.merge(:status => :processed)
96
103
  end
104
+
105
+ queue_name = job["queue_name"]
106
+ if queue_time && queue_name
107
+ ActiveJobHelpers.add_distribution_value(
108
+ "queue_time",
109
+ queue_time,
110
+ :queue => queue_name
111
+ )
112
+ end
97
113
  end
98
114
 
99
115
  private
@@ -172,6 +188,10 @@ module Appsignal
172
188
  def self.increment_counter(key, value, tags = {})
173
189
  Appsignal.increment_counter "active_job_#{key}", value, tags
174
190
  end
191
+
192
+ def self.add_distribution_value(key, value, tags = {})
193
+ Appsignal.add_distribution_value("active_job_#{key}", value, tags)
194
+ end
175
195
  end
176
196
  end
177
197
  end
@@ -11,27 +11,41 @@ module Appsignal
11
11
  end
12
12
 
13
13
  def install
14
- return unless Appsignal.config[:enable_at_exit_reporter]
15
-
16
14
  Kernel.at_exit(&AtExitCallback.method(:call))
17
15
  end
18
16
 
19
- # Report any unhandled errors and will crash the Ruby process.
17
+ # Stop AppSignal before the app exists.
18
+ #
19
+ # This is the default behavior and can be customized with the
20
+ # `enable_at_exit_hook` option.
21
+ #
22
+ # When the `enable_at_exit_reporter` option is set to `true` (the
23
+ # default), it will report any unhandled errors that will crash the Ruby
24
+ # process.
20
25
  #
21
26
  # If this error was previously reported by any of our instrumentation,
22
27
  # the error will not also be reported here. This way we don't report an
23
28
  # error from a Rake task or instrumented script twice.
24
29
  class AtExitCallback
25
30
  def self.call
31
+ report_error = false
32
+ return unless Appsignal.config&.[](:enable_at_exit_reporter)
33
+
26
34
  error = $! # rubocop:disable Style/SpecialGlobalVars
27
35
  return unless error
28
36
  return if ignored_error?(error)
29
37
  return if Appsignal::Transaction.last_errors.include?(error)
30
38
 
39
+ report_error = true
40
+
31
41
  Appsignal.report_error(error) do |transaction|
32
42
  transaction.set_namespace("unhandled")
33
43
  end
34
- Appsignal.stop("at_exit")
44
+ ensure
45
+ at_exit_hook = Appsignal.config&.[](:enable_at_exit_hook)
46
+ if at_exit_hook == "always" || (at_exit_hook == "on_error" && report_error)
47
+ Appsignal.stop("at_exit")
48
+ end
35
49
  end
36
50
 
37
51
  IGNORED_ERRORS = [
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class Hooks
5
+ # @api private
6
+ class OwnershipHook < Appsignal::Hooks::Hook
7
+ register :ownership
8
+
9
+ def dependencies_present?
10
+ defined?(::Ownership) &&
11
+ Gem::Version.new(::Ownership::VERSION) >= Gem::Version.new("0.2.0") &&
12
+ Appsignal.config &&
13
+ Appsignal.config[:instrument_ownership]
14
+ end
15
+
16
+ def install
17
+ require "appsignal/integrations/ownership"
18
+
19
+ # If a transaction is created in a code context that has an owner,
20
+ # set the namespace of the transaction to the owner.
21
+ Appsignal::Transaction.after_create <<
22
+ Appsignal::Integrations::OwnershipIntegrationHelper.method(:after_create)
23
+
24
+ # If an error was reported in a code context that has an owner,
25
+ # set the namespace of the transaction to the owner.
26
+ # In some circumstances, this will be more accurate than the last owner
27
+ # that was set for the transaction, which is what would otherwise be
28
+ # reported.
29
+ Appsignal::Transaction.before_complete <<
30
+ Appsignal::Integrations::OwnershipIntegrationHelper.method(:before_complete)
31
+
32
+ # If an owner is set in a code context that has an active transaction,
33
+ # set the namespace of the transaction to the owner.
34
+ unless ::Ownership.singleton_class.included_modules.include?(
35
+ Appsignal::Integrations::OwnershipIntegration
36
+ )
37
+ ::Ownership.singleton_class.prepend Appsignal::Integrations::OwnershipIntegration
38
+ end
39
+
40
+ Appsignal::Environment.report_enabled("ownership")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -84,6 +84,7 @@ require "appsignal/hooks/dry_monitor"
84
84
  require "appsignal/hooks/http"
85
85
  require "appsignal/hooks/mri"
86
86
  require "appsignal/hooks/net_http"
87
+ require "appsignal/hooks/ownership"
87
88
  require "appsignal/hooks/passenger"
88
89
  require "appsignal/hooks/puma"
89
90
  require "appsignal/hooks/rake"
@@ -8,22 +8,18 @@ namespace :appsignal do
8
8
  user = fetch(:appsignal_user, ENV["USER"] || ENV.fetch("USERNAME", nil))
9
9
  revision = fetch(:appsignal_revision, fetch(:current_revision))
10
10
 
11
- appsignal_config = Appsignal::Config.new(
12
- Dir.pwd,
13
- appsignal_env
14
- ).tap do |c|
15
- c.merge_dsl_options(fetch(:appsignal_config, {}))
16
- c.validate
11
+ Appsignal._load_config!(appsignal_env) do |config|
12
+ config&.merge_dsl_options(fetch(:appsignal_config, {}))
17
13
  end
18
14
  Appsignal._start_logger
19
15
 
20
- if appsignal_config&.active?
16
+ if Appsignal.config&.active?
21
17
  marker_data = {
22
18
  :revision => revision,
23
19
  :user => user
24
20
  }
25
21
 
26
- marker = Appsignal::Marker.new(marker_data, appsignal_config)
22
+ marker = Appsignal::Marker.new(marker_data, Appsignal.config)
27
23
  # {#dry_run?} helper was added in Capistrano 3.5.0
28
24
  # https://github.com/capistrano/capistrano/commit/38d8d6d2c8485f1b5643857465b16ff01da57aff
29
25
  if respond_to?(:dry_run?) && dry_run?
@@ -5,40 +5,37 @@ module Appsignal
5
5
  # @api private
6
6
  class Capistrano
7
7
  def self.tasks(config)
8
- config.load do # rubocop:disable Metrics/BlockLength
8
+ config.load do
9
9
  after "deploy", "appsignal:deploy"
10
10
  after "deploy:migrations", "appsignal:deploy"
11
11
 
12
12
  namespace :appsignal do
13
13
  task :deploy do
14
- env = fetch(:appsignal_env,
14
+ appsignal_env = fetch(:appsignal_env,
15
15
  fetch(:stage, fetch(:rails_env, fetch(:rack_env, "production"))))
16
16
  user = fetch(:appsignal_user, ENV["USER"] || ENV.fetch("USERNAME", nil))
17
17
  revision = fetch(:appsignal_revision, fetch(:current_revision))
18
18
 
19
- appsignal_config = Appsignal::Config.new(
20
- ENV.fetch("PWD", nil),
21
- env
22
- ).tap do |c|
23
- c.merge_dsl_options(fetch(:appsignal_config, {}))
24
- c.validate
19
+ Appsignal._load_config!(appsignal_env) do |conf|
20
+ conf&.merge_dsl_options(fetch(:appsignal_config, {}))
25
21
  end
26
22
  Appsignal._start_logger
27
23
 
28
- if appsignal_config&.active?
24
+ if Appsignal.config&.active?
29
25
  marker_data = {
30
26
  :revision => revision,
31
27
  :user => user
32
28
  }
33
29
 
34
- marker = Marker.new(marker_data, appsignal_config)
30
+ marker = Marker.new(marker_data, Appsignal.config)
35
31
  if config.dry_run
36
32
  puts "Dry run: AppSignal deploy marker not actually sent."
37
33
  else
38
34
  marker.transmit
39
35
  end
40
36
  else
41
- puts "Not notifying of deploy, config is not active for environment: #{env}"
37
+ puts "Not notifying of deploy, config is not active for " \
38
+ "environment: #{appsignal_env}"
42
39
  end
43
40
  end
44
41
  end
@@ -5,7 +5,8 @@ module Appsignal
5
5
  # @api private
6
6
  module HttpIntegration
7
7
  def request(verb, uri, opts = {})
8
- parsed_request_uri = uri.is_a?(URI) ? uri : URI.parse(uri.to_s)
8
+ uri_module = defined?(HTTP::URI) ? HTTP::URI : URI
9
+ parsed_request_uri = uri.is_a?(URI) ? uri : uri_module.parse(uri.to_s)
9
10
  request_uri = "#{parsed_request_uri.scheme}://#{parsed_request_uri.host}"
10
11
 
11
12
  Appsignal.instrument("request.http_rb", "#{verb.upcase} #{request_uri}") do
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ # @api private
6
+ module OwnershipIntegration
7
+ # Implement the `around_change` logic by monkey-patching the reader,
8
+ # instead of by using the `around_change=` writer. This allows customers
9
+ # to use the `around_change=` writer in their own code without
10
+ # accidentally overriding AppSignal's instrumentation.
11
+ def around_change
12
+ proc do |owner, block|
13
+ OwnershipIntegrationHelper.set(Appsignal::Transaction.current, owner)
14
+
15
+ original = super
16
+
17
+ if original
18
+ original.call(owner, block)
19
+ else
20
+ block.call
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module OwnershipIntegrationHelper
27
+ class << self
28
+ def set(transaction, owner)
29
+ return if owner.nil?
30
+
31
+ transaction.add_tags(:owner => owner)
32
+ transaction.set_namespace(owner) if set_namespace?
33
+ end
34
+
35
+ def after_create(transaction)
36
+ set(transaction, ::Ownership.owner)
37
+ end
38
+
39
+ def before_complete(transaction, error)
40
+ set(transaction, error.owner) if error.respond_to?(:owner)
41
+ end
42
+
43
+ private
44
+
45
+ def set_namespace?
46
+ Appsignal.config && Appsignal.config[:ownership_set_namespace]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,6 +4,16 @@ module Appsignal
4
4
  module Integrations
5
5
  # @api private
6
6
  module RakeIntegration
7
+ IGNORED_ERRORS = [
8
+ # Normal exits from the application we do not need to report
9
+ SystemExit,
10
+ SignalException
11
+ ].freeze
12
+
13
+ def self.ignored_error?(error)
14
+ IGNORED_ERRORS.include?(error.class)
15
+ end
16
+
7
17
  def execute(*args)
8
18
  transaction =
9
19
  if Appsignal.config[:enable_rake_performance_instrumentation]
@@ -16,8 +26,10 @@ module Appsignal
16
26
  end
17
27
  rescue Exception => error # rubocop:disable Lint/RescueException
18
28
  Appsignal::Integrations::RakeIntegrationHelper.register_at_exit_hook
19
- transaction ||= _appsignal_create_transaction
20
- transaction.set_error(error)
29
+ unless RakeIntegration.ignored_error?(error)
30
+ transaction ||= _appsignal_create_transaction
31
+ transaction.set_error(error)
32
+ end
21
33
  raise error
22
34
  ensure
23
35
  if transaction