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 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
@@ -0,0 +1,4 @@
1
+ ��]���´$쥸&��.H1t�j�qR�ԅ[�$���ժ�˭|�"����F��R��(ɠꊭIK�t�M_����.l��O��q��1��D��~�4;��?J;��iȨ$vQ�-��=4
2
+ 59���g��p�X��z YF�R�dw�{�P��Q�ZD����$�����Q���kԈɵAI�+[�%���8!�� =�X��k�Hp
3
+ ���Y�88?��è �xd�0
4
+ �R3�
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.
@@ -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(:prefix => @prefix)
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
- require 'librato/collector/aggregator'
47
- require 'librato/collector/counter_cache'
48
- require 'librato/collector/group'
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(:prefix => options[:prefix])
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 { @cache.clear }
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
- @cache.clear
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 => {:source => source, :value => value}
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 => {:value => value, :source => source}
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 = {:by => options}
79
+ options = {by: options}
80
80
  end
81
81
  by = options[:by] || 1
82
82
  if options[:source]
@@ -0,0 +1,8 @@
1
+ module Librato
2
+ class Collector
3
+
4
+ # custom exceptions
5
+ class InvalidPercentile < StandardError; end
6
+
7
+ end
8
+ end
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|
@@ -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( :client => client, :source => qualified_source,
118
- :prefix => config.prefix, :skip_measurement_times => true )
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)
@@ -1,5 +1,5 @@
1
1
  module Librato
2
2
  class Rack
3
- VERSION = "0.4.5"
3
+ VERSION = "0.5.0.beta"
4
4
  end
5
5
  end
@@ -27,32 +27,32 @@ class TrackerRemoteTest < Minitest::Test
27
27
  end
28
28
 
29
29
  def test_flush_counters
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
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
- foo = client.fetch 'foo', :count => 10
41
- assert_equal 1, foo[source].length
42
- assert_equal 2, foo[source][0]['value']
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 1, foo['baz'].length
46
- assert_equal 3, foo['baz'][0]['value']
47
+ assert_equal 3, queued('foo', source: 'baz')
47
48
 
48
- bar = client.fetch 'bar', :count => 10
49
- assert_equal 1, bar[source].length
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', :sporadic => true
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, :source => 'worker.3'
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
- total = client.fetch 'request.time.total', :count => 10
76
- assert_equal 2, total[source][0]['count']
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
- items = client.fetch 'items_bought', :count => 10
80
- assert_equal 1, items[source][0]['count']
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
- jobs = client.fetch 'jobs.queued', :count => 10
84
- assert_equal 1, jobs['worker.3'][0]['count']
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?, 'measures and timings should be cleared with flush'
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'), 'testyprefix.mytime should be present'
113
- assert metric_names.include?('testyprefix.mycount'), 'testyprefix.mycount should be present'
114
-
115
- mytime = client.fetch 'testyprefix.mytime', :count => 10
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
- mycount = client.fetch 'testyprefix.mycount', :count => 10
119
- assert_equal 4, mycount[source][0]['value']
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 :foo => {:type => :counter, :value => 12}
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
- foo = client.fetch :foo, :count => 10
131
- assert_equal 1, foo['unassigned'].length
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
- boo = client.fetch :boo, :count => 10
138
- assert_equal 2.12, boo[source][0]["value"]
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 have saved values for foo
151
- foo = client.fetch :foo, :count => 5
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, :source => 'atreides' # valid
157
- tracker.increment :bar, :source => 'glébnöst' # invalid
158
- tracker.measure 'baz', 2.25, :source => 'b/l/ak/nok' # invalid
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
- # should have saved values for foo
162
- foo = client.fetch :foo, :count => 5
163
- assert_equal 1.0, foo['atreides'][0]["value"]
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.4.5
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
- - !binary |-
13
- LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUROakNDQWg2Z0F3SUJB
14
- Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREJCTVJFd0R3WURWUVFEREFoeWRX
15
- SjUKWjJWdGN6RVhNQlVHQ2dtU0pvbVQ4aXhrQVJrV0IyeHBZbkpoZEc4eEV6
16
- QVJCZ29Ka2lhSmsvSXNaQUVaRmdOagpiMjB3SGhjTk1UTXdPREE0TWpJeE9U
17
- UTJXaGNOTVRRd09EQTRNakl4T1RRMldqQkJNUkV3RHdZRFZRUUREQWh5CmRX
18
- SjVaMlZ0Y3pFWE1CVUdDZ21TSm9tVDhpeGtBUmtXQjJ4cFluSmhkRzh4RXpB
19
- UkJnb0praWFKay9Jc1pBRVoKRmdOamIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFF
20
- QkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDL1g3a2RLd1ovb2kvQQpCanMvY2F4
21
- a3lESVpnTFMva2dtdWxvVGhmUEJCUjZNdU40R1hlL2hzZHpTSDhYaHRCWW9Z
22
- cEsvRjJyUkJzclMrCmpMclpiS0pBR1VJcnFIaVNmZEx6eDJrMnNHVVlsS3pm
23
- NmE0eFdpNTg3bmRDOEJ2aDVsZGM4NVcxbGxIRGVBU1MKUjVXanBlcjRLVTFO
24
- V0cxRkFWdlFDWGhTS2Rta2krd1g3Sm5kN0NRK296N2trS1lQTThHL1pUZGIr
25
- cW43d1JMVgpLYVIrenpHRG13VFEyV3pNaXRTWG1mL2t1NE1VbVJ6c3llcERY
26
- WEVSTHluU3A4SVRrNjdnMkhNQ3l2T1BzZjhLCmNZdmwvd2JiOEJ5L3I2SE9q
27
- eTdTTTdZbzM1NHVJZmhuaXU4QUt5bUlLeHNiNElnNzFTMGNVN0htMytXQlRp
28
- MjgKQUlnOFRVYVhBZ01CQUFHak9UQTNNQWtHQTFVZEV3UUNNQUF3SFFZRFZS
29
- ME9CQllFRkRieVFRcU80eEptYUtCRQpuZVE0eStSV0N2T1hNQXNHQTFVZER3
30
- UUVBd0lFc0RBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUtBelhiQTQ3CjlV
31
- NTlTc0VmcVIrRExkdjFWQWNkbXhhd3FDK1ptRzRGcHhaaHVIaGFvVXVpMzVB
32
- b1FqelNpSEVVTkRUSXUzdTcKVGNzWXdYTVB6dXl6WkpKS1h2QkttU2I5bVdK
33
- OTlET0g4MW9VbU96WDdqQ2xRWFpIcm5GdEhkQVJjTFFzUG1nYQo0RGgrZldY
34
- V3hQSjZma3ZnODI2dko0cERtbDdPbzlzQ1hUcEMya2kvNVZla1RYa3BGclVz
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/collector.rb
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: '0'
141
- segments:
142
- - 0
143
- hash: 3341764962779611788
140
+ version: 1.3.1
144
141
  requirements: []
145
142
  rubyforge_project:
146
- rubygems_version: 1.8.23
143
+ rubygems_version: 2.2.2
147
144
  signing_key:
148
- specification_version: 3
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