appsignal 3.13.0 → 4.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +499 -487
  3. data/CHANGELOG.md +113 -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 +2 -15
  11. data/lib/appsignal/cli/diagnose.rb +37 -28
  12. data/lib/appsignal/cli/install.rb +5 -1
  13. data/lib/appsignal/config.rb +57 -119
  14. data/lib/appsignal/demo.rb +2 -2
  15. data/lib/appsignal/extension/jruby.rb +14 -0
  16. data/lib/appsignal/helpers/instrumentation.rb +139 -417
  17. data/lib/appsignal/helpers/metrics.rb +0 -16
  18. data/lib/appsignal/hooks/action_cable.rb +8 -8
  19. data/lib/appsignal/hooks/active_job.rb +2 -2
  20. data/lib/appsignal/hooks/at_exit.rb +37 -0
  21. data/lib/appsignal/hooks.rb +1 -16
  22. data/lib/appsignal/integrations/action_cable.rb +2 -2
  23. data/lib/appsignal/integrations/capistrano/appsignal.cap +2 -4
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -4
  25. data/lib/appsignal/integrations/delayed_job_plugin.rb +3 -3
  26. data/lib/appsignal/integrations/que.rb +2 -2
  27. data/lib/appsignal/integrations/railtie.rb +26 -59
  28. data/lib/appsignal/integrations/rake.rb +2 -2
  29. data/lib/appsignal/integrations/resque.rb +2 -2
  30. data/lib/appsignal/integrations/shoryuken.rb +4 -4
  31. data/lib/appsignal/integrations/sidekiq.rb +3 -3
  32. data/lib/appsignal/integrations/webmachine.rb +2 -2
  33. data/lib/appsignal/loaders.rb +1 -1
  34. data/lib/appsignal/probes.rb +0 -9
  35. data/lib/appsignal/rack/abstract_middleware.rb +4 -26
  36. data/lib/appsignal/rack/event_handler.rb +4 -4
  37. data/lib/appsignal/rack/rails_instrumentation.rb +1 -1
  38. data/lib/appsignal/rack.rb +0 -25
  39. data/lib/appsignal/sample_data.rb +95 -0
  40. data/lib/appsignal/transaction.rb +235 -361
  41. data/lib/appsignal/utils/rails_helper.rb +4 -0
  42. data/lib/appsignal/version.rb +1 -1
  43. data/lib/appsignal.rb +19 -71
  44. data/spec/lib/appsignal/auth_check_spec.rb +1 -1
  45. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  46. data/spec/lib/appsignal/capistrano3_spec.rb +53 -13
  47. data/spec/lib/appsignal/check_in_spec.rb +1 -207
  48. data/spec/lib/appsignal/cli/demo_spec.rb +7 -27
  49. data/spec/lib/appsignal/cli/diagnose_spec.rb +145 -110
  50. data/spec/lib/appsignal/config_spec.rb +304 -379
  51. data/spec/lib/appsignal/extension_install_failure_spec.rb +5 -1
  52. data/spec/lib/appsignal/extension_spec.rb +5 -1
  53. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +1 -1
  54. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +1 -2
  55. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +1 -0
  56. data/spec/lib/appsignal/hooks/activejob_spec.rb +7 -12
  57. data/spec/lib/appsignal/hooks/at_exit_spec.rb +72 -0
  58. data/spec/lib/appsignal/hooks/gvl_spec.rb +10 -5
  59. data/spec/lib/appsignal/hooks/http_spec.rb +3 -3
  60. data/spec/lib/appsignal/hooks/net_http_spec.rb +3 -3
  61. data/spec/lib/appsignal/hooks/rake_spec.rb +6 -9
  62. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -10
  63. data/spec/lib/appsignal/hooks/redis_spec.rb +4 -7
  64. data/spec/lib/appsignal/hooks/resque_spec.rb +3 -5
  65. data/spec/lib/appsignal/hooks_spec.rb +0 -41
  66. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +29 -20
  67. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +4 -9
  68. data/spec/lib/appsignal/integrations/railtie_spec.rb +179 -157
  69. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +3 -5
  70. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +48 -62
  71. data/spec/lib/appsignal/loaders/hanami_spec.rb +6 -9
  72. data/spec/lib/appsignal/loaders/padrino_spec.rb +6 -10
  73. data/spec/lib/appsignal/loaders/sinatra_spec.rb +6 -9
  74. data/spec/lib/appsignal/loaders_spec.rb +8 -1
  75. data/spec/lib/appsignal/marker_spec.rb +1 -1
  76. data/spec/lib/appsignal/probes_spec.rb +4 -83
  77. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +4 -63
  78. data/spec/lib/appsignal/rack/event_handler_spec.rb +18 -15
  79. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +3 -11
  80. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +4 -5
  81. data/spec/lib/appsignal/sample_data_spec.rb +174 -0
  82. data/spec/lib/appsignal/transaction_spec.rb +791 -1031
  83. data/spec/lib/appsignal/transmitter_spec.rb +6 -8
  84. data/spec/lib/appsignal_spec.rb +294 -643
  85. data/spec/spec_helper.rb +1 -3
  86. data/spec/support/fixtures/projects/valid/config/appsignal.yml +4 -7
  87. data/spec/support/fixtures/projects/valid_with_rails_app/config/application.rb +16 -0
  88. data/spec/support/fixtures/projects/valid_with_rails_app/config/appsignal.yml +56 -0
  89. data/spec/support/fixtures/projects/valid_with_rails_app/config/environment.rb +5 -0
  90. data/spec/support/helpers/api_request_helper.rb +3 -2
  91. data/spec/support/helpers/config_helpers.rb +41 -11
  92. data/spec/support/helpers/dependency_helper.rb +8 -0
  93. data/spec/support/helpers/log_helpers.rb +1 -0
  94. data/spec/support/helpers/rails_helper.rb +6 -6
  95. data/spec/support/helpers/transaction_helpers.rb +2 -24
  96. data/spec/support/matchers/transaction.rb +3 -3
  97. data/spec/support/mocks/appsignal_mock.rb +3 -3
  98. data/spec/support/mocks/mock_probe.rb +2 -0
  99. data/spec/support/testing.rb +2 -2
  100. metadata +12 -22
  101. data/gemfiles/que_beta.gemfile +0 -5
  102. data/lib/appsignal/helpers/heartbeat.rb +0 -20
  103. data/lib/appsignal/integrations/grape.rb +0 -35
  104. data/lib/appsignal/integrations/hanami.rb +0 -13
  105. data/lib/appsignal/integrations/padrino.rb +0 -13
  106. data/lib/appsignal/integrations/sinatra.rb +0 -13
  107. data/lib/appsignal/rack/generic_instrumentation.rb +0 -22
  108. data/lib/appsignal/rack/streaming_listener.rb +0 -28
  109. data/spec/lib/appsignal/integrations/grape_spec.rb +0 -36
  110. data/spec/lib/appsignal/integrations/hanami_spec.rb +0 -17
  111. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -15
  112. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -15
  113. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +0 -81
  114. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +0 -69
  115. data/spec/support/fixtures/projects/valid/config/environments/development.rb +0 -0
  116. data/spec/support/fixtures/projects/valid/config/environments/production.rb +0 -0
  117. data/spec/support/fixtures/projects/valid/config/environments/test.rb +0 -0
  118. data/spec/support/rails/my_app.rb +0 -6
  119. /data/spec/support/fixtures/projects/{valid/config/application.rb → valid_with_rails_app/log/.gitkeep} +0 -0
@@ -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