Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ source 'https://rubygems.org'

# Specify your gem's dependencies in harness.gemspec
gemspec

gem 'mutant', git: 'https://github.com/ahawkins/mutant', branch: 'minitest'
4 changes: 4 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ Rake::TestTask.new(:test) do |test|
test.pattern = 'test/**/*_test.rb'
end

task :mutant do
sh "bundle exec mutant -I lib -r harness --use minitest '::Harness'"
end

task :default => :test
82 changes: 20 additions & 62 deletions lib/harness.rb
Original file line number Diff line number Diff line change
@@ -1,89 +1,47 @@
require "harness/version"

require 'statsd'
require 'singleton'

module Harness
class Config
Config = Class.new do
include Singleton
attr_accessor :collector, :queue
end

Measurement = Struct.new(:name, :value, :rate) do
def self.from_event(event)
payload = event.payload
value = payload.fetch name.split('::').last.downcase.to_sym

case value
when true
new event.name
when Fixnum
new event.name, value
when String
new value
when Hash
new(value[:name] || event.name, value[:value], value[:rate])
end
end

def sample_rate
rate.nil? ? 1 : rate
end
end

class Timer < Measurement
def self.from_event(event)
timer = super
timer.value = event.duration
timer
end

def ms
value
end

def log
Harness.timing name, ms, sample_rate
end
end

class Counter < Measurement
def log
if value.nil?
Harness.increment name, sample_rate
else
Harness.count name, value, sample_rate
end
end
end
end.instance

def self.config
@config ||= Config.new
Config
end

def self.increment(*args)
queue.push [:increment, args]
def self.increment(stat, sample_rate = 1)
queue.push [:increment, [stat, sample_rate]]
end

def self.decrement(*args)
queue.push [:decrement, args]
def self.decrement(stat, sample_rate = 1)
queue.push [:decrement, [stat, sample_rate]]
end

def self.timing(*args)
queue.push [:timing, args]
def self.count(stat, value, sample_rate = 1)
queue.push [:count, [stat, value, sample_rate]]
end

def self.count(*args)
queue.push [:count, args]
def self.timing(start, ms, sample_rate = 1)
queue.push [:timing, [start, ms, sample_rate]]
end

def self.time(stat, sample_rate = 1)
start = Time.now
result = yield
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
timing(stat, delta(start), sample_rate)
result
end

def self.gauge(*args)
queue.push [:gauge, args]
def self.delta(start, finish = Time.now)
((finish - start) * 1000).round
end

def self.gauge(stat, value, sample_rate = 1)
queue.push [:gauge, [stat, value, sample_rate]]
end

def self.queue
Expand Down
15 changes: 10 additions & 5 deletions lib/harness/async_queue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

module Harness
class AsyncQueue
attr_reader :consumer, :queue
attr_reader :queue, :consumer

def initialize(queue = Queue.new, collector = Harness.collector)
@queue = queue

def initialize
@queue = Queue.new
@consumer = Thread.new do
loop do
msg = queue.pop
Expand All @@ -22,8 +23,12 @@ def push(msg)
queue.push msg
end

def collector
Harness.collector
def stop
consumer.kill
end

def up?
consumer.alive?
end
end
end
19 changes: 6 additions & 13 deletions lib/harness/fake_collector.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Harness
class FakeCollector
Increment = Struct.new(:name, :amount, :rate)
Decrement = Struct.new(:name, :amount, :rate)
Gauge = Struct.new(:name, :value, :rate)
Increment = Struct.new(:name, :rate)
Decrement = Struct.new(:name, :rate)
Measurement = Struct.new(:name, :value, :rate)

attr_reader :gauges, :counters, :timers, :increments, :decrements

Expand All @@ -11,14 +11,7 @@ def initialize
end

def timing(*args)
timers << Harness::Timer.new(*args)
end

def time(stat, sample_rate = 1)
start = Time.now
result = yield
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
result
timers << Measurement.new(*args)
end

def increment(*args)
Expand All @@ -30,11 +23,11 @@ def decrement(*args)
end

def count(*args)
counters << Harness::Counter.new(*args)
counters << Measurement.new(*args)
end

def gauge(*args)
gauges << Gauge.new(*args)
gauges << Measurement.new(*args)
end
end
end
4 changes: 2 additions & 2 deletions lib/harness/null_collector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def count(*args)

end

def time(*args, &block)
yield block
def time(*args)
yield
end

def timing(*args)
Expand Down
7 changes: 7 additions & 0 deletions test/acceptance_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ def test_works_with_actual_statsd
true
end
end

def test_time_deltas_are_calculated_in_milliseconds
start = Time.now
finish = start + 2.5

assert_equal 2500, Harness.delta(start, finish)
end
end
59 changes: 57 additions & 2 deletions test/async_queue_test.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,69 @@
require_relative 'test_helper'
Thread.abort_on_exception = true

class AsyncQueueTest < MiniTest::Unit::TestCase
attr_reader :queue, :collector

class NullQueue
def push(*)

end
end

def setup
super
Harness.config.queue = NullQueue.new

@collector = Harness::FakeCollector.new
@queue = Harness::AsyncQueue.new Queue.new, collector
assert queue.up?, 'Precondition: consumer not running'
end

def teardown
queue.stop
super
end

def test_proceses_queue_after_instantiating
q = Queue.new
q << [:gauge, ['foo', 5]]

@queue = Harness::AsyncQueue.new q, collector

sleep 0.1

assert queue.up?, 'Queue should be working'

assert_gauge 'foo'
end

def test_uses_consumer_thread_to_not_block_the_main_thread
queue = Harness::AsyncQueue.new
queue.push [:gauge, ['foo', 5]]

sleep 0.1

assert queue.up?, 'Queue should be working'

assert_gauge 'foo'
end

def test_use_configured_collector_by_default
Harness.config.collector = collector

@queue = Harness::AsyncQueue.new

queue.push [:gauge, ['foo', 5]]

sleep 0.1

assert queue.up?, 'Queue should be working'

assert_gauge 'foo'
end

def test_consumer_can_be_stopped
assert queue.up?, 'Consumer must be alive'
queue.stop
sleep 0.1
refute queue.up?, 'Consumer should be killed'
end
end
Loading