harness 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|