scout_apm 3.0.0.pre1 → 3.0.0.pre2
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.markdown +12 -1
- data/lib/scout_apm.rb +3 -0
- data/lib/scout_apm/agent.rb +9 -6
- data/lib/scout_apm/agent/reporting.rb +5 -3
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +47 -1
- data/lib/scout_apm/background_worker.rb +4 -0
- data/lib/scout_apm/config.rb +2 -1
- data/lib/scout_apm/environment.rb +1 -1
- data/lib/scout_apm/histogram.rb +11 -2
- data/lib/scout_apm/instruments/mongoid.rb +14 -1
- data/lib/scout_apm/instruments/percentile_sampler.rb +36 -19
- data/lib/scout_apm/instruments/process/process_cpu.rb +3 -2
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/layer_converters/converter_base.rb +213 -0
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +19 -93
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +15 -100
- data/lib/scout_apm/metric_set.rb +6 -0
- data/lib/scout_apm/reporter.rb +53 -15
- data/lib/scout_apm/request_histograms.rb +4 -0
- data/lib/scout_apm/scored_item_set.rb +7 -0
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +21 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +9 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +2 -1
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -1
- data/lib/scout_apm/slow_job_record.rb +4 -1
- data/lib/scout_apm/slow_transaction.rb +18 -2
- data/lib/scout_apm/store.rb +42 -11
- data/lib/scout_apm/tracked_request.rb +1 -1
- data/lib/scout_apm/utils/gzip_helper.rb +24 -0
- data/lib/scout_apm/utils/numbers.rb +14 -0
- data/lib/scout_apm/version.rb +2 -2
- data/test/test_helper.rb +10 -0
- data/test/unit/config_test.rb +7 -9
- data/test/unit/histogram_test.rb +14 -0
- data/test/unit/instruments/percentile_sampler_test.rb +137 -0
- data/test/unit/serializers/payload_serializer_test.rb +3 -3
- data/test/unit/store_test.rb +51 -0
- data/test/unit/utils/numbers_test.rb +15 -0
- metadata +10 -4
@@ -63,6 +63,12 @@ module ScoutApm
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
# Equal to another set only if exactly the same set of items is inside
|
67
|
+
def eql?(other)
|
68
|
+
items == other.items
|
69
|
+
end
|
70
|
+
|
71
|
+
alias :== :eql?
|
66
72
|
|
67
73
|
private
|
68
74
|
|
@@ -75,5 +81,6 @@ module ScoutApm
|
|
75
81
|
items[new_item.name] = [new_item.score, new_item.call]
|
76
82
|
end
|
77
83
|
end
|
84
|
+
|
78
85
|
end
|
79
86
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
module ScoutApm
|
3
|
+
module Serializers
|
4
|
+
class HistogramsSerializerToJson
|
5
|
+
attr_reader :histograms
|
6
|
+
|
7
|
+
def initialize(histograms)
|
8
|
+
@histograms = histograms
|
9
|
+
end
|
10
|
+
|
11
|
+
def as_json
|
12
|
+
histograms.map do |histo|
|
13
|
+
{
|
14
|
+
"name" => histo.name,
|
15
|
+
"histogram" => histo.histogram.as_json,
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -2,9 +2,9 @@
|
|
2
2
|
module ScoutApm
|
3
3
|
module Serializers
|
4
4
|
class PayloadSerializer
|
5
|
-
def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs)
|
5
|
+
def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms)
|
6
6
|
if ScoutApm::Agent.instance.config.value("report_format") == 'json'
|
7
|
-
ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs)
|
7
|
+
ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms)
|
8
8
|
else
|
9
9
|
metadata = metadata.dup
|
10
10
|
metadata.default = nil
|
@@ -15,7 +15,13 @@ module ScoutApm
|
|
15
15
|
:metrics => metrics,
|
16
16
|
:slow_transactions => slow_transactions,
|
17
17
|
:jobs => jobs,
|
18
|
-
:slow_jobs => slow_jobs
|
18
|
+
:slow_jobs => slow_jobs,
|
19
|
+
|
20
|
+
# as_json returns a ruby object. Since it's not a simple
|
21
|
+
# array, use this to maintain compatibility with json
|
22
|
+
# payloads. At this point, the marshal code branch is
|
23
|
+
# very rarely used anyway.
|
24
|
+
:histograms => HistogramsSerializerToJson.new(histograms).as_json)
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
@@ -2,7 +2,7 @@ module ScoutApm
|
|
2
2
|
module Serializers
|
3
3
|
module PayloadSerializerToJson
|
4
4
|
class << self
|
5
|
-
def serialize(metadata, metrics, slow_transactions, jobs, slow_jobs)
|
5
|
+
def serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms)
|
6
6
|
metadata.merge!({:payload_version => 2})
|
7
7
|
|
8
8
|
jsonify_hash({:metadata => metadata,
|
@@ -10,6 +10,7 @@ module ScoutApm
|
|
10
10
|
:slow_transactions => rearrange_the_slow_transactions(slow_transactions),
|
11
11
|
:jobs => JobsSerializerToJson.new(jobs).as_json,
|
12
12
|
:slow_jobs => SlowJobsSerializerToJson.new(slow_jobs).as_json,
|
13
|
+
:histograms => HistogramsSerializerToJson.new(histograms).as_json,
|
13
14
|
})
|
14
15
|
end
|
15
16
|
|
@@ -25,6 +25,7 @@ module ScoutApm
|
|
25
25
|
"metrics" => MetricsToJsonSerializer.new(job.metrics).as_json, # New style of metrics
|
26
26
|
"allocation_metrics" => MetricsToJsonSerializer.new(job.allocation_metrics).as_json, # New style of metrics
|
27
27
|
"context" => job.context.to_hash,
|
28
|
+
"truncated_metrics" => job.truncated_metrics,
|
28
29
|
|
29
30
|
"score" => job.score,
|
30
31
|
}
|
@@ -33,4 +34,3 @@ module ScoutApm
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
36
|
-
|
@@ -21,8 +21,9 @@ module ScoutApm
|
|
21
21
|
attr_reader :seconds_since_startup
|
22
22
|
attr_reader :score
|
23
23
|
attr_reader :git_sha
|
24
|
+
attr_reader :truncated_metrics
|
24
25
|
|
25
|
-
def initialize(queue_name, job_name, time, total_time, exclusive_time, context, metrics, allocation_metrics, mem_delta, allocations, score)
|
26
|
+
def initialize(queue_name, job_name, time, total_time, exclusive_time, context, metrics, allocation_metrics, mem_delta, allocations, score, truncated_metrics)
|
26
27
|
@queue_name = queue_name
|
27
28
|
@job_name = job_name
|
28
29
|
@time = time
|
@@ -37,6 +38,8 @@ module ScoutApm
|
|
37
38
|
@hostname = ScoutApm::Environment.instance.hostname
|
38
39
|
@git_sha = ScoutApm::Environment.instance.git_revision.sha
|
39
40
|
@score = score
|
41
|
+
@truncated_metrics = truncated_metrics
|
42
|
+
|
40
43
|
ScoutApm::Agent.instance.logger.debug { "Slow Job [#{metric_name}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta}"}
|
41
44
|
end
|
42
45
|
|
@@ -17,7 +17,9 @@ module ScoutApm
|
|
17
17
|
attr_accessor :seconds_since_startup # hack - we need to reset these server side.
|
18
18
|
attr_accessor :git_sha # hack - we need to reset these server side.
|
19
19
|
|
20
|
-
|
20
|
+
attr_reader :truncated_metrics # True/False that says if we had to truncate the metrics of this trace
|
21
|
+
|
22
|
+
def initialize(uri, metric_name, total_call_time, metrics, allocation_metrics, context, time, raw_stackprof, mem_delta, allocations, score, truncated_metrics)
|
21
23
|
@uri = uri
|
22
24
|
@metric_name = metric_name
|
23
25
|
@total_call_time = total_call_time
|
@@ -32,6 +34,8 @@ module ScoutApm
|
|
32
34
|
@hostname = ScoutApm::Environment.instance.hostname
|
33
35
|
@score = score
|
34
36
|
@git_sha = ScoutApm::Environment.instance.git_revision.sha
|
37
|
+
@truncated_metrics = truncated_metrics
|
38
|
+
|
35
39
|
ScoutApm::Agent.instance.logger.debug { "Slow Request [#{uri}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta} Score: #{score}"}
|
36
40
|
end
|
37
41
|
|
@@ -46,7 +50,19 @@ module ScoutApm
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def as_json
|
49
|
-
json_attributes = [:key,
|
53
|
+
json_attributes = [:key,
|
54
|
+
:time,
|
55
|
+
:total_call_time,
|
56
|
+
:uri,
|
57
|
+
[:context, :context_hash],
|
58
|
+
:score,
|
59
|
+
:prof,
|
60
|
+
:mem_delta,
|
61
|
+
:allocations,
|
62
|
+
:seconds_since_startup,
|
63
|
+
:hostname,
|
64
|
+
:git_sha,
|
65
|
+
:truncated_metrics]
|
50
66
|
ScoutApm::AttributeArranger.call(self, json_attributes)
|
51
67
|
end
|
52
68
|
|
data/lib/scout_apm/store.rb
CHANGED
@@ -26,7 +26,23 @@ module ScoutApm
|
|
26
26
|
# Save newly collected metrics
|
27
27
|
def track!(metrics, options={})
|
28
28
|
@mutex.synchronize {
|
29
|
-
|
29
|
+
period = if options[:timestamp]
|
30
|
+
@reporting_periods[options[:timestamp]]
|
31
|
+
else
|
32
|
+
current_period
|
33
|
+
end
|
34
|
+
period.absorb_metrics!(metrics)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def track_histograms!(histograms, options={})
|
39
|
+
@mutex.synchronize {
|
40
|
+
period = if options[:timestamp]
|
41
|
+
@reporting_periods[options[:timestamp]]
|
42
|
+
else
|
43
|
+
current_period
|
44
|
+
end
|
45
|
+
period.merge_histograms!(histograms)
|
30
46
|
}
|
31
47
|
end
|
32
48
|
|
@@ -67,15 +83,15 @@ module ScoutApm
|
|
67
83
|
def write_to_layaway(layaway, force=false)
|
68
84
|
ScoutApm::Agent.instance.logger.debug("Writing to layaway#{" (Forced)" if force}")
|
69
85
|
|
70
|
-
|
71
|
-
|
86
|
+
reporting_periods.select { |time, rp| force || time.timestamp < current_timestamp.timestamp }.
|
87
|
+
each { |time, rp| collect_samplers(rp) }.
|
72
88
|
each { |time, rp| write_reporting_period(layaway, time, rp) }
|
73
|
-
}
|
74
89
|
end
|
75
90
|
|
76
91
|
def write_reporting_period(layaway, time, rp)
|
77
|
-
|
78
|
-
|
92
|
+
@mutex.synchronize {
|
93
|
+
layaway.write_reporting_period(rp)
|
94
|
+
}
|
79
95
|
rescue => e
|
80
96
|
ScoutApm::Agent.instance.logger.warn("Failed writing data to layaway file: #{e.message} / #{e.backtrace}")
|
81
97
|
ensure
|
@@ -91,12 +107,10 @@ module ScoutApm
|
|
91
107
|
def collect_samplers(rp)
|
92
108
|
@samplers.each do |sampler|
|
93
109
|
begin
|
94
|
-
|
95
|
-
rp.absorb_metrics!(metrics)
|
110
|
+
sampler.metrics(rp.timestamp, self)
|
96
111
|
rescue => e
|
97
112
|
ScoutApm::Agent.instance.logger.info "Error reading #{sampler.human_name} for period: #{rp}"
|
98
|
-
ScoutApm::Agent.instance.logger.debug e.message
|
99
|
-
ScoutApm::Agent.instance.logger.debug e.backtrace.join("\n")
|
113
|
+
ScoutApm::Agent.instance.logger.debug "#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
100
114
|
end
|
101
115
|
end
|
102
116
|
end
|
@@ -159,6 +173,9 @@ module ScoutApm
|
|
159
173
|
# A ScoredItemSet holding the "best" traces for the period
|
160
174
|
attr_reader :job_traces
|
161
175
|
|
176
|
+
# An Array of HistogramsReport
|
177
|
+
attr_reader :histograms
|
178
|
+
|
162
179
|
# A StoreReportingPeriodTimestamp representing the time that this
|
163
180
|
# collection of metrics is for
|
164
181
|
attr_reader :timestamp
|
@@ -171,6 +188,8 @@ module ScoutApm
|
|
171
188
|
@request_traces = ScoredItemSet.new
|
172
189
|
@job_traces = ScoredItemSet.new
|
173
190
|
|
191
|
+
@histograms = []
|
192
|
+
|
174
193
|
@metric_set = MetricSet.new
|
175
194
|
@jobs = Hash.new
|
176
195
|
end
|
@@ -181,7 +200,8 @@ module ScoutApm
|
|
181
200
|
merge_metrics!(other.metric_set).
|
182
201
|
merge_slow_transactions!(other.slow_transactions_payload).
|
183
202
|
merge_jobs!(other.jobs).
|
184
|
-
merge_slow_jobs!(other.slow_jobs_payload)
|
203
|
+
merge_slow_jobs!(other.slow_jobs_payload).
|
204
|
+
merge_histograms!(other.histograms)
|
185
205
|
self
|
186
206
|
end
|
187
207
|
|
@@ -230,6 +250,17 @@ module ScoutApm
|
|
230
250
|
self
|
231
251
|
end
|
232
252
|
|
253
|
+
def merge_histograms!(new_histograms)
|
254
|
+
new_histograms = Array(new_histograms)
|
255
|
+
@histograms = (histograms + new_histograms).
|
256
|
+
group_by { |histo| histo.name }.
|
257
|
+
map { |(_, histos)|
|
258
|
+
histos.inject { |merged, histo| merged.combine!(histo) }
|
259
|
+
}
|
260
|
+
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
233
264
|
#################################
|
234
265
|
# Retrieve Metrics for reporting
|
235
266
|
#################################
|
@@ -278,7 +278,7 @@ module ScoutApm
|
|
278
278
|
# If there's an instant_key, it means we need to report this right away
|
279
279
|
if instant?
|
280
280
|
trace = slow_converter.call
|
281
|
-
ScoutApm::InstantReporting.new(trace, instant_key).call
|
281
|
+
ScoutApm::InstantReporting.new(trace, instant_key).call
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Utils
|
3
|
+
# A simple wrapper around Ruby's built-in gzip support.
|
4
|
+
class GzipHelper
|
5
|
+
DEFAULT_GZIP_LEVEL = 5
|
6
|
+
|
7
|
+
attr_reader :level
|
8
|
+
|
9
|
+
def initialize(level = DEFAULT_GZIP_LEVEL)
|
10
|
+
@level = level
|
11
|
+
end
|
12
|
+
|
13
|
+
def deflate(str)
|
14
|
+
strio = StringIO.new
|
15
|
+
|
16
|
+
gz = Zlib::GzipWriter.new(strio, level)
|
17
|
+
gz.write str
|
18
|
+
gz.close
|
19
|
+
|
20
|
+
strio.string
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/scout_apm/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module ScoutApm
|
2
|
-
VERSION = "3.0.0.
|
3
|
-
end
|
2
|
+
VERSION = "3.0.0.pre2"
|
3
|
+
end
|
data/test/test_helper.rb
CHANGED
data/test/unit/config_test.rb
CHANGED
@@ -7,24 +7,24 @@ class ConfigTest < Minitest::Test
|
|
7
7
|
conf = ScoutApm::Config.without_file
|
8
8
|
|
9
9
|
# nil for random keys
|
10
|
-
assert_nil conf.value(
|
10
|
+
assert_nil conf.value('log_file_path')
|
11
11
|
|
12
12
|
# but has values for defaulted keys
|
13
|
-
assert conf.value(
|
13
|
+
assert conf.value('host')
|
14
14
|
|
15
15
|
# and still reads from ENV
|
16
16
|
ENV['SCOUT_CONFIG_TEST_KEY'] = 'testval'
|
17
|
-
assert_equal 'testval', conf.value(
|
17
|
+
assert_equal 'testval', conf.value('config_test_key')
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_loading_a_file
|
21
|
-
set_rack_env(
|
21
|
+
set_rack_env('production')
|
22
22
|
|
23
|
-
conf_file = File.expand_path(
|
23
|
+
conf_file = File.expand_path('../../data/config_test_1.yml', __FILE__)
|
24
24
|
conf = ScoutApm::Config.with_file(conf_file)
|
25
25
|
|
26
|
-
assert_equal
|
27
|
-
assert_equal
|
26
|
+
assert_equal 'debug', conf.value('log_level')
|
27
|
+
assert_equal 'APM Test Conf (Production)', conf.value('name')
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_loading_file_without_env_in_file
|
@@ -68,5 +68,3 @@ class ConfigTest < Minitest::Test
|
|
68
68
|
assert_equal ["a"], coercion.coerce(["a"])
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
72
|
-
|
data/test/unit/histogram_test.rb
CHANGED
@@ -79,6 +79,20 @@ class HistogramTest < Minitest::Test
|
|
79
79
|
assert combined.quantile(0) < combined.quantile(100)
|
80
80
|
end
|
81
81
|
|
82
|
+
def test_combine_dedups_identicals
|
83
|
+
hist1 = ScoutApm::NumericHistogram.new(5)
|
84
|
+
hist2 = ScoutApm::NumericHistogram.new(5)
|
85
|
+
hist1.add(1)
|
86
|
+
hist1.add(2)
|
87
|
+
hist2.add(2)
|
88
|
+
hist2.add(3)
|
89
|
+
|
90
|
+
combined = hist1.combine!(hist2)
|
91
|
+
assert_equal 4, combined.total
|
92
|
+
assert_equal [[1, 1], [2, 2], [1, 3]],
|
93
|
+
combined.bins.map{|bin| [bin.count, bin.value.to_i] }
|
94
|
+
end
|
95
|
+
|
82
96
|
def test_mean
|
83
97
|
hist = ScoutApm::NumericHistogram.new(5)
|
84
98
|
10.times {
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/instruments/percentile_sampler'
|
4
|
+
|
5
|
+
class PercentileSamplerTest < Minitest::Test
|
6
|
+
PercentileSampler = ScoutApm::Instruments::PercentileSampler
|
7
|
+
HistogramReport = ScoutApm::Instruments::HistogramReport
|
8
|
+
|
9
|
+
attr_reader :subject
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@subject = PercentileSampler.new(logger, histograms)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def test_initialize_with_logger_and_histogram_set
|
17
|
+
assert_equal subject.logger, logger
|
18
|
+
assert_equal subject.histograms, histograms
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_implements_instrument_interface
|
22
|
+
assert subject.respond_to?(:human_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_percentiles_returns_one_percentile_per_endpoint_at_time
|
26
|
+
histograms[time].add("foo", 10)
|
27
|
+
histograms[time].add("bar", 15)
|
28
|
+
histograms[time2].add("baz", 15)
|
29
|
+
|
30
|
+
assert_equal subject.percentiles(time).length, 2
|
31
|
+
assert_equal subject.percentiles(time2).length, 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_percentiles_clears_time_from_hash
|
35
|
+
histograms[time].add("foo", 10)
|
36
|
+
histograms[time2].add("baz", 15)
|
37
|
+
|
38
|
+
subject.percentiles(time)
|
39
|
+
|
40
|
+
assert_false histograms.key?(time)
|
41
|
+
assert histograms.key?(time + 10)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_percentiles_returns_histogram_reports
|
45
|
+
histograms[time].add("foo", 10)
|
46
|
+
|
47
|
+
assert subject.percentiles(time).
|
48
|
+
all?{ |item| item.is_a?(HistogramReport) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_percentiles_returns_correct_histogram_report
|
52
|
+
histograms[time].add("foo", 100)
|
53
|
+
histograms[time].add("foo", 200)
|
54
|
+
histograms[time].add("foo", 100)
|
55
|
+
histograms[time].add("foo", 300)
|
56
|
+
|
57
|
+
report = subject.percentiles(time).first
|
58
|
+
histogram = report.histogram
|
59
|
+
|
60
|
+
assert_equal "foo", report.name
|
61
|
+
assert_equal 4, histogram.total
|
62
|
+
assert_equal [[2, 100], [1, 200], [1, 300]],
|
63
|
+
histogram.bins.map{|bin| [bin.count, bin.value] }
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_metrics_saves_histogram_to_store
|
67
|
+
store = mock
|
68
|
+
store.expects(:track_histograms!)
|
69
|
+
subject.metrics(ScoutApm::StoreReportingPeriodTimestamp.new(time), store)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
################################################################################
|
74
|
+
# HistogramReport Test
|
75
|
+
################################################################################
|
76
|
+
def test_histogram_report_combine_refuses_to_combine_mismatched_name
|
77
|
+
assert_raises { HistogramReport.new("foo", histogram).combine!(HistogramReport.new("bar", histogram)) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_histogram_report_merge_keeps_name
|
81
|
+
report1 = HistogramReport.new("foo", histogram)
|
82
|
+
report2 = HistogramReport.new("foo", histogram)
|
83
|
+
combined = report1.combine!(report2)
|
84
|
+
|
85
|
+
assert "foo", combined.name
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_histogram_report_combine_merges_histograms
|
89
|
+
histogram1 = histogram
|
90
|
+
histogram2 = histogram
|
91
|
+
histogram1.add(1)
|
92
|
+
histogram1.add(2)
|
93
|
+
histogram2.add(2)
|
94
|
+
histogram2.add(3)
|
95
|
+
|
96
|
+
report1 = HistogramReport.new("foo", histogram1)
|
97
|
+
report2 = HistogramReport.new("foo", histogram2)
|
98
|
+
combined = report1.combine!(report2)
|
99
|
+
|
100
|
+
assert_equal 4, combined.histogram.total
|
101
|
+
assert_equal [[1, 1], [2, 2], [1, 3]],
|
102
|
+
combined.histogram.bins.map{|bin| [bin.count, bin.value.to_i] }
|
103
|
+
end
|
104
|
+
|
105
|
+
################################################################################
|
106
|
+
# Test Helpers
|
107
|
+
################################################################################
|
108
|
+
def logger
|
109
|
+
@logger ||= begin
|
110
|
+
@logger_io = StringIO.new
|
111
|
+
Logger.new(@logger_io)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def histograms
|
116
|
+
@histograms ||= begin
|
117
|
+
@request_histograms_by_time = Hash.new { |hash, key|
|
118
|
+
hash[key] = ScoutApm::RequestHistograms.new
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def histogram
|
124
|
+
max_bins = 20
|
125
|
+
ScoutApm::NumericHistogram.new(max_bins)
|
126
|
+
end
|
127
|
+
|
128
|
+
# An arbitrary time
|
129
|
+
def time
|
130
|
+
@time ||= Time.now
|
131
|
+
end
|
132
|
+
|
133
|
+
def time2
|
134
|
+
time + 10
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|