appsignal 3.10.0 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +197 -0
  4. data/Gemfile +1 -0
  5. data/Rakefile +1 -1
  6. data/benchmark.rake +99 -42
  7. data/lib/appsignal/cli/demo.rb +0 -1
  8. data/lib/appsignal/cli/diagnose.rb +1 -1
  9. data/lib/appsignal/config.rb +204 -130
  10. data/lib/appsignal/demo.rb +16 -26
  11. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  12. data/lib/appsignal/event_formatter.rb +3 -2
  13. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  14. data/lib/appsignal/hooks/action_cable.rb +21 -16
  15. data/lib/appsignal/hooks/active_job.rb +14 -8
  16. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  17. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  18. data/lib/appsignal/integrations/action_cable.rb +5 -7
  19. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  20. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  21. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  22. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  23. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  24. data/lib/appsignal/integrations/excon.rb +1 -0
  25. data/lib/appsignal/integrations/grape.rb +7 -0
  26. data/lib/appsignal/integrations/hanami.rb +8 -43
  27. data/lib/appsignal/integrations/http.rb +1 -0
  28. data/lib/appsignal/integrations/net_http.rb +1 -0
  29. data/lib/appsignal/integrations/object.rb +6 -0
  30. data/lib/appsignal/integrations/padrino.rb +8 -73
  31. data/lib/appsignal/integrations/que.rb +13 -20
  32. data/lib/appsignal/integrations/railtie.rb +36 -14
  33. data/lib/appsignal/integrations/rake.rb +1 -5
  34. data/lib/appsignal/integrations/redis.rb +1 -0
  35. data/lib/appsignal/integrations/redis_client.rb +1 -0
  36. data/lib/appsignal/integrations/resque.rb +2 -5
  37. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  38. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  39. data/lib/appsignal/integrations/sinatra.rb +8 -19
  40. data/lib/appsignal/integrations/unicorn.rb +1 -0
  41. data/lib/appsignal/integrations/webmachine.rb +2 -5
  42. data/lib/appsignal/loaders/grape.rb +13 -0
  43. data/lib/appsignal/loaders/hanami.rb +40 -0
  44. data/lib/appsignal/loaders/padrino.rb +68 -0
  45. data/lib/appsignal/loaders/sinatra.rb +24 -0
  46. data/lib/appsignal/loaders.rb +92 -0
  47. data/lib/appsignal/logger.rb +7 -3
  48. data/lib/appsignal/probes/helpers.rb +1 -0
  49. data/lib/appsignal/probes/mri.rb +1 -0
  50. data/lib/appsignal/probes/sidekiq.rb +1 -0
  51. data/lib/appsignal/probes.rb +3 -0
  52. data/lib/appsignal/rack/abstract_middleware.rb +20 -13
  53. data/lib/appsignal/rack/event_handler.rb +44 -13
  54. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  55. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  56. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  57. data/lib/appsignal/rack.rb +35 -0
  58. data/lib/appsignal/span.rb +1 -0
  59. data/lib/appsignal/transaction.rb +308 -101
  60. data/lib/appsignal/utils/data.rb +0 -1
  61. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  62. data/lib/appsignal/utils/integration_logger.rb +0 -13
  63. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  64. data/lib/appsignal/utils/json.rb +0 -1
  65. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  66. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  67. data/lib/appsignal/utils.rb +6 -0
  68. data/lib/appsignal/version.rb +1 -1
  69. data/lib/appsignal.rb +169 -14
  70. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  71. data/spec/lib/appsignal/cli/demo_spec.rb +0 -1
  72. data/spec/lib/appsignal/cli/diagnose/paths_spec.rb +1 -1
  73. data/spec/lib/appsignal/cli/diagnose_spec.rb +0 -1
  74. data/spec/lib/appsignal/config_spec.rb +291 -44
  75. data/spec/lib/appsignal/demo_spec.rb +1 -2
  76. data/spec/lib/appsignal/environment_spec.rb +4 -2
  77. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  78. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +3 -6
  79. data/spec/lib/appsignal/hooks/activejob_spec.rb +12 -3
  80. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  81. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +4 -7
  82. data/spec/lib/appsignal/hooks/excon_spec.rb +3 -6
  83. data/spec/lib/appsignal/hooks/gvl_spec.rb +2 -2
  84. data/spec/lib/appsignal/hooks/http_spec.rb +1 -3
  85. data/spec/lib/appsignal/hooks/net_http_spec.rb +1 -1
  86. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -8
  87. data/spec/lib/appsignal/hooks/redis_spec.rb +3 -6
  88. data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
  89. data/spec/lib/appsignal/hooks/sequel_spec.rb +3 -5
  90. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  91. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +1 -1
  92. data/spec/lib/appsignal/hooks/webmachine_spec.rb +1 -1
  93. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  94. data/spec/lib/appsignal/integrations/grape_spec.rb +36 -0
  95. data/spec/lib/appsignal/integrations/hanami_spec.rb +9 -178
  96. data/spec/lib/appsignal/integrations/http_spec.rb +1 -5
  97. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +4 -2
  98. data/spec/lib/appsignal/integrations/net_http_spec.rb +1 -1
  99. data/spec/lib/appsignal/integrations/object_spec.rb +1 -3
  100. data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -330
  101. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  102. data/spec/lib/appsignal/integrations/railtie_spec.rb +275 -191
  103. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  104. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +15 -13
  105. data/spec/lib/appsignal/integrations/sinatra_spec.rb +9 -104
  106. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  107. data/spec/lib/appsignal/loaders/grape_spec.rb +12 -0
  108. data/spec/lib/appsignal/loaders/hanami_spec.rb +95 -0
  109. data/spec/lib/appsignal/loaders/padrino_spec.rb +277 -0
  110. data/spec/lib/appsignal/loaders/sinatra_spec.rb +47 -0
  111. data/spec/lib/appsignal/loaders_spec.rb +137 -0
  112. data/spec/lib/appsignal/probes/sidekiq_spec.rb +1 -1
  113. data/spec/lib/appsignal/probes_spec.rb +6 -5
  114. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +51 -5
  115. data/spec/lib/appsignal/rack/event_handler_spec.rb +114 -10
  116. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +1 -1
  117. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +2 -35
  118. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +1 -1
  119. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  120. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -3
  121. data/spec/lib/appsignal/rack_spec.rb +63 -0
  122. data/spec/lib/appsignal/span_spec.rb +1 -3
  123. data/spec/lib/appsignal/transaction_spec.rb +1640 -1075
  124. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  125. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  126. data/spec/lib/appsignal_spec.rb +601 -36
  127. data/spec/lib/puma/appsignal_spec.rb +0 -3
  128. data/spec/spec_helper.rb +5 -4
  129. data/spec/support/helpers/config_helpers.rb +2 -1
  130. data/spec/support/helpers/loader_helper.rb +21 -0
  131. data/spec/support/helpers/transaction_helpers.rb +44 -20
  132. data/spec/support/matchers/transaction.rb +15 -1
  133. data/spec/support/stubs/appsignal/loaders/loader_stub.rb +7 -0
  134. data/spec/support/testing.rb +47 -1
  135. metadata +19 -2
@@ -4,7 +4,6 @@ require "logger"
4
4
 
5
5
  module Appsignal
6
6
  module Utils
7
- # @api private
8
7
  class IntegrationMemoryLogger
9
8
  LEVELS = {
10
9
  Logger::DEBUG => :DEBUG,
@@ -35,18 +34,6 @@ module Appsignal
35
34
  add(:WARN, message)
36
35
  end
37
36
 
38
- def seen_keys
39
- @seen_keys ||= Set.new
40
- end
41
-
42
- def warn_once_then_debug(key, message)
43
- if seen_keys.add?(key).nil?
44
- debug message
45
- else
46
- warn message
47
- end
48
- end
49
-
50
37
  def error(message)
51
38
  add(:ERROR, message)
52
39
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # @api private
6
5
  class JSON
7
6
  class << self
8
7
  def generate(body)
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # @api private
6
5
  class QueryParamsSanitizer
7
6
  REPLACEMENT_KEY = "?"
8
7
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Appsignal
4
4
  module Utils
5
- # @api private
6
5
  module StdoutAndLoggerMessage
7
6
  def self.warning(message, logger = Appsignal.internal_logger)
8
7
  Kernel.warn "appsignal WARNING: #{message}"
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Appsignal
4
+ # @api private
5
+ module Utils
6
+ end
7
+ end
8
+
3
9
  require "appsignal/utils/integration_memory_logger"
4
10
  require "appsignal/utils/stdout_and_logger_message"
5
11
  require "appsignal/utils/data"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.10.0"
4
+ VERSION = "3.12.0"
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
@@ -47,7 +66,7 @@ module Appsignal
47
66
  # @see Extension
48
67
  # @see extension_loaded?
49
68
  attr_accessor :extension_loaded
50
- # @!attribute [rw] logger
69
+ # @!attribute [rw] internal_logger
51
70
  # Accessor for the internal AppSignal logger.
52
71
  #
53
72
  # Not to be confused with our logging feature.
@@ -59,10 +78,8 @@ module Appsignal
59
78
  # {.start}) the contents of the "in memory logger" is written to the new
60
79
  # logger.
61
80
  #
62
- # @note some classes may have options to set custom loggers. Their
63
- # defaults are pointed to this attribute.
64
81
  # @api private
65
- # @return [Logger]
82
+ # @return [Utils::IntegrationLogger or Utils::IntegrationMemoryLogger]
66
83
  # @see start
67
84
  attr_writer :internal_logger
68
85
 
@@ -89,7 +106,9 @@ module Appsignal
89
106
  # Appsignal.start
90
107
  #
91
108
  # @example with custom loaded configuration
92
- # Appsignal.config = Appsignal::Config.new(Dir.pwd, "production")
109
+ # Appsignal.configure(:production) do |config|
110
+ # config.ignore_actions = ["My action"]
111
+ # end
93
112
  # Appsignal.start
94
113
  #
95
114
  # @return [void]
@@ -111,11 +130,13 @@ module Appsignal
111
130
 
112
131
  if config.valid?
113
132
  if config.active?
133
+ @started = true
114
134
  internal_logger.info "Starting AppSignal #{Appsignal::VERSION} " \
115
135
  "(#{$PROGRAM_NAME}, Ruby #{RUBY_VERSION}, #{RUBY_PLATFORM})"
116
136
  config.write_to_environment
117
137
  Appsignal::Extension.start
118
138
  Appsignal::Hooks.load_hooks
139
+ Appsignal::Loaders.start
119
140
 
120
141
  if config[:enable_allocation_tracking] && !Appsignal::System.jruby?
121
142
  Appsignal::Extension.install_allocation_event_hook
@@ -149,14 +170,102 @@ module Appsignal
149
170
  # @since 1.0.0
150
171
  def stop(called_by = nil)
151
172
  if called_by
152
- internal_logger.debug("Stopping appsignal (#{called_by})")
173
+ internal_logger.debug("Stopping AppSignal (#{called_by})")
153
174
  else
154
- internal_logger.debug("Stopping appsignal")
175
+ internal_logger.debug("Stopping AppSignal")
155
176
  end
156
177
  Appsignal::Extension.stop
157
178
  Appsignal::Probes.stop
158
179
  end
159
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
+
160
269
  def forked
161
270
  return unless active?
162
271
 
@@ -165,10 +274,44 @@ module Appsignal
165
274
  Appsignal::Extension.start
166
275
  end
167
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
+
309
+ # @api private
168
310
  def get_server_state(key)
169
311
  Appsignal::Extension.get_server_state(key)
170
312
  end
171
313
 
314
+ # @api private
172
315
  def in_memory_logger
173
316
  @in_memory_logger ||=
174
317
  Appsignal::Utils::IntegrationMemoryLogger.new.tap do |l|
@@ -176,6 +319,7 @@ module Appsignal
176
319
  end
177
320
  end
178
321
 
322
+ # @api private
179
323
  def internal_logger
180
324
  @internal_logger ||= in_memory_logger
181
325
  end
@@ -195,7 +339,7 @@ module Appsignal
195
339
  def start_logger
196
340
  callers = caller
197
341
  Appsignal::Utils::StdoutAndLoggerMessage.warning \
198
- "Callng 'Appsignal.start_logger' is deprecated. " \
342
+ "Calling 'Appsignal.start_logger' is deprecated. " \
199
343
  "The logger will be started when calling 'Appsignal.start'. " \
200
344
  "Remove the 'Appsignal.start_logger' call in the following file to " \
201
345
  "remove this message.\n#{callers.first}"
@@ -237,6 +381,16 @@ module Appsignal
237
381
  !!extension_loaded
238
382
  end
239
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
+
240
394
  # Returns the active state of the AppSignal integration.
241
395
  #
242
396
  # Conditions apply for AppSignal to be marked as active:
@@ -313,6 +467,7 @@ module Appsignal
313
467
  end
314
468
  end
315
469
 
470
+ require "appsignal/loaders"
316
471
  require "appsignal/environment"
317
472
  require "appsignal/system"
318
473
  require "appsignal/utils"
@@ -17,7 +17,7 @@ if DependencyHelper.capistrano2_present?
17
17
  c.dry_run = false
18
18
  end
19
19
  end
20
- before { Appsignal::Capistrano.tasks(capistrano_config) }
20
+ before { Appsignal::Integrations::Capistrano.tasks(capistrano_config) }
21
21
 
22
22
  def run
23
23
  capture_stdout(out_stream) do
@@ -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)