librato-rack 0.4.5 → 0.5.0.beta

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 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