scout_apm 2.5.1 → 5.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +68 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -5
- data/CHANGELOG.markdown +176 -3
- data/Gemfile +1 -7
- data/LICENSE.md +21 -28
- data/gems/README.md +28 -0
- data/gems/instruments.gemfile +6 -0
- data/gems/octoshark.gemfile +4 -0
- data/gems/rails3.gemfile +5 -0
- data/gems/rails4.gemfile +4 -0
- data/gems/rails5.gemfile +4 -0
- data/gems/rails6.gemfile +4 -0
- data/gems/sidekiq.gemfile +4 -0
- data/gems/typhoeus.gemfile +3 -0
- data/lib/scout_apm/agent/preconditions.rb +3 -3
- data/lib/scout_apm/agent.rb +22 -0
- data/lib/scout_apm/agent_context.rb +21 -2
- data/lib/scout_apm/app_server_load.rb +7 -2
- data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
- data/lib/scout_apm/auto_instrument/layer.rb +23 -0
- data/lib/scout_apm/auto_instrument/parser.rb +27 -0
- data/lib/scout_apm/auto_instrument/rails.rb +174 -0
- data/lib/scout_apm/auto_instrument.rb +5 -0
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
- data/lib/scout_apm/background_job_integrations/faktory.rb +103 -0
- data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +55 -0
- data/lib/scout_apm/background_job_integrations/que.rb +134 -0
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +2 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +15 -10
- data/lib/scout_apm/background_job_integrations/sneakers.rb +11 -11
- data/lib/scout_apm/config.rb +54 -6
- data/lib/scout_apm/detailed_trace.rb +3 -2
- data/lib/scout_apm/environment.rb +18 -1
- data/lib/scout_apm/error.rb +27 -0
- data/lib/scout_apm/error_service/error_buffer.rb +39 -0
- data/lib/scout_apm/error_service/error_record.rb +211 -0
- data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
- data/lib/scout_apm/error_service/middleware.rb +32 -0
- data/lib/scout_apm/error_service/notifier.rb +33 -0
- data/lib/scout_apm/error_service/payload.rb +47 -0
- data/lib/scout_apm/error_service/periodic_work.rb +17 -0
- data/lib/scout_apm/error_service/railtie.rb +11 -0
- data/lib/scout_apm/error_service/sidekiq.rb +80 -0
- data/lib/scout_apm/error_service.rb +34 -0
- data/lib/scout_apm/exceptions.rb +12 -0
- data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
- data/lib/scout_apm/external_service_metric_set.rb +97 -0
- data/lib/scout_apm/external_service_metric_stats.rb +85 -0
- data/lib/scout_apm/fake_store.rb +3 -0
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +7 -2
- data/lib/scout_apm/git_revision.rb +9 -0
- data/lib/scout_apm/ignored_uris.rb +3 -1
- data/lib/scout_apm/instant/middleware.rb +4 -1
- data/lib/scout_apm/instrument_manager.rb +22 -1
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +53 -29
- data/lib/scout_apm/instruments/action_view.rb +30 -9
- data/lib/scout_apm/instruments/active_record.rb +69 -19
- data/lib/scout_apm/instruments/elasticsearch.rb +93 -42
- data/lib/scout_apm/instruments/grape.rb +1 -1
- data/lib/scout_apm/instruments/http.rb +68 -0
- data/lib/scout_apm/instruments/http_client.rb +33 -14
- data/lib/scout_apm/instruments/influxdb.rb +2 -2
- data/lib/scout_apm/instruments/memcached.rb +58 -0
- data/lib/scout_apm/instruments/middleware_detailed.rb +1 -1
- data/lib/scout_apm/instruments/middleware_summary.rb +1 -1
- data/lib/scout_apm/instruments/mongoid.rb +10 -5
- data/lib/scout_apm/instruments/moped.rb +44 -19
- data/lib/scout_apm/instruments/net_http.rb +51 -16
- data/lib/scout_apm/instruments/rails_router.rb +1 -1
- data/lib/scout_apm/instruments/redis.rb +27 -12
- data/lib/scout_apm/instruments/redis5.rb +59 -0
- data/lib/scout_apm/instruments/sinatra.rb +3 -1
- data/lib/scout_apm/instruments/typhoeus.rb +90 -0
- data/lib/scout_apm/job_record.rb +4 -2
- data/lib/scout_apm/layaway_file.rb +4 -0
- data/lib/scout_apm/layer.rb +5 -2
- data/lib/scout_apm/layer_children_set.rb +9 -8
- data/lib/scout_apm/layer_converters/external_service_converter.rb +65 -0
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -0
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +2 -0
- data/lib/scout_apm/layer_converters/trace_converter.rb +7 -4
- data/lib/scout_apm/logger.rb +5 -1
- data/lib/scout_apm/middleware.rb +1 -1
- data/lib/scout_apm/periodic_work.rb +19 -0
- data/lib/scout_apm/remote/message.rb +4 -0
- data/lib/scout_apm/remote/server.rb +13 -1
- data/lib/scout_apm/reporter.rb +8 -3
- data/lib/scout_apm/reporting.rb +2 -1
- data/lib/scout_apm/request_histograms.rb +8 -0
- data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
- data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
- data/lib/scout_apm/serializers/external_service_serializer_to_json.rb +15 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +4 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +10 -3
- data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
- data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
- data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
- data/lib/scout_apm/slow_policy/policy.rb +21 -0
- data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
- data/lib/scout_apm/slow_request_policy.rb +18 -77
- data/lib/scout_apm/store.rb +31 -1
- data/lib/scout_apm/tracer.rb +2 -2
- data/lib/scout_apm/tracked_request.rb +35 -4
- data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
- data/lib/scout_apm/utils/marshal_logging.rb +90 -0
- data/lib/scout_apm/utils/sql_sanitizer.rb +47 -7
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +46 -1
- data/scout_apm.gemspec +14 -9
- data/test/test_helper.rb +2 -2
- data/test/tmp/README.md +17 -0
- data/test/unit/agent_context_test.rb +29 -0
- data/test/unit/auto_instrument/anonymous_block_value.rb +7 -0
- data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
- data/test/unit/auto_instrument/assignments.rb +31 -0
- data/test/unit/auto_instrument/controller-ast.txt +57 -0
- data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
- data/test/unit/auto_instrument/controller.rb +49 -0
- data/test/unit/auto_instrument/hanging_method.rb +6 -0
- data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
- data/test/unit/auto_instrument/rescue_from.rb +13 -0
- data/test/unit/auto_instrument_test.rb +62 -0
- data/test/unit/background_job_integrations/sidekiq_test.rb +17 -0
- data/test/unit/environment_test.rb +2 -2
- data/test/unit/error_service/error_buffer_test.rb +25 -0
- data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
- data/test/unit/external_service_metric_set_test.rb +67 -0
- data/test/unit/external_service_metric_stats_test.rb +106 -0
- data/test/unit/ignored_uris_test.rb +6 -0
- data/test/unit/instruments/active_record_test.rb +40 -0
- data/test/unit/instruments/http_client_test.rb +24 -0
- data/test/unit/instruments/http_test.rb +24 -0
- data/test/unit/instruments/moped_test.rb +24 -0
- data/test/unit/instruments/net_http_test.rb +11 -1
- data/test/unit/instruments/redis_test.rb +24 -0
- data/test/unit/instruments/typhoeus_test.rb +42 -0
- data/test/unit/layer_children_set_test.rb +9 -0
- data/test/unit/remote/{test_message.rb → message_test.rb} +0 -0
- data/test/unit/remote/{test_router.rb → route_test.rb} +0 -0
- data/test/unit/remote/{test_server.rb → server_test.rb} +4 -1
- data/test/unit/request_histograms_test.rb +17 -0
- data/test/unit/serializers/payload_serializer_test.rb +39 -3
- data/test/unit/slow_request_policy_test.rb +41 -13
- data/test/unit/sql_sanitizer_test.rb +106 -0
- data/test/unit/tracer_test.rb +25 -0
- metadata +118 -60
- data/.travis.yml +0 -25
- data/lib/scout_apm/instruments/.DS_Store +0 -0
- data/lib/scout_apm/slow_job_policy.rb +0 -111
- data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -25
- data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -26
- data/test/unit/instruments/active_record_instruments_test.rb +0 -5
- data/test/unit/slow_job_policy_test.rb +0 -6
@@ -16,32 +16,37 @@ module ScoutApm
|
|
16
16
|
@installed
|
17
17
|
end
|
18
18
|
|
19
|
-
def install
|
19
|
+
def install(prepend:)
|
20
20
|
if defined?(::Moped)
|
21
21
|
@installed = true
|
22
22
|
|
23
|
-
logger.info "Instrumenting Moped"
|
23
|
+
logger.info "Instrumenting Moped. Prepend: #{prepend}"
|
24
24
|
|
25
|
-
|
26
|
-
include ScoutApm::Tracer
|
25
|
+
if prepend
|
26
|
+
::Moped::Node.send(:include, ScoutApm::Tracer)
|
27
|
+
::Moped::Node.send(:prepend, MopedInstrumentationPrepend)
|
28
|
+
else
|
29
|
+
::Moped::Node.class_eval do
|
30
|
+
include ScoutApm::Tracer
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
def process_with_scout_instruments(operation, &callback)
|
33
|
+
if operation.respond_to?(:collection)
|
34
|
+
collection = operation.collection
|
35
|
+
name = "Process/#{collection}/#{operation.class.to_s.split('::').last}"
|
36
|
+
self.class.instrument("MongoDB", name, :annotate_layer => { :query => scout_sanitize_log(operation.log_inspect) }) do
|
37
|
+
process_without_scout_instruments(operation, &callback)
|
38
|
+
end
|
34
39
|
end
|
35
40
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
alias_method :process_without_scout_instruments, :process
|
42
|
+
alias_method :process, :process_with_scout_instruments
|
43
|
+
|
44
|
+
# replaces values w/ ?
|
45
|
+
def scout_sanitize_log(log)
|
46
|
+
return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
|
47
|
+
log.gsub(/(=>")((?:[^"]|"")*)"/) do
|
48
|
+
$1 + '?' + '"'
|
49
|
+
end
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
@@ -49,6 +54,26 @@ module ScoutApm
|
|
49
54
|
end
|
50
55
|
|
51
56
|
end
|
57
|
+
|
58
|
+
module MopedInstrumentationPrepend
|
59
|
+
def process(operation, &callback)
|
60
|
+
if operation.respond_to?(:collection)
|
61
|
+
collection = operation.collection
|
62
|
+
name = "Process/#{collection}/#{operation.class.to_s.split('::').last}"
|
63
|
+
self.class.instrument("MongoDB", name, :annotate_layer => { :query => scout_sanitize_log(operation.log_inspect) }) do
|
64
|
+
super(operation, &callback)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# replaces values w/ ?
|
70
|
+
def scout_sanitize_log(log)
|
71
|
+
return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
|
72
|
+
log.gsub(/(=>")((?:[^"]|"")*)"/) do
|
73
|
+
$1 + '?' + '"'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
52
77
|
end
|
53
78
|
end
|
54
79
|
|
@@ -16,34 +16,69 @@ module ScoutApm
|
|
16
16
|
@installed
|
17
17
|
end
|
18
18
|
|
19
|
-
def install
|
19
|
+
def install(prepend:)
|
20
20
|
if defined?(::Net) && defined?(::Net::HTTP)
|
21
21
|
@installed = true
|
22
22
|
|
23
|
-
logger.info "Instrumenting Net::HTTP"
|
23
|
+
logger.info "Instrumenting Net::HTTP. Prepend: #{prepend}"
|
24
24
|
|
25
|
-
|
26
|
-
include ScoutApm::Tracer
|
25
|
+
if prepend
|
26
|
+
::Net::HTTP.send(:include, ScoutApm::Tracer)
|
27
|
+
::Net::HTTP.send(:prepend, NetHttpInstrumentationPrepend)
|
28
|
+
else
|
29
|
+
::Net::HTTP.class_eval do
|
30
|
+
include ScoutApm::Tracer
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
def request_with_scout_instruments(*args, &block)
|
33
|
+
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(args.first)) do
|
34
|
+
request_without_scout_instruments(*args, &block)
|
35
|
+
end
|
31
36
|
end
|
32
|
-
end
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def request_scout_description(req)
|
39
|
+
path = req.path
|
40
|
+
path = path.path if path.respond_to?(:path)
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
# Protect against a nil address value
|
43
|
+
if @address.nil?
|
44
|
+
return "No Address Found"
|
45
|
+
end
|
46
|
+
|
47
|
+
max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
|
48
|
+
(@address + path.split('?').first)[0..(max_length - 1)]
|
49
|
+
rescue
|
50
|
+
""
|
51
|
+
end
|
41
52
|
|
42
|
-
|
43
|
-
|
53
|
+
alias request_without_scout_instruments request
|
54
|
+
alias request request_with_scout_instruments
|
55
|
+
end
|
44
56
|
end
|
45
57
|
end
|
46
58
|
end
|
47
59
|
end
|
60
|
+
|
61
|
+
module NetHttpInstrumentationPrepend
|
62
|
+
def request(request, *args, &block)
|
63
|
+
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(args.first)) do
|
64
|
+
super(request, *args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def request_scout_description(req)
|
69
|
+
path = req.path
|
70
|
+
path = path.path if path.respond_to?(:path)
|
71
|
+
|
72
|
+
# Protect against a nil address value
|
73
|
+
if @address.nil?
|
74
|
+
return "No Address Found"
|
75
|
+
end
|
76
|
+
|
77
|
+
max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
|
78
|
+
(@address + path.split('?').first)[0..(max_length - 1)]
|
79
|
+
rescue
|
80
|
+
""
|
81
|
+
end
|
82
|
+
end
|
48
83
|
end
|
49
84
|
end
|
@@ -16,28 +16,43 @@ module ScoutApm
|
|
16
16
|
@installed
|
17
17
|
end
|
18
18
|
|
19
|
-
def install
|
20
|
-
if defined?(::Redis) && defined?(::Redis::Client)
|
19
|
+
def install(prepend:)
|
20
|
+
if defined?(::Redis) && defined?(::Redis::Client) && ::Redis::Client.instance_methods(false).include?(:call)
|
21
21
|
@installed = true
|
22
22
|
|
23
|
-
logger.info "Instrumenting Redis"
|
23
|
+
logger.info "Instrumenting Redis. Prepend: #{prepend}"
|
24
24
|
|
25
|
-
|
26
|
-
include ScoutApm::Tracer
|
25
|
+
if prepend
|
26
|
+
::Redis::Client.send(:include, ScoutApm::Tracer)
|
27
|
+
::Redis::Client.send(:prepend, RedisClientInstrumentationPrepend)
|
28
|
+
else
|
29
|
+
::Redis::Client.class_eval do
|
30
|
+
include ScoutApm::Tracer
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
def call_with_scout_instruments(*args, &block)
|
33
|
+
command = args.first.first rescue "Unknown"
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
self.class.instrument("Redis", command) do
|
36
|
+
call_without_scout_instruments(*args, &block)
|
37
|
+
end
|
33
38
|
end
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
|
40
|
+
alias_method :call_without_scout_instruments, :call
|
41
|
+
alias_method :call, :call_with_scout_instruments
|
42
|
+
end
|
38
43
|
end
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
47
|
+
|
48
|
+
module RedisClientInstrumentationPrepend
|
49
|
+
def call(*args, &block)
|
50
|
+
command = args.first.first rescue "Unknown"
|
51
|
+
|
52
|
+
self.class.instrument("Redis", command) do
|
53
|
+
super(*args, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
42
57
|
end
|
43
58
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
class Redis5
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
@installed = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def logger
|
12
|
+
context.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def installed?
|
16
|
+
@installed
|
17
|
+
end
|
18
|
+
|
19
|
+
def install(prepend:)
|
20
|
+
if defined?(::Redis) && defined?(::Redis::Client) && ::Redis::Client.instance_methods(false).include?(:call_v)
|
21
|
+
@installed = true
|
22
|
+
|
23
|
+
logger.info "Instrumenting Redis5. Prepend: #{prepend}"
|
24
|
+
|
25
|
+
if prepend
|
26
|
+
::Redis::Client.send(:include, ScoutApm::Tracer)
|
27
|
+
::Redis::Client.send(:prepend, Redis5ClientInstrumentationPrepend)
|
28
|
+
else
|
29
|
+
::Redis::Client.class_eval do
|
30
|
+
include ScoutApm::Tracer
|
31
|
+
|
32
|
+
def call_with_scout_instruments(args, &block)
|
33
|
+
command = args.first rescue "Unknown"
|
34
|
+
|
35
|
+
self.class.instrument("Redis", command) do
|
36
|
+
call_without_scout_instruments(args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :call_without_scout_instruments, :call_v
|
41
|
+
alias_method :call_v, :call_with_scout_instruments
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Redis5ClientInstrumentationPrepend
|
49
|
+
def call(args, &block)
|
50
|
+
command = args.first rescue "Unknown"
|
51
|
+
|
52
|
+
self.class.instrument("Redis", command) do
|
53
|
+
super(args, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
1
3
|
# XXX: Is this file used?
|
2
4
|
module ScoutApm
|
3
5
|
module Instruments
|
@@ -13,7 +15,7 @@ module ScoutApm
|
|
13
15
|
@installed
|
14
16
|
end
|
15
17
|
|
16
|
-
def install
|
18
|
+
def install(prepend:)
|
17
19
|
if defined?(::Sinatra) && defined?(::Sinatra::Base) && ::Sinatra::Base.private_method_defined?(:dispatch!)
|
18
20
|
@installed = true
|
19
21
|
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
class Typhoeus
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
@installed = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def logger
|
12
|
+
context.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def installed?
|
16
|
+
@installed
|
17
|
+
end
|
18
|
+
|
19
|
+
def install(prepend:)
|
20
|
+
if defined?(::Typhoeus)
|
21
|
+
@installed = true
|
22
|
+
|
23
|
+
logger.info "Instrumenting Typhoeus"
|
24
|
+
|
25
|
+
::Typhoeus::Request.send(:prepend, TyphoeusInstrumentation)
|
26
|
+
::Typhoeus::Hydra.send(:prepend, TyphoeusHydraInstrumentation)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module TyphoeusHydraInstrumentation
|
31
|
+
def run(*args, &block)
|
32
|
+
layer = ScoutApm::Layer.new("HTTP", "Hydra")
|
33
|
+
layer.desc = scout_desc
|
34
|
+
|
35
|
+
req = ScoutApm::RequestManager.lookup
|
36
|
+
req.start_layer(layer)
|
37
|
+
|
38
|
+
begin
|
39
|
+
super(*args, &block)
|
40
|
+
ensure
|
41
|
+
req.stop_layer
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def scout_desc
|
46
|
+
"#{self.queued_requests.count} requests"
|
47
|
+
rescue
|
48
|
+
""
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module TyphoeusInstrumentation
|
53
|
+
def run(*args, &block)
|
54
|
+
layer = ScoutApm::Layer.new("HTTP", scout_request_verb)
|
55
|
+
layer.desc = scout_desc(scout_request_verb, scout_request_url)
|
56
|
+
|
57
|
+
req = ScoutApm::RequestManager.lookup
|
58
|
+
req.start_layer(layer)
|
59
|
+
|
60
|
+
begin
|
61
|
+
super(*args, &block)
|
62
|
+
ensure
|
63
|
+
req.stop_layer
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def scout_desc(verb, uri)
|
68
|
+
max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
|
69
|
+
(String(uri).split('?').first)[0..(max_length - 1)]
|
70
|
+
rescue
|
71
|
+
""
|
72
|
+
end
|
73
|
+
|
74
|
+
def scout_request_url
|
75
|
+
self.url
|
76
|
+
rescue
|
77
|
+
""
|
78
|
+
end
|
79
|
+
|
80
|
+
def scout_request_verb
|
81
|
+
self.options[:method].to_s
|
82
|
+
rescue
|
83
|
+
""
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/scout_apm/job_record.rb
CHANGED
@@ -32,8 +32,10 @@ module ScoutApm
|
|
32
32
|
|
33
33
|
# Modifies self and returns self, after merging in `other`.
|
34
34
|
def combine!(other)
|
35
|
-
|
36
|
-
|
35
|
+
if !self.eql?(other)
|
36
|
+
ScoutApm::Agent.instance.logger.debug("Mismatched Merge of Background Job: (Queue #{queue_name} == #{other.queue_name}) (Name #{job_name} == #{other.job_name}) (Hash #{hash} == #{other.hash})")
|
37
|
+
return self
|
38
|
+
end
|
37
39
|
|
38
40
|
@errors += other.errors
|
39
41
|
@metric_set = metric_set.combine!(other.metric_set)
|
@@ -30,6 +30,10 @@ module ScoutApm
|
|
30
30
|
|
31
31
|
def serialize(data)
|
32
32
|
Marshal.dump(data)
|
33
|
+
rescue
|
34
|
+
ScoutApm::Agent.instance.logger.info("Failed Marshalling LayawayFile")
|
35
|
+
ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
|
36
|
+
raise
|
33
37
|
end
|
34
38
|
|
35
39
|
def deserialize(data)
|
data/lib/scout_apm/layer.rb
CHANGED
@@ -36,7 +36,10 @@ module ScoutApm
|
|
36
36
|
|
37
37
|
# If this layer took longer than a fixed amount of time, store the
|
38
38
|
# backtrace of where it occurred.
|
39
|
-
|
39
|
+
attr_accessor :backtrace
|
40
|
+
|
41
|
+
# The file name associated with the layer. Only used for autoinstruments overhead logging.
|
42
|
+
attr_accessor :file_name
|
40
43
|
|
41
44
|
# As we go through a part of a request, instrumentation can store additional data
|
42
45
|
# Known Keys:
|
@@ -113,7 +116,7 @@ module ScoutApm
|
|
113
116
|
# In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.
|
114
117
|
def caller_array
|
115
118
|
# omits the first several callers which are in the ScoutAPM stack.
|
116
|
-
if ScoutApm::Agent.instance.context.environment.ruby_2?
|
119
|
+
if ScoutApm::Agent.instance.context.environment.ruby_2? || ScoutApm::Agent.instance.context.environment.ruby_3?
|
117
120
|
caller(3...BACKTRACE_CALLER_LIMIT)
|
118
121
|
else
|
119
122
|
caller[3...BACKTRACE_CALLER_LIMIT]
|
@@ -46,9 +46,15 @@ module ScoutApm
|
|
46
46
|
set = child_set(metric_type)
|
47
47
|
|
48
48
|
if set.size >= unique_cutoff
|
49
|
-
# find limited_layer
|
50
|
-
@limited_layers
|
51
|
-
@limited_layers
|
49
|
+
# find or create limited_layer
|
50
|
+
@limited_layers ||= Hash.new
|
51
|
+
layer = if @limited_layers.has_key?(metric_type)
|
52
|
+
@limited_layers[metric_type]
|
53
|
+
else
|
54
|
+
@limited_layers[metric_type] = LimitedLayer.new(metric_type)
|
55
|
+
end
|
56
|
+
|
57
|
+
layer.absorb(child)
|
52
58
|
else
|
53
59
|
# we have space just add it
|
54
60
|
set << child
|
@@ -76,10 +82,5 @@ module ScoutApm
|
|
76
82
|
def size
|
77
83
|
@children.size
|
78
84
|
end
|
79
|
-
|
80
|
-
# hold off initializing this until we know we need it
|
81
|
-
def init_limited_layers
|
82
|
-
@limited_layers ||= Hash.new { |hash, key| hash[key] = LimitedLayer.new(key) }
|
83
|
-
end
|
84
85
|
end
|
85
86
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module LayerConverters
|
3
|
+
class ExternalServiceConverter < ConverterBase
|
4
|
+
def initialize(*)
|
5
|
+
super
|
6
|
+
@external_service_metric_set = ExternalServiceMetricSet.new(context)
|
7
|
+
end
|
8
|
+
|
9
|
+
def register_hooks(walker)
|
10
|
+
super
|
11
|
+
|
12
|
+
return unless scope_layer
|
13
|
+
|
14
|
+
walker.on do |layer|
|
15
|
+
next if skip_layer?(layer)
|
16
|
+
stat = ExternalServiceMetricStats.new(
|
17
|
+
domain_name(layer),
|
18
|
+
operation_name(layer), # operation name/verb. GET/POST/PUT etc.
|
19
|
+
scope_layer.legacy_metric_name, # controller_scope
|
20
|
+
1, # count, this is a single call, so 1
|
21
|
+
layer.total_call_time
|
22
|
+
)
|
23
|
+
@external_service_metric_set << stat
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def record!
|
28
|
+
# Everything in the metric set here is from a single transaction, which
|
29
|
+
# we want to keep track of. (One web call did a User#find 10 times, but
|
30
|
+
# only due to 1 http request)
|
31
|
+
@external_service_metric_set.increment_transaction_count!
|
32
|
+
@store.track_external_service_metrics!(@external_service_metric_set)
|
33
|
+
|
34
|
+
nil # not returning anything in the layer results ... not used
|
35
|
+
end
|
36
|
+
|
37
|
+
def skip_layer?(layer)
|
38
|
+
layer.type != 'HTTP' ||
|
39
|
+
layer.limited? ||
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# If we can't name the domain name, default to:
|
46
|
+
DEFAULT_DOMAIN = "Unknown"
|
47
|
+
|
48
|
+
def domain_name(layer)
|
49
|
+
domain = ""
|
50
|
+
desc_str = layer.desc.to_s
|
51
|
+
desc_str = 'http://' + desc_str unless desc_str =~ /^http/i
|
52
|
+
domain = URI.parse(desc_str).host
|
53
|
+
rescue
|
54
|
+
# Do nothing
|
55
|
+
ensure
|
56
|
+
domain = DEFAULT_DOMAIN if domain.to_s.blank?
|
57
|
+
domain
|
58
|
+
end
|
59
|
+
|
60
|
+
def operation_name(layer)
|
61
|
+
"all" # Hardcode to "all" until we support breakout by verb
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -5,6 +5,10 @@
|
|
5
5
|
# show
|
6
6
|
# render :update
|
7
7
|
# end
|
8
|
+
|
9
|
+
# This doesn't cache the negative result when searching for a controller / job,
|
10
|
+
# so that we can ask again later after more of the request has occurred and
|
11
|
+
# correctly find it.
|
8
12
|
module ScoutApm
|
9
13
|
module LayerConverters
|
10
14
|
class FindLayerByType
|
@@ -58,6 +58,9 @@ module ScoutApm
|
|
58
58
|
code = "" # User#index for instance
|
59
59
|
|
60
60
|
spans = create_spans(request.root_layer)
|
61
|
+
if limited?
|
62
|
+
tags[:"scout.reached_span_cap"] = true
|
63
|
+
end
|
61
64
|
|
62
65
|
DetailedTrace.new(
|
63
66
|
transaction_id,
|
@@ -117,7 +120,9 @@ module ScoutApm
|
|
117
120
|
tags)
|
118
121
|
|
119
122
|
layer.children.each do |child|
|
120
|
-
|
123
|
+
# Don't create spans from limited layers. These don't have start/stop times and our processing can't
|
124
|
+
# handle these yet.
|
125
|
+
unless over_span_limit?(result) || child.is_a?(LimitedLayer)
|
121
126
|
result += create_spans(child, span_id)
|
122
127
|
end
|
123
128
|
end
|
@@ -152,10 +157,8 @@ module ScoutApm
|
|
152
157
|
# Limit Handling
|
153
158
|
################################################################################
|
154
159
|
|
155
|
-
# To prevent huge traces from being generated, we
|
160
|
+
# To prevent huge traces from being generated, we stop collecting
|
156
161
|
# spans as we go beyond some reasonably large count.
|
157
|
-
# Until the root cause of https://github.com/scoutapp/scout_apm_ruby/issues/267 is addressed,
|
158
|
-
# keep `MAX_SPANS` less than `DEFAULT_UNIQUE_CUTOFF`.
|
159
162
|
MAX_SPANS = 1500
|
160
163
|
|
161
164
|
def over_span_limit?(spans)
|
data/lib/scout_apm/logger.rb
CHANGED
@@ -70,13 +70,17 @@ module ScoutApm
|
|
70
70
|
|
71
71
|
def build_logger
|
72
72
|
logger_class.new(@log_destination)
|
73
|
+
rescue => e
|
74
|
+
logger = ::Logger.new(STDERR)
|
75
|
+
logger.error("Error while building ScoutApm logger: #{e.message}. Falling back to STDERR")
|
76
|
+
logger
|
73
77
|
end
|
74
78
|
|
75
79
|
def logger_class
|
76
80
|
klass = @opts.fetch(:logger_class, ::Logger)
|
77
81
|
case klass
|
78
82
|
when String
|
79
|
-
result = KlassHelper.lookup(klass)
|
83
|
+
result = Utils::KlassHelper.lookup(klass)
|
80
84
|
if result == :missing_class
|
81
85
|
::Logger
|
82
86
|
else
|
data/lib/scout_apm/middleware.rb
CHANGED
@@ -26,7 +26,7 @@ module ScoutApm
|
|
26
26
|
ScoutApm::Agent.instance.start
|
27
27
|
@started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
|
28
28
|
rescue => e
|
29
|
-
ScoutApm::Agent.instance.context.logger("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
29
|
+
ScoutApm::Agent.instance.context.logger.info("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -12,10 +12,29 @@ module ScoutApm
|
|
12
12
|
ScoutApm::Debug.instance.call_periodic_hooks
|
13
13
|
@reporting.process_metrics
|
14
14
|
clean_old_percentiles
|
15
|
+
|
16
|
+
if context.config.value('auto_instruments')
|
17
|
+
log_autoinstrument_significant_counts rescue nil
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
private
|
18
22
|
|
23
|
+
def log_autoinstrument_significant_counts
|
24
|
+
# Ex key/value -
|
25
|
+
# "/Users/dlite/projects/scout/apm/app/controllers/application_controller.rb"=>[[0.0, 689], [1.0, 16]]
|
26
|
+
hists = context.auto_instruments_layer_histograms.as_json
|
27
|
+
hists_summary = hists.map { |file, buckets|
|
28
|
+
total = buckets.map(&:last).inject(0) { |sum, count| sum + count }
|
29
|
+
significant = (buckets.last.last / total.to_f).round(2)
|
30
|
+
[
|
31
|
+
file,
|
32
|
+
{:total => total, :significant => significant}
|
33
|
+
]
|
34
|
+
}.to_h
|
35
|
+
context.logger.debug("AutoInstrument Significant Layer Histograms: #{hists_summary.pretty_inspect}")
|
36
|
+
end
|
37
|
+
|
19
38
|
# XXX: Move logic into a RequestHistogramsByTime class that can keep the timeout logic in it
|
20
39
|
def clean_old_percentiles
|
21
40
|
context.
|
@@ -17,6 +17,10 @@ module ScoutApm
|
|
17
17
|
|
18
18
|
def encode
|
19
19
|
Marshal.dump(self)
|
20
|
+
rescue
|
21
|
+
ScoutApm::Agent.instance.logger.info("Failed Marshalling Remote::Message")
|
22
|
+
ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(self).dive) rescue nil
|
23
|
+
raise
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|