appsignal 3.12.6-java → 4.0.0.beta.1-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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +499 -487
  3. data/CHANGELOG.md +151 -0
  4. data/Rakefile +31 -7
  5. data/benchmark.rake +4 -6
  6. data/build_matrix.yml +45 -39
  7. data/ext/agent.rb +27 -27
  8. data/ext/appsignal_extension.c +25 -0
  9. data/gemfiles/rails-7.2.gemfile +11 -0
  10. data/lib/appsignal/check_in/cron.rb +67 -0
  11. data/lib/appsignal/check_in.rb +46 -0
  12. data/lib/appsignal/cli/diagnose.rb +37 -28
  13. data/lib/appsignal/cli/install.rb +5 -1
  14. data/lib/appsignal/config.rb +57 -119
  15. data/lib/appsignal/demo.rb +2 -2
  16. data/lib/appsignal/extension/jruby.rb +14 -0
  17. data/lib/appsignal/helpers/instrumentation.rb +139 -414
  18. data/lib/appsignal/helpers/metrics.rb +0 -16
  19. data/lib/appsignal/hooks/action_cable.rb +8 -8
  20. data/lib/appsignal/hooks/active_job.rb +2 -2
  21. data/lib/appsignal/hooks/at_exit.rb +37 -0
  22. data/lib/appsignal/hooks.rb +1 -16
  23. data/lib/appsignal/integrations/action_cable.rb +2 -2
  24. data/lib/appsignal/integrations/capistrano/appsignal.cap +2 -4
  25. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -4
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +3 -3
  27. data/lib/appsignal/integrations/http.rb +2 -7
  28. data/lib/appsignal/integrations/que.rb +2 -2
  29. data/lib/appsignal/integrations/railtie.rb +26 -59
  30. data/lib/appsignal/integrations/rake.rb +2 -2
  31. data/lib/appsignal/integrations/resque.rb +2 -2
  32. data/lib/appsignal/integrations/shoryuken.rb +4 -4
  33. data/lib/appsignal/integrations/sidekiq.rb +3 -3
  34. data/lib/appsignal/integrations/webmachine.rb +2 -2
  35. data/lib/appsignal/loaders.rb +1 -1
  36. data/lib/appsignal/probes.rb +0 -9
  37. data/lib/appsignal/rack/abstract_middleware.rb +4 -26
  38. data/lib/appsignal/rack/event_handler.rb +4 -4
  39. data/lib/appsignal/rack/rails_instrumentation.rb +1 -1
  40. data/lib/appsignal/rack.rb +0 -25
  41. data/lib/appsignal/sample_data.rb +95 -0
  42. data/lib/appsignal/transaction.rb +235 -361
  43. data/lib/appsignal/utils/rails_helper.rb +4 -0
  44. data/lib/appsignal/version.rb +1 -1
  45. data/lib/appsignal.rb +20 -62
  46. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  47. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  48. data/spec/lib/appsignal/capistrano3_spec.rb +53 -13
  49. data/spec/lib/appsignal/{heartbeat_spec.rb → check_in_spec.rb} +45 -36
  50. data/spec/lib/appsignal/cli/demo_spec.rb +7 -27
  51. data/spec/lib/appsignal/cli/diagnose_spec.rb +145 -110
  52. data/spec/lib/appsignal/config_spec.rb +304 -379
  53. data/spec/lib/appsignal/extension_install_failure_spec.rb +5 -1
  54. data/spec/lib/appsignal/extension_spec.rb +5 -1
  55. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +1 -1
  56. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +1 -2
  57. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +1 -0
  58. data/spec/lib/appsignal/hooks/activejob_spec.rb +7 -12
  59. data/spec/lib/appsignal/hooks/at_exit_spec.rb +72 -0
  60. data/spec/lib/appsignal/hooks/gvl_spec.rb +10 -5
  61. data/spec/lib/appsignal/hooks/http_spec.rb +3 -3
  62. data/spec/lib/appsignal/hooks/net_http_spec.rb +3 -3
  63. data/spec/lib/appsignal/hooks/rake_spec.rb +6 -9
  64. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -10
  65. data/spec/lib/appsignal/hooks/redis_spec.rb +4 -7
  66. data/spec/lib/appsignal/hooks/resque_spec.rb +3 -5
  67. data/spec/lib/appsignal/hooks_spec.rb +0 -41
  68. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +29 -20
  69. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +4 -9
  70. data/spec/lib/appsignal/integrations/http_spec.rb +0 -21
  71. data/spec/lib/appsignal/integrations/railtie_spec.rb +179 -157
  72. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +3 -5
  73. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +48 -62
  74. data/spec/lib/appsignal/loaders/hanami_spec.rb +6 -9
  75. data/spec/lib/appsignal/loaders/padrino_spec.rb +6 -10
  76. data/spec/lib/appsignal/loaders/sinatra_spec.rb +6 -9
  77. data/spec/lib/appsignal/loaders_spec.rb +8 -1
  78. data/spec/lib/appsignal/marker_spec.rb +1 -1
  79. data/spec/lib/appsignal/probes_spec.rb +4 -83
  80. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +4 -63
  81. data/spec/lib/appsignal/rack/event_handler_spec.rb +18 -15
  82. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +3 -11
  83. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +4 -5
  84. data/spec/lib/appsignal/sample_data_spec.rb +174 -0
  85. data/spec/lib/appsignal/transaction_spec.rb +791 -1031
  86. data/spec/lib/appsignal/transmitter_spec.rb +6 -8
  87. data/spec/lib/appsignal_spec.rb +294 -643
  88. data/spec/spec_helper.rb +1 -3
  89. data/spec/support/fixtures/projects/valid/config/appsignal.yml +4 -7
  90. data/spec/support/fixtures/projects/valid_with_rails_app/config/application.rb +16 -0
  91. data/spec/support/fixtures/projects/valid_with_rails_app/config/appsignal.yml +56 -0
  92. data/spec/support/fixtures/projects/valid_with_rails_app/config/environment.rb +5 -0
  93. data/spec/support/helpers/api_request_helper.rb +3 -2
  94. data/spec/support/helpers/config_helpers.rb +41 -11
  95. data/spec/support/helpers/dependency_helper.rb +8 -0
  96. data/spec/support/helpers/log_helpers.rb +1 -0
  97. data/spec/support/helpers/rails_helper.rb +6 -6
  98. data/spec/support/helpers/transaction_helpers.rb +2 -24
  99. data/spec/support/matchers/transaction.rb +3 -3
  100. data/spec/support/mocks/appsignal_mock.rb +3 -3
  101. data/spec/support/mocks/mock_probe.rb +2 -0
  102. data/spec/support/testing.rb +2 -2
  103. metadata +14 -23
  104. data/gemfiles/que_beta.gemfile +0 -5
  105. data/lib/appsignal/heartbeat.rb +0 -59
  106. data/lib/appsignal/helpers/heartbeats.rb +0 -44
  107. data/lib/appsignal/integrations/grape.rb +0 -35
  108. data/lib/appsignal/integrations/hanami.rb +0 -13
  109. data/lib/appsignal/integrations/padrino.rb +0 -13
  110. data/lib/appsignal/integrations/sinatra.rb +0 -13
  111. data/lib/appsignal/rack/generic_instrumentation.rb +0 -22
  112. data/lib/appsignal/rack/streaming_listener.rb +0 -28
  113. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -36
  114. data/spec/lib/appsignal/integrations/hanami_spec.rb +0 -17
  115. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -15
  116. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -15
  117. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +0 -81
  118. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +0 -69
  119. data/spec/support/fixtures/projects/valid/config/environments/development.rb +0 -0
  120. data/spec/support/fixtures/projects/valid/config/environments/production.rb +0 -0
  121. data/spec/support/fixtures/projects/valid/config/environments/test.rb +0 -0
  122. data/spec/support/rails/my_app.rb +0 -6
  123. /data/spec/support/fixtures/projects/{valid/config/application.rb → valid_with_rails_app/log/.gitkeep} +0 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module CheckIn
5
+ class Cron
6
+ class << self
7
+ # @api private
8
+ def transmitter
9
+ @transmitter ||= Appsignal::Transmitter.new(
10
+ "#{Appsignal.config[:logging_endpoint]}/check_ins/json"
11
+ )
12
+ end
13
+ end
14
+
15
+ # @api private
16
+ attr_reader :identifier, :digest
17
+
18
+ def initialize(identifier:)
19
+ @identifier = identifier
20
+ @digest = SecureRandom.hex(8)
21
+ end
22
+
23
+ def start
24
+ transmit_event("start")
25
+ end
26
+
27
+ def finish
28
+ transmit_event("finish")
29
+ end
30
+
31
+ private
32
+
33
+ def event(kind)
34
+ {
35
+ :identifier => @identifier,
36
+ :digest => @digest,
37
+ :kind => kind,
38
+ :timestamp => Time.now.utc.to_i,
39
+ :check_in_type => "cron"
40
+ }
41
+ end
42
+
43
+ def transmit_event(kind)
44
+ unless Appsignal.active?
45
+ Appsignal.internal_logger.debug(
46
+ "AppSignal not active, not transmitting cron check-in event"
47
+ )
48
+ return
49
+ end
50
+
51
+ response = self.class.transmitter.transmit(event(kind))
52
+
53
+ if response.code.to_i >= 200 && response.code.to_i < 300
54
+ Appsignal.internal_logger.debug(
55
+ "Transmitted cron check-in `#{identifier}` (#{digest}) #{kind} event"
56
+ )
57
+ else
58
+ Appsignal.internal_logger.error(
59
+ "Failed to transmit cron check-in #{kind} event: status code was #{response.code}"
60
+ )
61
+ end
62
+ rescue => e
63
+ Appsignal.internal_logger.error("Failed to transmit cron check-in #{kind} event: #{e}")
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module CheckIn
5
+ class << self
6
+ # Track cron check-ins.
7
+ #
8
+ # Track the execution of certain processes by sending a cron check-in.
9
+ #
10
+ # To track the duration of a piece of code, pass a block to {.cron}
11
+ # to report both when the process starts, and when it finishes.
12
+ #
13
+ # If an exception is raised within the block, the finish event will not
14
+ # be reported, triggering a notification about the missing cron check-in.
15
+ # The exception will bubble outside of the cron check-in block.
16
+ #
17
+ # @example Send a cron check-in
18
+ # Appsignal::CheckIn.cron("send_invoices")
19
+ #
20
+ # @example Send a cron check-in with duration
21
+ # Appsignal::CheckIn.cron("send_invoices") do
22
+ # # your code
23
+ # end
24
+ #
25
+ # @param name [String] name of the cron check-in to report.
26
+ # @yield the block to monitor.
27
+ # @return [void]
28
+ # @since 3.13.0
29
+ # @see https://docs.appsignal.com/check-ins/cron
30
+ def cron(identifier)
31
+ cron = Appsignal::CheckIn::Cron.new(:identifier => identifier)
32
+ output = nil
33
+
34
+ if block_given?
35
+ cron.start
36
+ output = yield
37
+ end
38
+
39
+ cron.finish
40
+ output
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ require "appsignal/check_in/cron"
@@ -77,6 +77,10 @@ module Appsignal
77
77
  # @return [void]
78
78
  # @api private
79
79
  def run(options = {})
80
+ # Do not start AppSignal on `Appsignal.start` and run the extension
81
+ # and agent in diagnose mode.
82
+ ENV["_APPSIGNAL_DIAGNOSE"] = "true"
83
+
80
84
  self.coloring = options.delete(:color) { true }
81
85
  $stdout.sync = true
82
86
  header
@@ -184,21 +188,13 @@ module Appsignal
184
188
  end
185
189
 
186
190
  def configure_appsignal(options)
187
- current_path = Dir.pwd
188
- initial_config = {}
189
- if rails_app?
190
- data[:app][:rails] = true
191
- current_path = Rails.root
192
- initial_config[:name] =
193
- Appsignal::Utils::RailsHelper.detected_rails_app_name
194
- initial_config[:log_path] = current_path.join("log")
195
- end
191
+ # Try and load the Rails app, if any.
192
+ # This will configure AppSignal through the config file or an
193
+ # initializer.
194
+ require_rails_app_if_present
196
195
 
197
- Appsignal._config = Appsignal::Config.new(
198
- current_path,
199
- options.fetch(:environment, ENV.fetch("RACK_ENV", ENV.fetch("RAILS_ENV", nil))),
200
- initial_config
201
- )
196
+ # If no config was found by loading the app, load with the defaults.
197
+ Appsignal.configure(options.fetch(:environment, nil))
202
198
  Appsignal.config.write_to_environment
203
199
  Appsignal._start_logger
204
200
  Appsignal.internal_logger.info("Starting AppSignal diagnose")
@@ -211,9 +207,9 @@ module Appsignal
211
207
  return
212
208
  end
213
209
 
214
- ENV["_APPSIGNAL_DIAGNOSE"] = "true"
210
+ # Requires _APPSIGNAL_DIAGNOSE to be set. This is set at the
211
+ # beginning of the diagnose CLI.
215
212
  diagnostics_report_string = Appsignal::Extension.diagnose
216
- ENV.delete("_APPSIGNAL_DIAGNOSE")
217
213
 
218
214
  begin
219
215
  report = JSON.parse(diagnostics_report_string)
@@ -473,14 +469,12 @@ module Appsignal
473
469
  :sources => {
474
470
  :default => Appsignal::Config::DEFAULT_CONFIG,
475
471
  :system => config.system_config,
472
+ :loaders => config.loaders_config,
476
473
  :initial => config.initial_config,
477
474
  :file => config.file_config,
478
475
  :env => config.env_config,
479
- :override => config.override_config
480
- },
481
- :modifiers => {
482
- "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR" =>
483
- ENV.fetch("APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR", "")
476
+ :override => config.override_config,
477
+ :dsl => config.dsl_config
484
478
  }
485
479
  }
486
480
  print_config_options(config)
@@ -523,11 +517,6 @@ module Appsignal
523
517
  end
524
518
  end
525
519
 
526
- puts
527
- puts "Configuration modifiers"
528
- puts " APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR: " \
529
- "#{data[:config][:modifiers]["APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR"].inspect}"
530
-
531
520
  puts "\nRead more about how the diagnose config output is rendered\n" \
532
521
  "https://docs.appsignal.com/ruby/command-line/diagnose.html"
533
522
  end
@@ -642,9 +631,29 @@ module Appsignal
642
631
  puts "\n"
643
632
  end
644
633
 
645
- def rails_app?
634
+ def require_rails_app_if_present
635
+ return unless rails_present?
636
+
637
+ # Mark app as Rails app
638
+ data[:app][:rails] = true
639
+ # Manually require the railtie, because it wasn't loaded when the CLI
640
+ # started and AppSignal loaded, because the `Rails` constant wasn't
641
+ # present.
642
+ require "appsignal/integrations/railtie"
643
+ # Start the Rails app, including railties and initializers.
644
+ require Appsignal::Utils::RailsHelper.environment_config_path
645
+ rescue LoadError, StandardError => error
646
+ print_empty_line
647
+ puts "ERROR: Error encountered while loading the Rails app"
648
+ puts "#{error.class}: #{error.message}"
649
+ puts error.backtrace
650
+ data[:app][:load_error] =
651
+ "#{error.class}: #{error.message}\n#{error.backtrace.join("\n")}"
652
+ end
653
+
654
+ def rails_present?
655
+ # Try and load the Rails gem
646
656
  require "rails"
647
- require File.expand_path(File.join(Dir.pwd, "config", "application.rb"))
648
657
  true
649
658
  rescue LoadError
650
659
  false
@@ -237,6 +237,10 @@ module Appsignal
237
237
  puts "How do you want to configure AppSignal?"
238
238
  puts " (1) a config file"
239
239
  puts " (2) environment variables"
240
+ puts
241
+ puts " See our docs for information on the different configuration methods: "
242
+ puts " https://docs.appsignal.com/ruby/configuration.html"
243
+ puts
240
244
  loop do
241
245
  print " Choose (1/2): "
242
246
  case ask_for_input
@@ -264,7 +268,7 @@ module Appsignal
264
268
  puts " export APPSIGNAL_APP_NAME=#{config[:name]}" if name_overwritten
265
269
  puts
266
270
  puts " See the documentation for more configuration options:"
267
- puts " https://docs.appsignal.com/gem-settings/configuration.html"
271
+ puts " https://docs.appsignal.com/ruby/configuration.html"
268
272
  press_any_key
269
273
  break
270
274
  end
@@ -8,16 +8,19 @@ require "tmpdir"
8
8
 
9
9
  module Appsignal
10
10
  class Config
11
- include Appsignal::Utils::StdoutAndLoggerMessage
12
-
13
11
  # @api private
14
12
  def self.loader_defaults
15
13
  @loader_defaults ||= []
16
14
  end
17
15
 
18
16
  # @api private
19
- def self.add_loader_defaults(name, options)
20
- loader_defaults << [name, options]
17
+ def self.add_loader_defaults(name, env: nil, root_path: nil, **options)
18
+ loader_defaults << {
19
+ :name => name,
20
+ :env => env,
21
+ :root_path => root_path,
22
+ :options => options.compact
23
+ }
21
24
  end
22
25
 
23
26
  # Determine which env AppSignal should initialize with.
@@ -32,7 +35,7 @@ module Appsignal
32
35
  return env if env
33
36
  end
34
37
 
35
- loader_defaults.reverse.each do |(_loader_name, loader_defaults)|
38
+ loader_defaults.reverse.each do |loader_defaults|
36
39
  env = loader_defaults[:env]
37
40
  return env if env
38
41
  end
@@ -43,7 +46,7 @@ module Appsignal
43
46
  # Determine which root path AppSignal should initialize with.
44
47
  # @api private
45
48
  def self.determine_root_path
46
- loader_defaults.reverse.each do |(_loader_name, loader_defaults)|
49
+ loader_defaults.reverse.each do |loader_defaults|
47
50
  root_path = loader_defaults[:root_path]
48
51
  return root_path if root_path
49
52
  end
@@ -55,9 +58,9 @@ module Appsignal
55
58
  DEFAULT_CONFIG = {
56
59
  :activejob_report_errors => "all",
57
60
  :ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
58
- :debug => false,
59
61
  :dns_servers => [],
60
62
  :enable_allocation_tracking => true,
63
+ :enable_at_exit_reporter => true,
61
64
  :enable_host_metrics => true,
62
65
  :enable_minutely_probes => true,
63
66
  :enable_statsd => true,
@@ -90,8 +93,8 @@ module Appsignal
90
93
  ],
91
94
  :send_environment_metadata => true,
92
95
  :send_params => true,
93
- :sidekiq_report_errors => "all",
94
- :transaction_debug_mode => false
96
+ :send_session_data => true,
97
+ :sidekiq_report_errors => "all"
95
98
  }.freeze
96
99
 
97
100
  # @api private
@@ -127,14 +130,13 @@ module Appsignal
127
130
  "APPSIGNAL_SIDEKIQ_REPORT_ERRORS" => :sidekiq_report_errors,
128
131
  "APPSIGNAL_STATSD_PORT" => :statsd_port,
129
132
  "APPSIGNAL_WORKING_DIRECTORY_PATH" => :working_directory_path,
130
- "APPSIGNAL_WORKING_DIR_PATH" => :working_dir_path,
131
133
  "APP_REVISION" => :revision
132
134
  }.freeze
133
135
  # @api private
134
136
  ENV_BOOLEAN_KEYS = {
135
137
  "APPSIGNAL_ACTIVE" => :active,
136
- "APPSIGNAL_DEBUG" => :debug,
137
138
  "APPSIGNAL_ENABLE_ALLOCATION_TRACKING" => :enable_allocation_tracking,
139
+ "APPSIGNAL_ENABLE_AT_EXIT_REPORTER" => :enable_at_exit_reporter,
138
140
  "APPSIGNAL_ENABLE_HOST_METRICS" => :enable_host_metrics,
139
141
  "APPSIGNAL_ENABLE_MINUTELY_PROBES" => :enable_minutely_probes,
140
142
  "APPSIGNAL_ENABLE_STATSD" => :enable_statsd,
@@ -152,9 +154,7 @@ module Appsignal
152
154
  "APPSIGNAL_RUNNING_IN_CONTAINER" => :running_in_container,
153
155
  "APPSIGNAL_SEND_ENVIRONMENT_METADATA" => :send_environment_metadata,
154
156
  "APPSIGNAL_SEND_PARAMS" => :send_params,
155
- "APPSIGNAL_SEND_SESSION_DATA" => :send_session_data,
156
- "APPSIGNAL_SKIP_SESSION_DATA" => :skip_session_data,
157
- "APPSIGNAL_TRANSACTION_DEBUG_MODE" => :transaction_debug_mode
157
+ "APPSIGNAL_SEND_SESSION_DATA" => :send_session_data
158
158
  }.freeze
159
159
  # @api private
160
160
  ENV_ARRAY_KEYS = {
@@ -178,11 +178,6 @@ module Appsignal
178
178
  # Used in diagnose report.
179
179
  # @api private
180
180
  # @return [Hash]
181
- # @!attribute [r] initial_config
182
- # Config detected on the system level.
183
- # Used in diagnose report.
184
- # @api private
185
- # @return [Hash]
186
181
  # @!attribute [r] file_config
187
182
  # Config loaded from `config/appsignal.yml` config file.
188
183
  # Used in diagnose report.
@@ -195,8 +190,8 @@ module Appsignal
195
190
  # @return [Hash]
196
191
  # @!attribute [r] config_hash
197
192
  # Config used by the AppSignal gem.
198
- # Combined Hash of the {system_config}, {initial_config}, {file_config},
199
- # {env_config} attributes.
193
+ # Combined Hash of the {system_config}, {file_config}, {env_config}
194
+ # attributes.
200
195
  # @see #[]
201
196
  # @see #[]=
202
197
  # @api private
@@ -204,8 +199,8 @@ module Appsignal
204
199
 
205
200
  # @api private
206
201
  attr_accessor :root_path, :env, :config_hash
207
- attr_reader :system_config, :initial_config, :file_config, :env_config,
208
- :override_config, :dsl_config
202
+ attr_reader :system_config, :loaders_config, :initial_config, :file_config,
203
+ :env_config, :override_config, :dsl_config
209
204
  # @api private
210
205
  attr_accessor :logger
211
206
 
@@ -215,9 +210,6 @@ module Appsignal
215
210
  # @param initial_env [String] The environment to load when AppSignal is started. It
216
211
  # will look for an environment with this name in the `config/appsignal.yml`
217
212
  # config file.
218
- # @param initial_config [Hash<String, Object>] The initial configuration to
219
- # use. This will be overwritten by the file config and environment
220
- # variables config.
221
213
  # @param logger [Logger] The logger to use for the AppSignal gem. This is
222
214
  # used by the configuration class only. Default:
223
215
  # {Appsignal.internal_logger}. See also {Appsignal.start}.
@@ -231,13 +223,10 @@ module Appsignal
231
223
  # Configuration load order
232
224
  # @see https://docs.appsignal.com/ruby/instrumentation/integrating-appsignal.html
233
225
  # How to integrate AppSignal manually
234
- def initialize( # rubocop:disable Metrics/ParameterLists
226
+ def initialize(
235
227
  root_path,
236
228
  initial_env,
237
- initial_config = {},
238
- logger = Appsignal.internal_logger,
239
- config_file = nil,
240
- load_on_new = true # rubocop:disable Style/OptionalBooleanParameter
229
+ logger = Appsignal.internal_logger
241
230
  )
242
231
  @root_path = root_path
243
232
  @config_file_error = false
@@ -249,20 +238,14 @@ module Appsignal
249
238
  @env = initial_env.to_s
250
239
  @config_hash = {}
251
240
  @system_config = {}
252
- @initial_config = initial_config
241
+ @loaders_config = {}
242
+ @initial_config = {}
253
243
  @file_config = {}
254
244
  @env_config = {}
255
245
  @override_config = {}
256
246
  @dsl_config = {} # Can be set using `Appsignal.configure`
257
247
 
258
- return unless load_on_new
259
-
260
- # Always override environment if set via this env var.
261
- # TODO: This is legacy behavior. In the `Appsignal.configure` method the
262
- # env argument is leading.
263
- @env = ENV["APPSIGNAL_APP_ENV"] if ENV.key?("APPSIGNAL_APP_ENV")
264
248
  load_config
265
- validate
266
249
  end
267
250
 
268
251
  # @api private
@@ -275,8 +258,17 @@ module Appsignal
275
258
  @system_config = detect_from_system
276
259
  merge(system_config)
277
260
 
278
- # Merge initial config
279
- merge(initial_config)
261
+ # Set defaults from loaders in reverse order so the first register
262
+ # loader's defaults overwrite all others
263
+ self.class.loader_defaults.reverse.each do |loader_defaults|
264
+ defaults = loader_defaults[:options]
265
+ @loaders_config.merge!(defaults.merge(
266
+ :root_path => loader_defaults[:root_path],
267
+ :env => loader_defaults[:env]
268
+ ))
269
+ merge(defaults)
270
+ end
271
+
280
272
  # Track origin of env
281
273
  @initial_config[:env] = @initial_env.to_s
282
274
 
@@ -290,13 +282,6 @@ module Appsignal
290
282
  # Track origin of env
291
283
  env_loaded_from_env = ENV.fetch("APPSIGNAL_APP_ENV", nil)
292
284
  @env_config[:env] = env_loaded_from_env if env_loaded_from_env
293
-
294
- # Load config overrides
295
- @override_config = determine_overrides
296
- merge(override_config)
297
-
298
- # Handle deprecated config options
299
- maintain_backwards_compatibility
300
285
  end
301
286
 
302
287
  # @api private
@@ -326,12 +311,12 @@ module Appsignal
326
311
 
327
312
  # @api private
328
313
  def log_level
329
- level = ::Logger::DEBUG if config_hash[:debug] || config_hash[:transaction_debug_mode]
330
314
  option = config_hash[:log_level]
331
- if option
332
- log_level_option = LOG_LEVEL_MAP[option]
333
- level = log_level_option if log_level_option
334
- end
315
+ level =
316
+ if option
317
+ log_level_option = LOG_LEVEL_MAP[option]
318
+ log_level_option
319
+ end
335
320
  level.nil? ? Appsignal::Config::DEFAULT_LOG_LEVEL : level
336
321
  end
337
322
 
@@ -379,7 +364,6 @@ module Appsignal
379
364
  ENV["_APPSIGNAL_BIND_ADDRESS"] = config_hash[:bind_address].to_s
380
365
  ENV["_APPSIGNAL_CA_FILE_PATH"] = config_hash[:ca_file_path].to_s
381
366
  ENV["_APPSIGNAL_CPU_COUNT"] = config_hash[:cpu_count].to_s
382
- ENV["_APPSIGNAL_DEBUG_LOGGING"] = config_hash[:debug].to_s
383
367
  ENV["_APPSIGNAL_DNS_SERVERS"] = config_hash[:dns_servers].join(",")
384
368
  ENV["_APPSIGNAL_ENABLE_HOST_METRICS"] = config_hash[:enable_host_metrics].to_s
385
369
  ENV["_APPSIGNAL_ENABLE_STATSD"] = config_hash[:enable_statsd].to_s
@@ -406,24 +390,24 @@ module Appsignal
406
390
  ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"] = config_hash[:running_in_container].to_s
407
391
  ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = config_hash[:send_environment_metadata].to_s
408
392
  ENV["_APPSIGNAL_STATSD_PORT"] = config_hash[:statsd_port].to_s
409
- ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"] = config_hash[:transaction_debug_mode].to_s
410
393
  if config_hash[:working_directory_path]
411
394
  ENV["_APPSIGNAL_WORKING_DIRECTORY_PATH"] = config_hash[:working_directory_path]
412
395
  end
413
- if config_hash[:working_dir_path]
414
- ENV["_APPSIGNAL_WORKING_DIR_PATH"] = config_hash[:working_dir_path]
415
- end
416
396
  ENV["_APP_REVISION"] = config_hash[:revision].to_s
417
397
  end
418
398
 
419
399
  # @api private
420
400
  def merge_dsl_options(options)
421
- @dsl_options = options
401
+ @dsl_config = options
422
402
  merge(options)
423
403
  end
424
404
 
425
405
  # @api private
426
406
  def validate
407
+ # Apply any overrides for invalid settings.
408
+ @override_config = determine_overrides
409
+ merge(override_config)
410
+
427
411
  # Strip path from endpoint so we're backwards compatible with
428
412
  # earlier versions of the gem.
429
413
  # TODO: Move to its own method, maybe in `#[]=`?
@@ -444,6 +428,17 @@ module Appsignal
444
428
  end
445
429
  end
446
430
 
431
+ # Deep freeze the config object so it cannot be modified during the runtime
432
+ # of the Ruby app.
433
+ #
434
+ # @api private
435
+ # @since 4.0.0
436
+ def freeze
437
+ super
438
+ config_hash.freeze
439
+ config_hash.transform_values(&:freeze)
440
+ end
441
+
447
442
  private
448
443
 
449
444
  def config_file
@@ -475,20 +470,9 @@ module Appsignal
475
470
  nil
476
471
  end
477
472
  rescue => e
478
- # TODO: Remove in the next major version
479
473
  @config_file_error = true
480
- extra_message =
481
- if inactive_on_config_file_error?
482
- "Not starting AppSignal because " \
483
- "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR is set."
484
- else
485
- "Skipping file config. In future versions AppSignal will not start " \
486
- "on a config file error. To opt-in to this new behavior set " \
487
- "'APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR=1' in your system " \
488
- "environment."
489
- end
490
474
  message = "An error occurred while loading the AppSignal config file. " \
491
- "#{extra_message}\n" \
475
+ "Not starting AppSignal.\n" \
492
476
  "File: #{config_file.inspect}\n" \
493
477
  "#{e.class.name}: #{e}"
494
478
  Kernel.warn "appsignal: #{message}"
@@ -496,27 +480,6 @@ module Appsignal
496
480
  nil
497
481
  end
498
482
 
499
- # Maintain backwards compatibility with deprecated config options.
500
- #
501
- # Add warnings for deprecated config options here if they have no
502
- # replacement, or should be non-functional.
503
- #
504
- # Add them to {determine_overrides} if replacement config options should be
505
- # set instead.
506
- #
507
- # Make sure to remove the contents of this method in the next major
508
- # version, but the method itself with an empty body can stick around as a
509
- # structure for future deprecations.
510
- def maintain_backwards_compatibility
511
- return unless config_hash.key?(:working_dir_path)
512
-
513
- stdout_and_logger_warning \
514
- "The `working_dir_path` option is deprecated, please use " \
515
- "`working_directory_path` instead and specify the " \
516
- "full path to the working directory",
517
- logger
518
- end
519
-
520
483
  def load_from_environment
521
484
  config = {}
522
485
 
@@ -556,32 +519,13 @@ module Appsignal
556
519
  end
557
520
 
558
521
  # Set config options based on the final user config. Fix any conflicting
559
- # config or set new config options based on deprecated config options.
560
- #
561
- # Make sure to remove behavior for deprecated config options in this method
562
- # in the next major version, but the method itself with an empty body can
563
- # stick around as a structure for future deprecations.
522
+ # config.
564
523
  def determine_overrides
565
524
  config = {}
566
525
  # If an error was detected during config file reading/parsing and the new
567
526
  # behavior is enabled to not start AppSignal on incomplete config, do not
568
527
  # start AppSignal.
569
- # TODO: Make default behavior in next major version. Remove
570
- # `inactive_on_config_file_error?` call.
571
- config[:active] = false if @config_file_error && inactive_on_config_file_error?
572
- skip_session_data = config_hash[:skip_session_data]
573
- send_session_data = config_hash[:send_session_data]
574
- if skip_session_data.nil? # Deprecated option is not set
575
- if send_session_data.nil? # Not configured by user
576
- config[:send_session_data] = true # Set default value
577
- end
578
- else
579
- stdout_and_logger_warning "The `skip_session_data` config option is " \
580
- "deprecated. Please use `send_session_data` instead.",
581
- logger
582
- # Not configured by user
583
- config[:send_session_data] = !skip_session_data if send_session_data.nil?
584
- end
528
+ config[:active] = false if @config_file_error
585
529
 
586
530
  if config_hash[:activejob_report_errors] == "discard" &&
587
531
  !Appsignal::Hooks::ActiveJobHook.version_7_1_or_higher?
@@ -603,12 +547,6 @@ module Appsignal
603
547
  end
604
548
  end
605
549
 
606
- # Does it use the new behavior?
607
- def inactive_on_config_file_error?
608
- value = ENV.fetch("APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR", false)
609
- ["1", "true"].include?(value)
610
- end
611
-
612
550
  # @api private
613
551
  class ConfigDSL
614
552
  attr_reader :dsl_options
@@ -70,14 +70,14 @@ module Appsignal
70
70
  end
71
71
 
72
72
  def add_params_to(transaction)
73
- transaction.set_params(
73
+ transaction.add_params(
74
74
  "controller" => "demo",
75
75
  "action" => "hello"
76
76
  )
77
77
  end
78
78
 
79
79
  def add_headers_to(transaction)
80
- transaction.set_headers(
80
+ transaction.add_headers(
81
81
  "REMOTE_ADDR" => "127.0.0.1",
82
82
  "REQUEST_METHOD" => "GET",
83
83
  "SERVER_NAME" => "localhost",
@@ -132,6 +132,9 @@ module Appsignal
132
132
  attach_function :appsignal_complete_transaction,
133
133
  [:pointer],
134
134
  :void
135
+ attach_function :appsignal_duplicate_transaction,
136
+ [:pointer, :appsignal_string],
137
+ :pointer
135
138
  attach_function :appsignal_transaction_to_json,
136
139
  [:pointer],
137
140
  :appsignal_string
@@ -432,6 +435,17 @@ module Appsignal
432
435
  Extension.appsignal_finish_transaction(pointer, gc_duration_ms)
433
436
  end
434
437
 
438
+ def duplicate(new_transaction_id)
439
+ duplicate_transaction = Extension.appsignal_duplicate_transaction(
440
+ pointer,
441
+ make_appsignal_string(new_transaction_id)
442
+ )
443
+
444
+ return if !duplicate_transaction || duplicate_transaction.null?
445
+
446
+ Transaction.new(duplicate_transaction)
447
+ end
448
+
435
449
  def complete
436
450
  Extension.appsignal_complete_transaction(pointer)
437
451
  end