scout_apm 2.1.32 → 2.2.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.markdown +2 -161
- data/Rakefile +2 -2
- data/ext/allocations/allocations.c +0 -6
- data/ext/allocations/extconf.rb +0 -1
- data/ext/stacks/extconf.rb +33 -0
- data/ext/stacks/scout_atomics.h +86 -0
- data/ext/stacks/stacks.c +744 -0
- data/lib/scout_apm.rb +16 -24
- data/lib/scout_apm/agent.rb +38 -93
- data/lib/scout_apm/agent/logging.rb +1 -6
- data/lib/scout_apm/agent/reporting.rb +6 -8
- data/lib/scout_apm/app_server_load.rb +10 -21
- data/lib/scout_apm/attribute_arranger.rb +2 -0
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -71
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +27 -66
- data/lib/scout_apm/background_worker.rb +15 -19
- data/lib/scout_apm/capacity.rb +57 -0
- data/lib/scout_apm/config.rb +29 -135
- data/lib/scout_apm/context.rb +5 -9
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
- data/lib/scout_apm/environment.rb +15 -22
- data/lib/scout_apm/histogram.rb +2 -11
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +57 -198
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +59 -90
- data/lib/scout_apm/instruments/active_record.rb +5 -7
- data/lib/scout_apm/instruments/delayed_job.rb +57 -0
- data/lib/scout_apm/instruments/grape.rb +3 -4
- data/lib/scout_apm/instruments/middleware_detailed.rb +6 -4
- data/lib/scout_apm/instruments/middleware_summary.rb +1 -39
- data/lib/scout_apm/instruments/mongoid.rb +3 -24
- data/lib/scout_apm/instruments/net_http.rb +2 -7
- data/lib/scout_apm/instruments/percentile_sampler.rb +19 -36
- data/lib/scout_apm/instruments/process/process_cpu.rb +2 -3
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/layaway.rb +33 -76
- data/lib/scout_apm/layer.rb +59 -16
- data/lib/scout_apm/layer_converters/converter_base.rb +0 -199
- data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +90 -15
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +101 -13
- data/lib/scout_apm/metric_set.rb +1 -9
- data/lib/scout_apm/metric_stats.rb +8 -8
- data/lib/scout_apm/reporter.rb +15 -51
- data/lib/scout_apm/request_histograms.rb +0 -4
- data/lib/scout_apm/request_manager.rb +1 -2
- data/lib/scout_apm/scored_item_set.rb +0 -7
- data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +3 -9
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +5 -2
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
- data/lib/scout_apm/server_integrations/puma.rb +2 -5
- data/lib/scout_apm/slow_item_set.rb +80 -0
- data/lib/scout_apm/slow_job_record.rb +1 -6
- data/lib/scout_apm/slow_transaction.rb +2 -20
- data/lib/scout_apm/store.rb +12 -50
- data/lib/scout_apm/trace_compactor.rb +311 -0
- data/lib/scout_apm/tracked_request.rb +37 -128
- data/lib/scout_apm/utils/backtrace_parser.rb +5 -7
- data/lib/scout_apm/utils/fake_stacks.rb +83 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +4 -6
- data/test/test_helper.rb +0 -56
- data/test/unit/config_test.rb +9 -60
- data/test/unit/histogram_test.rb +0 -14
- data/test/unit/layaway_test.rb +16 -31
- data/test/unit/serializers/payload_serializer_test.rb +105 -3
- data/test/unit/slow_item_set_test.rb +94 -0
- data/test/unit/slow_job_policy_test.rb +49 -0
- data/test/unit/slow_request_policy_test.rb +5 -4
- data/test/unit/utils/backtrace_parser_test.rb +0 -19
- data/tester.rb +53 -0
- metadata +29 -124
- data/.rubocop.yml +0 -8
- data/Guardfile +0 -42
- data/ext/rusage/README.md +0 -26
- data/ext/rusage/extconf.rb +0 -5
- data/ext/rusage/rusage.c +0 -52
- data/lib/scout_apm/background_job_integrations/resque.rb +0 -85
- data/lib/scout_apm/background_recorder.rb +0 -43
- data/lib/scout_apm/debug.rb +0 -37
- data/lib/scout_apm/git_revision.rb +0 -51
- data/lib/scout_apm/instruments/action_view.rb +0 -49
- data/lib/scout_apm/instruments/resque.rb +0 -40
- data/lib/scout_apm/layer_children_set.rb +0 -77
- data/lib/scout_apm/limited_layer.rb +0 -122
- data/lib/scout_apm/rack.rb +0 -26
- data/lib/scout_apm/remote/message.rb +0 -23
- data/lib/scout_apm/remote/recorder.rb +0 -57
- data/lib/scout_apm/remote/router.rb +0 -49
- data/lib/scout_apm/remote/server.rb +0 -58
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
- data/lib/scout_apm/synchronous_recorder.rb +0 -26
- data/lib/scout_apm/utils/gzip_helper.rb +0 -24
- data/lib/scout_apm/utils/numbers.rb +0 -14
- data/lib/scout_apm/utils/scm.rb +0 -14
- data/test/unit/background_job_integrations/sidekiq_test.rb +0 -104
- data/test/unit/context_test.rb +0 -30
- data/test/unit/git_revision_test.rb +0 -15
- data/test/unit/instruments/net_http_test.rb +0 -21
- data/test/unit/instruments/percentile_sampler_test.rb +0 -137
- data/test/unit/layer_children_set_test.rb +0 -88
- data/test/unit/limited_layer_test.rb +0 -53
- data/test/unit/remote/test_message.rb +0 -13
- data/test/unit/remote/test_router.rb +0 -33
- data/test/unit/remote/test_server.rb +0 -15
- data/test/unit/store_test.rb +0 -89
- data/test/unit/test_tracked_request.rb +0 -87
- data/test/unit/utils/numbers_test.rb +0 -15
- data/test/unit/utils/scm.rb +0 -17
@@ -57,7 +57,7 @@ module ScoutApm
|
|
57
57
|
walker.walk do |layer|
|
58
58
|
next if layer == job_layer
|
59
59
|
next if layer == queue_layer
|
60
|
-
next if
|
60
|
+
next if layer.annotations[:ignorable]
|
61
61
|
|
62
62
|
# we don't need to use the full metric name for scoped metrics as we
|
63
63
|
# only display metrics aggregrated by type, just use "ActiveRecord"
|
@@ -2,6 +2,7 @@ module ScoutApm
|
|
2
2
|
module LayerConverters
|
3
3
|
class SlowJobConverter < ConverterBase
|
4
4
|
def initialize(*)
|
5
|
+
@backtraces = []
|
5
6
|
super
|
6
7
|
|
7
8
|
# After call to super, so @request is populated
|
@@ -10,8 +11,6 @@ module ScoutApm
|
|
10
11
|
else
|
11
12
|
-1
|
12
13
|
end
|
13
|
-
|
14
|
-
setup_subscopable_callbacks
|
15
14
|
end
|
16
15
|
|
17
16
|
def name
|
@@ -33,7 +32,6 @@ module ScoutApm
|
|
33
32
|
mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(request.capture_mem_delta!)
|
34
33
|
|
35
34
|
timing_metrics, allocation_metrics = create_metrics
|
36
|
-
|
37
35
|
unless ScoutApm::Instruments::Allocations::ENABLED
|
38
36
|
allocation_metrics = {}
|
39
37
|
end
|
@@ -49,9 +47,7 @@ module ScoutApm
|
|
49
47
|
allocation_metrics,
|
50
48
|
mem_delta,
|
51
49
|
job_layer.total_allocations,
|
52
|
-
score
|
53
|
-
limited?
|
54
|
-
)
|
50
|
+
score)
|
55
51
|
end
|
56
52
|
|
57
53
|
def queue_layer
|
@@ -62,29 +58,108 @@ module ScoutApm
|
|
62
58
|
@job_layer ||= find_first_layer_of_type("Job")
|
63
59
|
end
|
64
60
|
|
65
|
-
def skip_layer?(layer)
|
66
|
-
super(layer) || layer == queue_layer
|
67
|
-
end
|
68
|
-
|
69
61
|
def create_metrics
|
70
62
|
metric_hash = Hash.new
|
71
63
|
allocation_metric_hash = Hash.new
|
72
64
|
|
65
|
+
# Keep a list of subscopes, but only ever use the front one. The rest
|
66
|
+
# get pushed/popped in cases when we have many levels of subscopable
|
67
|
+
# layers. This lets us push/pop without otherwise keeping track very closely.
|
68
|
+
subscope_layers = []
|
69
|
+
|
70
|
+
walker.before do |layer|
|
71
|
+
if layer.subscopable?
|
72
|
+
subscope_layers.push(layer)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
walker.after do |layer|
|
77
|
+
if layer.subscopable?
|
78
|
+
subscope_layers.pop
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
73
82
|
walker.walk do |layer|
|
74
|
-
|
83
|
+
# Sometimes we start capturing a layer without knowing if we really
|
84
|
+
# want to make an entry for it. See ActiveRecord instrumentation for
|
85
|
+
# an example. We start capturing before we know if a query is cached
|
86
|
+
# or not, and want to skip any cached queries.
|
87
|
+
next if layer.annotations[:ignorable]
|
75
88
|
|
76
89
|
# The queue_layer is useful to capture for other reasons, but doesn't
|
77
90
|
# create a MetricMeta/Stat of its own
|
78
91
|
next if layer == queue_layer
|
79
92
|
|
80
|
-
|
81
|
-
|
82
|
-
|
93
|
+
meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
|
94
|
+
subscope_name = subscope_layers.first.legacy_metric_name
|
95
|
+
{:scope => subscope_name}
|
96
|
+
elsif layer == job_layer # We don't scope the controller under itself
|
97
|
+
{}
|
98
|
+
else
|
99
|
+
{:scope => job_layer.legacy_metric_name}
|
100
|
+
end
|
101
|
+
|
102
|
+
# Specific Metric
|
103
|
+
meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
|
104
|
+
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
105
|
+
meta.extra.merge!(layer.annotations)
|
106
|
+
|
107
|
+
if layer.backtrace
|
108
|
+
bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
|
109
|
+
if bt.any? # we could walk thru the call stack and not find in-app code
|
110
|
+
meta.backtrace = bt
|
111
|
+
# Why not just call meta.backtrace and call it done? The walker could access a later later that generates the same MetricMeta but doesn't have a backtrace. This could be
|
112
|
+
# lost in the metric_hash if it is replaced by the new key.
|
113
|
+
@backtraces << meta
|
114
|
+
else
|
115
|
+
ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
120
|
+
allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
121
|
+
stat = metric_hash[meta]
|
122
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
123
|
+
stat = allocation_metric_hash[meta]
|
124
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
125
|
+
|
126
|
+
# Merged Metric (no specifics, just sum up by type)
|
127
|
+
meta = MetricMeta.new("#{layer.type}/all")
|
128
|
+
metric_hash[meta] ||= MetricStats.new(false)
|
129
|
+
allocation_metric_hash[meta] ||= MetricStats.new(false)
|
130
|
+
stat = metric_hash[meta]
|
131
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
132
|
+
stat = allocation_metric_hash[meta]
|
133
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
134
|
+
|
135
|
+
stat.add_traces(layer.traces.as_json)
|
136
|
+
|
137
|
+
# Debug logging for scoutprof traces
|
138
|
+
if ScoutApm::Agent.instance.config.value('profile')
|
139
|
+
if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
|
140
|
+
ScoutApm::Agent.instance.logger.debug do
|
141
|
+
traces_inspect = layer.traces.inspect
|
142
|
+
"****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}:\n#{traces_inspect}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
else
|
146
|
+
if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
|
147
|
+
ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end # walker.walk
|
83
151
|
|
84
152
|
metric_hash = attach_backtraces(metric_hash)
|
85
153
|
allocation_metric_hash = attach_backtraces(allocation_metric_hash)
|
86
154
|
|
87
|
-
[metric_hash,
|
155
|
+
[metric_hash,allocation_metric_hash]
|
156
|
+
end
|
157
|
+
|
158
|
+
def attach_backtraces(metric_hash)
|
159
|
+
@backtraces.each do |meta_with_backtrace|
|
160
|
+
metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
|
161
|
+
end
|
162
|
+
metric_hash
|
88
163
|
end
|
89
164
|
end
|
90
165
|
end
|
@@ -2,6 +2,7 @@ module ScoutApm
|
|
2
2
|
module LayerConverters
|
3
3
|
class SlowRequestConverter < ConverterBase
|
4
4
|
def initialize(*)
|
5
|
+
@backtraces = [] # An Array of MetricMetas that have a backtrace
|
5
6
|
super
|
6
7
|
|
7
8
|
# After call to super, so @request is populated
|
@@ -10,8 +11,6 @@ module ScoutApm
|
|
10
11
|
else
|
11
12
|
-1
|
12
13
|
end
|
13
|
-
|
14
|
-
setup_subscopable_callbacks
|
15
14
|
end
|
16
15
|
|
17
16
|
def name
|
@@ -25,8 +24,8 @@ module ScoutApm
|
|
25
24
|
# Unconditionally attempts to convert this into a SlowTransaction object.
|
26
25
|
# Can return nil if the request didn't have any scope_layer.
|
27
26
|
def call
|
28
|
-
|
29
|
-
return nil unless
|
27
|
+
scope = scope_layer
|
28
|
+
return nil unless scope
|
30
29
|
|
31
30
|
ScoutApm::Agent.instance.slow_request_policy.stored!(request)
|
32
31
|
|
@@ -36,13 +35,12 @@ module ScoutApm
|
|
36
35
|
uri = request.annotations[:uri] || ""
|
37
36
|
|
38
37
|
timing_metrics, allocation_metrics = create_metrics
|
39
|
-
|
40
38
|
unless ScoutApm::Instruments::Allocations::ENABLED
|
41
39
|
allocation_metrics = {}
|
42
40
|
end
|
43
41
|
|
44
42
|
SlowTransaction.new(uri,
|
45
|
-
|
43
|
+
scope.legacy_metric_name,
|
46
44
|
root_layer.total_call_time,
|
47
45
|
timing_metrics,
|
48
46
|
allocation_metrics,
|
@@ -51,29 +49,119 @@ module ScoutApm
|
|
51
49
|
[], # stackprof, now unused.
|
52
50
|
mem_delta,
|
53
51
|
root_layer.total_allocations,
|
54
|
-
@points
|
55
|
-
|
52
|
+
@points)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Iterates over the TrackedRequest's MetricMetas that have backtraces and attaches each to correct MetricMeta in the Metric Hash.
|
56
|
+
def attach_backtraces(metric_hash)
|
57
|
+
@backtraces.each do |meta_with_backtrace|
|
58
|
+
metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
|
59
|
+
end
|
60
|
+
metric_hash
|
56
61
|
end
|
57
62
|
|
58
63
|
# Full metrics from this request. These get stored permanently in a SlowTransaction.
|
59
64
|
# Some merging of metrics will happen here, so if a request calls the same
|
60
65
|
# ActiveRecord or View repeatedly, it'll get merged.
|
61
|
-
#
|
66
|
+
#
|
62
67
|
# This returns a 2-element of Metric Hashes (the first element is timing metrics, the second element is allocation metrics)
|
63
68
|
def create_metrics
|
64
69
|
metric_hash = Hash.new
|
65
70
|
allocation_metric_hash = Hash.new
|
66
71
|
|
72
|
+
# Keep a list of subscopes, but only ever use the front one. The rest
|
73
|
+
# get pushed/popped in cases when we have many levels of subscopable
|
74
|
+
# layers. This lets us push/pop without otherwise keeping track very closely.
|
75
|
+
subscope_layers = []
|
76
|
+
|
77
|
+
walker.before do |layer|
|
78
|
+
if layer.subscopable?
|
79
|
+
subscope_layers.push(layer)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
walker.after do |layer|
|
84
|
+
if layer.subscopable?
|
85
|
+
subscope_layers.pop
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
67
89
|
walker.walk do |layer|
|
68
|
-
|
69
|
-
|
70
|
-
|
90
|
+
# Sometimes we start capturing a layer without knowing if we really
|
91
|
+
# want to make an entry for it. See ActiveRecord instrumentation for
|
92
|
+
# an example. We start capturing before we know if a query is cached
|
93
|
+
# or not, and want to skip any cached queries.
|
94
|
+
if layer.annotations[:ignorable]
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
|
99
|
+
subscope_name = subscope_layers.first.legacy_metric_name
|
100
|
+
{:scope => subscope_name}
|
101
|
+
elsif layer == scope_layer # We don't scope the controller under itself
|
102
|
+
{}
|
103
|
+
else
|
104
|
+
{:scope => scope_layer.legacy_metric_name}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Specific Metric
|
108
|
+
meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
|
109
|
+
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
110
|
+
meta.extra.merge!(layer.annotations)
|
111
|
+
if layer.backtrace
|
112
|
+
bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
|
113
|
+
if bt.any? # we could walk thru the call stack and not find in-app code
|
114
|
+
meta.backtrace = bt
|
115
|
+
# Why not just call meta.backtrace and call it done? The walker
|
116
|
+
# could access a later later that generates the same MetricMeta
|
117
|
+
# but doesn't have a backtrace. This could be lost in the
|
118
|
+
# metric_hash if it is replaced by the new key.
|
119
|
+
@backtraces << meta
|
120
|
+
else
|
121
|
+
ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
125
|
+
allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
126
|
+
# timing
|
127
|
+
stat = metric_hash[meta]
|
128
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
129
|
+
stat.add_traces(layer.traces.as_json)
|
130
|
+
|
131
|
+
# Debug logging for scoutprof traces
|
132
|
+
if ScoutApm::Agent.instance.config.value('profile')
|
133
|
+
if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
|
134
|
+
ScoutApm::Agent.instance.logger.debug do
|
135
|
+
traces_inspect = layer.traces.inspect
|
136
|
+
"****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}:\n#{traces_inspect}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
else
|
140
|
+
if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
|
141
|
+
ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# allocations
|
146
|
+
stat = allocation_metric_hash[meta]
|
147
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
148
|
+
|
149
|
+
# Merged Metric (no specifics, just sum up by type)
|
150
|
+
meta = MetricMeta.new("#{layer.type}/all")
|
151
|
+
metric_hash[meta] ||= MetricStats.new(false)
|
152
|
+
allocation_metric_hash[meta] ||= MetricStats.new(false)
|
153
|
+
# timing
|
154
|
+
stat = metric_hash[meta]
|
155
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
156
|
+
# allocations
|
157
|
+
stat = allocation_metric_hash[meta]
|
158
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
71
159
|
end
|
72
160
|
|
73
161
|
metric_hash = attach_backtraces(metric_hash)
|
74
162
|
allocation_metric_hash = attach_backtraces(allocation_metric_hash)
|
75
163
|
|
76
|
-
[metric_hash,
|
164
|
+
[metric_hash,allocation_metric_hash]
|
77
165
|
end
|
78
166
|
end
|
79
167
|
end
|
data/lib/scout_apm/metric_set.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
class MetricSet
|
3
|
-
# We can't aggregate
|
4
|
-
# Controller, and Percentiles so pass through these metrics directly
|
5
|
-
#
|
3
|
+
# We can't aggregate CPU, Memory, Capacity, or Controller, so pass through these metrics directly
|
6
4
|
# TODO: Figure out a way to not have this duplicate what's in Samplers, and also on server's ingest
|
7
5
|
PASSTHROUGH_METRICS = ["CPU", "Memory", "Instance", "Controller", "SlowTransaction", "Percentile", "Job"]
|
8
6
|
|
@@ -51,11 +49,5 @@ module ScoutApm
|
|
51
49
|
@combine_in_progress = false
|
52
50
|
self
|
53
51
|
end
|
54
|
-
|
55
|
-
|
56
|
-
def eql?(other)
|
57
|
-
metrics == other.metrics
|
58
|
-
end
|
59
|
-
alias :== :eql?
|
60
52
|
end
|
61
53
|
end
|
@@ -9,6 +9,7 @@ class MetricStats
|
|
9
9
|
attr_accessor :sum_of_squares
|
10
10
|
attr_accessor :queue
|
11
11
|
attr_accessor :latency
|
12
|
+
attr_accessor :traces
|
12
13
|
|
13
14
|
def initialize(scoped = false)
|
14
15
|
@scoped = scoped
|
@@ -18,6 +19,7 @@ class MetricStats
|
|
18
19
|
self.min_call_time = 0.0
|
19
20
|
self.max_call_time = 0.0
|
20
21
|
self.sum_of_squares = 0.0
|
22
|
+
self.traces = []
|
21
23
|
end
|
22
24
|
|
23
25
|
# Note, that you must include exclusive_time if you wish to set
|
@@ -39,6 +41,10 @@ class MetricStats
|
|
39
41
|
self
|
40
42
|
end
|
41
43
|
|
44
|
+
def add_traces(traces)
|
45
|
+
self.traces += Array(traces)
|
46
|
+
end
|
47
|
+
|
42
48
|
# combines data from another MetricStats object
|
43
49
|
def combine!(other)
|
44
50
|
self.call_count += other.call_count
|
@@ -47,18 +53,12 @@ class MetricStats
|
|
47
53
|
self.min_call_time = other.min_call_time if self.min_call_time.zero? or other.min_call_time < self.min_call_time
|
48
54
|
self.max_call_time = other.max_call_time if other.max_call_time > self.max_call_time
|
49
55
|
self.sum_of_squares += other.sum_of_squares
|
56
|
+
self.traces = Array(self.traces) + Array(other.traces)
|
50
57
|
self
|
51
58
|
end
|
52
59
|
|
53
|
-
# To avoid conflicts with different JSON libaries handle JSON ourselves.
|
54
|
-
# Time-based metrics are converted to milliseconds from seconds.
|
55
|
-
def to_json(*a)
|
56
|
-
%Q[{"total_exclusive_time":#{total_exclusive_time*1000},"min_call_time":#{min_call_time*1000},"call_count":#{call_count},"sum_of_squares":#{sum_of_squares*1000},"total_call_time":#{total_call_time*1000},"max_call_time":#{max_call_time*1000}}]
|
57
|
-
end
|
58
|
-
|
59
60
|
def as_json
|
60
|
-
json_attributes = [:call_count, :total_call_time, :total_exclusive_time, :min_call_time, :max_call_time]
|
61
|
-
# uri, context
|
61
|
+
json_attributes = [:call_count, :total_call_time, :total_exclusive_time, :min_call_time, :max_call_time, :traces]
|
62
62
|
ScoutApm::AttributeArranger.call(self, json_attributes)
|
63
63
|
end
|
64
64
|
end
|
data/lib/scout_apm/reporter.rb
CHANGED
@@ -17,36 +17,31 @@ module ScoutApm
|
|
17
17
|
@instant_key = instant_key
|
18
18
|
end
|
19
19
|
|
20
|
+
# TODO: Parse & return a real response object, not the HTTP Response object
|
20
21
|
def report(payload, headers = {})
|
21
|
-
|
22
|
+
# Some posts (typically ones under development) bypass the ingestion pipeline and go directly to the webserver. They use direct_host instead of host
|
23
|
+
hosts = [:deploy_hook, :instant_trace].include?(type) ? config.value('direct_host') : config.value('host')
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
compress_payload_size = payload.length
|
30
|
-
ScoutApm::Agent.instance.logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
|
25
|
+
Array(hosts).each do |host|
|
26
|
+
full_uri = uri(host)
|
27
|
+
response = post(full_uri, payload, headers)
|
28
|
+
unless response && response.is_a?(Net::HTTPSuccess)
|
29
|
+
logger.warn "Error on checkin to #{full_uri.to_s}: #{response.inspect}"
|
30
|
+
end
|
31
31
|
end
|
32
|
-
|
33
|
-
post_payload(hosts, payload, headers)
|
34
32
|
end
|
35
33
|
|
36
34
|
def uri(host)
|
37
|
-
encoded_app_name = CGI.escape(Environment.instance.application_name)
|
38
|
-
key = config.value('key')
|
39
|
-
|
40
35
|
case type
|
41
36
|
when :checkin
|
42
|
-
URI.parse("#{host}/apps/checkin.scout?key=#{key}&name=#{
|
37
|
+
URI.parse("#{host}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
|
43
38
|
when :app_server_load
|
44
|
-
URI.parse("#{host}/apps/app_server_load.scout?key=#{key}&name=#{
|
39
|
+
URI.parse("#{host}/apps/app_server_load.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
|
45
40
|
when :deploy_hook
|
46
|
-
URI.parse("#{host}/apps/deploy.scout?key=#{key}&name=#{
|
41
|
+
URI.parse("#{host}/apps/deploy.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
|
47
42
|
when :instant_trace
|
48
|
-
URI.parse("#{host}/apps/instant_trace.scout?key=#{key}&name=#{
|
49
|
-
end.tap
|
43
|
+
URI.parse("#{host}/apps/instant_trace.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}&instant_key=#{instant_key}")
|
44
|
+
end.tap{|u| logger.debug("Posting to #{u.to_s}")}
|
50
45
|
end
|
51
46
|
|
52
47
|
def can_report?
|
@@ -111,10 +106,7 @@ module ScoutApm
|
|
111
106
|
# Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
|
112
107
|
def http(url)
|
113
108
|
proxy_uri = URI.parse(config.value('proxy').to_s)
|
114
|
-
http = Net::HTTP::Proxy(proxy_uri.host,
|
115
|
-
proxy_uri.port,
|
116
|
-
proxy_uri.user,
|
117
|
-
proxy_uri.password).new(url.host, url.port)
|
109
|
+
http = Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
|
118
110
|
if url.is_a?(URI::HTTPS)
|
119
111
|
http.use_ssl = true
|
120
112
|
http.ca_file = CA_FILE
|
@@ -122,33 +114,5 @@ module ScoutApm
|
|
122
114
|
end
|
123
115
|
http
|
124
116
|
end
|
125
|
-
|
126
|
-
def compress_payload(payload)
|
127
|
-
[
|
128
|
-
ScoutApm::Utils::GzipHelper.new.deflate(payload),
|
129
|
-
{ 'Content-Encoding' => 'gzip' }
|
130
|
-
]
|
131
|
-
end
|
132
|
-
|
133
|
-
# Some posts (typically ones under development) bypass the ingestion
|
134
|
-
# pipeline and go directly to the webserver. They use direct_host instead
|
135
|
-
# of host
|
136
|
-
def determine_hosts
|
137
|
-
if [:deploy_hook, :instant_trace].include?(type)
|
138
|
-
config.value('direct_host')
|
139
|
-
else
|
140
|
-
config.value('host')
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def post_payload(hosts, payload, headers)
|
145
|
-
Array(hosts).each do |host|
|
146
|
-
full_uri = uri(host)
|
147
|
-
response = post(full_uri, payload, headers)
|
148
|
-
unless response && response.is_a?(Net::HTTPSuccess)
|
149
|
-
logger.warn "Error on checkin to #{full_uri}: #{response.inspect}"
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
117
|
end
|
154
118
|
end
|