appsignal 4.10.4-java → 5.0.0-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/Rakefile +195 -4
- data/appsignal.gemspec +8 -0
- data/build_matrix.yml +12 -0
- data/ext/appsignal_extension.c +14 -0
- data/lib/appsignal/backends.rb +55 -0
- data/lib/appsignal/cli/diagnose.rb +2 -10
- data/lib/appsignal/config.rb +377 -13
- data/lib/appsignal/demo.rb +12 -10
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +34 -22
- data/lib/appsignal/event_formatter/active_job/perform_formatter.rb +35 -0
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +19 -0
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +27 -0
- data/lib/appsignal/event_formatter/recorded_elsewhere.rb +17 -0
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +24 -0
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +5 -0
- data/lib/appsignal/event_formatter/view_component/render_formatter.rb +21 -10
- data/lib/appsignal/event_formatter.rb +78 -0
- data/lib/appsignal/extension.rb +4 -0
- data/lib/appsignal/helpers/instrumentation.rb +324 -20
- data/lib/appsignal/helpers/metrics.rb +3 -24
- data/lib/appsignal/hooks/action_cable.rb +26 -8
- data/lib/appsignal/hooks/active_job.rb +184 -47
- data/lib/appsignal/hooks/at_exit.rb +4 -1
- data/lib/appsignal/hooks/excon.rb +20 -0
- data/lib/appsignal/hooks/faraday.rb +16 -0
- data/lib/appsignal/hooks/http.rb +5 -0
- data/lib/appsignal/hooks/resque.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +32 -2
- data/lib/appsignal/hooks/shoryuken.rb +3 -3
- data/lib/appsignal/hooks/sidekiq.rb +1 -1
- data/lib/appsignal/integrations/action_cable.rb +5 -2
- data/lib/appsignal/integrations/active_support_notifications.rb +59 -19
- data/lib/appsignal/integrations/data_mapper.rb +14 -2
- data/lib/appsignal/integrations/delayed_job_plugin.rb +81 -8
- data/lib/appsignal/integrations/dry_monitor.rb +39 -15
- data/lib/appsignal/integrations/excon/appsignal_middleware.rb +21 -0
- data/lib/appsignal/integrations/excon.rb +52 -15
- data/lib/appsignal/integrations/faraday.rb +47 -12
- data/lib/appsignal/integrations/http.rb +43 -1
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +73 -4
- data/lib/appsignal/integrations/net_http.rb +31 -2
- data/lib/appsignal/integrations/puma.rb +4 -1
- data/lib/appsignal/integrations/que.rb +256 -37
- data/lib/appsignal/integrations/railtie.rb +4 -1
- data/lib/appsignal/integrations/rake.rb +9 -3
- data/lib/appsignal/integrations/redis.rb +22 -1
- data/lib/appsignal/integrations/redis_client.rb +22 -1
- data/lib/appsignal/integrations/resque.rb +81 -11
- data/lib/appsignal/integrations/shoryuken.rb +159 -12
- data/lib/appsignal/integrations/sidekiq.rb +94 -16
- data/lib/appsignal/integrations/webmachine.rb +56 -5
- data/lib/appsignal/loaders/padrino.rb +2 -1
- data/lib/appsignal/logger/extension_backend.rb +24 -0
- data/lib/appsignal/logger/opentelemetry_backend.rb +66 -0
- data/lib/appsignal/logger.rb +13 -9
- data/lib/appsignal/metrics/extension_backend.rb +47 -0
- data/lib/appsignal/metrics/opentelemetry_backend.rb +89 -0
- data/lib/appsignal/opentelemetry/attributes.rb +31 -0
- data/lib/appsignal/opentelemetry/dependencies.rb +35 -0
- data/lib/appsignal/opentelemetry/error_type.rb +37 -0
- data/lib/appsignal/opentelemetry/http_client_request.rb +83 -0
- data/lib/appsignal/opentelemetry/http_method.rb +59 -0
- data/lib/appsignal/opentelemetry/http_response.rb +30 -0
- data/lib/appsignal/opentelemetry/http_server_request.rb +79 -0
- data/lib/appsignal/opentelemetry/messaging.rb +82 -0
- data/lib/appsignal/opentelemetry/proxied_exporter.rb +83 -0
- data/lib/appsignal/opentelemetry/rendering.rb +29 -0
- data/lib/appsignal/opentelemetry/sql_db_system.rb +89 -0
- data/lib/appsignal/opentelemetry.rb +495 -0
- data/lib/appsignal/rack/abstract_middleware.rb +66 -4
- data/lib/appsignal/rack/body_wrapper.rb +18 -5
- data/lib/appsignal/rack/event_handler.rb +44 -4
- data/lib/appsignal/rack/grape_middleware.rb +1 -0
- data/lib/appsignal/rack/hanami_middleware.rb +2 -1
- data/lib/appsignal/rack/instrumentation_middleware.rb +1 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +1 -0
- data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -0
- data/lib/appsignal/rack.rb +68 -12
- data/lib/appsignal/sample_data.rb +4 -0
- data/lib/appsignal/transaction/base_backend.rb +128 -0
- data/lib/appsignal/transaction/extension_backend.rb +229 -0
- data/lib/appsignal/transaction/opentelemetry_backend.rb +847 -0
- data/lib/appsignal/transaction.rb +714 -164
- data/lib/appsignal/utils/request_headers.rb +78 -0
- data/lib/appsignal/utils/stdout_and_logger_message.rb +9 -0
- data/lib/appsignal/utils.rb +1 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +10 -0
- data/sig/appsignal.rbi +630 -37
- data/sig/appsignal.rbs +582 -27
- metadata +25 -1
data/lib/appsignal/config.rb
CHANGED
|
@@ -91,6 +91,7 @@ module Appsignal
|
|
|
91
91
|
DEFAULT_CONFIG = {
|
|
92
92
|
:activejob_report_errors => "all",
|
|
93
93
|
:ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
|
|
94
|
+
:collector_endpoint => nil,
|
|
94
95
|
:dns_servers => [],
|
|
95
96
|
:enable_allocation_tracking => true,
|
|
96
97
|
:enable_at_exit_hook => "on_error",
|
|
@@ -107,8 +108,12 @@ module Appsignal
|
|
|
107
108
|
:enable_rake_performance_instrumentation => false,
|
|
108
109
|
:endpoint => "https://push.appsignal.com",
|
|
109
110
|
:files_world_accessible => true,
|
|
111
|
+
:filter_attributes => [],
|
|
112
|
+
:filter_function_parameters => [],
|
|
110
113
|
:filter_metadata => [],
|
|
111
114
|
:filter_parameters => [],
|
|
115
|
+
:filter_request_payload => [],
|
|
116
|
+
:filter_request_query_parameters => [],
|
|
112
117
|
:filter_session_data => [],
|
|
113
118
|
:ignore_actions => [],
|
|
114
119
|
:ignore_errors => [],
|
|
@@ -129,6 +134,11 @@ module Appsignal
|
|
|
129
134
|
:instrument_sequel => true,
|
|
130
135
|
:instrument_shoryuken => true,
|
|
131
136
|
:instrument_sidekiq => true,
|
|
137
|
+
:keep_request_environment => [],
|
|
138
|
+
:keep_request_headers => %w[
|
|
139
|
+
accept accept-charset accept-encoding accept-language cache-control
|
|
140
|
+
connection content-length range
|
|
141
|
+
],
|
|
132
142
|
:log => "file",
|
|
133
143
|
:logging_endpoint => "https://appsignal-endpoint.net",
|
|
134
144
|
:ownership_set_namespace => false,
|
|
@@ -139,9 +149,14 @@ module Appsignal
|
|
|
139
149
|
REQUEST_METHOD REQUEST_PATH SERVER_NAME SERVER_PORT
|
|
140
150
|
SERVER_PROTOCOL
|
|
141
151
|
],
|
|
152
|
+
:response_headers => [],
|
|
142
153
|
:send_environment_metadata => true,
|
|
154
|
+
:send_function_parameters => true,
|
|
143
155
|
:send_params => true,
|
|
156
|
+
:send_request_payload => true,
|
|
157
|
+
:send_request_query_parameters => true,
|
|
144
158
|
:send_session_data => true,
|
|
159
|
+
:service_name => nil,
|
|
145
160
|
:sidekiq_report_errors => "all",
|
|
146
161
|
:default_tags => {}
|
|
147
162
|
}.freeze
|
|
@@ -161,12 +176,25 @@ module Appsignal
|
|
|
161
176
|
"trace" => ::Logger::DEBUG
|
|
162
177
|
}.freeze
|
|
163
178
|
|
|
179
|
+
# Environment variables set by deployment platforms that name the
|
|
180
|
+
# revision that is being deployed, in the order the agent reads them.
|
|
181
|
+
# The agent detects the revision this way as well, but only for the data
|
|
182
|
+
# it sends itself, so the gem has to do it for collector mode.
|
|
183
|
+
# @!visibility private
|
|
184
|
+
PLATFORM_REVISION_ENV_VARS = [
|
|
185
|
+
"HEROKU_SLUG_COMMIT",
|
|
186
|
+
"RENDER_GIT_COMMIT",
|
|
187
|
+
"KAMAL_VERSION",
|
|
188
|
+
"CONTAINER_VERSION" # Scalingo
|
|
189
|
+
].freeze
|
|
190
|
+
|
|
164
191
|
# @!visibility private
|
|
165
192
|
STRING_OPTIONS = {
|
|
166
193
|
:activejob_report_errors => "APPSIGNAL_ACTIVEJOB_REPORT_ERRORS",
|
|
167
194
|
:name => "APPSIGNAL_APP_NAME",
|
|
168
195
|
:bind_address => "APPSIGNAL_BIND_ADDRESS",
|
|
169
196
|
:ca_file_path => "APPSIGNAL_CA_FILE_PATH",
|
|
197
|
+
:collector_endpoint => "APPSIGNAL_COLLECTOR_ENDPOINT",
|
|
170
198
|
:enable_at_exit_hook => "APPSIGNAL_ENABLE_AT_EXIT_HOOK",
|
|
171
199
|
:hostname => "APPSIGNAL_HOSTNAME",
|
|
172
200
|
:host_role => "APPSIGNAL_HOST_ROLE",
|
|
@@ -177,6 +205,7 @@ module Appsignal
|
|
|
177
205
|
:logging_endpoint => "APPSIGNAL_LOGGING_ENDPOINT",
|
|
178
206
|
:endpoint => "APPSIGNAL_PUSH_API_ENDPOINT",
|
|
179
207
|
:push_api_key => "APPSIGNAL_PUSH_API_KEY",
|
|
208
|
+
:service_name => "APPSIGNAL_SERVICE_NAME",
|
|
180
209
|
:sidekiq_report_errors => "APPSIGNAL_SIDEKIQ_REPORT_ERRORS",
|
|
181
210
|
:statsd_port => "APPSIGNAL_STATSD_PORT",
|
|
182
211
|
:nginx_port => "APPSIGNAL_NGINX_PORT",
|
|
@@ -221,21 +250,31 @@ module Appsignal
|
|
|
221
250
|
:ownership_set_namespace => "APPSIGNAL_OWNERSHIP_SET_NAMESPACE",
|
|
222
251
|
:running_in_container => "APPSIGNAL_RUNNING_IN_CONTAINER",
|
|
223
252
|
:send_environment_metadata => "APPSIGNAL_SEND_ENVIRONMENT_METADATA",
|
|
253
|
+
:send_function_parameters => "APPSIGNAL_SEND_FUNCTION_PARAMETERS",
|
|
224
254
|
:send_params => "APPSIGNAL_SEND_PARAMS",
|
|
255
|
+
:send_request_payload => "APPSIGNAL_SEND_REQUEST_PAYLOAD",
|
|
256
|
+
:send_request_query_parameters => "APPSIGNAL_SEND_REQUEST_QUERY_PARAMETERS",
|
|
225
257
|
:send_session_data => "APPSIGNAL_SEND_SESSION_DATA"
|
|
226
258
|
}.freeze
|
|
227
259
|
|
|
228
260
|
# @!visibility private
|
|
229
261
|
ARRAY_OPTIONS = {
|
|
230
262
|
:dns_servers => "APPSIGNAL_DNS_SERVERS",
|
|
263
|
+
:filter_attributes => "APPSIGNAL_FILTER_ATTRIBUTES",
|
|
264
|
+
:filter_function_parameters => "APPSIGNAL_FILTER_FUNCTION_PARAMETERS",
|
|
231
265
|
:filter_metadata => "APPSIGNAL_FILTER_METADATA",
|
|
232
266
|
:filter_parameters => "APPSIGNAL_FILTER_PARAMETERS",
|
|
267
|
+
:filter_request_payload => "APPSIGNAL_FILTER_REQUEST_PAYLOAD",
|
|
268
|
+
:filter_request_query_parameters => "APPSIGNAL_FILTER_REQUEST_QUERY_PARAMETERS",
|
|
233
269
|
:filter_session_data => "APPSIGNAL_FILTER_SESSION_DATA",
|
|
234
270
|
:ignore_actions => "APPSIGNAL_IGNORE_ACTIONS",
|
|
235
271
|
:ignore_errors => "APPSIGNAL_IGNORE_ERRORS",
|
|
236
272
|
:ignore_logs => "APPSIGNAL_IGNORE_LOGS",
|
|
237
273
|
:ignore_namespaces => "APPSIGNAL_IGNORE_NAMESPACES",
|
|
238
|
-
:
|
|
274
|
+
:keep_request_environment => "APPSIGNAL_KEEP_REQUEST_ENVIRONMENT",
|
|
275
|
+
:keep_request_headers => "APPSIGNAL_KEEP_REQUEST_HEADERS",
|
|
276
|
+
:request_headers => "APPSIGNAL_REQUEST_HEADERS",
|
|
277
|
+
:response_headers => "APPSIGNAL_RESPONSE_HEADERS"
|
|
239
278
|
}.freeze
|
|
240
279
|
|
|
241
280
|
# @!visibility private
|
|
@@ -248,6 +287,67 @@ module Appsignal
|
|
|
248
287
|
:default_tags => "APPSIGNAL_DEFAULT_TAGS"
|
|
249
288
|
}.freeze
|
|
250
289
|
|
|
290
|
+
# Collector mode requires Ruby 3.1+. The OpenTelemetry Ruby SDK relies on
|
|
291
|
+
# `Process._fork` (introduced in Ruby 3.1) for its fork hooks, without
|
|
292
|
+
# which background reader threads don't restart in child processes and
|
|
293
|
+
# buffered telemetry is lost after a fork.
|
|
294
|
+
# @!visibility private
|
|
295
|
+
MIN_RUBY_VERSION_FOR_COLLECTOR_MODE = "3.1"
|
|
296
|
+
|
|
297
|
+
# @!visibility private
|
|
298
|
+
COLLECTOR_ONLY_OPTIONS = [
|
|
299
|
+
:filter_attributes,
|
|
300
|
+
:filter_function_parameters,
|
|
301
|
+
:filter_request_payload,
|
|
302
|
+
:filter_request_query_parameters,
|
|
303
|
+
:keep_request_environment,
|
|
304
|
+
:keep_request_headers,
|
|
305
|
+
:response_headers,
|
|
306
|
+
:send_function_parameters,
|
|
307
|
+
:send_request_payload,
|
|
308
|
+
:send_request_query_parameters,
|
|
309
|
+
:service_name
|
|
310
|
+
].freeze
|
|
311
|
+
|
|
312
|
+
# @!visibility private
|
|
313
|
+
COLLECTOR_ONLY_REPLACED_OPTIONS = {
|
|
314
|
+
:filter_function_parameters => :filter_parameters,
|
|
315
|
+
:filter_request_payload => :filter_parameters,
|
|
316
|
+
:filter_request_query_parameters => :filter_parameters,
|
|
317
|
+
:keep_request_environment => :request_headers,
|
|
318
|
+
:keep_request_headers => :request_headers,
|
|
319
|
+
:send_function_parameters => :send_params,
|
|
320
|
+
:send_request_payload => :send_params,
|
|
321
|
+
:send_request_query_parameters => :send_params
|
|
322
|
+
}.freeze
|
|
323
|
+
|
|
324
|
+
# @!visibility private
|
|
325
|
+
DEPRECATED_COLLECTOR_OPTIONS = {
|
|
326
|
+
:filter_parameters => {
|
|
327
|
+
:filter_request_payload => :derived_as_is,
|
|
328
|
+
:filter_function_parameters => :derived_as_is,
|
|
329
|
+
:filter_request_query_parameters => :derived_as_is
|
|
330
|
+
},
|
|
331
|
+
:request_headers => {
|
|
332
|
+
:keep_request_headers => :derived_header_names,
|
|
333
|
+
:keep_request_environment => :derived_environment_keys
|
|
334
|
+
},
|
|
335
|
+
:send_params => {
|
|
336
|
+
:send_request_payload => :derived_as_is,
|
|
337
|
+
:send_request_query_parameters => :derived_as_is,
|
|
338
|
+
:send_function_parameters => :derived_as_is
|
|
339
|
+
}
|
|
340
|
+
}.freeze
|
|
341
|
+
|
|
342
|
+
# @!visibility private
|
|
343
|
+
SOURCE_ORDER = [
|
|
344
|
+
:default, :derived, :system, :loaders, :initial, :file, :env, :override,
|
|
345
|
+
:dsl
|
|
346
|
+
].freeze
|
|
347
|
+
|
|
348
|
+
# @!visibility private
|
|
349
|
+
APPLICATION_SOURCES = [:initial, :file, :env, :dsl].freeze
|
|
350
|
+
|
|
251
351
|
# @!visibility private
|
|
252
352
|
attr_reader :root_path, :env, :config_hash
|
|
253
353
|
|
|
@@ -259,8 +359,23 @@ module Appsignal
|
|
|
259
359
|
#
|
|
260
360
|
# Used by the diagnose report to list which value was read from which source.
|
|
261
361
|
# @!visibility private
|
|
262
|
-
attr_reader :
|
|
263
|
-
:env_config, :override_config, :dsl_config
|
|
362
|
+
attr_reader :derived_config, :system_config, :loaders_config,
|
|
363
|
+
:initial_config, :file_config, :env_config, :override_config, :dsl_config
|
|
364
|
+
|
|
365
|
+
# @!visibility private
|
|
366
|
+
def config_sources
|
|
367
|
+
{
|
|
368
|
+
:default => DEFAULT_CONFIG,
|
|
369
|
+
:derived => derived_config,
|
|
370
|
+
:system => system_config,
|
|
371
|
+
:loaders => loaders_config,
|
|
372
|
+
:initial => initial_config,
|
|
373
|
+
:file => file_config,
|
|
374
|
+
:env => env_config,
|
|
375
|
+
:override => override_config,
|
|
376
|
+
:dsl => dsl_config
|
|
377
|
+
}
|
|
378
|
+
end
|
|
264
379
|
|
|
265
380
|
# Initialize a new AppSignal configuration object.
|
|
266
381
|
#
|
|
@@ -296,6 +411,7 @@ module Appsignal
|
|
|
296
411
|
@initial_config = {}
|
|
297
412
|
@file_config = {}
|
|
298
413
|
@env_config = {}
|
|
414
|
+
@derived_config = {}
|
|
299
415
|
@override_config = {}
|
|
300
416
|
@dsl_config = {} # Can be set using `Appsignal.configure`
|
|
301
417
|
|
|
@@ -452,6 +568,57 @@ module Appsignal
|
|
|
452
568
|
valid? && active_for_env?
|
|
453
569
|
end
|
|
454
570
|
|
|
571
|
+
# Check if collector mode is configured.
|
|
572
|
+
#
|
|
573
|
+
# Returns true when a non-empty `collector_endpoint` is set and the
|
|
574
|
+
# running Ruby version is at least {MIN_RUBY_VERSION_FOR_COLLECTOR_MODE}.
|
|
575
|
+
# On older Rubies, `collector_endpoint` is ignored (with a warning) and
|
|
576
|
+
# the AppSignal agent is used instead.
|
|
577
|
+
#
|
|
578
|
+
# This is the *intent* check — it answers "did the user ask for
|
|
579
|
+
# collector mode, and could we honor it?". It does not say whether the
|
|
580
|
+
# OpenTelemetry SDK actually booted. See {#collector_mode?} for that.
|
|
581
|
+
#
|
|
582
|
+
# Memoised: the result is cached on first call so hot paths avoid
|
|
583
|
+
# re-running the string-strip predicate, and so the unsupported-Ruby
|
|
584
|
+
# warning is emitted at most once per `Config` instance.
|
|
585
|
+
#
|
|
586
|
+
# @return [Boolean] True if collector mode is configured.
|
|
587
|
+
def collector_mode_configured?
|
|
588
|
+
return @collector_mode_configured if defined?(@collector_mode_configured)
|
|
589
|
+
|
|
590
|
+
endpoint = config_hash[:collector_endpoint]
|
|
591
|
+
configured = !endpoint.nil? && !endpoint.to_s.strip.empty?
|
|
592
|
+
|
|
593
|
+
if configured && Gem::Version.new(RUBY_VERSION) <
|
|
594
|
+
Gem::Version.new(MIN_RUBY_VERSION_FOR_COLLECTOR_MODE)
|
|
595
|
+
Appsignal::Utils::StdoutAndLoggerMessage.warning(
|
|
596
|
+
"Collector mode requires Ruby #{MIN_RUBY_VERSION_FOR_COLLECTOR_MODE} or higher " \
|
|
597
|
+
"(running Ruby #{RUBY_VERSION}). The `collector_endpoint` option will be " \
|
|
598
|
+
"ignored and the AppSignal agent will be used instead."
|
|
599
|
+
)
|
|
600
|
+
@collector_mode_configured = false
|
|
601
|
+
else
|
|
602
|
+
@collector_mode_configured = configured
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# Check if AppSignal is actively running in collector mode.
|
|
607
|
+
#
|
|
608
|
+
# True only if collector mode is {#collector_mode_configured? configured}
|
|
609
|
+
# *and* `Appsignal::OpenTelemetry.configure` has successfully booted the
|
|
610
|
+
# SDK in this process. Use this for backend dispatch on hot paths
|
|
611
|
+
# (metric and log emits): if the OTel boot failed, callers fall back to
|
|
612
|
+
# the agent backend rather than silently dropping data into no-op
|
|
613
|
+
# providers.
|
|
614
|
+
#
|
|
615
|
+
# @return [Boolean] True if collector mode is configured and started.
|
|
616
|
+
def collector_mode?
|
|
617
|
+
collector_mode_configured? &&
|
|
618
|
+
defined?(Appsignal::OpenTelemetry) &&
|
|
619
|
+
Appsignal::OpenTelemetry.started?
|
|
620
|
+
end
|
|
621
|
+
|
|
455
622
|
# @!visibility private
|
|
456
623
|
def write_to_environment
|
|
457
624
|
ENV["_APPSIGNAL_ACTIVE"] = active?.to_s
|
|
@@ -465,6 +632,10 @@ module Appsignal
|
|
|
465
632
|
ENV["_APPSIGNAL_ENABLE_HOST_METRICS"] = config_hash[:enable_host_metrics].to_s
|
|
466
633
|
ENV["_APPSIGNAL_ENABLE_STATSD"] = config_hash[:enable_statsd].to_s
|
|
467
634
|
ENV["_APPSIGNAL_ENABLE_NGINX_METRICS"] = config_hash[:enable_nginx_metrics].to_s
|
|
635
|
+
# The gem never sends OpenTelemetry data to the agent, so it has no
|
|
636
|
+
# reason to listen for it. Written out rather than left to the agent's
|
|
637
|
+
# own default, so a value set in the environment cannot turn it on.
|
|
638
|
+
ENV["_APPSIGNAL_ENABLE_OPENTELEMETRY_HTTP"] = "false"
|
|
468
639
|
ENV["_APPSIGNAL_APP_ENV"] = env
|
|
469
640
|
ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = config_hash[:files_world_accessible].to_s
|
|
470
641
|
ENV["_APPSIGNAL_FILTER_PARAMETERS"] = config_hash[:filter_parameters].join(",")
|
|
@@ -481,6 +652,7 @@ module Appsignal
|
|
|
481
652
|
ENV["_APPSIGNAL_LOG_LEVEL"] = config_hash[:log_level]
|
|
482
653
|
ENV["_APPSIGNAL_LOG_FILE_PATH"] = log_file_path.to_s if log_file_path
|
|
483
654
|
ENV["_APPSIGNAL_LOGGING_ENDPOINT"] = config_hash[:logging_endpoint]
|
|
655
|
+
ENV["_APPSIGNAL_PLATFORM"] = config_hash[:platform].to_s
|
|
484
656
|
ENV["_APPSIGNAL_PROCESS_NAME"] = $PROGRAM_NAME
|
|
485
657
|
ENV["_APPSIGNAL_PUSH_API_ENDPOINT"] = config_hash[:endpoint]
|
|
486
658
|
ENV["_APPSIGNAL_PUSH_API_KEY"] = config_hash[:push_api_key]
|
|
@@ -500,9 +672,11 @@ module Appsignal
|
|
|
500
672
|
merge(options)
|
|
501
673
|
end
|
|
502
674
|
|
|
503
|
-
# Apply any overrides for invalid settings.
|
|
504
675
|
# @!visibility private
|
|
505
676
|
def apply_overrides
|
|
677
|
+
@derived_config = determine_derived
|
|
678
|
+
merge(derived_config)
|
|
679
|
+
|
|
506
680
|
@override_config = determine_overrides
|
|
507
681
|
merge(override_config)
|
|
508
682
|
end
|
|
@@ -528,6 +702,25 @@ module Appsignal
|
|
|
528
702
|
else
|
|
529
703
|
@valid = true
|
|
530
704
|
end
|
|
705
|
+
|
|
706
|
+
warn_for_mode_mismatch
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
# Emit warnings when a configuration option is set that has no effect in
|
|
710
|
+
# the current mode (collector vs. agent).
|
|
711
|
+
#
|
|
712
|
+
# Uses {#collector_mode_configured?} (intent) rather than
|
|
713
|
+
# {#collector_mode?} so the warnings fire based on what the user asked
|
|
714
|
+
# for, independent of whether the OpenTelemetry SDK successfully booted.
|
|
715
|
+
# @!visibility private
|
|
716
|
+
def warn_for_mode_mismatch
|
|
717
|
+
if collector_mode_configured?
|
|
718
|
+
warn_user_modified(DEPRECATED_COLLECTOR_OPTIONS.keys) do |option|
|
|
719
|
+
deprecated_collector_option_message(option)
|
|
720
|
+
end
|
|
721
|
+
else
|
|
722
|
+
warn_collector_only_options
|
|
723
|
+
end
|
|
531
724
|
end
|
|
532
725
|
|
|
533
726
|
# Deep freeze the config object so it cannot be modified during the runtime
|
|
@@ -551,6 +744,54 @@ module Appsignal
|
|
|
551
744
|
|
|
552
745
|
private
|
|
553
746
|
|
|
747
|
+
def deprecated_collector_option_message(option)
|
|
748
|
+
replacements = DEPRECATED_COLLECTOR_OPTIONS.fetch(option).keys
|
|
749
|
+
message = "The collector is in use. The '#{option}' configuration " \
|
|
750
|
+
"option is deprecated in collector mode. It is replaced by " \
|
|
751
|
+
"#{quoted_option_list(replacements)}."
|
|
752
|
+
|
|
753
|
+
derived = replacements.select { |name| derived_config.key?(name) }
|
|
754
|
+
return message if derived.empty?
|
|
755
|
+
|
|
756
|
+
values = derived.map { |name| "\n #{name}: #{derived_config[name].inspect}" }
|
|
757
|
+
"#{message} Set these options to keep reporting what this application " \
|
|
758
|
+
"reports now:#{values.join}"
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def quoted_option_list(names)
|
|
762
|
+
names = names.map { |name| "'#{name}'" }
|
|
763
|
+
return names.first if names.length == 1
|
|
764
|
+
|
|
765
|
+
"#{names[0..-2].join(", ")} and #{names.last}"
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
def warn_collector_only_options
|
|
769
|
+
options = COLLECTOR_ONLY_OPTIONS.select { |option| configured?(option) }
|
|
770
|
+
return if options.empty?
|
|
771
|
+
|
|
772
|
+
options.each do |option|
|
|
773
|
+
logger.warn(
|
|
774
|
+
"The agent is in use. The '#{option}' configuration option is " \
|
|
775
|
+
"only used by the collector and will be ignored."
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
replacement = COLLECTOR_ONLY_REPLACED_OPTIONS[option]
|
|
779
|
+
logger.warn("Use the '#{replacement}' option instead.") if replacement
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
logger.info(
|
|
783
|
+
"To use the collector, set the 'collector_endpoint' configuration option."
|
|
784
|
+
)
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def warn_user_modified(options)
|
|
788
|
+
options.each do |option|
|
|
789
|
+
next unless configured?(option)
|
|
790
|
+
|
|
791
|
+
logger.warn(yield(option))
|
|
792
|
+
end
|
|
793
|
+
end
|
|
794
|
+
|
|
554
795
|
def logger
|
|
555
796
|
Appsignal.internal_logger
|
|
556
797
|
end
|
|
@@ -571,13 +812,57 @@ module Appsignal
|
|
|
571
812
|
|
|
572
813
|
hash[:enable_at_exit_hook] = "always" if Appsignal::Extension.running_in_container?
|
|
573
814
|
|
|
574
|
-
# Set revision from REVISION file
|
|
575
|
-
#
|
|
576
|
-
|
|
577
|
-
|
|
815
|
+
# Set the revision from a REVISION file in the project root, which
|
|
816
|
+
# helps with Capistrano and Hatchbox.io deployments, or from the
|
|
817
|
+
# environment variable the deployment platform sets.
|
|
818
|
+
revision = detect_revision_from_file || detect_revision_from_platform
|
|
819
|
+
hash[:revision] = revision if revision
|
|
820
|
+
|
|
821
|
+
hostname = detect_hostname
|
|
822
|
+
hash[:hostname] = hostname if hostname
|
|
823
|
+
|
|
824
|
+
platform = detect_platform
|
|
825
|
+
hash[:platform] = platform if platform
|
|
578
826
|
end
|
|
579
827
|
end
|
|
580
828
|
|
|
829
|
+
# Detect the platform the application is deployed on, the way the agent
|
|
830
|
+
# does. The agent only detects it for the data it reports itself, so the
|
|
831
|
+
# gem has to do it for collector mode. There is no config option for it,
|
|
832
|
+
# because these are the only two platforms that can be recognized.
|
|
833
|
+
def detect_platform
|
|
834
|
+
return "dokku" unless ENV.fetch("DOKKU_ROOT", nil).to_s.empty?
|
|
835
|
+
return "heroku" unless ENV.fetch("DYNO", nil).to_s.empty?
|
|
836
|
+
|
|
837
|
+
nil
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
# Detect the hostname the way the agent does: the Heroku dyno name first,
|
|
841
|
+
# then the name the host reports for itself. The agent only detects it for
|
|
842
|
+
# the data it reports itself, so the gem has to do it for collector mode.
|
|
843
|
+
def detect_hostname
|
|
844
|
+
dyno = ENV.fetch("DYNO", nil)
|
|
845
|
+
return dyno unless dyno.to_s.empty?
|
|
846
|
+
|
|
847
|
+
hostname = Socket.gethostname
|
|
848
|
+
hostname unless hostname.to_s.empty?
|
|
849
|
+
rescue SystemCallError => e
|
|
850
|
+
logger.debug "Unable to detect the hostname: #{e.class}: #{e.message}"
|
|
851
|
+
nil
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def detect_revision_from_platform
|
|
855
|
+
PLATFORM_REVISION_ENV_VARS.each do |env_var|
|
|
856
|
+
revision = ENV.fetch(env_var, nil)
|
|
857
|
+
next if revision.to_s.empty?
|
|
858
|
+
|
|
859
|
+
logger.debug "Detected revision from the #{env_var} environment variable"
|
|
860
|
+
return revision
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
nil
|
|
864
|
+
end
|
|
865
|
+
|
|
581
866
|
def detect_revision_from_file
|
|
582
867
|
return unless root_path
|
|
583
868
|
|
|
@@ -715,9 +1000,68 @@ module Appsignal
|
|
|
715
1000
|
config[:sidekiq_report_errors] = "all"
|
|
716
1001
|
end
|
|
717
1002
|
|
|
1003
|
+
# A config file holding a YAML null gives an array option a `nil`, which
|
|
1004
|
+
# nothing downstream expects. Correct it to the empty list the option means.
|
|
1005
|
+
ARRAY_OPTIONS.each_key do |option|
|
|
1006
|
+
config[option] = [] if config_hash[option].nil?
|
|
1007
|
+
end
|
|
1008
|
+
|
|
718
1009
|
config
|
|
719
1010
|
end
|
|
720
1011
|
|
|
1012
|
+
def determine_derived
|
|
1013
|
+
derived = {}
|
|
1014
|
+
|
|
1015
|
+
DEPRECATED_COLLECTOR_OPTIONS.each do |option, replacements|
|
|
1016
|
+
next unless configured?(option)
|
|
1017
|
+
|
|
1018
|
+
replacements.each do |replacement, derivation|
|
|
1019
|
+
next if set_above_derived?(replacement)
|
|
1020
|
+
|
|
1021
|
+
derived[replacement] = send(derivation, config_hash[option])
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
derived
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
def derived_as_is(value)
|
|
1029
|
+
value
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
def derived_header_names(value)
|
|
1033
|
+
Array(value).filter_map do |key|
|
|
1034
|
+
Appsignal::Utils::RequestHeaders.header_name(key)
|
|
1035
|
+
end
|
|
1036
|
+
end
|
|
1037
|
+
|
|
1038
|
+
def derived_environment_keys(value)
|
|
1039
|
+
Array(value).reject do |key|
|
|
1040
|
+
Appsignal::Utils::RequestHeaders.header_name(key) ||
|
|
1041
|
+
Appsignal::Utils::RequestHeaders::TRANSLATED_ENV_KEYS.include?(key)
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
def configured?(option)
|
|
1046
|
+
config_hash[option] != DEFAULT_CONFIG[option] && user_set?(option)
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
def user_set?(option)
|
|
1050
|
+
set_by_any?(APPLICATION_SOURCES, option)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def set_above_derived?(option)
|
|
1054
|
+
set_by_any?(sources_above_derived, option)
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def sources_above_derived
|
|
1058
|
+
SOURCE_ORDER[(SOURCE_ORDER.index(:derived) + 1)..] - [:override]
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
def set_by_any?(sources, option)
|
|
1062
|
+
sources.any? { |source| config_sources.fetch(source).key?(option) }
|
|
1063
|
+
end
|
|
1064
|
+
|
|
721
1065
|
def merge(new_config)
|
|
722
1066
|
new_config.each do |key, value|
|
|
723
1067
|
logger.debug("Config key '#{key}' is being overwritten") unless config_hash[key].nil?
|
|
@@ -742,14 +1086,19 @@ module Appsignal
|
|
|
742
1086
|
# @see AppSignal Ruby gem configuration
|
|
743
1087
|
# https://docs.appsignal.com/ruby/configuration.html
|
|
744
1088
|
class ConfigDSL
|
|
745
|
-
# @!visibility private
|
|
746
|
-
# @return [Hash] Hash containing the DSL option values
|
|
747
|
-
attr_reader :dsl_options
|
|
748
|
-
|
|
749
1089
|
# @!visibility private
|
|
750
1090
|
def initialize(config)
|
|
751
1091
|
@config = config
|
|
752
1092
|
@dsl_options = {}
|
|
1093
|
+
@assigned_options = Set.new
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
# @!visibility private
|
|
1097
|
+
# @return [Hash] Hash containing the DSL option values
|
|
1098
|
+
def dsl_options
|
|
1099
|
+
@dsl_options.reject do |key, value|
|
|
1100
|
+
!@assigned_options.include?(key) && unchanged_option?(key, value)
|
|
1101
|
+
end
|
|
753
1102
|
end
|
|
754
1103
|
|
|
755
1104
|
# Returns the application's root path.
|
|
@@ -935,6 +1284,12 @@ module Appsignal
|
|
|
935
1284
|
# @return [Array<String>] Ignore log messages by substrings
|
|
936
1285
|
# @!attribute [rw] ignore_namespaces
|
|
937
1286
|
# @return [Array<String>] Ignore traces by namespaces
|
|
1287
|
+
# @!attribute [rw] keep_request_environment
|
|
1288
|
+
# @return [Array<String>] Rack environment keys to report in collector
|
|
1289
|
+
# mode, named the way Rack names them
|
|
1290
|
+
# @!attribute [rw] keep_request_headers
|
|
1291
|
+
# @return [Array<String>] HTTP request headers to report in collector
|
|
1292
|
+
# mode, named the way OpenTelemetry names them
|
|
938
1293
|
# @!attribute [rw] request_headers
|
|
939
1294
|
# @return [Array<String>] HTTP request headers to include in error reports
|
|
940
1295
|
|
|
@@ -989,14 +1344,23 @@ module Appsignal
|
|
|
989
1344
|
if @dsl_options.key?(key)
|
|
990
1345
|
@dsl_options[key]
|
|
991
1346
|
else
|
|
992
|
-
@dsl_options[key] =
|
|
1347
|
+
@dsl_options[key] = initial_option_value(key)
|
|
993
1348
|
end
|
|
994
1349
|
end
|
|
995
1350
|
|
|
996
1351
|
def update_option(key, value)
|
|
1352
|
+
@assigned_options << key
|
|
997
1353
|
@dsl_options[key] = value
|
|
998
1354
|
end
|
|
999
1355
|
|
|
1356
|
+
def initial_option_value(key)
|
|
1357
|
+
@config[key].dup
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
def unchanged_option?(key, value)
|
|
1361
|
+
value == initial_option_value(key)
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1000
1364
|
# Parse tags from various input formats and validate values
|
|
1001
1365
|
# @param value [Hash, nil] input tags
|
|
1002
1366
|
# @return [Hash] validated tags with string keys
|
data/lib/appsignal/demo.rb
CHANGED
|
@@ -70,28 +70,30 @@ module Appsignal
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def add_params_to(transaction)
|
|
73
|
-
transaction.
|
|
73
|
+
transaction.add_request_payload(
|
|
74
74
|
"controller" => "demo",
|
|
75
75
|
"action" => "hello"
|
|
76
76
|
)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def add_headers_to(transaction)
|
|
80
|
-
transaction.
|
|
80
|
+
transaction.add_request_headers(
|
|
81
|
+
"accept" => "text/html,application/xhtml+xml",
|
|
82
|
+
"accept-encoding" => "gzip, deflate, sdch",
|
|
83
|
+
"accept-language" => "en-US,en;q=0.8,nl;q=0.6",
|
|
84
|
+
"cache-control" => "max-age=0",
|
|
85
|
+
"connection" => "keep-alive",
|
|
86
|
+
"user-agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0)",
|
|
87
|
+
"referer" => "http://appsignal.com/accounts"
|
|
88
|
+
)
|
|
89
|
+
transaction.add_request_environment(
|
|
81
90
|
"REMOTE_ADDR" => "127.0.0.1",
|
|
82
91
|
"REQUEST_METHOD" => "GET",
|
|
83
92
|
"SERVER_NAME" => "localhost",
|
|
84
93
|
"SERVER_PORT" => "80",
|
|
85
94
|
"SERVER_PROTOCOL" => "HTTP/1.1",
|
|
86
95
|
"REQUEST_PATH" => "/hello",
|
|
87
|
-
"PATH_INFO" => "/hello"
|
|
88
|
-
"HTTP_ACCEPT" => "text/html,application/xhtml+xml",
|
|
89
|
-
"HTTP_ACCEPT_ENCODING" => "gzip, deflate, sdch",
|
|
90
|
-
"HTTP_ACCEPT_LANGUAGE" => "en-US,en;q=0.8,nl;q=0.6",
|
|
91
|
-
"HTTP_CACHE_CONTROL" => "max-age=0",
|
|
92
|
-
"HTTP_CONNECTION" => "keep-alive",
|
|
93
|
-
"HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0)",
|
|
94
|
-
"HTTP_REFERER" => "http://appsignal.com/accounts"
|
|
96
|
+
"PATH_INFO" => "/hello"
|
|
95
97
|
)
|
|
96
98
|
end
|
|
97
99
|
end
|
|
@@ -7,13 +7,27 @@ module Appsignal
|
|
|
7
7
|
class RenderFormatter < Appsignal::EventFormatter
|
|
8
8
|
BLANK = ""
|
|
9
9
|
|
|
10
|
+
def opentelemetry_attributes(_payload)
|
|
11
|
+
Appsignal::OpenTelemetry::Rendering.attributes
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def format(payload)
|
|
11
|
-
|
|
15
|
+
# The title is the template's path made relative to the application's
|
|
16
|
+
# root, so a template rendered outside an application gets no title.
|
|
17
|
+
return unless payload[:identifier] && root_path
|
|
12
18
|
|
|
13
19
|
[payload[:identifier].sub(root_path, BLANK), nil]
|
|
14
20
|
end
|
|
15
21
|
|
|
22
|
+
# The application's root, which a template's path is made relative to.
|
|
23
|
+
#
|
|
24
|
+
# Whether there is an application is decided here, when the event is
|
|
25
|
+
# formatted, rather than when this file is loaded. AppSignal can be
|
|
26
|
+
# required before Rails is, and deciding it at load time would leave
|
|
27
|
+
# every template render in the application without a title.
|
|
16
28
|
def root_path
|
|
29
|
+
return unless defined?(Rails)
|
|
30
|
+
|
|
17
31
|
@root_path ||= "#{Rails.root}/"
|
|
18
32
|
end
|
|
19
33
|
end
|
|
@@ -21,24 +35,22 @@ module Appsignal
|
|
|
21
35
|
end
|
|
22
36
|
end
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
end
|
|
38
|
+
Appsignal::EventFormatter.register(
|
|
39
|
+
"render_partial.action_view",
|
|
40
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
41
|
+
)
|
|
42
|
+
Appsignal::EventFormatter.register(
|
|
43
|
+
"render_template.action_view",
|
|
44
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
45
|
+
)
|
|
46
|
+
# Action View reports the template's path for these two as well, so they are
|
|
47
|
+
# titled the same way. A collection reports the partial it rendered for each
|
|
48
|
+
# item, and a layout reports itself.
|
|
49
|
+
Appsignal::EventFormatter.register(
|
|
50
|
+
"render_collection.action_view",
|
|
51
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
52
|
+
)
|
|
53
|
+
Appsignal::EventFormatter.register(
|
|
54
|
+
"render_layout.action_view",
|
|
55
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
56
|
+
)
|