harness 0.9.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.md +108 -323
- data/harness.gemspec +3 -12
- data/lib/harness.rb +69 -68
- data/lib/harness/async_queue.rb +29 -0
- data/lib/harness/fake_collector.rb +40 -0
- data/lib/harness/instrumentation.rb +21 -6
- data/lib/harness/null_collector.rb +23 -0
- data/lib/harness/subscription.rb +7 -0
- data/lib/harness/sync_queue.rb +14 -0
- data/lib/harness/version.rb +1 -1
- data/test/acceptance_test.rb +20 -0
- data/test/active_support_notifications_test.rb +93 -0
- data/test/async_queue_test.rb +14 -0
- data/test/instrumentation_test.rb +54 -0
- data/test/null_collector_test.rb +29 -0
- data/test/test_helper.rb +38 -34
- metadata +21 -196
- data/lib/harness/adapters/librato_adapter.rb +0 -90
- data/lib/harness/adapters/memory_adapter.rb +0 -27
- data/lib/harness/adapters/null_adapter.rb +0 -11
- data/lib/harness/adapters/stathat_adapter.rb +0 -75
- data/lib/harness/adapters/statsd_adapter.rb +0 -50
- data/lib/harness/consumer.rb +0 -35
- data/lib/harness/counter.rb +0 -29
- data/lib/harness/gauge.rb +0 -23
- data/lib/harness/integration/action_controller.rb +0 -9
- data/lib/harness/integration/action_mailer.rb +0 -9
- data/lib/harness/integration/action_view.rb +0 -9
- data/lib/harness/integration/active_model_serializers.rb +0 -9
- data/lib/harness/integration/active_support.rb +0 -9
- data/lib/harness/integration/sidekiq.rb +0 -47
- data/lib/harness/job.rb +0 -23
- data/lib/harness/measurement.rb +0 -43
- data/lib/harness/queues/delayed_job_queue.rb +0 -7
- data/lib/harness/queues/resque_queue.rb +0 -29
- data/lib/harness/queues/sidekiq_queue.rb +0 -31
- data/lib/harness/queues/synchronous_queue.rb +0 -18
- data/lib/harness/railtie.rb +0 -71
- data/lib/harness/tasks.rake +0 -6
- data/test/integration/counters_with_redis_test.rb +0 -67
- data/test/integration/instrumentation_test.rb +0 -50
- data/test/integration/integrations/action_controller_test.rb +0 -51
- data/test/integration/integrations/action_mailer_test.rb +0 -22
- data/test/integration/integrations/action_view_test.rb +0 -32
- data/test/integration/integrations/active_model_serializers_test.rb +0 -22
- data/test/integration/integrations/active_support_test.rb +0 -41
- data/test/integration/integrations/sidekiq_test.rb +0 -54
- data/test/integration/logging_test.rb +0 -17
- data/test/integration/queues/delayed_job_test.rb +0 -59
- data/test/integration/queues/resque_test.rb +0 -24
- data/test/integration/queues/sidekiq_test.rb +0 -34
- data/test/integration/railtie_test.rb +0 -26
- data/test/unit/adapters/librato_adapter_test.rb +0 -169
- data/test/unit/adapters/memory_adapter_test.rb +0 -22
- data/test/unit/adapters/stathat_adapter_test.rb +0 -144
- data/test/unit/adapters/statsd_adapter_test.rb +0 -74
- data/test/unit/counter_test.rb +0 -84
- data/test/unit/gauge_test.rb +0 -93
- data/test/unit/harness_test.rb +0 -39
- data/test/unit/measurement_test.rb +0 -76
data/harness.gemspec
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('../lib/harness/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["ahawkins"]
|
6
6
|
gem.email = ["adam@hawkins.io"]
|
7
|
-
gem.description = %q{
|
7
|
+
gem.description = %q{Collect high level application performance metrics and forward them for analysis}
|
8
8
|
gem.summary = %q{}
|
9
9
|
gem.homepage = "https://github.com/ahawkins/harness"
|
10
10
|
|
@@ -16,17 +16,8 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = Harness::VERSION
|
17
17
|
|
18
18
|
gem.add_dependency "activesupport", ">= 3"
|
19
|
-
gem.add_dependency "
|
20
|
-
gem.add_dependency "redis-namespace"
|
21
|
-
gem.add_dependency "multi_json"
|
19
|
+
gem.add_dependency "statsd-ruby"
|
22
20
|
|
23
21
|
gem.add_development_dependency "simplecov"
|
24
|
-
gem.add_development_dependency "
|
25
|
-
gem.add_development_dependency "resque"
|
26
|
-
gem.add_development_dependency "sidekiq"
|
27
|
-
gem.add_development_dependency "delayed_job_active_record"
|
28
|
-
gem.add_development_dependency "active_model_serializers"
|
29
|
-
gem.add_development_dependency "rails", ">= 3"
|
30
|
-
gem.add_development_dependency "sqlite3"
|
31
|
-
gem.add_development_dependency "statsd-instrument"
|
22
|
+
gem.add_development_dependency "rake"
|
32
23
|
end
|
data/lib/harness.rb
CHANGED
@@ -1,52 +1,57 @@
|
|
1
1
|
require "harness/version"
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
require 'redis'
|
6
|
-
require 'redis/namespace'
|
7
|
-
|
3
|
+
require 'statsd'
|
8
4
|
require 'active_support/notifications'
|
9
|
-
require 'active_support/core_ext/string'
|
10
|
-
require 'active_support/core_ext/hash/keys'
|
11
|
-
require 'active_support/core_ext/numeric'
|
12
|
-
require 'active_support/core_ext/integer'
|
13
|
-
|
14
|
-
require 'active_support/ordered_options'
|
15
5
|
|
16
6
|
module Harness
|
17
|
-
class LoggingError < RuntimeError ; end
|
18
|
-
class NoCounter < RuntimeError ; end
|
19
|
-
|
20
7
|
class Config
|
21
|
-
|
22
|
-
|
23
|
-
attr_reader :instrument
|
8
|
+
attr_accessor :collector, :queue
|
9
|
+
end
|
24
10
|
|
25
|
-
|
26
|
-
|
11
|
+
Measurement = Struct.new(:name, :value, :rate) do
|
12
|
+
def self.from_event(event)
|
13
|
+
payload = event.payload
|
14
|
+
value = payload.fetch name.split('::').last.downcase.to_sym
|
15
|
+
|
16
|
+
case value
|
17
|
+
when true
|
18
|
+
new event.name
|
19
|
+
when Fixnum
|
20
|
+
new event.name, value
|
21
|
+
when String
|
22
|
+
new value
|
23
|
+
when Hash
|
24
|
+
new(value[:name] || event.name, value[:value], value[:rate])
|
25
|
+
end
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
30
|
-
|
31
|
-
@adapter = "Harness::#{val.to_s.camelize}Adapter".constantize.new
|
32
|
-
else
|
33
|
-
@adapter = val
|
34
|
-
end
|
28
|
+
def sample_rate
|
29
|
+
rate.nil? ? 1 : rate
|
35
30
|
end
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
class Timer < Measurement
|
34
|
+
def self.from_event(event)
|
35
|
+
timer = super
|
36
|
+
timer.value = event.duration
|
37
|
+
timer
|
38
|
+
end
|
39
|
+
|
40
|
+
def ms
|
41
|
+
value
|
43
42
|
end
|
44
43
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
def log
|
45
|
+
Harness.timing name, ms, sample_rate
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Counter < Measurement
|
50
|
+
def log
|
51
|
+
if value.nil?
|
52
|
+
Harness.increment name, sample_rate
|
53
|
+
else
|
54
|
+
Harness.count name, value, sample_rate
|
50
55
|
end
|
51
56
|
end
|
52
57
|
end
|
@@ -55,53 +60,49 @@ module Harness
|
|
55
60
|
@config ||= Config.new
|
56
61
|
end
|
57
62
|
|
58
|
-
def self.
|
59
|
-
|
63
|
+
def self.increment(*args)
|
64
|
+
queue.push [:increment, args]
|
60
65
|
end
|
61
66
|
|
62
|
-
def self.
|
63
|
-
|
67
|
+
def self.decrement(*args)
|
68
|
+
queue.push [:decrement, args]
|
64
69
|
end
|
65
70
|
|
66
|
-
def self.
|
67
|
-
|
71
|
+
def self.timing(*args)
|
72
|
+
queue.push [:timing, args]
|
68
73
|
end
|
69
74
|
|
70
|
-
def self.
|
71
|
-
|
75
|
+
def self.count(*args)
|
76
|
+
queue.push [:count, args]
|
72
77
|
end
|
73
78
|
|
74
|
-
def self.
|
75
|
-
|
79
|
+
def self.time(stat, sample_rate = 1)
|
80
|
+
start = Time.now
|
81
|
+
result = yield
|
82
|
+
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
|
83
|
+
result
|
76
84
|
end
|
77
85
|
|
78
|
-
def self.
|
79
|
-
|
80
|
-
redis.set counter, 0
|
81
|
-
end
|
86
|
+
def self.gauge(*args)
|
87
|
+
queue.push [:gauge, args]
|
82
88
|
end
|
83
89
|
|
84
|
-
def self.
|
85
|
-
|
90
|
+
def self.queue
|
91
|
+
config.queue
|
86
92
|
end
|
87
|
-
end
|
88
|
-
|
89
|
-
require 'harness/measurement'
|
90
|
-
require 'harness/counter'
|
91
|
-
require 'harness/gauge'
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
require 'harness/queues/synchronous_queue'
|
94
|
+
def self.collector
|
95
|
+
config.collector
|
96
|
+
end
|
97
|
+
end
|
98
98
|
|
99
|
-
require 'harness/
|
100
|
-
require 'harness/
|
101
|
-
require 'harness/adapters/null_adapter'
|
99
|
+
require 'harness/sync_queue'
|
100
|
+
require 'harness/async_queue'
|
102
101
|
|
103
|
-
require 'harness/
|
102
|
+
require 'harness/null_collector'
|
103
|
+
require 'harness/fake_collector'
|
104
104
|
|
105
|
-
require '
|
105
|
+
require 'harness/instrumentation'
|
106
|
+
require 'harness/subscription'
|
106
107
|
|
107
|
-
Harness.
|
108
|
+
Harness.config.queue = Harness::AsyncQueue.new
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Harness
|
4
|
+
class AsyncQueue
|
5
|
+
attr_reader :consumer, :queue
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@queue = Queue.new
|
9
|
+
@consumer = Thread.new do
|
10
|
+
loop do
|
11
|
+
msg = queue.pop
|
12
|
+
|
13
|
+
method_name = msg.first
|
14
|
+
args = msg.last
|
15
|
+
|
16
|
+
collector.__send__ method_name, *args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def push(msg)
|
22
|
+
queue.push msg
|
23
|
+
end
|
24
|
+
|
25
|
+
def collector
|
26
|
+
Harness.collector
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Harness
|
2
|
+
class FakeCollector
|
3
|
+
Increment = Struct.new(:name, :amount, :rate)
|
4
|
+
Decrement = Struct.new(:name, :amount, :rate)
|
5
|
+
Gauge = Struct.new(:name, :value, :rate)
|
6
|
+
|
7
|
+
attr_reader :gauges, :counters, :timers, :increments, :decrements
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@gauges, @counters, @timers, @increments, @decrements = [], [], [], [], []
|
11
|
+
end
|
12
|
+
|
13
|
+
def timing(*args)
|
14
|
+
timers << Harness::Timer.new(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def time(stat, sample_rate = 1)
|
18
|
+
start = Time.now
|
19
|
+
result = yield
|
20
|
+
timing(stat, ((Time.now - start) * 1000).round, sample_rate)
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def increment(*args)
|
25
|
+
increments << Increment.new(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def decrement(*args)
|
29
|
+
decrements << Decrement.new(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def count(*args)
|
33
|
+
counters << Harness::Counter.new(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def gauge(*args)
|
37
|
+
gauges << Gauge.new(*args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,8 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module Harness
|
2
|
+
module Instrumentation
|
3
|
+
def timing(*args)
|
4
|
+
Harness.timing *args
|
5
|
+
end
|
6
|
+
|
7
|
+
def time(*args, &block)
|
8
|
+
Harness.time *args, &block
|
9
|
+
end
|
10
|
+
|
11
|
+
def gauge(*args)
|
12
|
+
Harness.gauge *args
|
13
|
+
end
|
14
|
+
|
15
|
+
def increment(*args)
|
16
|
+
Harness.increment *args
|
17
|
+
end
|
18
|
+
|
19
|
+
def decrement(*args)
|
20
|
+
Harness.decrement *args
|
21
|
+
end
|
7
22
|
end
|
8
23
|
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
ActiveSupport::Notifications.subscribe do |*args|
|
2
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
3
|
+
next if event.payload[:exception]
|
4
|
+
|
5
|
+
Harness::Timer.from_event(event).log if event.payload[:timer]
|
6
|
+
Harness::Counter.from_event(event).log if event.payload[:counter]
|
7
|
+
end
|
data/lib/harness/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class AcceptanceTest < MiniTest::Unit::TestCase
|
4
|
+
def test_works_with_actual_statsd
|
5
|
+
Harness.config.collector = Statsd.new
|
6
|
+
Harness.config.queue = Harness::SyncQueue.new
|
7
|
+
|
8
|
+
Harness.increment 'foo', 0.6
|
9
|
+
Harness.decrement 'foo', 0.5
|
10
|
+
Harness.count 'foo', 5, 0.5
|
11
|
+
|
12
|
+
Harness.gauge 'foo', 0.5, 0.1
|
13
|
+
|
14
|
+
Harness.timing 'foo', 0.5, 0.1
|
15
|
+
|
16
|
+
Harness.time 'foo', 1 do
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class ActiveSupportNotificationsTestCase < MiniTest::Unit::TestCase
|
4
|
+
def test_timers_are_logged
|
5
|
+
instrument 'test', timer: true
|
6
|
+
|
7
|
+
refute_empty timers
|
8
|
+
timer = timers.first
|
9
|
+
assert_equal 'test', timer.name
|
10
|
+
assert_kind_of Float, timer.ms
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_timer_name_can_be_customized
|
14
|
+
instrument 'test', timer: 'foo'
|
15
|
+
|
16
|
+
refute_empty timers
|
17
|
+
timer = timers.first
|
18
|
+
assert_equal 'foo', timer.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_timer_rate_can_be_customized
|
22
|
+
instrument 'test', timer: { rate: 0.5, name: 'foo', value: 5 }
|
23
|
+
|
24
|
+
refute_empty timers
|
25
|
+
timer = timers.first
|
26
|
+
assert_equal 0.5, timer.rate
|
27
|
+
assert_equal 'foo', timer.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_passed_value_does_not_override_blocks
|
31
|
+
instrument 'test', timer: { rate: 0.5, name: 'foo', value: 5 }
|
32
|
+
|
33
|
+
refute_empty timers
|
34
|
+
timer = timers.first
|
35
|
+
refute_equal 5, timer.value
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_does_not_log_timers_on_exception
|
39
|
+
begin
|
40
|
+
instrument 'test', timer: true do
|
41
|
+
raise
|
42
|
+
end
|
43
|
+
rescue
|
44
|
+
end
|
45
|
+
|
46
|
+
assert_empty timers
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_counters_with_no_value_are_treated_as_increments
|
50
|
+
instrument 'test', counter: true
|
51
|
+
|
52
|
+
refute_empty increments
|
53
|
+
counter = increments.first
|
54
|
+
assert_equal 'test', counter.name
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_counters_can_have_explicit_values
|
58
|
+
instrument 'test', counter: 5
|
59
|
+
|
60
|
+
refute_empty counters
|
61
|
+
counter = counters.first
|
62
|
+
assert_equal 'test', counter.name
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_counter_name_can_be_customized
|
66
|
+
instrument 'test', counter: 'foo'
|
67
|
+
|
68
|
+
refute_empty increments
|
69
|
+
counter = increments.first
|
70
|
+
assert_equal 'foo', counter.name
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_counter_rate_can_be_customized
|
74
|
+
instrument 'test', counter: { rate: 0.5, name: 'foo', value: 5 }
|
75
|
+
|
76
|
+
refute_empty counters
|
77
|
+
counter = counters.first
|
78
|
+
assert_equal 0.5, counter.rate
|
79
|
+
assert_equal 'foo', counter.name
|
80
|
+
assert_equal 5, counter.value
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_does_not_log_counters_on_exceptions
|
84
|
+
begin
|
85
|
+
instrument 'test', counter: true do
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
rescue
|
89
|
+
end
|
90
|
+
|
91
|
+
assert_empty counters
|
92
|
+
end
|
93
|
+
end
|