appsignal 3.11.0-java → 3.12.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -0
  3. data/Rakefile +1 -1
  4. data/lib/appsignal/cli/diagnose.rb +1 -1
  5. data/lib/appsignal/config.rb +150 -32
  6. data/lib/appsignal/demo.rb +1 -6
  7. data/lib/appsignal/helpers/instrumentation.rb +2 -2
  8. data/lib/appsignal/integrations/grape.rb +7 -0
  9. data/lib/appsignal/integrations/hanami.rb +8 -43
  10. data/lib/appsignal/integrations/padrino.rb +8 -73
  11. data/lib/appsignal/integrations/railtie.rb +35 -13
  12. data/lib/appsignal/integrations/sinatra.rb +8 -19
  13. data/lib/appsignal/loaders/grape.rb +13 -0
  14. data/lib/appsignal/loaders/hanami.rb +40 -0
  15. data/lib/appsignal/loaders/padrino.rb +68 -0
  16. data/lib/appsignal/loaders/sinatra.rb +24 -0
  17. data/lib/appsignal/loaders.rb +92 -0
  18. data/lib/appsignal/rack/abstract_middleware.rb +2 -1
  19. data/lib/appsignal/rack/event_handler.rb +5 -5
  20. data/lib/appsignal/rack.rb +6 -0
  21. data/lib/appsignal/version.rb +1 -1
  22. data/lib/appsignal.rb +163 -9
  23. data/spec/lib/appsignal/cli/demo_spec.rb +0 -1
  24. data/spec/lib/appsignal/cli/diagnose/paths_spec.rb +1 -1
  25. data/spec/lib/appsignal/cli/diagnose_spec.rb +0 -1
  26. data/spec/lib/appsignal/config_spec.rb +153 -1
  27. data/spec/lib/appsignal/demo_spec.rb +1 -2
  28. data/spec/lib/appsignal/environment_spec.rb +4 -2
  29. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +3 -6
  30. data/spec/lib/appsignal/hooks/activejob_spec.rb +3 -3
  31. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +4 -7
  32. data/spec/lib/appsignal/hooks/excon_spec.rb +3 -6
  33. data/spec/lib/appsignal/hooks/gvl_spec.rb +2 -2
  34. data/spec/lib/appsignal/hooks/http_spec.rb +1 -3
  35. data/spec/lib/appsignal/hooks/net_http_spec.rb +1 -1
  36. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -8
  37. data/spec/lib/appsignal/hooks/redis_spec.rb +3 -6
  38. data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
  39. data/spec/lib/appsignal/hooks/sequel_spec.rb +3 -5
  40. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +1 -1
  41. data/spec/lib/appsignal/hooks/webmachine_spec.rb +1 -1
  42. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +2 -2
  43. data/spec/lib/appsignal/integrations/grape_spec.rb +36 -0
  44. data/spec/lib/appsignal/integrations/hanami_spec.rb +9 -178
  45. data/spec/lib/appsignal/integrations/http_spec.rb +1 -5
  46. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +4 -2
  47. data/spec/lib/appsignal/integrations/net_http_spec.rb +1 -1
  48. data/spec/lib/appsignal/integrations/object_spec.rb +1 -3
  49. data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -330
  50. data/spec/lib/appsignal/integrations/railtie_spec.rb +275 -191
  51. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +1 -1
  52. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +11 -9
  53. data/spec/lib/appsignal/integrations/sinatra_spec.rb +9 -104
  54. data/spec/lib/appsignal/loaders/grape_spec.rb +12 -0
  55. data/spec/lib/appsignal/loaders/hanami_spec.rb +95 -0
  56. data/spec/lib/appsignal/loaders/padrino_spec.rb +277 -0
  57. data/spec/lib/appsignal/loaders/sinatra_spec.rb +47 -0
  58. data/spec/lib/appsignal/loaders_spec.rb +137 -0
  59. data/spec/lib/appsignal/probes/sidekiq_spec.rb +1 -1
  60. data/spec/lib/appsignal/probes_spec.rb +6 -5
  61. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +3 -2
  62. data/spec/lib/appsignal/rack/event_handler_spec.rb +33 -0
  63. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +1 -1
  64. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +2 -35
  65. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +1 -1
  66. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -3
  67. data/spec/lib/appsignal/span_spec.rb +1 -3
  68. data/spec/lib/appsignal/transaction_spec.rb +61 -70
  69. data/spec/lib/appsignal_spec.rb +284 -26
  70. data/spec/lib/puma/appsignal_spec.rb +0 -3
  71. data/spec/spec_helper.rb +5 -4
  72. data/spec/support/helpers/config_helpers.rb +2 -1
  73. data/spec/support/helpers/loader_helper.rb +21 -0
  74. data/spec/support/matchers/transaction.rb +3 -2
  75. data/spec/support/stubs/appsignal/loaders/loader_stub.rb +7 -0
  76. data/spec/support/testing.rb +46 -0
  77. metadata +15 -2
@@ -1,24 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "appsignal"
4
- require "appsignal/rack/sinatra_instrumentation"
5
4
 
6
- Appsignal.internal_logger.debug("Loading Sinatra (#{Sinatra::VERSION}) integration")
5
+ Appsignal::Utils::StdoutAndLoggerMessage.warning(
6
+ "The 'require \"appsignal/integrations/sinatra\"' file require integration " \
7
+ "method is deprecated. " \
8
+ "Please follow the Sinatra setup guide in our docs for the new method: " \
9
+ "https://docs.appsignal.com/ruby/integrations/sinatra.html"
10
+ )
7
11
 
8
- unless Appsignal.active?
9
- app_settings = ::Sinatra::Application.settings
10
- Appsignal.config = Appsignal::Config.new(
11
- app_settings.root || Dir.pwd,
12
- app_settings.environment
13
- )
14
-
15
- Appsignal.start
16
- end
17
-
18
- if Appsignal.active?
19
- ::Sinatra::Base.use(
20
- ::Rack::Events,
21
- [Appsignal::Rack::EventHandler.new]
22
- )
23
- ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
24
- end
12
+ Appsignal.load(:sinatra)
13
+ Appsignal.start
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class GrapeLoader < Loader
6
+ register :grape
7
+
8
+ def on_load
9
+ require "appsignal/rack/grape_middleware"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class HanamiLoader < Loader
6
+ register :hanami
7
+
8
+ def on_load
9
+ hanami_app_config = ::Hanami.app.config
10
+ register_config_defaults(
11
+ :root_path => hanami_app_config.root.to_s,
12
+ :env => hanami_app_config.env
13
+ )
14
+ end
15
+
16
+ def on_start
17
+ require "appsignal/rack/hanami_middleware"
18
+
19
+ hanami_app_config = ::Hanami.app.config
20
+ hanami_app_config.middleware.use(
21
+ ::Rack::Events,
22
+ [Appsignal::Rack::EventHandler.new]
23
+ )
24
+ hanami_app_config.middleware.use(Appsignal::Rack::HanamiMiddleware)
25
+
26
+ ::Hanami::Action.prepend Appsignal::Loaders::HanamiLoader::HanamiIntegration
27
+ end
28
+
29
+ module HanamiIntegration
30
+ def call(env)
31
+ super
32
+ ensure
33
+ transaction = env[::Appsignal::Rack::APPSIGNAL_TRANSACTION]
34
+
35
+ transaction&.set_action_if_nil(self.class.name)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class PadrinoLoader < Loader
6
+ register :padrino
7
+
8
+ def on_load
9
+ register_config_defaults(
10
+ :root_path => Padrino.mounted_root,
11
+ :env => Padrino.env
12
+ )
13
+ end
14
+
15
+ def on_start
16
+ require "appsignal/rack/sinatra_instrumentation"
17
+
18
+ Padrino::Application.prepend(Appsignal::Loaders::PadrinoLoader::PadrinoIntegration)
19
+
20
+ Padrino.before_load do
21
+ Padrino.use ::Rack::Events, [Appsignal::Rack::EventHandler.new]
22
+ Padrino.use Appsignal::Rack::SinatraBaseInstrumentation,
23
+ :instrument_event_name => "process_action.padrino"
24
+ end
25
+ end
26
+
27
+ module PadrinoIntegration
28
+ def route!(base = settings, pass_block = nil)
29
+ return super if !Appsignal.active? || env["sinatra.static_file"]
30
+
31
+ begin
32
+ super
33
+ ensure
34
+ transaction = Appsignal::Transaction.current
35
+ transaction.set_action_if_nil(get_payload_action(request))
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def get_payload_action(request)
42
+ # Short-circuit is there's no request object to obtain information from
43
+ return settings.name.to_s unless request
44
+
45
+ # Newer versions expose the action / controller on the request class.
46
+ # Newer versions also still expose a route_obj so we must prioritize the
47
+ # action/fullpath methods.
48
+ # The `request.action` and `request.controller` values are `nil` when a
49
+ # endpoint is not found, `""` if not specified by the user.
50
+ controller_name = request.controller if request.respond_to?(:controller)
51
+ action_name = request.action if request.respond_to?(:action)
52
+ action_name ||= ""
53
+
54
+ return "#{settings.name}:#{controller_name}##{action_name}" unless action_name.empty?
55
+
56
+ # Older versions of Padrino work with a route object
57
+ if request.respond_to?(:route_obj) && request.route_obj
58
+ return "#{settings.name}:#{request.route_obj.original_path}"
59
+ end
60
+
61
+ # Fall back to the application name if we haven't found an action name in
62
+ # any previous methods.
63
+ "#{settings.name}#unknown"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Loaders
5
+ class SinatraLoader < Loader
6
+ register :sinatra
7
+
8
+ def on_load
9
+ app_settings = ::Sinatra::Application.settings
10
+ register_config_defaults(
11
+ :root_path => app_settings.root,
12
+ :env => app_settings.environment
13
+ )
14
+ end
15
+
16
+ def on_start
17
+ require "appsignal/rack/sinatra_instrumentation"
18
+
19
+ ::Sinatra::Base.use(::Rack::Events, [Appsignal::Rack::EventHandler.new])
20
+ ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module Loaders
6
+ class << self
7
+ def loaders
8
+ @loaders ||= {}
9
+ end
10
+
11
+ def instances
12
+ @instances ||= {}
13
+ end
14
+
15
+ def register(name, klass)
16
+ loaders[name.to_sym] = klass
17
+ end
18
+
19
+ def registered?(name)
20
+ loaders.key?(name)
21
+ end
22
+
23
+ def unregister(name)
24
+ loaders.delete(name)
25
+ end
26
+
27
+ def load(name_str)
28
+ name = name_str.to_sym
29
+
30
+ unless registered?(name)
31
+ require_loader(name)
32
+ unless registered?(name)
33
+ Appsignal.internal_logger
34
+ .warn("No loader found with the name '#{name}'.")
35
+ return
36
+ end
37
+ end
38
+
39
+ Appsignal.internal_logger.debug("Loading '#{name}' loader")
40
+
41
+ begin
42
+ loader_klass = loaders[name]
43
+ loader = loader_klass.new
44
+ instances[name] = loader
45
+ loader.on_load if loader.respond_to?(:on_load)
46
+ rescue => e
47
+ Appsignal.internal_logger.error(
48
+ "An error occurred while loading the '#{name}' loader: " \
49
+ "#{e.class}: #{e.message}\n#{e.backtrace}"
50
+ )
51
+ end
52
+ end
53
+
54
+ def start
55
+ instances.each do |name, instance|
56
+ Appsignal.internal_logger.debug("Starting '#{name}' loader")
57
+ begin
58
+ instance.on_start if instance.respond_to?(:on_start)
59
+ rescue => e
60
+ Appsignal.internal_logger.error(
61
+ "An error occurred while starting the '#{name}' loader: " \
62
+ "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
63
+ )
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def require_loader(name)
71
+ require "appsignal/loaders/#{name}"
72
+ rescue LoadError
73
+ nil
74
+ end
75
+ end
76
+
77
+ class Loader
78
+ class << self
79
+ attr_reader :loader_name
80
+
81
+ def register(name)
82
+ @loader_name = name
83
+ Loaders.register(name, self)
84
+ end
85
+ end
86
+
87
+ def register_config_defaults(options)
88
+ Appsignal::Config.add_loader_defaults(self.class.loader_name, options)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -91,9 +91,10 @@ module Appsignal
91
91
  def call_app(env, transaction)
92
92
  status, headers, obody = @app.call(env)
93
93
  body =
94
- if obody.is_a? Appsignal::Rack::BodyWrapper
94
+ if env[Appsignal::Rack::APPSIGNAL_RESPONSE_INSTRUMENTED]
95
95
  obody
96
96
  else
97
+ env[Appsignal::Rack::APPSIGNAL_RESPONSE_INSTRUMENTED] = true
97
98
  # Instrument response body and closing of the response body
98
99
  Appsignal::Rack::BodyWrapper.wrap(obody, transaction)
99
100
  end
@@ -2,11 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Rack
5
- APPSIGNAL_TRANSACTION = "appsignal.transaction"
6
- APPSIGNAL_EVENT_HANDLER_ID = "appsignal.event_handler_id"
7
- APPSIGNAL_EVENT_HANDLER_HAS_ERROR = "appsignal.event_handler.error"
8
- RACK_AFTER_REPLY = "rack.after_reply"
9
-
10
5
  # Instrumentation middleware using Rack's Events module.
11
6
  #
12
7
  # We recommend using this in combination with the
@@ -56,6 +51,8 @@ module Appsignal
56
51
 
57
52
  # @api private
58
53
  def on_start(request, _response)
54
+ return unless Appsignal.active?
55
+
59
56
  event_handler = self
60
57
  self.class.safe_execution("Appsignal::Rack::EventHandler#on_start") do
61
58
  request.env[APPSIGNAL_EVENT_HANDLER_ID] ||= id
@@ -90,6 +87,8 @@ module Appsignal
90
87
 
91
88
  # @api private
92
89
  def on_error(request, _response, error)
90
+ return unless Appsignal.active?
91
+
93
92
  self.class.safe_execution("Appsignal::Rack::EventHandler#on_error") do
94
93
  return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
95
94
 
@@ -103,6 +102,7 @@ module Appsignal
103
102
 
104
103
  # @api private
105
104
  def on_finish(request, response)
105
+ return unless Appsignal.active?
106
106
  return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
107
107
 
108
108
  transaction = request.env[APPSIGNAL_TRANSACTION]
@@ -3,6 +3,12 @@
3
3
  module Appsignal
4
4
  # @api private
5
5
  module Rack
6
+ APPSIGNAL_TRANSACTION = "appsignal.transaction"
7
+ APPSIGNAL_EVENT_HANDLER_ID = "appsignal.event_handler_id"
8
+ APPSIGNAL_EVENT_HANDLER_HAS_ERROR = "appsignal.event_handler.error"
9
+ APPSIGNAL_RESPONSE_INSTRUMENTED = "appsignal.response_instrumentation_active"
10
+ RACK_AFTER_REPLY = "rack.after_reply"
11
+
6
12
  class Utils
7
13
  # Fetch the queue start time from the request environment.
8
14
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.11.0"
4
+ VERSION = "3.12.1"
5
5
  end
data/lib/appsignal.rb CHANGED
@@ -22,8 +22,8 @@ module Appsignal
22
22
  include Helpers::Instrumentation
23
23
  include Helpers::Metrics
24
24
 
25
- # Accessor for the AppSignal configuration.
26
- # Return the current AppSignal configuration.
25
+ # The loaded AppSignal configuration.
26
+ # Returns the current AppSignal configuration.
27
27
  #
28
28
  # Can return `nil` if no configuration has been set or automatically loaded
29
29
  # by an automatic integration or by calling {.start}.
@@ -31,12 +31,31 @@ module Appsignal
31
31
  # @example
32
32
  # Appsignal.config
33
33
  #
34
- # @example Setting the configuration
35
- # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
36
- #
37
34
  # @return [Config, nil]
35
+ # @see configure
36
+ # @see Config
37
+ attr_reader :config
38
+
39
+ # Set the AppSignal config.
40
+ #
41
+ # @deprecated Use {Appsignal.configure} instead.
42
+ # @param conf [Appsignal::Config]
43
+ # @return [void]
38
44
  # @see Config
39
- attr_accessor :config
45
+ def config=(conf)
46
+ Appsignal::Utils::StdoutAndLoggerMessage.warning \
47
+ "Configuring AppSignal with `Appsignal.config=` is deprecated. " \
48
+ "Use `Appsignal.configure { |config| ... }` to configure AppSignal. " \
49
+ "https://docs.appsignal.com/ruby/configuration.html\n" \
50
+ "#{caller.first}"
51
+ @config = conf
52
+ end
53
+
54
+ # @api private
55
+ def _config=(conf)
56
+ @config = conf
57
+ end
58
+
40
59
  # Accessor for toggle if the AppSignal C-extension is loaded.
41
60
  #
42
61
  # Can be `nil` if extension has not been loaded yet. See
@@ -87,7 +106,9 @@ module Appsignal
87
106
  # Appsignal.start
88
107
  #
89
108
  # @example with custom loaded configuration
90
- # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
109
+ # Appsignal.configure(:production) do |config|
110
+ # config.ignore_actions = ["My action"]
111
+ # end
91
112
  # Appsignal.start
92
113
  #
93
114
  # @return [void]
@@ -109,11 +130,13 @@ module Appsignal
109
130
 
110
131
  if config.valid?
111
132
  if config.active?
133
+ @started = true
112
134
  internal_logger.info "Starting AppSignal #{Appsignal::VERSION} " \
113
135
  "(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
114
136
  config.write_to_environment
115
137
  Appsignal::Extension.start
116
138
  Appsignal::Hooks.load_hooks
139
+ Appsignal::Loaders.start
117
140
 
118
141
  if config[:enable_allocation_tracking] && !Appsignal::System.jruby?
119
142
  Appsignal::Extension.install_allocation_event_hook
@@ -147,14 +170,102 @@ module Appsignal
147
170
  # @since 1.0.0
148
171
  def stop(called_by = nil)
149
172
  if called_by
150
- internal_logger.debug("Stopping appsignal (#{called_by})")
173
+ internal_logger.debug("Stopping AppSignal (#{called_by})")
151
174
  else
152
- internal_logger.debug("Stopping appsignal")
175
+ internal_logger.debug("Stopping AppSignal")
153
176
  end
154
177
  Appsignal::Extension.stop
155
178
  Appsignal::Probes.stop
156
179
  end
157
180
 
181
+ # Configure the AppSignal Ruby gem using a DSL.
182
+ #
183
+ # Pass a block to the configure method to configure the Ruby gem.
184
+ #
185
+ # Each config option defined in our docs can be fetched, set and modified
186
+ # via a helper method in the given block.
187
+ #
188
+ # After AppSignal has started using {start}, the configuration can not be
189
+ # modified. Any calls to this helper will be ignored.
190
+ #
191
+ # This helper should not be used to configure multiple environments, like
192
+ # done in the YAML file. Configure the environment you want active when the
193
+ # application starts.
194
+ #
195
+ # @example Configure AppSignal for the application
196
+ # Appsignal.configure do |config|
197
+ # config.path = "/the/app/path"
198
+ # config.active = ENV["APP_ACTIVE"] == "true"
199
+ # config.push_api_key = File.read("appsignal_key.txt").chomp
200
+ # config.ignore_actions = ENDPOINTS.select { |e| e.public? }.map(&:name)
201
+ # config.request_headers << "MY_CUSTOM_HEADER"
202
+ # end
203
+ #
204
+ # @example Configure AppSignal for the application and select the environment
205
+ # Appsignal.configure(:production) do |config|
206
+ # config.active = true
207
+ # end
208
+ #
209
+ # @example Automatically detects the app environment
210
+ # # Tries to determine the app environment automatically from the
211
+ # # environment and the libraries it integrates with.
212
+ # ENV["RACK_ENV"] = "production"
213
+ #
214
+ # Appsignal.configure do |config|
215
+ # config.env # => "production"
216
+ # end
217
+ #
218
+ # @example Calling configure multiple times for different environments resets the configuration
219
+ # Appsignal.configure(:development) do |config|
220
+ # config.ignore_actions = ["My action"]
221
+ # end
222
+ #
223
+ # Appsignal.configure(:production) do |config|
224
+ # config.ignore_actions # => []
225
+ # end
226
+ #
227
+ # @example Load config without a block
228
+ # # This will require either ENV vars being set
229
+ # # or the config/appsignal.yml being present
230
+ # Appsignal.configure
231
+ # # Or for the environment given as an argument
232
+ # Appsignal.configure(:production)
233
+ #
234
+ # @yield [Config] Gives the {Config} instance to the block.
235
+ # @return [void]
236
+ # @see config
237
+ # @see Config
238
+ # @see https://docs.appsignal.com/ruby/configuration.html Configuration guide
239
+ # @see https://docs.appsignal.com/ruby/configuration/options.html Configuration options
240
+ def configure(env = nil)
241
+ if Appsignal.started?
242
+ Appsignal.internal_logger
243
+ .warn("AppSignal is already started. Ignoring `Appsignal.configure` call.")
244
+ return
245
+ end
246
+
247
+ if config && config.env == env.to_s
248
+ config
249
+ else
250
+ self._config = Appsignal::Config.new(
251
+ Dir.pwd,
252
+ env || ENV["APPSIGNAL_APP_ENV"] || ENV["RAILS_ENV"] || ENV.fetch("RACK_ENV", nil),
253
+ {},
254
+ Appsignal.internal_logger,
255
+ nil,
256
+ false
257
+ )
258
+ config.load_config
259
+ end
260
+
261
+ config_dsl = Appsignal::Config::ConfigDSL.new(config)
262
+ if block_given?
263
+ yield config_dsl
264
+ config.merge_dsl_options(config_dsl.dsl_options)
265
+ end
266
+ config.validate
267
+ end
268
+
158
269
  def forked
159
270
  return unless active?
160
271
 
@@ -163,6 +274,38 @@ module Appsignal
163
274
  Appsignal::Extension.start
164
275
  end
165
276
 
277
+ # Load an AppSignal integration.
278
+ #
279
+ # Load one of the supported integrations via our loader system.
280
+ # This will set config defaults and integratie with the library if
281
+ # AppSignal is active upon start.
282
+ #
283
+ # @example Load Sinatra integrations
284
+ # # First load the integration
285
+ # Appsignal.load(:sinatra)
286
+ # # Start AppSignal
287
+ # Appsignal.start
288
+ #
289
+ # @example Load Sinatra integrations and define custom config
290
+ # # First load the integration
291
+ # Appsignal.load(:sinatra)
292
+ #
293
+ # # Customize config
294
+ # Appsignal.configure do |config|
295
+ # config.ignore_actions = ["GET /ping"]
296
+ # end
297
+ #
298
+ #
299
+ # # Start AppSignal
300
+ # Appsignal.start
301
+ #
302
+ # @param integration_name [String, Symbol] Name of the integration to load.
303
+ # @return [void]
304
+ # @since 3.12.0
305
+ def load(integration_name)
306
+ Loaders.load(integration_name)
307
+ end
308
+
166
309
  # @api private
167
310
  def get_server_state(key)
168
311
  Appsignal::Extension.get_server_state(key)
@@ -238,6 +381,16 @@ module Appsignal
238
381
  !!extension_loaded
239
382
  end
240
383
 
384
+ # Returns if {.start} has been called before with a valid config to start
385
+ # AppSignal.
386
+ #
387
+ # @return [Boolean]
388
+ # @see Extension
389
+ # @since 3.12.0
390
+ def started?
391
+ defined?(@started) ? @started : false
392
+ end
393
+
241
394
  # Returns the active state of the AppSignal integration.
242
395
  #
243
396
  # Conditions apply for AppSignal to be marked as active:
@@ -314,6 +467,7 @@ module Appsignal
314
467
  end
315
468
  end
316
469
 
470
+ require "appsignal/loaders"
317
471
  require "appsignal/environment"
318
472
  require "appsignal/system"
319
473
  require "appsignal/utils"
@@ -13,7 +13,6 @@ describe Appsignal::CLI::Demo do
13
13
  ENV.delete("RACK_ENV")
14
14
  stub_api_request config, "auth"
15
15
  end
16
- after { Appsignal.config = nil }
17
16
 
18
17
  def run
19
18
  run_within_dir project_fixture_path
@@ -4,7 +4,7 @@ require "appsignal/cli/diagnose/paths"
4
4
 
5
5
  describe Appsignal::CLI::Diagnose::Paths do
6
6
  describe "#paths" do
7
- before { Appsignal.config = project_fixture_config }
7
+ before { start_agent }
8
8
 
9
9
  it "returns gem installation path as package_install_path" do
10
10
  expect(described_class.new.paths[:package_install_path]).to eq(
@@ -67,7 +67,6 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
67
67
  capture_diagnatics_report_request
68
68
  end
69
69
  before(:send_report => :no_cli_option) { options["no-send-report"] = nil }
70
- after { Appsignal.config = nil }
71
70
 
72
71
  def capture_diagnatics_report_request
73
72
  stub_diagnostics_report_request.to_rack(DiagnosticsReportEndpoint)