diff --git a/lib/metriks/reporter/flattening_registry_enumerator.rb b/lib/metriks/reporter/flattening_registry_enumerator.rb new file mode 100644 index 0000000..22f3cf1 --- /dev/null +++ b/lib/metriks/reporter/flattening_registry_enumerator.rb @@ -0,0 +1,117 @@ +module Metriks + module Reporter + class FlatteningRegistryEnumerator + include Enumerable + + def initialize(registry, options = {}) + @registry = registry + unless options[:current_rate] + @last = Hash.new { |h,k| h[k] = 0 } + end + + if options[:percentiles] + @percentiles = options[:percentiles] + else + @percentiles = [ 0.75, 0.95, 0.98, 0.99, 0.999 ] + end + end + + def each(&block) + @registry.each do |name, metric| + for_metric(name, metric, &block) + end + end + + def for_metric(name, metric, &block) + name = name.to_s.gsub(/ +/, '_') + + case metric + when Metriks::Meter + if @last + count = metric.count + block.call(name, count - @last[name], metric.class) + @last[name] = count + else + block.call("#{name}.count", metric.count, metric.class) + block.call("#{name}.one_minute_rate", metric.one_minute_rate, metric.class) + block.call("#{name}.five_minute_rate", metric.five_minute_rate, metric.class) + block.call("#{name}.fifteen_minute_rate", metric.fifteen_minute_rate, metric.class) + block.call("#{name}.mean_rate", metric.mean_rate, metric.class) + end + + when Metriks::Counter + block.call("#{name}", metric.count, metric.class) + when Metriks::Gauge + block.call("#{name}", metric.value, metric.class) + when Metriks::UtilizationTimer + if @last + count = metric.count + block.call(name, count - @last[name], metric.class) + @last[name] = count + else + block.call("#{name}.one_minute_rate", metric.one_minute_rate, metric.class) + block.call("#{name}.five_minute_rate", metric.five_minute_rate, metric.class) + block.call("#{name}.fifteen_minute_rate", metric.fifteen_minute_rate, metric.class) + end + + block.call("#{name}.min", metric.min, metric.class) + block.call("#{name}.max", metric.max, metric.class) + block.call("#{name}.stddev", metric.stddev, metric.class) + + block.call("#{name}.one_minute_utilization", metric.one_minute_utilization, metric.class) + block.call("#{name}.five_minute_utilization", metric.five_minute_utilization, metric.class) + block.call("#{name}.fifteen_minute_utilization", metric.fifteen_minute_utilization, metric.class) + + snapshot = metric.snapshot + + block.call("#{name}.median", snapshot.median, metric.class) + + for_percentiles(name, metric, snapshot, &block) + + when Metriks::Timer + if @last + count = metric.count + block.call(name, count - @last[name], metric.class) + @last["#{name}.count"] = count + else + block.call("#{name}.count", metric.count, metric.class) + block.call("#{name}.one_minute_rate", metric.one_minute_rate, metric.class) + block.call("#{name}.five_minute_rate", metric.five_minute_rate, metric.class) + block.call("#{name}.fifteen_minute_rate", metric.fifteen_minute_rate, metric.class) + block.call("#{name}.mean_rate", metric.mean_rate, metric.class) + end + + block.call("#{name}.min", metric.min, metric.class) + block.call("#{name}.max", metric.max, metric.class) + block.call("#{name}.stddev", metric.stddev, metric.class) + + snapshot = metric.snapshot + + block.call("#{name}.median", snapshot.median, metric.class) + + for_percentiles(name, metric, snapshot, &block) + + when Metriks::Histogram + block.call("#{name}.count", metric.count, metric.class) + block.call("#{name}.min", metric.min, metric.class) + block.call("#{name}.max", metric.max, metric.class) + block.call("#{name}.stddev", metric.stddev, metric.class) + + snapshot = metric.snapshot + + block.call("#{name}.median", snapshot.median, metric.class) + + for_percentiles(name, metric, snapshot, &block) + end + end + + + def for_percentiles(name, metric, snapshot, &block) + @percentiles.each do |percentile| + percentile_name = (percentile * 100).to_f.to_s.gsub(/0+$/, '').gsub('.', '') + block.call("#{name}.#{percentile_name}th_percentile", snapshot.value(percentile), metric.class) + end + end + end + end +end \ No newline at end of file diff --git a/lib/metriks/reporter/graphite.rb b/lib/metriks/reporter/graphite.rb index cc2a347..3acae1a 100644 --- a/lib/metriks/reporter/graphite.rb +++ b/lib/metriks/reporter/graphite.rb @@ -1,4 +1,5 @@ require 'socket' +require 'metriks/reporter/flattening_registry_enumerator' module Metriks::Reporter class Graphite @@ -47,71 +48,19 @@ def restart end def write - @registry.each do |name, metric| - case metric - when Metriks::Meter - write_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate - ] - when Metriks::Counter - write_metric name, metric, [ - :count - ] - when Metriks::Gauge - write_metric name, metric, [ - :value - ] - when Metriks::UtilizationTimer - write_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate, - :min, :max, :mean, :stddev, - :one_minute_utilization, :five_minute_utilization, - :fifteen_minute_utilization, :mean_utilization, - ], [ - :median, :get_95th_percentile - ] - when Metriks::Timer - write_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate, - :min, :max, :mean, :stddev - ], [ - :median, :get_95th_percentile - ] - when Metriks::Histogram - write_metric name, metric, [ - :count, :min, :max, :mean, :stddev - ], [ - :median, :get_95th_percentile - ] - end + Metriks::Reporter::FlatteningRegistryEnumerator.new(@registry).each do |name, value| + write_metric(name, value) end end - def write_metric(base_name, metric, keys, snapshot_keys = []) + def write_metric(name, value) time = Time.now.to_i - base_name = base_name.to_s.gsub(/ +/, '_') if @prefix - base_name = "#{@prefix}.#{base_name}" - end - - keys.flatten.each do |key| - name = key.to_s.gsub(/^get_/, '') - value = metric.send(key) - socket.write("#{base_name}.#{name} #{value} #{time}\n") + name = "#{@prefix}.#{name}" end - unless snapshot_keys.empty? - snapshot = metric.snapshot - snapshot_keys.flatten.each do |key| - name = key.to_s.gsub(/^get_/, '') - value = snapshot.send(key) - socket.write("#{base_name}.#{name} #{value} #{time}\n") - end - end + socket.write("#{name} #{value} #{time}\n") rescue Errno::EPIPE socket.close end diff --git a/lib/metriks/reporter/librato_metrics.rb b/lib/metriks/reporter/librato_metrics.rb index bdcaa2c..9dc1bfc 100644 --- a/lib/metriks/reporter/librato_metrics.rb +++ b/lib/metriks/reporter/librato_metrics.rb @@ -1,5 +1,6 @@ require 'metriks/time_tracker' require 'net/https' +require 'metriks/reporter/flattening_registry_enumerator' module Metriks::Reporter class LibratoMetrics @@ -12,14 +13,14 @@ def initialize(email, token, options = {}) @prefix = options[:prefix] @source = options[:source] - @registry = options[:registry] || Metriks::Registry.default + @registry = options[:registry] || Metriks::Registry.default @time_tracker = Metriks::TimeTracker.new(options[:interval] || 60) - @on_error = options[:on_error] || proc { |ex| } + @on_error = options[:on_error] || proc { |ex| } end def start @thread ||= Thread.new do - loop do + while true @time_tracker.sleep Thread.new do @@ -43,57 +44,6 @@ def restart start end - def write - gauges = [] - @registry.each do |name, metric| - gauges << case metric - when Metriks::Meter - prepare_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate - ] - when Metriks::Counter - prepare_metric name, metric, [ - :count - ] - when Metriks::Gauge - prepare_metric name, metric, [ - :value - ] - when Metriks::UtilizationTimer - prepare_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate, - :min, :max, :mean, :stddev, - :one_minute_utilization, :five_minute_utilization, - :fifteen_minute_utilization, :mean_utilization, - ], [ - :median, :get_95th_percentile - ] - when Metriks::Timer - prepare_metric name, metric, [ - :count, :one_minute_rate, :five_minute_rate, - :fifteen_minute_rate, :mean_rate, - :min, :max, :mean, :stddev - ], [ - :median, :get_95th_percentile - ] - when Metriks::Histogram - prepare_metric name, metric, [ - :count, :min, :max, :mean, :stddev - ], [ - :median, :get_95th_percentile - ] - end - end - - gauges.flatten! - - unless gauges.empty? - submit(form_data(gauges.flatten)) - end - end - def submit(data) url = URI.parse('https://metrics-api.librato.com/v1/metrics') req = Net::HTTP::Post.new(url.path) @@ -115,59 +65,25 @@ def submit(data) end end - def form_data(metrics) - data = {} - - metrics.each_with_index do |gauge, idx| - gauge.each do |key, value| - if value - data["gauges[#{idx}][#{key}]"] = value.to_s - end - end - end - - data - end - - def prepare_metric(base_name, metric, keys, snapshot_keys = []) - results = [] - time = @time_tracker.now_floored + def write + time = @time_tracker.now_floored - base_name = base_name.to_s.gsub(/ +/, '_') - if @prefix - base_name = "#{@prefix}.#{base_name}" - end + enumerator = Metriks::Reporter::FlatteningRegistryEnumerator.new(@registry) - keys.flatten.each do |key| - name = key.to_s.gsub(/^get_/, '') - value = metric.send(key) - - results << { - :type => "gauge", - :name => "#{base_name}.#{name}", - :source => @source, - :measure_time => time, - :value => value - } - end + data = {} - unless snapshot_keys.empty? - snapshot = metric.snapshot - snapshot_keys.flatten.each do |key| - name = key.to_s.gsub(/^get_/, '') - value = snapshot.send(key) - - results << { - :type => "gauge", - :name => "#{base_name}.#{name}", - :source => @source, - :measure_time => time, - :value => value - } + enumerator.each_with_index do |(name, value, klass), idx| + if prefix + name = "#{prefix}.#{name}" end + + data["gauges[#{idx}][name]"] = name.to_s + data["gauges[#{idx}][source]"] = @source + data["gauges[#{idx}][measure_time]"] = time.to_i + data["gauges[#{idx}][value]"] = value end - results + submit(data) unless data.empty? end end end diff --git a/test/graphite_reporter_test.rb b/test/graphite_reporter_test.rb index 51dc9c4..0c59c15 100644 --- a/test/graphite_reporter_test.rb +++ b/test/graphite_reporter_test.rb @@ -35,7 +35,7 @@ def test_write @reporter.write assert_match /timer.testing.median \d/, @stringio.string - assert_match /gauge.testing.value 123/, @stringio.string - assert_match /gauge.testing.block.value 456/, @stringio.string + assert_match /gauge.testing 123/, @stringio.string + assert_match /gauge.testing.block 456/, @stringio.string end end diff --git a/test/librato_metrics_reporter_test.rb b/test/librato_metrics_reporter_test.rb index 4c98455..4d9ebad 100644 --- a/test/librato_metrics_reporter_test.rb +++ b/test/librato_metrics_reporter_test.rb @@ -28,8 +28,16 @@ def test_write @registry.utilization_timer('utilization_timer.testing').update(1.5) @registry.gauge('gauge.testing') { 123 } - @reporter.expects(:submit) + @reporter.expects(:submit).with do |data| + data.detect { |(k,v)| k =~ /gauges\[\d+\]\[name\]/ && v == 'gauge.testing' } && + data.detect { |(k,v)| k =~ /gauges\[\d+\]\[value\]/ && v.to_s == '123' } + end @reporter.write end + + def test_empty_write + @reporter.expects(:submit).never + @reporter.write + end end