librato-rack 0.4.5 → 0.5.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +4 -0
- data.tar.gz.sig +0 -0
- data/README.md +20 -2
- data/lib/librato/collector.rb +7 -4
- data/lib/librato/collector/aggregator.rb +69 -5
- data/lib/librato/collector/counter_cache.rb +4 -4
- data/lib/librato/collector/exceptions.rb +8 -0
- data/lib/librato/rack.rb +3 -3
- data/lib/librato/rack/tracker.rb +11 -4
- data/lib/librato/rack/version.rb +1 -1
- data/test/remote/tracker_test.rb +66 -55
- data/test/unit/collector/aggregator_test.rb +78 -1
- metadata +56 -59
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7be4fbb8ab1659249f7bf95cd6cd3f77726ebda5
|
4
|
+
data.tar.gz: 18b5b1cc87d44a10b1512ae6c1884ea018b1e92c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c5956f91aee12213650b9736bf209af4d7d4f8c6dfadd2e21d9b7d1dcb54d67747c8ce08d0d1f6e2226a12f7bc4657f3d9003bd92e3849d93ab94d2099de8098
|
7
|
+
data.tar.gz: 784ba024019aa150bcf3509182333c2b5e48b55291d1056a26f38322b4e414aeedc1242d03d5c3a0d1bdc4a0cebbae1d606a35060c0af6d55515a964ee1f275b
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
CHANGED
Binary file
|
data/README.md
CHANGED
@@ -64,7 +64,7 @@ If you want to do more complex configuration, use your own environment variables
|
|
64
64
|
|
65
65
|
use Librato::Rack, :config => config
|
66
66
|
|
67
|
-
See the configuration class for all available options.
|
67
|
+
See the [configuration class](https://github.com/librato/librato-rack/blob/master/lib/librato/rack/configuration.rb) for all available options.
|
68
68
|
|
69
69
|
##### Running on Heroku
|
70
70
|
|
@@ -131,6 +131,22 @@ The block form auto-submits the time it took for its contents to execute as the
|
|
131
131
|
@twitter = Twitter.lookup(user)
|
132
132
|
end
|
133
133
|
|
134
|
+
###### percentiles (beta)
|
135
|
+
|
136
|
+
By defaults timings will send the average, sum, max and min for every minute. If you want to send percentiles as well you can specify them inline while instrumenting:
|
137
|
+
|
138
|
+
# track a single percentile
|
139
|
+
Librato.timing 'api.request.time', time, percentile: 95
|
140
|
+
|
141
|
+
# track multiple percentiles
|
142
|
+
Librato.timing 'api.request.time', time, percentile: [95, 99]
|
143
|
+
|
144
|
+
You can also use percentiles with the block form of timings:
|
145
|
+
|
146
|
+
Librato.timing 'my.important.event', percentile: 95 do
|
147
|
+
# do work
|
148
|
+
end
|
149
|
+
|
134
150
|
#### group
|
135
151
|
|
136
152
|
There is also a grouping helper, to make managing nested metrics easier. So this:
|
@@ -155,6 +171,8 @@ Symbols can be used interchangeably with strings for metric names.
|
|
155
171
|
|
156
172
|
Never fear, [we have some guidelines](https://github.com/librato/librato-rails/wiki/Monitoring-Background-Workers) for how to instrument your workers properly.
|
157
173
|
|
174
|
+
If you are using `librato-rack` with sidekiq, [see these notes about setup](https://github.com/librato/librato-rails/wiki/Monitoring-Background-Workers#monitoring-long-running-threaded-workers-sidekiq-etc).
|
175
|
+
|
158
176
|
## Cross-Process Aggregation
|
159
177
|
|
160
178
|
`librato-rack` submits measurements back to the Librato platform on a _per-process_ basis. By default these measurements are then combined into a single measurement per source (default is your hostname) before persisting the data.
|
@@ -185,4 +203,4 @@ If you are debugging setup locally you can set `flush_interval` to something sho
|
|
185
203
|
|
186
204
|
## Copyright
|
187
205
|
|
188
|
-
Copyright (c) 2013 [Librato Inc.](http://librato.com) See LICENSE for details.
|
206
|
+
Copyright (c) 2013-2014 [Librato Inc.](http://librato.com) See LICENSE for details.
|
data/lib/librato/collector.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Librato
|
2
4
|
# collects and stores measurement values over time so they can be
|
3
5
|
# reported periodically to the Metrics service
|
@@ -10,7 +12,7 @@ module Librato
|
|
10
12
|
|
11
13
|
# access to internal aggregator object
|
12
14
|
def aggregate
|
13
|
-
@aggregator_cache ||= Aggregator.new(:
|
15
|
+
@aggregator_cache ||= Aggregator.new(prefix: @prefix)
|
14
16
|
end
|
15
17
|
|
16
18
|
# access to internal counters object
|
@@ -43,6 +45,7 @@ module Librato
|
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
require_relative 'collector/aggregator'
|
49
|
+
require_relative 'collector/counter_cache'
|
50
|
+
require_relative 'collector/exceptions'
|
51
|
+
require_relative 'collector/group'
|
@@ -1,14 +1,19 @@
|
|
1
|
+
require 'hetchy'
|
2
|
+
|
1
3
|
module Librato
|
2
4
|
class Collector
|
3
5
|
# maintains storage of timing and measurement type measurements
|
4
6
|
#
|
5
7
|
class Aggregator
|
8
|
+
SOURCE_SEPARATOR = '$$'
|
9
|
+
|
6
10
|
extend Forwardable
|
7
11
|
|
8
12
|
def_delegators :@cache, :empty?, :prefix, :prefix=
|
9
13
|
|
10
14
|
def initialize(options={})
|
11
|
-
@cache = Librato::Metrics::Aggregator.new(:
|
15
|
+
@cache = Librato::Metrics::Aggregator.new(prefix: options[:prefix])
|
16
|
+
@percentiles = {}
|
12
17
|
@lock = Mutex.new
|
13
18
|
end
|
14
19
|
|
@@ -16,8 +21,11 @@ module Librato
|
|
16
21
|
fetch(key)
|
17
22
|
end
|
18
23
|
|
24
|
+
# retrieve current value of a metric/source/percentage. this exists
|
25
|
+
# primarily for debugging/testing and isn't called routinely.
|
19
26
|
def fetch(key, options={})
|
20
27
|
return nil if @cache.empty?
|
28
|
+
return fetch_percentile(key, options) if options[:percentile]
|
21
29
|
gauges = nil
|
22
30
|
source = options[:source]
|
23
31
|
@lock.synchronize { gauges = @cache.queued[:gauges] }
|
@@ -30,17 +38,19 @@ module Librato
|
|
30
38
|
nil
|
31
39
|
end
|
32
40
|
|
41
|
+
# clear all stored values
|
33
42
|
def delete_all
|
34
|
-
@lock.synchronize {
|
43
|
+
@lock.synchronize { clear_storage }
|
35
44
|
end
|
36
45
|
|
37
46
|
# transfer all measurements to queue and reset internal status
|
38
|
-
def flush_to(queue)
|
47
|
+
def flush_to(queue, opts={})
|
39
48
|
queued = nil
|
40
49
|
@lock.synchronize do
|
41
50
|
return if @cache.empty?
|
42
51
|
queued = @cache.queued
|
43
|
-
@
|
52
|
+
flush_percentiles(queue, opts) unless @percentiles.empty?
|
53
|
+
clear_storage unless opts[:preserve]
|
44
54
|
end
|
45
55
|
queue.merge!(queued) if queued
|
46
56
|
end
|
@@ -80,18 +90,72 @@ module Librato
|
|
80
90
|
options = args[-1]
|
81
91
|
end
|
82
92
|
source = options[:source]
|
93
|
+
percentiles = Array(options[:percentile])
|
83
94
|
|
84
95
|
@lock.synchronize do
|
85
96
|
if source
|
86
|
-
@cache.add event => {:
|
97
|
+
@cache.add event => {source: source, value: value}
|
87
98
|
else
|
88
99
|
@cache.add event => value
|
89
100
|
end
|
101
|
+
|
102
|
+
percentiles.each do |perc|
|
103
|
+
store = fetch_percentile_store(event, source)
|
104
|
+
store[:reservoir] << value
|
105
|
+
track_percentile(store, perc)
|
106
|
+
end
|
90
107
|
end
|
91
108
|
returned
|
92
109
|
end
|
93
110
|
alias :timing :measure
|
94
111
|
|
112
|
+
private
|
113
|
+
|
114
|
+
def clear_storage
|
115
|
+
@cache.clear
|
116
|
+
@percentiles.each do |key, val|
|
117
|
+
val[:reservoir].clear
|
118
|
+
val[:percs].clear
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def fetch_percentile(key, options)
|
123
|
+
store = fetch_percentile_store(key, options[:source])
|
124
|
+
return nil unless store
|
125
|
+
store[:reservoir].percentile(options[:percentile])
|
126
|
+
end
|
127
|
+
|
128
|
+
def fetch_percentile_store(event, source)
|
129
|
+
keyname = source ? "#{event}#{SOURCE_SEPARATOR}#{source}" : event
|
130
|
+
@percentiles[keyname] ||= {
|
131
|
+
reservoir: Hetchy::Reservoir.new(size: 1000),
|
132
|
+
percs: Set.new
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def flush_percentiles(queue, opts)
|
137
|
+
@percentiles.each do |key, val|
|
138
|
+
metric, source = key.split(SOURCE_SEPARATOR)
|
139
|
+
val[:percs].each do |perc|
|
140
|
+
perc_name = perc.to_s[0,5].gsub('.','')
|
141
|
+
payload = if source
|
142
|
+
{ value: val[:reservoir].percentile(perc), source: source }
|
143
|
+
else
|
144
|
+
val[:reservoir].percentile(perc)
|
145
|
+
end
|
146
|
+
queue.add "#{metric}.p#{perc_name}" => payload
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def track_percentile(store, perc)
|
152
|
+
if perc < 0.0 || perc > 100.0
|
153
|
+
raise InvalidPercentile, "Percentiles must be between 0.0 and 100.0"
|
154
|
+
end
|
155
|
+
store[:percs].add(perc)
|
156
|
+
end
|
157
|
+
|
95
158
|
end
|
159
|
+
|
96
160
|
end
|
97
161
|
end
|
@@ -44,18 +44,18 @@ module Librato
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# transfer all measurements to queue and reset internal status
|
47
|
-
def flush_to(queue)
|
47
|
+
def flush_to(queue, opts={})
|
48
48
|
counts = nil
|
49
49
|
@lock.synchronize do
|
50
50
|
# work off of a duplicate data set so we block for
|
51
51
|
# as little time as possible
|
52
52
|
counts = @cache.dup
|
53
|
-
reset_cache
|
53
|
+
reset_cache unless opts[:preserve]
|
54
54
|
end
|
55
55
|
counts.each do |metric, value|
|
56
56
|
metric, source = metric.split(SEPARATOR)
|
57
57
|
if source
|
58
|
-
queue.add metric => {:
|
58
|
+
queue.add metric => {value: value, source: source}
|
59
59
|
else
|
60
60
|
queue.add metric => value
|
61
61
|
end
|
@@ -76,7 +76,7 @@ module Librato
|
|
76
76
|
def increment(counter, options={})
|
77
77
|
if options.is_a?(Fixnum)
|
78
78
|
# suppport legacy style
|
79
|
-
options = {:
|
79
|
+
options = {by: options}
|
80
80
|
end
|
81
81
|
by = options[:by] || 1
|
82
82
|
if options[:source]
|
data/lib/librato/rack.rb
CHANGED
@@ -107,10 +107,10 @@ module Librato
|
|
107
107
|
case queue_start.length
|
108
108
|
when 16 # microseconds
|
109
109
|
wait = ((Time.now.to_f * 1000000).to_i - queue_start.to_i) / 1000.0
|
110
|
-
tracker.timing 'rack.request.queue.time', wait
|
110
|
+
tracker.timing 'rack.request.queue.time', wait, percentile: 95
|
111
111
|
when 13 # milliseconds
|
112
112
|
wait = (Time.now.to_f * 1000).to_i - queue_start.to_i
|
113
|
-
tracker.timing 'rack.request.queue.time', wait
|
113
|
+
tracker.timing 'rack.request.queue.time', wait, percentile: 95
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -119,7 +119,7 @@ module Librato
|
|
119
119
|
return if config.disable_rack_metrics
|
120
120
|
tracker.group 'rack.request' do |group|
|
121
121
|
group.increment 'total'
|
122
|
-
group.timing 'time', duration
|
122
|
+
group.timing 'time', duration, percentile: 95
|
123
123
|
group.increment 'slow' if duration > 200.0
|
124
124
|
|
125
125
|
group.group 'status' do |s|
|
data/lib/librato/rack/tracker.rb
CHANGED
@@ -64,6 +64,13 @@ module Librato
|
|
64
64
|
config.source_pids ? "#{source}.#{$$}" : source
|
65
65
|
end
|
66
66
|
|
67
|
+
# current local instrumentation to be sent on next flush
|
68
|
+
# this is for debugging, don't call rapidly in production as it
|
69
|
+
# may introduce latency
|
70
|
+
def queued
|
71
|
+
build_flush_queue(collector, true).queued
|
72
|
+
end
|
73
|
+
|
67
74
|
# given current state, should the tracker start a reporter thread?
|
68
75
|
def should_start?
|
69
76
|
if !config.user || !config.token
|
@@ -113,11 +120,11 @@ module Librato
|
|
113
120
|
end
|
114
121
|
end
|
115
122
|
|
116
|
-
def build_flush_queue(collector)
|
117
|
-
queue = ValidatingQueue.new( :
|
118
|
-
:
|
123
|
+
def build_flush_queue(collector, preserve=false)
|
124
|
+
queue = ValidatingQueue.new( client: client, source: qualified_source,
|
125
|
+
prefix: config.prefix, skip_measurement_times: true )
|
119
126
|
[collector.counters, collector.aggregate].each do |cache|
|
120
|
-
cache.flush_to(queue)
|
127
|
+
cache.flush_to(queue, preserve: preserve)
|
121
128
|
end
|
122
129
|
queue.add 'rack.processes' => 1
|
123
130
|
trace_queued(queue.queued) #if should_log?(:trace)
|
data/lib/librato/rack/version.rb
CHANGED
data/test/remote/tracker_test.rb
CHANGED
@@ -27,32 +27,32 @@ class TrackerRemoteTest < Minitest::Test
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_flush_counters
|
30
|
-
tracker.increment :foo
|
31
|
-
tracker.increment :bar, 2
|
32
|
-
tracker.increment :foo
|
33
|
-
tracker.increment :foo, :
|
30
|
+
tracker.increment :foo # simple
|
31
|
+
tracker.increment :bar, 2 # specified
|
32
|
+
tracker.increment :foo # multincrement
|
33
|
+
tracker.increment :foo, source: 'baz', by: 3 # custom source
|
34
|
+
@queued = tracker.queued
|
34
35
|
tracker.flush
|
35
36
|
|
37
|
+
# metrics are SSA, so should exist but won't have measurements yet
|
36
38
|
metric_names = client.list.map { |m| m['name'] }
|
37
39
|
assert metric_names.include?('foo'), 'foo should be present'
|
38
40
|
assert metric_names.include?('bar'), 'bar should be present'
|
39
41
|
|
40
|
-
|
41
|
-
assert_equal
|
42
|
-
assert_equal 2, foo
|
42
|
+
# interogate queued payload for expected values
|
43
|
+
assert_equal source, @queued[:source]
|
44
|
+
assert_equal 2, queued('foo')
|
43
45
|
|
44
46
|
# custom source
|
45
|
-
assert_equal
|
46
|
-
assert_equal 3, foo['baz'][0]['value']
|
47
|
+
assert_equal 3, queued('foo', source: 'baz')
|
47
48
|
|
48
|
-
|
49
|
-
assert_equal
|
50
|
-
assert_equal 2, bar[source][0]['value']
|
49
|
+
# different counter
|
50
|
+
assert_equal 2, queued('bar')
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_counter_persistent_through_flush
|
54
54
|
tracker.increment 'knightrider'
|
55
|
-
tracker.increment 'badguys', :
|
55
|
+
tracker.increment 'badguys', sporadic: true
|
56
56
|
assert_equal 1, collector.counters['knightrider']
|
57
57
|
assert_equal 1, collector.counters['badguys']
|
58
58
|
|
@@ -65,24 +65,23 @@ class TrackerRemoteTest < Minitest::Test
|
|
65
65
|
tracker.timing 'request.time.total', 122.1
|
66
66
|
tracker.measure 'items_bought', 20
|
67
67
|
tracker.timing 'request.time.total', 81.3
|
68
|
-
tracker.timing 'jobs.queued', 5, :
|
68
|
+
tracker.timing 'jobs.queued', 5, source: 'worker.3'
|
69
|
+
@queued = tracker.queued
|
69
70
|
tracker.flush
|
70
71
|
|
72
|
+
# metrics are SSA, so should exist but won't have measurements yet
|
71
73
|
metric_names = client.list.map { |m| m['name'] }
|
72
74
|
assert metric_names.include?('request.time.total'), 'request.time.total should be present'
|
73
75
|
assert metric_names.include?('items_bought'), 'request.time.db should be present'
|
74
76
|
|
75
|
-
|
76
|
-
|
77
|
-
assert_in_delta 203.4, total[source][0]['sum'], 0.1
|
77
|
+
assert_equal 2, queued('request.time.total')[:count]
|
78
|
+
assert_in_delta 203.4, queued('request.time.total')[:sum], 0.1
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
assert_in_delta 20, items[source][0]['sum'], 0.1
|
80
|
+
assert_equal 1, queued('items_bought')[:count]
|
81
|
+
assert_in_delta 20, queued('items_bought')[:sum], 0.1
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
assert_in_delta 5, jobs['worker.3'][0]['sum'], 0.1
|
83
|
+
assert_equal 1, queued('jobs.queued', source: 'worker.3')[:count]
|
84
|
+
assert_in_delta 5, queued('jobs.queued', source: 'worker.3')[:sum], 0.1
|
86
85
|
end
|
87
86
|
|
88
87
|
def test_flush_should_purge_measures_and_timings
|
@@ -90,77 +89,70 @@ class TrackerRemoteTest < Minitest::Test
|
|
90
89
|
tracker.measure 'items_bought', 20
|
91
90
|
tracker.flush
|
92
91
|
|
93
|
-
assert collector.aggregate.empty?,
|
92
|
+
assert collector.aggregate.empty?,
|
93
|
+
'measures and timings should be cleared with flush'
|
94
94
|
end
|
95
95
|
|
96
|
-
# Disabled for now because we always send a running process
|
97
|
-
# count at a minimum
|
98
|
-
#
|
99
|
-
# def test_empty_flush_should_not_be_sent
|
100
|
-
# tracker.flush
|
101
|
-
# assert_equal [], client.list
|
102
|
-
# end
|
103
|
-
|
104
96
|
def test_flush_respects_prefix
|
105
97
|
config.prefix = 'testyprefix'
|
106
98
|
|
107
99
|
tracker.timing 'mytime', 221.1
|
108
100
|
tracker.increment 'mycount', 4
|
101
|
+
@queued = tracker.queued
|
109
102
|
tracker.flush
|
110
103
|
|
111
104
|
metric_names = client.list.map { |m| m['name'] }
|
112
|
-
assert metric_names.include?('testyprefix.mytime'),
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
assert_equal 1, mytime[source][0]['count']
|
105
|
+
assert metric_names.include?('testyprefix.mytime'),
|
106
|
+
'testyprefix.mytime should be present'
|
107
|
+
assert metric_names.include?('testyprefix.mycount'), '
|
108
|
+
testyprefix.mycount should be present'
|
117
109
|
|
118
|
-
|
119
|
-
assert_equal 4, mycount
|
110
|
+
assert_equal 1, queued('testyprefix.mytime')[:count]
|
111
|
+
assert_equal 4, queued('testyprefix.mycount')
|
120
112
|
end
|
121
113
|
|
122
114
|
def test_flush_recovers_from_failure
|
123
115
|
# create a metric foo of counter type
|
124
|
-
client.submit :
|
116
|
+
client.submit foo: {type: :counter, value: 12}
|
125
117
|
|
126
118
|
# failing flush - submit a foo measurement as a gauge (type mismatch)
|
127
119
|
tracker.measure :foo, 2.12
|
128
|
-
tracker.flush
|
129
120
|
|
130
|
-
|
131
|
-
|
132
|
-
assert_nil foo[source] # shouldn't have been accepted
|
121
|
+
# won't be accepted
|
122
|
+
tracker.flush
|
133
123
|
|
134
124
|
tracker.measure :boo, 2.12
|
135
125
|
tracker.flush
|
136
126
|
|
137
|
-
|
138
|
-
|
127
|
+
metric_names = client.list.map { |m| m['name'] }
|
128
|
+
assert metric_names.include?('boo')
|
139
129
|
end
|
140
130
|
|
141
131
|
def test_flush_handles_invalid_metric_names
|
142
132
|
tracker.increment :foo # valid
|
143
133
|
tracker.increment 'fübar' # invalid
|
144
134
|
tracker.measure 'fu/bar/baz', 12.1 # invalid
|
135
|
+
@queued = tracker.queued
|
145
136
|
tracker.flush
|
146
137
|
|
147
138
|
metric_names = client.list.map { |m| m['name'] }
|
148
139
|
assert metric_names.include?('foo')
|
149
140
|
|
150
|
-
# should
|
151
|
-
|
152
|
-
assert_equal 1.0, foo[source][0]["value"]
|
141
|
+
# should be sending value for foo
|
142
|
+
assert_equal 1.0, queued('foo')
|
153
143
|
end
|
154
144
|
|
155
145
|
def test_flush_handles_invalid_sources_names
|
156
|
-
tracker.increment :foo, :
|
157
|
-
tracker.increment :bar, :
|
158
|
-
tracker.measure 'baz', 2.25, :
|
146
|
+
tracker.increment :foo, source: 'atreides' # valid
|
147
|
+
tracker.increment :bar, source: 'glébnöst' # invalid
|
148
|
+
tracker.measure 'baz', 2.25, source: 'b/l/ak/nok' # invalid
|
149
|
+
@queued = tracker.queued
|
159
150
|
tracker.flush
|
160
151
|
|
161
|
-
|
162
|
-
|
163
|
-
|
152
|
+
metric_names = client.list.map { |m| m['name'] }
|
153
|
+
assert metric_names.include?('foo')
|
154
|
+
|
155
|
+
assert_equal 1.0, queued('foo', source: 'atreides')
|
164
156
|
end
|
165
157
|
|
166
158
|
private
|
@@ -181,6 +173,25 @@ class TrackerRemoteTest < Minitest::Test
|
|
181
173
|
@tracker.config
|
182
174
|
end
|
183
175
|
|
176
|
+
# wrapper to make api format more easy to query
|
177
|
+
def queued(name, opts={})
|
178
|
+
raise "No queued found" unless @queued
|
179
|
+
source = opts[:source] || nil
|
180
|
+
|
181
|
+
@queued[:gauges].each do |g|
|
182
|
+
if g[:name] == name.to_s && g[:source] == source
|
183
|
+
if g[:count]
|
184
|
+
# complex metric, return the whole hash
|
185
|
+
return g
|
186
|
+
else
|
187
|
+
# return just the value
|
188
|
+
return g[:value]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
raise "No queued entry with '#{name}' found."
|
193
|
+
end
|
194
|
+
|
184
195
|
def source
|
185
196
|
@tracker.qualified_source
|
186
197
|
end
|
@@ -27,6 +27,52 @@ module Librato
|
|
27
27
|
assert_in_delta @agg['another.task'][:sum], 100, 50
|
28
28
|
end
|
29
29
|
|
30
|
+
def test_percentiles
|
31
|
+
# simple case
|
32
|
+
[0.1, 0.2, 0.3].each do |val|
|
33
|
+
@agg.timing 'a.sample.thing', val, percentile: 50
|
34
|
+
end
|
35
|
+
assert_equal 0.2, @agg.fetch('a.sample.thing', percentile: 50),
|
36
|
+
'can calculate percentile'
|
37
|
+
|
38
|
+
# multiple percentiles
|
39
|
+
[0.2, 0.35].each do |val|
|
40
|
+
@agg.timing 'a.sample.thing', val, percentile: [80, 95]
|
41
|
+
end
|
42
|
+
assert_equal 0.31, @agg.fetch('a.sample.thing', percentile: 65),
|
43
|
+
'can calculate another percentile simultaneously'
|
44
|
+
assert_equal 0.35, @agg.fetch('a.sample.thing', percentile: 95),
|
45
|
+
'can calculate another percentile simultaneously'
|
46
|
+
|
47
|
+
# ensure storage is efficient: this is a little gross because we
|
48
|
+
# have to inquire past the public interface, but important to verify
|
49
|
+
assert_equal 1, @agg.instance_variable_get('@percentiles').length,
|
50
|
+
'maintains all samples for same metric/source in one pool'
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_percentiles_invalid
|
54
|
+
# less than 0.0
|
55
|
+
assert_raises(Librato::Collector::InvalidPercentile) {
|
56
|
+
@agg.timing 'a.sample.thing', 123, percentile: -25.5
|
57
|
+
}
|
58
|
+
|
59
|
+
# greater than 100.0
|
60
|
+
assert_raises(Librato::Collector::InvalidPercentile) {
|
61
|
+
@agg.timing 'a.sample.thing', 123, percentile: 100.2
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_percentiles_with_source
|
66
|
+
Array(1..10).each do |val|
|
67
|
+
@agg.timing 'a.sample.thing', val, percentile: 50, source: 'foo'
|
68
|
+
end
|
69
|
+
assert_equal 5.5,
|
70
|
+
@agg.fetch('a.sample.thing', source: 'foo', percentile: 50),
|
71
|
+
'can calculate percentile with source'
|
72
|
+
end
|
73
|
+
|
74
|
+
# Todo: mult percentiles, block form, with source, invalid percentile
|
75
|
+
|
30
76
|
def test_return_values
|
31
77
|
simple = @agg.timing 'simple', 20
|
32
78
|
assert_equal nil, simple
|
@@ -58,12 +104,43 @@ module Librato
|
|
58
104
|
|
59
105
|
q = Librato::Metrics::Queue.new
|
60
106
|
@agg.flush_to(q)
|
107
|
+
|
61
108
|
expected = Set.new([
|
62
109
|
{:name=>"meaning.of.life", :count=>1, :sum=>1.0, :min=>1.0, :max=>1.0},
|
63
|
-
{:name=>"meaning.of.life", :count=>1, :sum=>42.0, :min=>42.0, :max=>42.0, :source=>"douglas_adams"}
|
110
|
+
{:name=>"meaning.of.life", :count=>1, :sum=>42.0, :min=>42.0, :max=>42.0, :source=>"douglas_adams"}
|
111
|
+
])
|
64
112
|
assert_equal expected, Set.new(q.queued[:gauges])
|
65
113
|
end
|
66
114
|
|
115
|
+
def test_flush_percentiles
|
116
|
+
[1,2,3].each { |i| @agg.timing 'a.timing', i, percentile: 95 }
|
117
|
+
[1,2,3].each { |i| @agg.timing 'b.timing', i, source: 'f', percentile: [50, 99.9] }
|
118
|
+
|
119
|
+
q = Librato::Metrics::Queue.new
|
120
|
+
@agg.flush_to(q)
|
121
|
+
|
122
|
+
queued = q.queued[:gauges]
|
123
|
+
a_timing = queued.detect{ |q| q[:name] == 'a.timing.p95' }
|
124
|
+
b_timing_50 = queued.detect{ |q| q[:name] == 'b.timing.p50' }
|
125
|
+
b_timing_999 = queued.detect{ |q| q[:name] == 'b.timing.p999' }
|
126
|
+
|
127
|
+
refute_nil a_timing, 'sending a.timing percentile'
|
128
|
+
refute_nil b_timing_50, 'sending b.timing 50th percentile'
|
129
|
+
refute_nil b_timing_999, 'sending a.timing 99.9th percentile'
|
130
|
+
|
131
|
+
assert_equal 3, a_timing[:value]
|
132
|
+
assert_equal 2, b_timing_50[:value]
|
133
|
+
assert_equal 3, b_timing_999[:value]
|
134
|
+
|
135
|
+
assert_nil a_timing[:source], 'no source set'
|
136
|
+
assert_equal 'f', b_timing_50[:source], 'proper source set'
|
137
|
+
assert_equal 'f', b_timing_999[:source], 'proper source set'
|
138
|
+
|
139
|
+
# flushing clears percentages to track
|
140
|
+
storage = @agg.instance_variable_get('@percentiles')
|
141
|
+
assert_equal 0, storage['a.timing'][:percs].length, 'clears percentiles'
|
142
|
+
end
|
143
|
+
|
67
144
|
end
|
68
145
|
end
|
69
146
|
end
|
metadata
CHANGED
@@ -1,74 +1,77 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: librato-rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.5.0.beta
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Matt Sanders
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain:
|
12
|
-
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
UVJYamxYUGttVDMvCnhhODU4QkdSanZVNTlXUEUxM1FHaWJhN1lJZUh0UkV2
|
36
|
-
Tng0MkpJZm9KTVY3NG9mcktJdVR3OUNNdG8yZ3o5WHgKS3gxbmNuMDdBK2JK
|
37
|
-
bktaNmhlblFBRjFDSDk2WmNxY0pIMTc5UzJ0SWlLRE04a2VlUklVT1BDM1dU
|
38
|
-
MGZhb2svMgpnQTJvemRyODUxYy9uQT09Ci0tLS0tRU5EIENFUlRJRklDQVRF
|
39
|
-
LS0tLS0K
|
40
|
-
date: 2014-05-13 00:00:00.000000000 Z
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MQ0wCwYDVQQDDARtYXR0
|
14
|
+
MRcwFQYKCZImiZPyLGQBGRYHbGlicmF0bzETMBEGCgmSJomT8ixkARkWA2NvbTAe
|
15
|
+
Fw0xNTA5MDMwMDE4MzBaFw0xNjA5MDIwMDE4MzBaMD0xDTALBgNVBAMMBG1hdHQx
|
16
|
+
FzAVBgoJkiaJk/IsZAEZFgdsaWJyYXRvMRMwEQYKCZImiZPyLGQBGRYDY29tMIIB
|
17
|
+
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9ox6O79gTdEjOHX3JWi4ZDU
|
18
|
+
g4xJoDWh5+NSeL8d2OaS3A8zOtBTbrPB3lKohkM7ZFgEymIr9mD0DVIe2y3PXDJ+
|
19
|
+
POaZLDNzix+08M3sQLoP5MvnuzRIpNbAtVf31CMz830GUkPGtSeXc4dcgkTR+u9t
|
20
|
+
gYLek7X2FXdO4/3hVlyQed5rurXg6IGc3xkznPLJz08v7gBXVTd7ZD/TA9JiVPAb
|
21
|
+
NpDpqeJ0cUGoNOmvr90lENnE4L3QUcXoWnIDokdgrT6e2+u3fqm9Awi4PHIeJRF1
|
22
|
+
CDLNk4fF076J5wldCu9GSDyOR864j8s8P4grqg3/W5nlcA/duj70FdBevXEGdQID
|
23
|
+
AQABo3MwcTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUhLbYf+E+
|
24
|
+
mD2AKqV3UPnSvYHWhoowGwYDVR0RBBQwEoEQbWF0dEBsaWJyYXRvLmNvbTAbBgNV
|
25
|
+
HRIEFDASgRBtYXR0QGxpYnJhdG8uY29tMA0GCSqGSIb3DQEBBQUAA4IBAQAFzeZI
|
26
|
+
B1KPHoagOEfFSRjEOM+Oc8OCch9GrvkXIJmygb7ek0IaamM265rosHkZs/GFJUWM
|
27
|
+
2g/DjyLazNaFJ3k5vHIWdH11oLfoJ2atJJZuv5DoMATv6bZEPy6HEW8UAFbNt1kg
|
28
|
+
e5VTA7yy+JNFm3/3jt2JzOX6cnJs4gUra3zgSCte69sFVYD/lcasMUM+/xRmubPz
|
29
|
+
A2QAjpamhamK23fX5Iu0Taj0/U8VzB8tWC8wbp6Q/2rGBSG31tTM9Mt415XXSuKt
|
30
|
+
9WLxxDpp4oArOj0hucFoQ9V6f68TZdS1u5/LcIw/ZJ+7sXVYmMCgDIjdZ+p7VVwq
|
31
|
+
aqIKyXbNfJC+dTit
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 2015-09-03 00:00:00.000000000 Z
|
41
34
|
dependencies:
|
42
35
|
- !ruby/object:Gem::Dependency
|
43
36
|
name: librato-metrics
|
44
37
|
requirement: !ruby/object:Gem::Requirement
|
45
|
-
none: false
|
46
38
|
requirements:
|
47
|
-
- - ~>
|
39
|
+
- - "~>"
|
48
40
|
- !ruby/object:Gem::Version
|
49
41
|
version: '1.1'
|
50
42
|
type: :runtime
|
51
43
|
prerelease: false
|
52
44
|
version_requirements: !ruby/object:Gem::Requirement
|
53
|
-
none: false
|
54
45
|
requirements:
|
55
|
-
- - ~>
|
46
|
+
- - "~>"
|
56
47
|
- !ruby/object:Gem::Version
|
57
48
|
version: '1.1'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: hetchy
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.0'
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.0'
|
58
63
|
- !ruby/object:Gem::Dependency
|
59
64
|
name: minitest
|
60
65
|
requirement: !ruby/object:Gem::Requirement
|
61
|
-
none: false
|
62
66
|
requirements:
|
63
|
-
- -
|
67
|
+
- - ">="
|
64
68
|
- !ruby/object:Gem::Version
|
65
69
|
version: '0'
|
66
70
|
type: :development
|
67
71
|
prerelease: false
|
68
72
|
version_requirements: !ruby/object:Gem::Requirement
|
69
|
-
none: false
|
70
73
|
requirements:
|
71
|
-
- -
|
74
|
+
- - ">="
|
72
75
|
- !ruby/object:Gem::Version
|
73
76
|
version: '0'
|
74
77
|
description: Rack middleware to report key app statistics and custom instrumentation
|
@@ -79,10 +82,17 @@ executables: []
|
|
79
82
|
extensions: []
|
80
83
|
extra_rdoc_files: []
|
81
84
|
files:
|
85
|
+
- CHANGELOG.md
|
86
|
+
- LICENSE
|
87
|
+
- README.md
|
88
|
+
- Rakefile
|
89
|
+
- lib/librato-rack.rb
|
90
|
+
- lib/librato/collector.rb
|
82
91
|
- lib/librato/collector/aggregator.rb
|
83
92
|
- lib/librato/collector/counter_cache.rb
|
93
|
+
- lib/librato/collector/exceptions.rb
|
84
94
|
- lib/librato/collector/group.rb
|
85
|
-
- lib/librato/
|
95
|
+
- lib/librato/rack.rb
|
86
96
|
- lib/librato/rack/configuration.rb
|
87
97
|
- lib/librato/rack/errors.rb
|
88
98
|
- lib/librato/rack/logger.rb
|
@@ -90,12 +100,6 @@ files:
|
|
90
100
|
- lib/librato/rack/validating_queue.rb
|
91
101
|
- lib/librato/rack/version.rb
|
92
102
|
- lib/librato/rack/worker.rb
|
93
|
-
- lib/librato/rack.rb
|
94
|
-
- lib/librato-rack.rb
|
95
|
-
- LICENSE
|
96
|
-
- Rakefile
|
97
|
-
- README.md
|
98
|
-
- CHANGELOG.md
|
99
103
|
- test/apps/basic.ru
|
100
104
|
- test/apps/custom.ru
|
101
105
|
- test/apps/deprecated.ru
|
@@ -119,33 +123,26 @@ files:
|
|
119
123
|
homepage: https://github.com/librato/librato-rack
|
120
124
|
licenses:
|
121
125
|
- BSD 3-clause
|
126
|
+
metadata: {}
|
122
127
|
post_install_message:
|
123
128
|
rdoc_options: []
|
124
129
|
require_paths:
|
125
130
|
- lib
|
126
131
|
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
-
none: false
|
128
132
|
requirements:
|
129
|
-
- -
|
133
|
+
- - ">="
|
130
134
|
- !ruby/object:Gem::Version
|
131
135
|
version: '0'
|
132
|
-
segments:
|
133
|
-
- 0
|
134
|
-
hash: 3341764962779611788
|
135
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
-
none: false
|
137
137
|
requirements:
|
138
|
-
- -
|
138
|
+
- - ">"
|
139
139
|
- !ruby/object:Gem::Version
|
140
|
-
version:
|
141
|
-
segments:
|
142
|
-
- 0
|
143
|
-
hash: 3341764962779611788
|
140
|
+
version: 1.3.1
|
144
141
|
requirements: []
|
145
142
|
rubyforge_project:
|
146
|
-
rubygems_version:
|
143
|
+
rubygems_version: 2.2.2
|
147
144
|
signing_key:
|
148
|
-
specification_version:
|
145
|
+
specification_version: 4
|
149
146
|
summary: Use Librato Metrics with your rack application
|
150
147
|
test_files:
|
151
148
|
- test/apps/basic.ru
|
metadata.gz.sig
CHANGED
Binary file
|