[DRE-commits] [ruby-metriks] 01/04: Imported Upstream version 0.9.9.6

Tim Potter tpot-guest at moszumanska.debian.org
Tue Aug 5 06:22:47 UTC 2014


This is an automated email from the git hooks/post-receive script.

tpot-guest pushed a commit to branch master
in repository ruby-metriks.

commit 0d0305af5a7c571cec0a2db5f0bb5c089c58737a
Author: Tim Potter <tpot at hp.com>
Date:   Fri Aug 1 13:25:31 2014 +1000

    Imported Upstream version 0.9.9.6
---
 Gemfile                                      |   9 +
 LICENSE                                      |  21 ++
 README.md                                    | 409 +++++++++++++++++++++++++++
 Rakefile                                     | 150 ++++++++++
 benchmark/samplers.rb                        |  92 ++++++
 checksums.yaml.gz                            | Bin 0 -> 269 bytes
 lib/metriks.rb                               |  35 +++
 lib/metriks/counter.rb                       |  44 +++
 lib/metriks/ewma.rb                          |  63 +++++
 lib/metriks/exponentially_decaying_sample.rb | 102 +++++++
 lib/metriks/gauge.rb                         |  27 ++
 lib/metriks/histogram.rb                     | 112 ++++++++
 lib/metriks/meter.rb                         |  85 ++++++
 lib/metriks/registry.rb                      | 207 ++++++++++++++
 lib/metriks/reporter/graphite.rb             | 119 ++++++++
 lib/metriks/reporter/librato_metrics.rb      | 173 +++++++++++
 lib/metriks/reporter/logger.rb               | 129 +++++++++
 lib/metriks/reporter/proc_title.rb           |  59 ++++
 lib/metriks/reporter/riemann.rb              | 119 ++++++++
 lib/metriks/simple_moving_average.rb         |  60 ++++
 lib/metriks/snapshot.rb                      |  59 ++++
 lib/metriks/time_tracker.rb                  |  26 ++
 lib/metriks/timer.rb                         | 101 +++++++
 lib/metriks/uniform_sample.rb                |  40 +++
 lib/metriks/utilization_timer.rb             |  43 +++
 metadata.yml                                 | 154 ++++++++++
 metriks.gemspec                              | 102 +++++++
 test/counter_test.rb                         |  39 +++
 test/gauge_test.rb                           |  46 +++
 test/graphite_reporter_test.rb               |  41 +++
 test/histogram_test.rb                       | 199 +++++++++++++
 test/librato_metrics_reporter_test.rb        |  35 +++
 test/logger_reporter_test.rb                 |  49 ++++
 test/meter_test.rb                           |  38 +++
 test/metriks_test.rb                         |  31 ++
 test/proc_title_reporter_test.rb             |  25 ++
 test/registry_test.rb                        |  49 ++++
 test/riemann_reporter_test.rb                |  88 ++++++
 test/test_helper.rb                          |  33 +++
 test/thread_error_handling_tests.rb          |  20 ++
 test/timer_test.rb                           |  32 +++
 test/utilization_timer_test.rb               |  25 ++
 42 files changed, 3290 insertions(+)

diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..0fa6145
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,9 @@
+source :rubygems
+
+gemspec
+
+group :test do
+  gem 'rake', '0.8.7'
+  gem 'riemann-client', '~> 0.0.7'
+  gem 'rbtree', :platform => :mri_18
+end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8368051
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2012 Eric Lindvall
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..656f128
--- /dev/null
+++ b/README.md
@@ -0,0 +1,409 @@
+# Metriks Client
+
+This is an experiment in making a threadsafe, low impact library to measure
+aspects of your ruby.
+
+The library is very much a work-in-progress. It is being developed as
+I find needs while developing [Papertrail](https://papertrailapp.com/).
+
+
+# Installing
+
+The API is still in flux, but you can add this to your project by installing
+the gem.
+
+To install, add this to your `Gemfile`:
+
+``` ruby
+gem 'metriks'
+```
+
+and re-run `bundle`.
+
+
+# Metric API Overview
+
+## Counters
+
+Basic atomic counter. Used as an underlying metric for many of the other
+more advanced metrics.
+
+
+### increment(incr = 1)
+
+Increment the counter. Without an argument it will increment by `1`.
+
+``` ruby
+  counter = Metriks.counter('calls')
+  counter.increment
+```
+
+### decrement(decr = 1)
+
+Decrement the counter. Without an argument it will decrement by `1`.
+
+``` ruby
+  counter = Metriks.counter('calls')
+  counter.decrement
+```
+
+#### count()
+
+Return the current value of the counter.
+
+``` ruby
+  counter = Metriks.counter('calls')
+  puts "counter: #{counter.count}"
+```
+
+## Gauges
+
+A gauge is an instantaneous measurement of a value.
+
+It takes a callback to measure the value in form of a block or a callable
+object.
+
+**WARNING:** The code in the callback is executed every time the `#value`
+method is called on the gauge. Most of the time this will be done by a
+metriks reporter that is running in a separate thread.
+
+``` ruby
+  # Callback as block
+  gauge = Metriks.gauge('queue.size') { queue.size }
+
+  # Callback as object responding to #call
+  callable = proc { queue.size }
+  gauge = Metriks.gauge('queue.size', callable)
+```
+
+### set(val)
+
+Set the current value.
+
+``` ruby
+  gauge = Metriks.gauge('queue_size')
+  gauge.set(queue.size)
+```
+
+### value()
+
+Returns the value returned by the callback (if one is defined), returns the
+value set via `#set` (or the default of 0) otherwise.
+
+``` ruby
+  gauge = Metriks.gauge('queue_size')
+  puts "queue size: #{gauge.value}"
+```
+
+## Meters
+
+A meter that measures the mean throughput and the one-, five-, and
+fifteen-minute exponentially-weighted moving average throughputs.
+
+### mark(val = 1)
+
+Record an event with the meter. Without an argument it will record one event.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  meter.mark
+```
+
+### count()
+
+Returns the total number of events that have been recorded.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  puts "total: #{meter.count}"
+```
+
+### one_minute_rate()
+
+Returns the one-minute average rate.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  puts "rate: #{meter.one_minute_rate}/sec"
+```
+
+### five_minute_rate()
+
+Returns the five-minute average rate.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  puts "rate: #{meter.five_minute_rate}/sec"
+```
+
+### fifteen_minute_rate()
+
+Returns the fifteen-minute average rate.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  puts "rate: #{meter.fifteen_minute_rate}/sec"
+```
+
+### mean_rate()
+
+Returns the mean (average) rate of the events since the start of the process.
+
+``` ruby
+  meter = Metriks.meter('requests')
+  puts "rate: #{meter.mean_rate}/sec"
+```
+
+## Timers
+
+A timer that measures the average time as well as throughput metrics via
+a meter.
+
+### update(duration)
+
+Records the duration of an operation. This normally wouldn't need to be
+called — the `#time` method is provided to simplify recording a duration.
+
+``` ruby
+  timer = Metriks.timer('requests')
+  t0 = Time.now
+  work
+  timer.update(Time.now - t0)
+```
+
+### time(callable = nil, &block)
+
+Measure the amount of time a proc takes to execute. Takes either a block
+or an object responding to `#call` (normally a `proc` or `lambda`).
+
+``` ruby
+  timer = Metriks.timer('requests')
+  timer.time do
+    work
+  end
+```
+
+If neither a block or an object is passed to the method, an object that
+responds to `#stop` will be returned. When `#stop` is called, the time
+will be recorded.
+
+``` ruby
+  timer = Metriks.timer('requests')
+  t = timer.time
+  work
+  t.stop
+```
+
+### count()
+
+Returns the number of measurements that have been made.
+
+``` ruby
+  timer = Metriks.timer('requests')
+  puts "calls: #{timer.count}"
+```
+
+### one_minute_rate()
+
+Returns the one-minute average rate.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "rate: #{meter.one_minute_rate}/sec"
+```
+
+### five_minute_rate()
+
+Returns the five-minute average rate.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "rate: #{meter.five_minute_rate}/sec"
+```
+
+### fifteen_minute_rate()
+
+Returns the fifteen-minute average rate.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "rate: #{meter.fifteen_minute_rate}/sec"
+```
+
+### mean_rate()
+
+Returns the mean (average) rate of the events since the start of the process.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "rate: #{meter.mean_rate}/sec"
+```
+
+### min()
+
+Returns the minimum amount of time spent in the operation.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "time: #{meter.min} seconds"
+```
+
+### max()
+
+Returns the maximum time spent in the operation.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "time: #{meter.max} seconds"
+```
+
+### mean()
+
+Returns the mean (average) time spent in the operation.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "time: #{meter.mean} seconds"
+```
+
+### stddev()
+
+Returns the standard deviation of the mean spent in the operation.
+
+``` ruby
+  meter = Metriks.timer('requests')
+  puts "time: #{meter.stddev} seconds"
+```
+
+
+## Utilization Timer
+
+A specialized `Timer` that calculates the percentage (between `0.0` and `1.0`) of
+wall-clock time that was spent. It includes all of the methods of `Timer`.
+
+
+### one_minute_utilization()
+
+Returns the one-minute average utilization as a percentage between `0.0` and `1.0`.
+
+``` ruby
+  meter = Metriks.utilization_timer('requests')
+  puts "utilization: #{meter.one_minute_utilization * 100}%"
+```
+
+### five_minute_utilization()
+
+Returns the five-minute average utilization as a percentage between `0.0` and `1.0`.
+
+``` ruby
+  meter = Metriks.utilization_timer('requests')
+  puts "utilization: #{meter.five_minute_utilization * 100}%"
+```
+
+### fifteen_minute_utilization()
+
+Returns the fifteen-minute average utilization as a percentage between `0.0` and `1.0`.
+
+``` ruby
+  meter = Metriks.utilization_timer('requests')
+  puts "utilization: #{meter.fifteen_minute_utilization * 100}%"
+```
+
+### mean_utilization()
+
+Returns the mean (average) utilization as a percentage between `0.0` and `1.0`
+since the process started.
+
+``` ruby
+  meter = Metriks.utilization_timer('requests')
+  puts "utilization: #{meter.mean_utilization * 100}%"
+```
+
+
+# Reporter Overview
+
+How to get metrics out of the process.
+
+## Graphite Reporter
+
+Sends metrics to Graphite every 60 seconds.
+
+``` ruby
+  reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
+  reporter.start
+```
+
+
+## Logger Reporter
+
+Send metrics to a logger every 60 seconds.
+
+``` ruby
+  reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
+  reporter.start
+```
+
+
+## Librato Metrics Reporter
+
+Send metrics to Librato Metrics every 60 seconds.
+
+``` ruby
+  reporter = Metriks::Reporter::LibratoMetrics.new('email', 'token')
+  reporter.start
+```
+
+
+## Proc Title Reporter
+
+Provides a simple way to get up-to-date statistics from a process by
+updating the proctitle every 5 seconds (default).
+
+``` ruby
+  reporter = Metriks::Reporter::ProcTitle.new :interval => 5
+  reporter.add 'reqs', 'sec' do
+    Metriks.meter('rack.requests').one_minute_rate
+  end
+  reporter.start
+```
+
+will display:
+
+```
+501      17015 26.0  1.9 416976 246956 ?       Ss   18:54  11:43 thin reqs: 273.3/sec
+```
+
+## Sematext Metrics Reporter
+
+[metriks-sematext](https://github.com/sematext/metriks-sematext) gem provides reporter for sending metrics to [SPM](http://sematext.com/spm/index.html).
+
+# Application Server Configuration
+
+Depending on how your application server operates, you may need to configure how reporters are created. Please look at [Troubleshooting](https://github.com/eric/metriks/wiki/Troubleshooting) for more information.
+
+# Plans
+
+An incomplete list of things I would like to see added:
+
+* Rack middleware to measure utilization, throughput and worker time
+* Basic reporters:
+  * Rack endpoint returning JSON
+  * [Statsd](https://github.com/etsy/statsd) reporter
+* Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)
+
+
+# Credits
+
+Most of the inspiration for this project comes from Coda Hale's amazing
+[Metrics, Metrics Everywhere][metrics-talk] talk at CodeConf and his sweet
+[Metrics][metrics] Java Library.
+
+[metrics-talk]: http://pivotallabs.com/talks/139-metrics-metrics-everywhere
+[metrics]: https://github.com/codahale/metrics
+
+
+# License
+
+Copyright (c) 2012 Eric Lindvall
+
+Published under the MIT License, see LICENSE
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..99da70b
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,150 @@
+require 'rubygems'
+require 'rake'
+require 'date'
+
+#############################################################################
+#
+# Helper functions
+#
+#############################################################################
+
+def name
+  @name ||= Dir['*.gemspec'].first.split('.').first
+end
+
+def version
+  line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
+  line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
+end
+
+def date
+  Date.today.to_s
+end
+
+def rubyforge_project
+  name
+end
+
+def gemspec_file
+  "#{name}.gemspec"
+end
+
+def gem_file
+  "#{name}-#{version}.gem"
+end
+
+def replace_header(head, header_name)
+  head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
+end
+
+#############################################################################
+#
+# Standard tasks
+#
+#############################################################################
+
+task :default => :test
+
+require 'rake/testtask'
+Rake::TestTask.new(:test) do |test|
+  test.libs << 'lib' << 'test'
+  test.pattern = 'test/**/*_test.rb'
+  test.verbose = true
+end
+
+desc "Generate RCov test coverage and open in your browser"
+task :coverage do
+  require 'rcov'
+  sh "rm -fr coverage"
+  sh "rcov test/*_test.rb"
+  sh "open coverage/index.html"
+end
+
+require 'rake/rdoctask'
+Rake::RDocTask.new do |rdoc|
+  rdoc.rdoc_dir = 'rdoc'
+  rdoc.title = "#{name} #{version}"
+  rdoc.rdoc_files.include('README*')
+  rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+desc "Open an irb session preloaded with this library"
+task :console do
+  sh "irb -rubygems -r ./lib/#{name}.rb"
+end
+
+#############################################################################
+#
+# Custom tasks (add your own tasks here)
+#
+#############################################################################
+
+
+
+#############################################################################
+#
+# Packaging tasks
+#
+#############################################################################
+
+desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
+task :release => :build do
+  unless `git branch` =~ /^\* master$/
+    puts "You must be on the master branch to release!"
+    exit!
+  end
+  sh "git commit --allow-empty -a -m 'Release #{version}'"
+  sh "git tag v#{version}"
+  sh "git push origin master"
+  sh "git push origin v#{version}"
+  sh "gem push pkg/#{name}-#{version}.gem"
+end
+
+desc "Build #{gem_file} into the pkg directory"
+task :build => :gemspec do
+  sh "mkdir -p pkg"
+  sh "gem build #{gemspec_file}"
+  sh "mv #{gem_file} pkg"
+end
+
+desc "Generate #{gemspec_file}"
+task :gemspec => :validate do
+  # read spec file and split out manifest section
+  spec = File.read(gemspec_file)
+  head, manifest, tail = spec.split("  # = MANIFEST =\n")
+
+  # replace name version and date
+  replace_header(head, :name)
+  replace_header(head, :version)
+  replace_header(head, :date)
+  #comment this out if your rubyforge_project has a different name
+  replace_header(head, :rubyforge_project)
+
+  # determine file list from git ls-files
+  files = `git ls-files`.
+    split("\n").
+    sort.
+    reject { |file| file =~ /^\./ }.
+    reject { |file| file =~ /^(rdoc|pkg)/ }.
+    map { |file| "    #{file}" }.
+    join("\n")
+
+  # piece file back together and write
+  manifest = "  s.files = %w[\n#{files}\n  ]\n"
+  spec = [head, manifest, tail].join("  # = MANIFEST =\n")
+  File.open(gemspec_file, 'w') { |io| io.write(spec) }
+  puts "Updated #{gemspec_file}"
+end
+
+desc "Validate #{gemspec_file}"
+task :validate do
+  libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
+  unless libfiles.empty?
+    puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
+    exit!
+  end
+  unless Dir['VERSION*'].empty?
+    puts "A `VERSION` file at root level violates Gem best practices."
+    exit!
+  end
+end
diff --git a/benchmark/samplers.rb b/benchmark/samplers.rb
new file mode 100644
index 0000000..bc2fa4c
--- /dev/null
+++ b/benchmark/samplers.rb
@@ -0,0 +1,92 @@
+#!/usr/bin/env ruby
+
+require 'benchmark'
+require 'metriks'
+require 'rbtree'
+require 'avl_tree'
+require 'red_black_tree'
+
+fib_times = ARGV[0] ? ARGV[0].to_i : 10
+iter      = ARGV[1] ? ARGV[1].to_i : 100000
+
+
+class TimerBenchmarker
+  attr_reader :iter, :fib_times
+
+  def initialize(fib_times, iter)
+    @fib_times = fib_times
+    @iter      = iter
+    @mapping   = { :plain => nil }
+  end
+
+  def measure(key, value)
+    @mapping[key] = value
+  end
+
+  def run
+    @results = {}
+    @mapping.each do |key, timer|
+      @results[key] = Benchmark.realtime do
+        if timer
+          for i in 1..iter
+            timer.time do
+              fib(fib_times)
+            end
+          end
+        else
+          for i in 1..iter
+            fib(fib_times)
+          end
+        end
+      end
+    end
+    report
+  end
+
+  def report
+    results = @results.sort_by { |k,v| v }
+    results.each_with_index do |(name, time), idx|
+      puts "%23s: %f secs %f secs/call" % [
+        name, time, time / iter
+      ]
+
+      if idx > 0
+        prev_name, prev_time = results[idx - 1]
+        puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
+          (time - prev_time) / prev_time * 100, prev_name,
+          (time - prev_time) / iter
+        ]
+      end
+
+      if idx > 1
+        plain_name, plain_time = results[0]
+        puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
+          (time - plain_time) / plain_time * 100, plain_name,
+          (time - plain_time) / iter
+        ]
+      end
+    end
+  end
+
+  def fib(n)
+    n < 2 ? n : fib(n-1) + fib(n-2)
+  end
+end
+
+reporter = TimerBenchmarker.new(fib_times, iter)
+
+reporter.measure :uniform, Metriks::Timer.new(Metriks::Histogram.new_uniform)
+
+reporter.measure :exponential, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
+  Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RBTree.new))
+
+reporter.measure :exponential_avl, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
+  Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, AVLTree.new))
+
+reporter.measure :exponential_red_black, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
+  Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RedBlackTree.new))
+
+puts "fib(#{fib_times}): #{iter} iterations"
+puts "-" * 50
+
+reporter.run
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
new file mode 100644
index 0000000..59c8af5
Binary files /dev/null and b/checksums.yaml.gz differ
diff --git a/lib/metriks.rb b/lib/metriks.rb
new file mode 100644
index 0000000..b09f0cb
--- /dev/null
+++ b/lib/metriks.rb
@@ -0,0 +1,35 @@
+
+module Metriks
+  VERSION = '0.9.9.6'
+
+  def self.get(name)
+    Metriks::Registry.default.get(name)
+  end
+
+  def self.counter(name)
+    Metriks::Registry.default.counter(name)
+  end
+
+  def self.gauge(name, callable = nil, &block)
+    Metriks::Registry.default.gauge(name, callable, &block)
+  end
+
+  def self.timer(name)
+    Metriks::Registry.default.timer(name)
+  end
+
+  def self.utilization_timer(name)
+    Metriks::Registry.default.utilization_timer(name)
+  end
+
+  def self.meter(name)
+    Metriks::Registry.default.meter(name)
+  end
+
+  def self.histogram(name)
+    Metriks::Registry.default.histogram(name)
+  end
+end
+
+require 'metriks/registry'
+require 'metriks/reporter/proc_title'
diff --git a/lib/metriks/counter.rb b/lib/metriks/counter.rb
new file mode 100644
index 0000000..93b9662
--- /dev/null
+++ b/lib/metriks/counter.rb
@@ -0,0 +1,44 @@
+require 'atomic'
+
+module Metriks
+  # Public: Counters are one of the simplest metrics whose only operations
+  # are increment and decrement.
+  class Counter
+    # Public: Initialize a new Counter.
+    def initialize
+      @count = Atomic.new(0)
+    end
+
+    # Public: Reset the counter back to 0
+    #
+    # Returns nothing.
+    def clear
+      @count.value = 0
+    end
+
+    # Public: Increment the counter.
+    #
+    # incr - The value to add to the counter.
+    #
+    # Returns nothing.
+    def increment(incr = 1)
+      @count.update { |v| v + incr }
+    end
+
+    # Public: Decrement the counter.
+    #
+    # decr - The value to subtract from the counter.
+    #
+    # Returns nothing.
+    def decrement(decr = 1)
+      @count.update { |v| v - decr }
+    end
+
+    # Public: The current count.
+    #
+    # Returns the count.
+    def count
+      @count.value
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/ewma.rb b/lib/metriks/ewma.rb
new file mode 100644
index 0000000..d169dfe
--- /dev/null
+++ b/lib/metriks/ewma.rb
@@ -0,0 +1,63 @@
+require 'atomic'
+
+module Metriks
+  class EWMA
+    INTERVAL = 5.0
+    SECONDS_PER_MINUTE = 60.0
+
+    ONE_MINUTE      = 1
+    FIVE_MINUTES    = 5
+    FIFTEEN_MINUTES = 15
+
+    M1_ALPHA  = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / ONE_MINUTE)
+    M5_ALPHA  = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIVE_MINUTES)
+    M15_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIFTEEN_MINUTES)
+
+    def self.new_m1
+      new(M1_ALPHA, INTERVAL)
+    end
+
+    def self.new_m5
+      new(M5_ALPHA, INTERVAL)
+    end
+
+    def self.new_m15
+      new(M15_ALPHA, INTERVAL)
+    end
+
+    def initialize(alpha, interval)
+      @alpha    = alpha
+      @interval = interval
+
+      @initialized = false
+      @rate        = Atomic.new(0.0)
+      @uncounted   = Atomic.new(0)
+    end
+
+    def clear
+      @initialized = false
+      @rate.value = 0.0
+      @uncounted.value = 0
+    end
+
+    def update(value)
+      @uncounted.update { |v| v + value }
+    end
+
+    def tick
+      count = @uncounted.swap(0)
+      instant_rate = count / @interval.to_f
+
+      if @initialized
+        @rate.update { |v| v + @alpha * (instant_rate - v) }
+      else
+        @rate.value = instant_rate
+        @initialized = true
+      end
+    end
+
+    def rate
+      @rate.value
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/exponentially_decaying_sample.rb b/lib/metriks/exponentially_decaying_sample.rb
new file mode 100644
index 0000000..05da0f5
--- /dev/null
+++ b/lib/metriks/exponentially_decaying_sample.rb
@@ -0,0 +1,102 @@
+require 'atomic'
+require 'red_black_tree'
+require 'metriks/snapshot'
+
+module Metriks
+  class ExponentiallyDecayingSample
+    RESCALE_THRESHOLD = 60 * 60 # 1 hour
+
+    def initialize(reservoir_size, alpha, values = nil)
+      @values = values || RedBlackTree.new
+      @count = Atomic.new(0)
+      @next_scale_time = Atomic.new(0)
+      @alpha = alpha
+      @reservoir_size = reservoir_size
+      @mutex = Mutex.new
+      clear
+    end
+
+    def clear
+      @mutex.synchronize do
+        @values.clear
+        @count.value = 0
+        @next_scale_time.value = Time.now + RESCALE_THRESHOLD
+        @start_time = Time.now
+      end
+    end
+
+    def size
+      count = @count.value
+      count < @reservoir_size ? count : @reservoir_size
+    end
+
+    def snapshot
+      @mutex.synchronize do
+        Snapshot.new(@values.values)
+      end
+    end
+
+    def update(value, timestamp = Time.now)
+      @mutex.synchronize do
+        priority = weight(timestamp - @start_time) / rand
+        priority = Float::MAX if priority.infinite?
+        new_count = @count.update { |v| v + 1 }
+
+        if priority.nan?
+          warn "ExponentiallyDecayingSample found priority of NaN. timestamp: #{timestamp.to_f} start_time: #{@start_time.to_f}"
+          return
+        end
+
+        if new_count <= @reservoir_size
+          @values[priority] = value
+        else
+          first_priority = @values.first[0]
+          if first_priority < priority
+            unless @values[priority]
+              @values[priority] = value
+
+              until @values.delete(first_priority)
+                first_priority = @values.first[0]
+              end
+            end
+          end
+        end
+      end
+
+      now = Time.new
+      next_time = @next_scale_time.value
+      if now >= next_time
+        rescale(now, next_time)
+      end
+    end
+
+    def weight(time)
+      Math.exp(@alpha * time)
+    end
+
+    def rescale(now, next_time)
+      if @next_scale_time.compare_and_swap(next_time, now + RESCALE_THRESHOLD)
+        @mutex.synchronize do
+          old_start_time = @start_time
+          @start_time = Time.now
+          @values.keys.each do |key|
+            value = @values.delete(key)
+            new_key = key * Math.exp(- at alpha * (@start_time - old_start_time))
+
+            if key.nan?
+              warn "ExponentiallyDecayingSample found a key of NaN. old_start_time: #{old_start_time.to_f} start_time: #{@start_time.to_f}"
+              next
+            end
+
+            if new_key.nan?
+              warn "ExponentiallyDecayingSample found a new_key of NaN. key: #{key} old_start_time: #{old_start_time.to_f} start_time: #{@start_time.to_f}"
+              next
+            end
+
+            @values[new_key] = value
+          end
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/gauge.rb b/lib/metriks/gauge.rb
new file mode 100644
index 0000000..19c222b
--- /dev/null
+++ b/lib/metriks/gauge.rb
@@ -0,0 +1,27 @@
+require 'atomic'
+
+module Metriks
+  class Gauge
+    # Public: Initialize a new Gauge.
+    def initialize(callable = nil, &block)
+      @gauge = Atomic.new(nil)
+      @callback = callable || block
+    end
+
+    # Public: Set a new value.
+    #
+    # val - The new value.
+    #
+    # Returns nothing.
+    def set(val)
+      @gauge.value = val
+    end
+
+    # Public: The current value.
+    #
+    # Returns the gauge value.
+    def value
+      @callback ? @callback.call : @gauge.value
+    end
+  end
+end
diff --git a/lib/metriks/histogram.rb b/lib/metriks/histogram.rb
new file mode 100644
index 0000000..7237c50
--- /dev/null
+++ b/lib/metriks/histogram.rb
@@ -0,0 +1,112 @@
+require 'atomic'
+require 'metriks/uniform_sample'
+require 'metriks/exponentially_decaying_sample'
+
+module Metriks
+  class Histogram
+    DEFAULT_SAMPLE_SIZE = 1028
+    DEFAULT_ALPHA = 0.015
+
+    def self.new_uniform
+      new(Metriks::UniformSample.new(DEFAULT_SAMPLE_SIZE))
+    end
+
+    def self.new_exponentially_decaying
+      new(Metriks::ExponentiallyDecayingSample.new(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA))
+    end
+
+    def initialize(sample)
+      @sample   = sample
+      @count    = Atomic.new(0)
+      @min      = Atomic.new(nil)
+      @max      = Atomic.new(nil)
+      @sum      = Atomic.new(0)
+      @variance = Atomic.new([ -1, 0 ])
+    end
+
+    def clear
+      @sample.clear
+      @count.value = 0
+      @min.value = nil
+      @max.value = nil
+      @sum.value = 0
+      @variance.value = [ -1, 0 ]
+    end
+
+    def update(value)
+      @count.update { |v| v + 1 }
+      @sample.update(value)
+      self.max = value
+      self.min = value
+      @sum.update { |v| v + value }
+      update_variance(value)
+    end
+
+    def snapshot
+      @sample.snapshot
+    end
+
+    def count
+      @count.value
+    end
+
+    def max
+      count > 0 ? @max.value : 0.0
+    end
+
+    def min
+      count > 0 ? @min.value : 0.0
+    end
+
+    def mean
+      count > 0 ? @sum.value / count : 0.0
+    end
+
+    def stddev
+      count > 0 ? variance ** 0.5 : 0.0
+    end
+
+    def variance
+      count <= 1 ? 0.0 : @variance.value[1] / (count - 1)
+    end
+
+    def max=(potential_max)
+      done = false
+
+      while !done
+        current_max = @max.value
+        done = (!current_max.nil? && current_max >= potential_max) || @max.compare_and_swap(current_max, potential_max)
+      end
+    end
+
+    def min=(potential_min)
+      done = false
+
+      while !done
+        current_min = @min.value
+        done = (!current_min.nil? && current_min <= potential_min) || @min.compare_and_swap(current_min, potential_min)
+      end
+    end
+
+    def update_variance(value)
+      @variance.update do |old_values|
+        new_values = Array.new(2)
+        if old_values[0] == -1
+          new_values[0] = value
+          new_values[1] = 0
+        else
+          old_m = old_values[0]
+          old_s = old_values[1]
+
+          new_m = old_m + ((value - old_m) / count)
+          new_s = old_s + ((value - old_m) * (value - new_m))
+
+          new_values[0] = new_m
+          new_values[1] = new_s
+        end
+
+        new_values
+      end
+    end
+  end
+end
diff --git a/lib/metriks/meter.rb b/lib/metriks/meter.rb
new file mode 100644
index 0000000..3e4d64d
--- /dev/null
+++ b/lib/metriks/meter.rb
@@ -0,0 +1,85 @@
+require 'atomic'
+
+require 'metriks/ewma'
+
+module Metriks
+  class Meter
+    TICK_INTERVAL = 5.0
+
+    def initialize(averager_klass = Metriks::EWMA)
+      @count = Atomic.new(0)
+      @start_time = Time.now.to_f
+      @last_tick = Atomic.new(@start_time)
+
+      @m1_rate  = averager_klass.new_m1
+      @m5_rate  = averager_klass.new_m5
+      @m15_rate = averager_klass.new_m15
+    end
+
+    def clear
+      @count.value = 0
+      @start_time = Time.now.to_f
+      @last_tick.value = @start_time
+      @m1_rate.clear
+      @m5_rate.clear
+      @m15_rate.clear
+    end
+
+    def tick
+      @m1_rate.tick
+      @m5_rate.tick
+      @m15_rate.tick
+    end
+
+    def tick_if_nessesary
+      old_tick = @last_tick.value
+      new_tick = Time.new.to_f
+      age = new_tick - old_tick
+      if age > TICK_INTERVAL && @last_tick.compare_and_swap(old_tick, new_tick)
+        required_ticks = age / TICK_INTERVAL
+        required_ticks.to_i.times do
+          tick
+        end
+      end
+    end
+
+    def mark(val = 1)
+      tick_if_nessesary
+      @count.update { |v| v + val }
+      @m1_rate.update(val)
+      @m5_rate.update(val)
+      @m15_rate.update(val)
+    end
+
+    def count
+      @count.value
+    end
+
+    def one_minute_rate
+      tick_if_nessesary
+      @m1_rate.rate
+    end
+
+    def five_minute_rate
+      tick_if_nessesary
+      @m5_rate.rate
+    end
+
+    def fifteen_minute_rate
+      tick_if_nessesary
+      @m15_rate.rate
+    end
+
+    def mean_rate
+      if count == 0
+        return 0.0
+      else
+        elapsed = Time.now.to_f - @start_time
+        count / elapsed
+      end
+    end
+
+    def stop
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/registry.rb b/lib/metriks/registry.rb
new file mode 100644
index 0000000..480ce02
--- /dev/null
+++ b/lib/metriks/registry.rb
@@ -0,0 +1,207 @@
+require 'metriks/counter'
+require 'metriks/timer'
+require 'metriks/utilization_timer'
+require 'metriks/meter'
+require 'metriks/gauge'
+
+module Metriks
+  # Public: A collection of metrics
+  class Registry
+    # Public: The default registry for the process.
+    #
+    # Returns the default Registry for the process.
+    def self.default
+      @default ||= new
+    end
+
+    # Public: Initializes a new Registry.
+    def initialize
+      @mutex = Mutex.new
+      @metrics = {}
+    end
+
+    # Public: Clear all of the metrics in the Registry. This ensures all
+    # metrics that have been added are stopped.
+    #
+    # Returns nothing.
+    def clear
+      @mutex.synchronize do
+        @metrics.each do |key, metric|
+          metric.stop if metric.respond_to?(:stop)
+        end
+
+        @metrics = {}
+      end
+    end
+
+    # Public: Clear all of the metrics in the Registry. This has the same
+    # effect as calling #clear.
+    #
+    # Returns nothing.
+    def stop
+      clear
+    end
+
+    # Public: Iterate over all of the counters.
+    #
+    # Examples
+    #
+    #   registry.each do |name, metric|
+    #     puts name
+    #   end
+    #
+    # Returns nothing.
+    def each(&block)
+      metrics = @mutex.synchronize do
+        @metrics.dup
+      end
+
+      metrics.each(&block)
+    end
+
+    # Public: Fetch or create a new counter metric. Counters are one of the
+    # simplest metrics whose only operations are increment and decrement.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.counter('method.calls')
+    #
+    # Returns the Metriks::Counter identified by the name.
+    def counter(name)
+      add_or_get(name, Metriks::Counter)
+    end
+
+    # Public: Fetch or create a new gauge metric.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.gauge('disk_space.used') { 1 }
+    #
+    # Returns the Metriks::Gauge identified by the name.
+    def gauge(name, callable = nil, &block)
+      add_or_get(name, Metriks::Gauge) do
+        Metriks::Gauge.new(callable, &block)
+      end
+    end
+
+    # Public: Fetch or create a new meter metric. Meters are a counter that
+    # tracks throughput along with the count.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.meter('resque.calls')
+    #
+    # Returns the Metriks::Meter identified by the name.
+    def meter(name)
+      add_or_get(name, Metriks::Meter)
+    end
+
+    # Public: Fetch or create a new timer metric. Timers provide the means to
+    # time the execution of a method including statistics on the number of
+    # invocations, average length of time, throughput.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.timer('resque.worker')
+    #
+    # Returns the Metriks::Timer identified by the name.
+    def timer(name)
+      add_or_get(name, Metriks::Timer)
+    end
+
+    # Public: Fetch or create a new utilization timer metric.
+    #
+    # Utilization timers are a specialized version of a timer that calculate
+    # the percentage of wall-clock time (between 0 and 1) that was spent in
+    # the method. This metric is most valuable in a single-threaded
+    # environment where a processes is waiting on an external resource like a
+    # message queue or HTTP server.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.utilization_timer('rack.utilization')
+    #
+    # Returns the Metriks::UtilizationTimer identified by the name.
+    def utilization_timer(name)
+      add_or_get(name, Metriks::UtilizationTimer)
+    end
+
+    # Public: Fetch or create a new histogram metric. Histograms record values
+    # and expose statistics about the distribution of the data like median and
+    # 95th percentile.
+    #
+    # name - The String name of the metric to define or fetch
+    #
+    # Examples
+    #
+    #   registry.histogram('backlog.wait')
+    #
+    # Returns the Metriks::Histogram identified by the name.
+    def histogram(name)
+      add_or_get(name, Metriks::Histogram) do
+        Metriks::Histogram.new_exponentially_decaying
+      end
+    end
+
+    # Public: Fetch an existing metric.
+    #
+    # name - The String name of the metric to fetch
+    #
+    # Examples
+    #
+    #   registry.get('rack.utilization')
+    #
+    # Returns the metric or nil.
+    def get(name)
+      @mutex.synchronize do
+        @metrics[name]
+      end
+    end
+
+    # Public: Add a new metric.
+    #
+    # name - The String name of the metric to add
+    # metric - The metric instance to add
+    #
+    # Examples
+    #
+    #   registry.add('method.calls', Metriks::Counter.new)
+    #
+    # Returns nothing.
+    # Raises RuntimeError if the metric name is already defined
+    def add(name, metric)
+      @mutex.synchronize do
+        if @metrics[name]
+          raise "Metric '#{name}' already defined"
+        else
+          @metrics[name] = metric
+        end
+      end
+    end
+
+    protected
+    def add_or_get(name, klass, &create_metric)
+      @mutex.synchronize do
+        if metric = @metrics[name]
+          if !metric.is_a?(klass)
+            raise "Metric already defined as '#{metric.class}'"
+          else
+            return metric
+          end
+        else
+          @metrics[name] = create_metric ? create_metric.call : klass.new
+        end
+      end
+    end
+  end
+end
diff --git a/lib/metriks/reporter/graphite.rb b/lib/metriks/reporter/graphite.rb
new file mode 100644
index 0000000..cc2a347
--- /dev/null
+++ b/lib/metriks/reporter/graphite.rb
@@ -0,0 +1,119 @@
+require 'socket'
+
+module Metriks::Reporter
+  class Graphite
+    attr_reader :host, :port
+
+    def initialize(host, port, options = {})
+      @host = host
+      @port = port
+
+      @prefix = options[:prefix]
+
+      @registry  = options[:registry] || Metriks::Registry.default
+      @interval  = options[:interval] || 60
+      @on_error  = options[:on_error] || proc { |ex| }
+    end
+
+    def socket
+      @socket = nil if @socket && @socket.closed?
+      @socket ||= TCPSocket.new(@host, @port)
+    end
+
+    def start
+      @thread ||= Thread.new do
+        loop do
+          sleep @interval
+
+          Thread.new do
+            begin
+              write
+            rescue Exception => ex
+              @on_error[ex] rescue nil
+            end
+          end
+        end
+      end
+    end
+
+    def stop
+      @thread.kill if @thread
+      @thread = nil
+    end
+
+    def restart
+      stop
+      start
+    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
+      end
+    end
+
+    def write_metric(base_name, metric, keys, snapshot_keys = [])
+      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")
+      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
+    rescue Errno::EPIPE
+      socket.close
+    end
+  end
+end
diff --git a/lib/metriks/reporter/librato_metrics.rb b/lib/metriks/reporter/librato_metrics.rb
new file mode 100644
index 0000000..bdcaa2c
--- /dev/null
+++ b/lib/metriks/reporter/librato_metrics.rb
@@ -0,0 +1,173 @@
+require 'metriks/time_tracker'
+require 'net/https'
+
+module Metriks::Reporter
+  class LibratoMetrics
+    attr_accessor :prefix, :source
+
+    def initialize(email, token, options = {})
+      @email = email
+      @token = token
+
+      @prefix = options[:prefix]
+      @source = options[:source]
+
+      @registry  = options[:registry] || Metriks::Registry.default
+      @time_tracker = Metriks::TimeTracker.new(options[:interval] || 60)
+      @on_error  = options[:on_error] || proc { |ex| }
+    end
+
+    def start
+      @thread ||= Thread.new do
+        loop do
+          @time_tracker.sleep
+
+          Thread.new do
+            begin
+              write
+            rescue Exception => ex
+              @on_error[ex] rescue nil
+            end
+          end
+        end
+      end
+    end
+
+    def stop
+      @thread.kill if @thread
+      @thread = nil
+    end
+
+    def restart
+      stop
+      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)
+      req.basic_auth(@email, @token)
+      req.set_form_data(data)
+
+      http = Net::HTTP.new(url.host, url.port)
+      http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+      http.use_ssl = true
+      store = OpenSSL::X509::Store.new
+      store.set_default_paths
+      http.cert_store = store
+
+      case res = http.start { |http| http.request(req) }
+      when Net::HTTPSuccess, Net::HTTPRedirection
+        # OK
+      else
+        res.error!
+      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
+
+      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)
+
+        results << {
+          :type => "gauge",
+          :name => "#{base_name}.#{name}",
+          :source => @source,
+          :measure_time => time,
+          :value => value
+        }
+      end
+
+      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
+          }
+        end
+      end
+
+      results
+    end
+  end
+end
diff --git a/lib/metriks/reporter/logger.rb b/lib/metriks/reporter/logger.rb
new file mode 100644
index 0000000..5904f9b
--- /dev/null
+++ b/lib/metriks/reporter/logger.rb
@@ -0,0 +1,129 @@
+require 'logger'
+require 'metriks/time_tracker'
+
+module Metriks::Reporter
+  class Logger
+    attr_accessor :prefix, :log_level, :logger
+
+    def initialize(options = {})
+      @logger    = options[:logger]    || ::Logger.new(STDOUT)
+      @log_level = options[:log_level] || ::Logger::INFO
+      @prefix    = options[:prefix]    || 'metriks:'
+
+      @registry     = options[:registry] || Metriks::Registry.default
+      @time_tracker = Metriks::TimeTracker.new(options[:interval] || 60)
+      @on_error     = options[:on_error] || proc { |ex| }
+    end
+
+    def start
+      @thread ||= Thread.new do
+        loop do
+          @time_tracker.sleep
+
+          begin
+            write
+          rescue Exception => ex
+            @on_error[ex] rescue nil
+          end
+        end
+      end
+    end
+
+    def stop
+      @thread.kill if @thread
+      @thread = nil
+    end
+
+    def restart
+      stop
+      start
+    end
+
+    def flush
+      if !@last_write || @last_write.min != Time.now.min
+        write
+      end
+    end
+
+    def write
+      @last_write = Time.now
+
+      @registry.each do |name, metric|
+        case metric
+        when Metriks::Meter
+          log_metric name, 'meter', metric, [
+            :count, :one_minute_rate, :five_minute_rate,
+            :fifteen_minute_rate, :mean_rate
+          ]
+        when Metriks::Counter
+          log_metric name, 'counter', metric, [
+            :count
+          ]
+        when Metriks::Gauge
+          log_metric name, 'gauge', metric, [
+            :value
+          ]
+        when Metriks::UtilizationTimer
+          log_metric name, 'utilization_timer', 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
+          log_metric name, 'timer', metric, [
+            :count, :one_minute_rate, :five_minute_rate,
+            :fifteen_minute_rate, :mean_rate,
+            :min, :max, :mean, :stddev
+          ], [
+            :median, :get_95th_percentile
+          ]
+        when Metriks::Histogram
+          log_metric name, 'histogram', metric, [
+            :count, :min, :max, :mean, :stddev
+          ], [
+            :median, :get_95th_percentile
+          ]
+        end
+      end
+    end
+
+    def extract_from_metric(metric, *keys)
+      keys.flatten.collect do |key|
+        name = key.to_s.gsub(/^get_/, '')
+        [ { name => metric.send(key) } ]
+      end
+    end
+
+    def log_metric(name, type, metric, keys, snapshot_keys = [])
+      message = []
+
+      message << @prefix if @prefix
+      message << { :time => Time.now.to_i }
+
+      message << { :name => name }
+      message << { :type => type }
+      message += extract_from_metric(metric, keys)
+
+      unless snapshot_keys.empty?
+        snapshot = metric.snapshot
+        message += extract_from_metric(snapshot, snapshot_keys)
+      end
+
+      @logger.add(@log_level, format_message(message))
+    end
+
+    def format_message(args)
+      args.map do |arg|
+        case arg
+        when Hash then arg.map { |name, value| "#{name}=#{format_message([value])}" }
+        when Array then format_message(arg)
+        else arg
+        end
+      end.join(' ')
+    end
+  end
+end
diff --git a/lib/metriks/reporter/proc_title.rb b/lib/metriks/reporter/proc_title.rb
new file mode 100644
index 0000000..0ed3eb0
--- /dev/null
+++ b/lib/metriks/reporter/proc_title.rb
@@ -0,0 +1,59 @@
+module Metriks::Reporter
+  class ProcTitle
+    def initialize(options = {})
+      @rounding = options[:rounding] || 1
+      @prefix   = options[:prefix]   || $0.dup
+
+      @interval  = options[:interval] || 5
+      @on_error  = options[:on_error] || proc { |ex| }
+
+      @metrics  = []
+    end
+
+    def add(name, suffix = nil, &block)
+      @metrics << [ name, suffix, block ]
+    end
+
+    def empty?
+      @metrics.empty?
+    end
+
+    def start
+      @thread ||= Thread.new do
+        loop do
+          begin
+            unless @metrics.empty?
+              title = generate_title
+              if title && !title.empty?
+                $0 = "#{@prefix} #{title}"
+              end
+            end
+          rescue Exception => ex
+            @on_error[ex] rescue nil
+          end
+          sleep @interval
+        end
+      end
+    end
+
+    def stop
+      @thread.kill if @thread
+      @thread = nil
+    end
+
+    def restart
+      stop
+      start
+    end
+
+    protected
+    def generate_title
+      @metrics.collect do |name, suffix, block|
+        val = block.call
+        val = "%.#{@rounding}f" % val if val.is_a?(Float)
+
+        "#{name}: #{val}#{suffix}"
+      end.join(' ')
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/reporter/riemann.rb b/lib/metriks/reporter/riemann.rb
new file mode 100644
index 0000000..bdab081
--- /dev/null
+++ b/lib/metriks/reporter/riemann.rb
@@ -0,0 +1,119 @@
+module Metriks::Reporter
+  class Riemann
+    require 'riemann/client'
+
+    attr_accessor :client
+    def initialize(options = {})
+      @client = ::Riemann::Client.new(
+        :host => options[:host],
+        :port => options[:port]
+      )
+      @registry = options[:registry] || Metriks::Registry.default
+      @interval = options[:interval] || 60
+      @on_error = options[:on_error] || proc { |ex| }
+      
+      @default_event = options[:default_event] || {}
+      @default_event[:ttl] ||= @interval * 1.5
+    end
+
+    def start
+      @thread ||= Thread.new do
+        loop do
+          sleep @interval
+          
+          Thread.new do
+            begin
+              write
+            rescue Exception => ex
+              @on_error[ex] rescue nil
+            end
+          end
+        end
+      end
+    end
+
+    def stop
+      @thread.kill if @thread
+      @thread = nil
+    end
+
+    def restart
+      stop
+      start
+    end
+
+    def flush
+      # Is this supposed to take interval into account? --aphyr
+      if !@last_write || @last_write.min != Time.now.min
+        write
+      end
+    end
+
+    def write
+      @last_write = Time.now
+
+      @registry.each do |name, metric|
+        case metric
+        when Metriks::Meter
+          send_metric name, 'meter', metric, [
+            :count, :one_minute_rate, :five_minute_rate,
+            :fifteen_minute_rate, :mean_rate
+          ]
+        when Metriks::Counter
+          send_metric name, 'counter', metric, [
+            :count
+          ]
+        when Metriks::Gauge
+          send_metric name, 'gauge', metric, [
+            :value
+          ]
+        when Metriks::UtilizationTimer
+          send_metric name, 'utilization_timer', 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
+          send_metric name, 'timer', metric, [
+            :count, :one_minute_rate, :five_minute_rate,
+            :fifteen_minute_rate, :mean_rate,
+            :min, :max, :mean, :stddev
+          ], [
+            :median, :get_95th_percentile
+          ]
+        when Metriks::Histogram
+          send_metric name, 'histogram', metric, [
+            :count, :min, :max, :mean, :stddev
+          ], [
+            :median, :get_95th_percentile
+          ]
+        end
+      end
+    end
+
+    def send_metric(name, type, metric, keys, snapshot_keys = [])
+      keys.each do |key|
+        @client << @default_event.merge(
+          :service => "#{name} #{key}",
+          :metric => metric.send(key),
+          :tags => [type]
+        )
+      end
+
+      unless snapshot_keys.empty?
+        snapshot = metric.snapshot
+        snapshot_keys.each do |key|
+          @client << @default_event.merge(
+            :service => "#{name} #{key}",
+            :metric => snapshot.send(key),
+            :tags => [type]
+          )
+        end
+      end
+    end
+  end
+end
diff --git a/lib/metriks/simple_moving_average.rb b/lib/metriks/simple_moving_average.rb
new file mode 100644
index 0000000..fb0d08f
--- /dev/null
+++ b/lib/metriks/simple_moving_average.rb
@@ -0,0 +1,60 @@
+require 'atomic'
+
+module Metriks
+  class SimpleMovingAverage
+    INTERVAL = 5.0
+    SECONDS_PER_MINUTE = 60.0
+
+    ONE_MINUTE      = 1
+    FIVE_MINUTES    = 5
+    FIFTEEN_MINUTES = 15
+
+    def self.new_m1
+      new(ONE_MINUTE * SECONDS_PER_MINUTE, INTERVAL)
+    end
+
+    def self.new_m5
+      new(FIVE_MINUTES * SECONDS_PER_MINUTE, INTERVAL)
+    end
+
+    def self.new_m15
+      new(FIFTEEN_MINUTES * SECONDS_PER_MINUTE, INTERVAL)
+    end
+
+    def initialize(duration, interval)
+      @interval = interval
+      @duration = duration
+
+      @values = Array.new((duration / interval).to_i) { Atomic.new(nil) }
+      @index  = Atomic.new(0)
+    end
+
+    def clear
+      @values.each do |value|
+        value.value = nil
+      end
+      @index.value = 0
+    end
+
+    def update(value)
+      @values[@index.value].update { |v| v ? v + value : value }
+    end
+
+    def tick
+      @index.update { |v| v < @values.length - 1 ? v + 1 : 0 }
+    end
+
+    def rate
+      num, count = 0.0, 0.0
+
+      @values.each do |value|
+        if v = value.value
+          num   += v
+          count += 1
+        end
+      end
+
+      num / count / @interval.to_f
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/snapshot.rb b/lib/metriks/snapshot.rb
new file mode 100644
index 0000000..7885c5f
--- /dev/null
+++ b/lib/metriks/snapshot.rb
@@ -0,0 +1,59 @@
+module Metriks
+  class Snapshot
+    MEDIAN_Q = 0.5
+    P75_Q = 0.75
+    P95_Q = 0.95
+    P98_Q = 0.98
+    P99_Q = 0.99
+    P999_Q = 0.999
+
+    attr_reader :values
+
+    def initialize(values)
+      @values = values.sort
+    end
+
+    def value(quantile)
+      raise ArgumentError, "quantile must be between 0.0 and 1.0" if quantile < 0.0 || quantile > 1.0
+
+      return 0.0 if @values.empty?
+
+      pos = quantile * (@values.length + 1)
+
+      return @values.first if pos < 1
+      return @values.last if pos >= @values.length
+
+      lower = @values[pos.to_i - 1]
+      upper = @values[pos.to_i]
+      lower + (pos - pos.floor) * (upper - lower)
+    end
+
+    def size
+      @values.length
+    end
+
+    def median
+      value(MEDIAN_Q)
+    end
+
+    def get_75th_percentile
+      value(P75_Q)
+    end
+
+    def get_95th_percentile
+      value(P95_Q)
+    end
+
+    def get_98th_percentile
+      value(P98_Q)
+    end
+
+    def get_99th_percentile
+      value(P99_Q)
+    end
+
+    def get_999th_percentile
+      value(P999_Q)
+    end
+  end
+end
diff --git a/lib/metriks/time_tracker.rb b/lib/metriks/time_tracker.rb
new file mode 100644
index 0000000..6c5f620
--- /dev/null
+++ b/lib/metriks/time_tracker.rb
@@ -0,0 +1,26 @@
+module Metriks
+  class TimeTracker
+    def initialize(interval)
+      @interval = interval
+      @next_time = Time.now.to_f
+    end
+
+    def sleep
+      sleep_time = next_time - Time.now.to_f
+      if sleep_time > 0
+        Kernel.sleep(sleep_time)
+      end
+    end
+
+    def now_floored
+      time = Time.now.to_i
+      time - (time % @interval)
+    end
+
+    def next_time
+      now = Time.now.to_f
+      @next_time = now if @next_time <= now
+      @next_time += @interval - (@next_time % @interval)
+    end
+  end
+end
diff --git a/lib/metriks/timer.rb b/lib/metriks/timer.rb
new file mode 100644
index 0000000..cf1181f
--- /dev/null
+++ b/lib/metriks/timer.rb
@@ -0,0 +1,101 @@
+require 'atomic'
+require 'hitimes'
+
+require 'metriks/meter'
+require 'metriks/histogram'
+
+module Metriks
+  class Timer
+    class Context
+      def initialize(timer)
+        @timer    = timer
+        @interval = Hitimes::Interval.now
+      end
+
+      def restart
+        @interval = Hitimes::Interval.now
+      end
+
+      def stop
+        @interval.stop
+        @timer.update(@interval.duration)
+      end
+    end
+
+    def initialize(histogram = Metriks::Histogram.new_exponentially_decaying)
+      @meter     = Metriks::Meter.new
+      @histogram = histogram
+    end
+
+    def clear
+      @meter.clear
+      @histogram.clear
+    end
+
+    def update(duration)
+      if duration >= 0
+        @meter.mark
+        @histogram.update(duration)
+      end
+    end
+
+    def time(callable = nil, &block)
+      callable ||= block
+      context = Context.new(self)
+
+      if callable.nil?
+        return context
+      end
+
+      begin
+        return callable.call
+      ensure
+        context.stop
+      end
+    end
+
+    def snapshot
+      @histogram.snapshot
+    end
+
+    def count
+      @histogram.count
+    end
+
+    def one_minute_rate
+      @meter.one_minute_rate
+    end
+
+    def five_minute_rate
+      @meter.five_minute_rate
+    end
+
+    def fifteen_minute_rate
+      @meter.fifteen_minute_rate
+    end
+
+    def mean_rate
+      @meter.mean_rate
+    end
+
+    def min
+      @histogram.min
+    end
+
+    def max
+      @histogram.max
+    end
+
+    def mean
+      @histogram.mean
+    end
+
+    def stddev
+      @histogram.stddev
+    end
+
+    def stop
+      @meter.stop
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/uniform_sample.rb b/lib/metriks/uniform_sample.rb
new file mode 100644
index 0000000..b3d65ac
--- /dev/null
+++ b/lib/metriks/uniform_sample.rb
@@ -0,0 +1,40 @@
+require 'atomic'
+require 'metriks/snapshot'
+
+module Metriks
+  class UniformSample
+    def initialize(reservoir_size)
+      @values = Array.new(reservoir_size, 0)
+      @count  = Atomic.new(0)
+    end
+
+    def clear
+      @values.length.times do |idx|
+        @values[idx] = 0
+      end
+      @count.value = 0
+    end
+
+    def size
+      count = @count.value
+      count > @values.length ? @values.length : count
+    end
+
+    def snapshot
+      Snapshot.new(@values.slice(0, size))
+    end
+
+    def update(value)
+      new_count = @count.update { |v| v + 1 }
+
+      if new_count <= @values.length
+        @values[new_count - 1] = value
+      else
+        idx = rand(new_count)
+        if idx < @values.length
+          @values[idx] = value
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/metriks/utilization_timer.rb b/lib/metriks/utilization_timer.rb
new file mode 100644
index 0000000..171c45c
--- /dev/null
+++ b/lib/metriks/utilization_timer.rb
@@ -0,0 +1,43 @@
+require 'metriks/timer'
+
+module Metriks
+  class UtilizationTimer < Metriks::Timer
+    def initialize
+      super
+      @duration_meter = Metriks::Meter.new
+    end
+
+    def clear
+      super
+      @duration_meter.clear
+    end
+
+    def update(duration)
+      super
+      if duration >= 0
+        @duration_meter.mark(duration)
+      end
+    end
+
+    def one_minute_utilization
+      @duration_meter.one_minute_rate
+    end
+
+    def five_minute_utilization
+      @duration_meter.five_minute_rate
+    end
+
+    def fifteen_minute_utilization
+      @duration_meter.fifteen_minute_rate
+    end
+
+    def mean_utilization
+      @duration_meter.mean_rate
+    end
+
+    def stop
+      super
+      @duration_meter.stop
+    end
+  end
+end
\ No newline at end of file
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..1b2e20e
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,154 @@
+--- !ruby/object:Gem::Specification
+name: metriks
+version: !ruby/object:Gem::Version
+  version: 0.9.9.6
+platform: ruby
+authors:
+- Eric Lindvall
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2014-02-24 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+  name: atomic
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '1.0'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '1.0'
+- !ruby/object:Gem::Dependency
+  name: hitimes
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '1.1'
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '1.1'
+- !ruby/object:Gem::Dependency
+  name: avl_tree
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 1.1.2
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 1.1.2
+- !ruby/object:Gem::Dependency
+  name: mocha
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '0.10'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '0.10'
+description: An experimental metrics client.
+email: eric at sevenscale.com
+executables: []
+extensions: []
+extra_rdoc_files:
+- README.md
+- LICENSE
+files:
+- Gemfile
+- LICENSE
+- README.md
+- Rakefile
+- benchmark/samplers.rb
+- lib/metriks.rb
+- lib/metriks/counter.rb
+- lib/metriks/ewma.rb
+- lib/metriks/exponentially_decaying_sample.rb
+- lib/metriks/gauge.rb
+- lib/metriks/histogram.rb
+- lib/metriks/meter.rb
+- lib/metriks/registry.rb
+- lib/metriks/reporter/graphite.rb
+- lib/metriks/reporter/librato_metrics.rb
+- lib/metriks/reporter/logger.rb
+- lib/metriks/reporter/proc_title.rb
+- lib/metriks/reporter/riemann.rb
+- lib/metriks/simple_moving_average.rb
+- lib/metriks/snapshot.rb
+- lib/metriks/time_tracker.rb
+- lib/metriks/timer.rb
+- lib/metriks/uniform_sample.rb
+- lib/metriks/utilization_timer.rb
+- metriks.gemspec
+- test/counter_test.rb
+- test/gauge_test.rb
+- test/graphite_reporter_test.rb
+- test/histogram_test.rb
+- test/librato_metrics_reporter_test.rb
+- test/logger_reporter_test.rb
+- test/meter_test.rb
+- test/metriks_test.rb
+- test/proc_title_reporter_test.rb
+- test/registry_test.rb
+- test/riemann_reporter_test.rb
+- test/test_helper.rb
+- test/thread_error_handling_tests.rb
+- test/timer_test.rb
+- test/utilization_timer_test.rb
+homepage: https://github.com/eric/metriks
+licenses: []
+metadata: {}
+post_install_message: 
+rdoc_options:
+- --charset=UTF-8
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+requirements: []
+rubyforge_project: 
+rubygems_version: 2.0.3
+signing_key: 
+specification_version: 2
+summary: An experimental metrics client
+test_files:
+- test/counter_test.rb
+- test/gauge_test.rb
+- test/graphite_reporter_test.rb
+- test/histogram_test.rb
+- test/librato_metrics_reporter_test.rb
+- test/logger_reporter_test.rb
+- test/meter_test.rb
+- test/metriks_test.rb
+- test/proc_title_reporter_test.rb
+- test/registry_test.rb
+- test/riemann_reporter_test.rb
+- test/timer_test.rb
+- test/utilization_timer_test.rb
diff --git a/metriks.gemspec b/metriks.gemspec
new file mode 100644
index 0000000..31502d2
--- /dev/null
+++ b/metriks.gemspec
@@ -0,0 +1,102 @@
+## This is the rakegem gemspec template. Make sure you read and understand
+## all of the comments. Some sections require modification, and others can
+## be deleted if you don't need them. Once you understand the contents of
+## this file, feel free to delete any comments that begin with two hash marks.
+## You can find comprehensive Gem::Specification documentation, at
+## http://docs.rubygems.org/read/chapter/20
+Gem::Specification.new do |s|
+  s.specification_version = 2 if s.respond_to? :specification_version=
+  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+  s.rubygems_version = '1.3.5'
+
+  ## Leave these as is they will be modified for you by the rake gemspec task.
+  ## If your rubyforge_project name is different, then edit it and comment out
+  ## the sub! line in the Rakefile
+  s.name              = 'metriks'
+  s.version           = '0.9.9.6'
+  s.date              = '2014-02-24'
+
+  ## Make sure your summary is short. The description may be as long
+  ## as you like.
+  s.summary     = "An experimental metrics client"
+  s.description = "An experimental metrics client."
+
+  ## List the primary authors. If there are a bunch of authors, it's probably
+  ## better to set the email to an email list or something. If you don't have
+  ## a custom homepage, consider using your GitHub URL or the like.
+  s.authors  = ["Eric Lindvall"]
+  s.email    = 'eric at sevenscale.com'
+  s.homepage = 'https://github.com/eric/metriks'
+
+  ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
+  ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
+  s.require_paths = %w[lib]
+
+  ## Specify any RDoc options here. You'll want to add your README and
+  ## LICENSE files to the extra_rdoc_files list.
+  s.rdoc_options = ["--charset=UTF-8"]
+  s.extra_rdoc_files = %w[README.md LICENSE]
+
+  ## List your runtime dependencies here. Runtime dependencies are those
+  ## that are needed for an end user to actually USE your code.
+  s.add_dependency('atomic', ["~> 1.0"])
+  s.add_dependency('hitimes', [ "~> 1.1"])
+  s.add_dependency('avl_tree', [ "~> 1.1.2" ])
+
+  ## List your development dependencies here. Development dependencies are
+  ## those that are only needed during development
+  # s.add_development_dependency('tomdoc', ["~> 0.2"])
+  s.add_development_dependency('mocha', ['~> 0.10'])
+
+  ## Leave this section as-is. It will be automatically generated from the
+  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
+  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
+  # = MANIFEST =
+  s.files = %w[
+    Gemfile
+    LICENSE
+    README.md
+    Rakefile
+    benchmark/samplers.rb
+    lib/metriks.rb
+    lib/metriks/counter.rb
+    lib/metriks/ewma.rb
+    lib/metriks/exponentially_decaying_sample.rb
+    lib/metriks/gauge.rb
+    lib/metriks/histogram.rb
+    lib/metriks/meter.rb
+    lib/metriks/registry.rb
+    lib/metriks/reporter/graphite.rb
+    lib/metriks/reporter/librato_metrics.rb
+    lib/metriks/reporter/logger.rb
+    lib/metriks/reporter/proc_title.rb
+    lib/metriks/reporter/riemann.rb
+    lib/metriks/simple_moving_average.rb
+    lib/metriks/snapshot.rb
+    lib/metriks/time_tracker.rb
+    lib/metriks/timer.rb
+    lib/metriks/uniform_sample.rb
+    lib/metriks/utilization_timer.rb
+    metriks.gemspec
+    test/counter_test.rb
+    test/gauge_test.rb
+    test/graphite_reporter_test.rb
+    test/histogram_test.rb
+    test/librato_metrics_reporter_test.rb
+    test/logger_reporter_test.rb
+    test/meter_test.rb
+    test/metriks_test.rb
+    test/proc_title_reporter_test.rb
+    test/registry_test.rb
+    test/riemann_reporter_test.rb
+    test/test_helper.rb
+    test/thread_error_handling_tests.rb
+    test/timer_test.rb
+    test/utilization_timer_test.rb
+  ]
+  # = MANIFEST =
+
+  ## Test files will be grabbed from the file list. Make sure the path glob
+  ## matches what you actually use.
+  s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
+end
diff --git a/test/counter_test.rb b/test/counter_test.rb
new file mode 100644
index 0000000..8540a43
--- /dev/null
+++ b/test/counter_test.rb
@@ -0,0 +1,39 @@
+require 'test_helper'
+
+require 'metriks/counter'
+
+class CounterTest < Test::Unit::TestCase
+  include ThreadHelper
+
+  def setup
+    @counter = Metriks::Counter.new
+  end
+
+  def test_increment
+    @counter.increment
+
+    assert_equal 1, @counter.count
+  end
+
+  def test_increment_threaded
+    thread 10, :n => 100 do
+      @counter.increment
+    end
+
+    assert_equal 1000, @counter.count
+  end
+
+  def test_increment_by_more
+    @counter.increment 10
+
+    assert_equal 10, @counter.count
+  end
+  
+  def test_increment_by_more_threaded
+    thread 10, :n => 100 do
+      @counter.increment 10
+    end
+
+    assert_equal 10000, @counter.count
+  end
+end
diff --git a/test/gauge_test.rb b/test/gauge_test.rb
new file mode 100644
index 0000000..66a22c6
--- /dev/null
+++ b/test/gauge_test.rb
@@ -0,0 +1,46 @@
+require 'test_helper'
+
+require 'metriks/gauge'
+
+class GaugeTest < Test::Unit::TestCase
+  def test_gauge
+    gauge = Metriks::Gauge.new
+
+    3.times do |i|
+      gauge.set(i + 1)
+    end
+
+    assert_equal 3, gauge.value
+
+    gauge.set(1)
+
+    assert_equal 1, gauge.value
+  end
+
+  def test_gauge_default
+    gauge = Metriks::Gauge.new
+    assert_equal nil, gauge.value
+  end
+
+  def test_gauge_callback_via_block
+    gauge = Metriks::Gauge.new { 56 }
+
+    assert_equal 56, gauge.value
+  end
+
+  def test_gauge_callback_via_callable_object
+    callable = Class.new(Struct.new(:value)) {
+      def call
+        value
+      end
+    }
+
+    gauge = Metriks::Gauge.new(callable.new(987))
+
+    assert_equal 987, gauge.value
+
+    gauge = Metriks::Gauge.new(proc { 123 })
+
+    assert_equal 123, gauge.value
+  end
+end
diff --git a/test/graphite_reporter_test.rb b/test/graphite_reporter_test.rb
new file mode 100644
index 0000000..51dc9c4
--- /dev/null
+++ b/test/graphite_reporter_test.rb
@@ -0,0 +1,41 @@
+require 'test_helper'
+require 'thread_error_handling_tests'
+
+require 'metriks/reporter/graphite'
+
+class GraphiteReporterTest < Test::Unit::TestCase
+  include ThreadErrorHandlingTests
+
+  def build_reporter(options={})
+    Metriks::Reporter::Graphite.new('localhost', 3333, { :registry => @registry }.merge(options))
+  end
+
+  def setup
+    @registry = Metriks::Registry.new
+    @reporter = build_reporter
+    @stringio = StringIO.new
+
+    @reporter.stubs(:socket).returns(@stringio)
+  end
+
+  def teardown
+    @reporter.stop
+    @registry.stop
+  end
+
+  def test_write
+    @registry.meter('meter.testing').mark
+    @registry.counter('counter.testing').increment
+    @registry.timer('timer.testing').update(1.5)
+    @registry.histogram('histogram.testing').update(1.5)
+    @registry.utilization_timer('utilization_timer.testing').update(1.5)
+    @registry.gauge('gauge.testing').set(123)
+    @registry.gauge('gauge.testing.block') { 456 }
+
+    @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
+  end
+end
diff --git a/test/histogram_test.rb b/test/histogram_test.rb
new file mode 100644
index 0000000..41642ba
--- /dev/null
+++ b/test/histogram_test.rb
@@ -0,0 +1,199 @@
+require 'test_helper'
+
+require 'metriks/histogram'
+
+class HistogramTest < Test::Unit::TestCase
+  include ThreadHelper
+
+  def setup
+  end
+
+  def test_uniform_sample_min
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 5, @histogram.min
+  end
+
+  def test_uniform_sample_max
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 10, @histogram.max
+  end
+
+  def test_uniform_sample_mean
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 7, @histogram.mean
+  end
+
+  def test_uniform_sample_mean_threaded
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    thread 10, :n => 100 do
+      @histogram.update(5)
+      @histogram.update(10)
+    end
+
+    assert_equal 7, @histogram.mean
+  end
+  
+  def test_uniform_sample_2000
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    2000.times do |idx|
+      @histogram.update(idx)
+    end
+
+    assert_equal 1999, @histogram.max
+  end
+  
+  def test_uniform_sample_2000_threaded
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    t = 10
+    thread t do |i|
+      2000.times do |x|
+        if (x % t) == i
+          @histogram.update x
+        end
+      end
+    end
+
+    assert_equal 1999, @histogram.max
+  end
+
+  def test_uniform_sample_snashot
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    100.times do |idx|
+      @histogram.update(idx)
+    end
+
+    snapshot = @histogram.snapshot
+
+    assert_equal 49.5, snapshot.median
+  end
+
+  def test_uniform_sample_snapshot_threaded
+    @histogram = Metriks::Histogram.new(Metriks::UniformSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE))
+
+    thread 10 do
+      100.times do |idx|
+        @histogram.update(idx)
+      end
+    end
+
+    snapshot = @histogram.snapshot
+
+    assert_equal 49.5, snapshot.median
+  end
+
+  def test_exponential_sample_min
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 5, @histogram.min
+  end
+  
+  def test_exponential_sample_max
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 10, @histogram.max
+  end
+
+  def test_exponential_sample_mean
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    @histogram.update(5)
+    @histogram.update(10)
+
+    assert_equal 7, @histogram.mean
+  end
+  
+  def test_exponential_sample_mean_threaded
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    thread 10, :n => 100 do
+      @histogram.update(5)
+      @histogram.update(10)
+    end
+
+    assert_equal 7, @histogram.mean
+  end
+
+  def test_exponential_sample_2000
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    2000.times do |idx|
+      @histogram.update(idx)
+    end
+
+    assert_equal 1999, @histogram.max
+  end
+  
+  def test_exponential_sample_2000_threaded
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    t = 10
+    thread t do |i|
+      2000.times do |idx|
+        if (idx % t) == i
+          @histogram.update(idx)
+        end
+      end
+    end
+
+    assert_equal 1999, @histogram.max
+  end
+
+  def test_exponential_sample_snashot
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    100.times do |idx|
+      @histogram.update(idx)
+    end
+
+    snapshot = @histogram.snapshot
+
+    assert_equal 49.5, snapshot.median
+  end
+
+  def test_exponential_sample_snapshot_threaded
+    @histogram = Metriks::Histogram.new(Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA))
+
+    thread 10 do
+      100.times do |idx|
+        @histogram.update(idx)
+      end
+    end
+
+    snapshot = @histogram.snapshot
+
+    assert_equal 49.5, snapshot.median
+  end
+
+  def test_long_idle_sample
+    Time.stubs(:now).returns(Time.at(2000))
+    sample = Metriks::ExponentiallyDecayingSample.new(Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA)
+    Time.unstub(:now)
+    @histogram = Metriks::Histogram.new(sample)
+
+    @histogram.update(5)
+
+    assert_equal 5, @histogram.min
+  end
+end
diff --git a/test/librato_metrics_reporter_test.rb b/test/librato_metrics_reporter_test.rb
new file mode 100644
index 0000000..4c98455
--- /dev/null
+++ b/test/librato_metrics_reporter_test.rb
@@ -0,0 +1,35 @@
+require 'test_helper'
+require 'thread_error_handling_tests'
+
+require 'metriks/reporter/librato_metrics'
+
+class LibratoMetricsReporterTest < Test::Unit::TestCase
+  include ThreadErrorHandlingTests
+
+  def build_reporter(options={})
+    Metriks::Reporter::LibratoMetrics.new('user', 'password', { :registry => @registry }.merge(options))
+  end
+
+  def setup
+    @registry = Metriks::Registry.new
+    @reporter = build_reporter
+  end
+
+  def teardown
+    @reporter.stop
+    @registry.stop
+  end
+
+  def test_write
+    @registry.meter('meter.testing').mark
+    @registry.counter('counter.testing').increment
+    @registry.timer('timer.testing').update(1.5)
+    @registry.histogram('histogram.testing').update(1.5)
+    @registry.utilization_timer('utilization_timer.testing').update(1.5)
+    @registry.gauge('gauge.testing') { 123 }
+
+    @reporter.expects(:submit)
+
+    @reporter.write
+  end
+end
diff --git a/test/logger_reporter_test.rb b/test/logger_reporter_test.rb
new file mode 100644
index 0000000..7dafa1c
--- /dev/null
+++ b/test/logger_reporter_test.rb
@@ -0,0 +1,49 @@
+require 'test_helper'
+require 'thread_error_handling_tests'
+
+require 'logger'
+require 'metriks/reporter/logger'
+
+class LoggerReporterTest < Test::Unit::TestCase
+  include ThreadErrorHandlingTests
+
+  def build_reporter(options={})
+    Metriks::Reporter::Logger.new({ :registry => @registry, :logger => @logger }.merge(options))
+  end
+
+  def setup
+    @stringio = StringIO.new
+    @logger   = ::Logger.new(@stringio)
+    @registry = Metriks::Registry.new
+
+    @reporter = build_reporter
+
+    @registry.meter('meter.testing').mark
+    @registry.counter('counter.testing').increment
+    @registry.timer('timer.testing').update(1.5)
+    @registry.histogram('histogram.testing').update(1.5)
+    @registry.utilization_timer('utilization_timer.testing').update(1.5)
+    @registry.gauge('gauge.testing').set(123)
+  end
+
+  def teardown
+    @reporter.stop
+    @registry.stop
+  end
+
+  def test_write
+    @reporter.write
+
+    assert_match /time=\d/, @stringio.string
+    assert_match /median=\d/, @stringio.string
+    assert_match /value=123/, @stringio.string
+  end
+
+  def test_flush
+    @reporter.flush
+
+    assert_match /time=\d/, @stringio.string
+    assert_match /median=\d/, @stringio.string
+    assert_match /value=123/, @stringio.string
+  end
+end
diff --git a/test/meter_test.rb b/test/meter_test.rb
new file mode 100644
index 0000000..8d4e0c0
--- /dev/null
+++ b/test/meter_test.rb
@@ -0,0 +1,38 @@
+require 'test_helper'
+
+require 'metriks/meter'
+
+class MeterTest < Test::Unit::TestCase
+  include ThreadHelper
+
+  def setup
+    @meter = Metriks::Meter.new
+  end
+
+  def teardown
+    @meter.stop
+  end
+
+  def test_meter
+    @meter.mark
+
+    assert_equal 1, @meter.count
+  end
+
+  def test_meter_threaded
+    thread 10, :n => 100 do
+      @meter.mark
+    end
+
+    assert_equal 1000, @meter.count
+  end
+
+  def test_one_minute_rate
+    @meter.mark 1000
+
+    # Pretend it's been 5 seconds
+    @meter.tick
+
+    assert_equal 200, @meter.one_minute_rate
+  end
+end
diff --git a/test/metriks_test.rb b/test/metriks_test.rb
new file mode 100644
index 0000000..41f8dd1
--- /dev/null
+++ b/test/metriks_test.rb
@@ -0,0 +1,31 @@
+require 'test_helper'
+
+class MetriksTest < Test::Unit::TestCase
+  def setup
+    Metriks::Registry.default.clear
+  end
+
+  def teardown
+    Metriks::Registry.default.clear
+  end
+
+  def test_counter
+    assert_not_nil Metriks.counter('testing')
+  end
+
+  def test_meter
+    assert_not_nil Metriks.meter('testing')
+  end
+
+  def test_timer
+    assert_not_nil Metriks.timer('testing')
+  end
+
+  def test_utilization_timer
+    assert_not_nil Metriks.utilization_timer('testing')
+  end
+
+  def test_histogram
+    assert_not_nil Metriks.histogram('testing')
+  end
+end
\ No newline at end of file
diff --git a/test/proc_title_reporter_test.rb b/test/proc_title_reporter_test.rb
new file mode 100644
index 0000000..f35c943
--- /dev/null
+++ b/test/proc_title_reporter_test.rb
@@ -0,0 +1,25 @@
+require 'test_helper'
+
+require 'metriks/reporter/proc_title'
+
+class ProcTitleReporterTest < Test::Unit::TestCase
+  def setup
+    @reporter = Metriks::Reporter::ProcTitle.new
+    @original_proctitle = $0.dup
+  end
+
+  def teardown
+    @reporter.stop
+    $0 = @original_proctitle
+  end
+
+  def test_generate_title
+    @reporter.add 'test', 'sec' do
+      50.333
+    end
+
+    title = @reporter.send(:generate_title)
+
+    assert_equal 'test: 50.3/sec', title
+  end
+end
\ No newline at end of file
diff --git a/test/registry_test.rb b/test/registry_test.rb
new file mode 100644
index 0000000..22122cf
--- /dev/null
+++ b/test/registry_test.rb
@@ -0,0 +1,49 @@
+require 'test_helper'
+
+require 'metriks/registry'
+
+class RegistryTest < Test::Unit::TestCase
+  def setup
+    @registry = Metriks::Registry.new
+  end
+
+  def teardown
+    @registry.stop
+  end
+
+  def test_counter
+    assert_not_nil @registry.counter('testing')
+  end
+
+  def test_meter
+    assert_not_nil @registry.meter('testing')
+  end
+
+  def test_timer
+    assert_not_nil @registry.timer('testing')
+  end
+
+  def test_utilization_timer
+    assert_not_nil @registry.utilization_timer('testing')
+  end
+
+  def test_histogram
+    assert_not_nil @registry.histogram('testing')
+  end
+
+  def test_mismatched_metrics
+    @registry.histogram('histogram')
+    assert_raises(RuntimeError) { @registry.timer('histogram') }
+
+    @registry.timer('timer')
+    assert_raises(RuntimeError) { @registry.histogram('timer') }
+  end
+
+  def test_calling_counter_twice
+    assert_not_nil @registry.counter('testing')
+  end
+
+  def test_default
+    assert_not_nil Metriks::Registry.default
+  end
+end
\ No newline at end of file
diff --git a/test/riemann_reporter_test.rb b/test/riemann_reporter_test.rb
new file mode 100644
index 0000000..98662ab
--- /dev/null
+++ b/test/riemann_reporter_test.rb
@@ -0,0 +1,88 @@
+require 'test_helper'
+require 'thread_error_handling_tests'
+
+require 'metriks/reporter/riemann'
+
+class RiemannReporterTest < Test::Unit::TestCase
+  include ThreadErrorHandlingTests
+
+  def build_reporter(options={})
+    Metriks::Reporter::Riemann.new({
+      :host => "foo",
+      :port => 1234,
+      :registry => @registry,
+      :default_event => {:host => "h"}
+    }.merge(options))
+  end
+
+  def setup
+    @registry = Metriks::Registry.new
+    @reporter = build_reporter
+  end
+
+  def teardown
+    @reporter.stop
+    @registry.stop
+  end
+
+  def test_init
+    assert_equal @reporter.client.host, "foo"
+    assert_equal @reporter.client.port, 1234
+  end
+
+  def test_write
+    @registry.meter('meter.testing').mark
+    @registry.counter('counter.testing').increment
+    @registry.timer('timer.testing').update(1.5)
+    @registry.histogram('histogram.testing').update(1.5)
+    @registry.utilization_timer('utilization_timer.testing').update(1.5)
+    @registry.gauge('gauge.testing') { 123 }
+
+    @reporter.client.expects(:<<).at_least_once
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "meter.testing count",
+      :metric => 1,
+      :tags => ["meter"],
+      :ttl => 90
+    )
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "counter.testing count",
+      :metric => 1,
+      :tags => ["counter"],
+      :ttl => 90
+    )
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "timer.testing max",
+      :metric => 1.5,
+      :tags => ["timer"],
+      :ttl => 90
+    )
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "histogram.testing max",
+      :metric => 1.5,
+      :tags => ["histogram"],
+      :ttl => 90
+    )
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "utilization_timer.testing mean",
+      :metric => 1.5,
+      :tags => ["utilization_timer"],
+      :ttl => 90
+    )
+
+    @reporter.client.expects(:<<).with(
+      :host => "h",
+      :service => "gauge.testing value",
+      :metric => 123,
+      :tags => ["gauge"],
+      :ttl => 90
+    )
+
+    @reporter.write
+  end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
new file mode 100644
index 0000000..e5cd9ee
--- /dev/null
+++ b/test/test_helper.rb
@@ -0,0 +1,33 @@
+require 'test/unit'
+require 'pp'
+
+require 'mocha'
+
+require 'metriks'
+
+Thread.abort_on_exception = true
+
+module ThreadHelper
+  require 'thread'
+
+  # Run the given block on n threads in parallel. Returns an array of the
+  # return values of each thread's last invocation of block. Options:
+
+  # :n: call block n times per thread. Default 1.
+  def thread(threads = 2, opts = {})
+    n = opts[:n] || 1
+    results = []
+   
+    threads.times.map do |i|
+      Thread.new do
+        n.times do
+          results[i] = yield i
+        end
+      end
+    end.each do |thread|
+      thread.join
+    end
+    
+    results
+  end
+end
diff --git a/test/thread_error_handling_tests.rb b/test/thread_error_handling_tests.rb
new file mode 100644
index 0000000..3f4094d
--- /dev/null
+++ b/test/thread_error_handling_tests.rb
@@ -0,0 +1,20 @@
+module ThreadErrorHandlingTests
+  def test_passes_errors_in_thread_loop_to_on_error_handler
+    rescued_error = nil
+    error_handler_called = false
+    reporter = build_reporter(:interval => 0.0001, :on_error => lambda { |e|
+      error_handler_called = true
+      rescued_error = e
+    })
+
+    reporter.stubs(:write).raises(StandardError, "boom")
+
+    reporter.start
+    sleep 0.02
+    assert_equal true, error_handler_called
+    assert_equal "boom", rescued_error.message
+  ensure
+    reporter.stop
+  end
+end
+
diff --git a/test/timer_test.rb b/test/timer_test.rb
new file mode 100644
index 0000000..87eb0d4
--- /dev/null
+++ b/test/timer_test.rb
@@ -0,0 +1,32 @@
+require 'test_helper'
+
+require 'metriks/timer'
+
+class TimerTest < Test::Unit::TestCase
+  def setup
+    @timer = Metriks::Timer.new
+  end
+
+  def teardown
+    @timer.stop
+  end
+
+  def test_timer
+    3.times do
+      @timer.time do
+        sleep 0.1
+      end
+    end
+
+    assert_in_delta 0.1, @timer.mean, 0.01
+    assert_in_delta 0.1, @timer.snapshot.median, 0.01
+  end
+
+  def test_timer_without_block
+    t = @timer.time
+    sleep 0.1
+    t.stop
+
+    assert_in_delta 0.1, @timer.mean, 0.01
+  end
+end
\ No newline at end of file
diff --git a/test/utilization_timer_test.rb b/test/utilization_timer_test.rb
new file mode 100644
index 0000000..11961ee
--- /dev/null
+++ b/test/utilization_timer_test.rb
@@ -0,0 +1,25 @@
+require 'test_helper'
+
+require 'metriks/utilization_timer'
+
+class UtilizationTimerTest < Test::Unit::TestCase
+  def setup
+    @timer = Metriks::UtilizationTimer.new
+  end
+
+  def teardown
+    @timer.stop
+  end
+
+  def test_timer
+    5.times do
+      @timer.update(0.10)
+      @timer.update(0.15)
+    end
+
+    @timer.instance_variable_get(:@meter).tick
+    @timer.instance_variable_get(:@duration_meter).tick
+
+    assert_in_delta 0.25, @timer.one_minute_utilization, 0.1
+  end
+end
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-metriks.git



More information about the Pkg-ruby-extras-commits mailing list