goliath-chimp 0.0.1 → 0.0.2
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.
- data/lib/goliath/chimp.rb +5 -0
- data/lib/goliath/chimp/plugins/activity_monitor.rb +37 -0
- data/lib/goliath/chimp/rack/env_extractor.rb +26 -0
- data/lib/goliath/chimp/rack/server_metrics.rb +38 -0
- data/lib/goliath/chimp/version.rb +1 -1
- data/spec/plugins/activity_monitor_spec.rb +38 -0
- data/spec/rack/env_extractor_spec.rb +74 -0
- data/spec/rack/server_metrics_spec.rb +46 -0
- data/spec/spec_helper.rb +3 -0
- metadata +13 -4
data/lib/goliath/chimp.rb
CHANGED
@@ -3,9 +3,14 @@ require 'gorillib/metaprogramming/concern'
|
|
3
3
|
require 'multi_json'
|
4
4
|
|
5
5
|
require 'goliath/chimp/handler'
|
6
|
+
|
7
|
+
require 'goliath/chimp/plugins/activity_monitor'
|
8
|
+
|
9
|
+
require 'goliath/chimp/rack/env_extractor'
|
6
10
|
require 'goliath/chimp/rack/api_version'
|
7
11
|
require 'goliath/chimp/rack/control_methods'
|
8
12
|
require 'goliath/chimp/rack/force_content_type'
|
13
|
+
require 'goliath/chimp/rack/server_metrics'
|
9
14
|
|
10
15
|
require 'goliath/chimp/rack/formatters/json'
|
11
16
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Goliath::Chimp
|
2
|
+
module Plugin
|
3
|
+
class ActivityMonitor
|
4
|
+
|
5
|
+
attr_reader :logger, :status, :previous
|
6
|
+
|
7
|
+
def initialize(address, port, config, status, logger)
|
8
|
+
@status = status
|
9
|
+
@logger = logger
|
10
|
+
@previous = Time.now.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(options = {})
|
14
|
+
interval = options[:window] || 60
|
15
|
+
EM::Synchrony.add_periodic_timer(interval) do
|
16
|
+
current = latency
|
17
|
+
status[:reactor] = {
|
18
|
+
latency: current,
|
19
|
+
ratio: (current / interval).round(6),
|
20
|
+
}
|
21
|
+
report status
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def latency
|
26
|
+
snapshot = Time.now.to_f
|
27
|
+
laten = snapshot - previous
|
28
|
+
@previous = snapshot
|
29
|
+
laten
|
30
|
+
end
|
31
|
+
|
32
|
+
def report metric
|
33
|
+
logger.debug metric
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Goliath::Chimp
|
2
|
+
module Rack
|
3
|
+
module EnvExtractor
|
4
|
+
|
5
|
+
# Helper method for extracting potentially nested
|
6
|
+
# values from the env hash. Also normalizes
|
7
|
+
# string vs. symbol access.
|
8
|
+
#
|
9
|
+
def extract_from_env(env, key, default = nil)
|
10
|
+
return default unless env.is_a? Hash
|
11
|
+
case key
|
12
|
+
when String, Symbol
|
13
|
+
env[key.to_s] || env[key.to_sym] || default
|
14
|
+
when Array
|
15
|
+
slice = env[key.shift]
|
16
|
+
key.empty? ? slice : extract_from_env(slice, key)
|
17
|
+
when Hash
|
18
|
+
extract_from_env(env, key.to_a.flatten)
|
19
|
+
else
|
20
|
+
default
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Goliath::Chimp
|
2
|
+
module Rack
|
3
|
+
class ServerMetrics
|
4
|
+
include Goliath::Rack::AsyncMiddleware
|
5
|
+
include EnvExtractor
|
6
|
+
|
7
|
+
attr_reader :path, :env_key, :default
|
8
|
+
|
9
|
+
def initialize(app, options = {})
|
10
|
+
@app = app
|
11
|
+
@path = options[:path] || '/metrics'
|
12
|
+
@env_key = options[:env_key]
|
13
|
+
@default = options[:default] || '/*'
|
14
|
+
end
|
15
|
+
|
16
|
+
def call env
|
17
|
+
if env['PATH_INFO'] == path
|
18
|
+
[ 200, {}, env['status'] ]
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def post_process(env, status, headers, body)
|
25
|
+
base_metrics = { count: 0, total_millis: 0 }
|
26
|
+
env['status'][:requests] ||= Hash.new{ |h, k| h[k] = Hash.new{ |h, k| h[k] = base_metrics } }
|
27
|
+
request_key = extract_from_env(env, env_key, default)
|
28
|
+
request_method = env['REQUEST_METHOD'].downcase.to_sym
|
29
|
+
metrics = env['status'][:requests][request_key][request_method]
|
30
|
+
metrics[:count] += 1
|
31
|
+
elapsed_millis = ((Time.now.to_f - env[:start_time]) * 1000).round
|
32
|
+
metrics[:total_millis] += elapsed_millis
|
33
|
+
[status, headers, body]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Goliath::Chimp::Plugin::ActivityMonitor do
|
4
|
+
|
5
|
+
let(:log_output) { StringIO.new }
|
6
|
+
let(:logger) { Logger.new log_output }
|
7
|
+
let(:example_time){ Time.new(2014, 1, 1, 3, 15) }
|
8
|
+
subject(:monitor) { described_class.new('localhost', 3467, {}, {}, logger) }
|
9
|
+
|
10
|
+
before(:each){ Time.stub(:now).and_return(example_time, example_time + 30) }
|
11
|
+
|
12
|
+
context '#run' do
|
13
|
+
it 'establishes a periodic latency monitor' do
|
14
|
+
EM.synchrony do
|
15
|
+
monitor.should_receive(:report).with(reactor: { latency: 30.0, ratio: 300.0 })
|
16
|
+
monitor.run(window: 0.1)
|
17
|
+
EM::Synchrony.add_timer(0.2){ EM.stop }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#latency' do
|
23
|
+
it 'returns the latency' do
|
24
|
+
monitor.latency.should eq(30.0)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'updates the previous snapshot' do
|
28
|
+
expect{ monitor.latency }.to change{ monitor.previous }.from(example_time.to_f).to((example_time + 30).to_f)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#report' do
|
33
|
+
it 'logs the metric' do
|
34
|
+
monitor.logger.should_receive(:debug).with('metrics')
|
35
|
+
monitor.report 'metrics'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Goliath::Chimp::Rack::EnvExtractor do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
extractor_mod = described_class
|
7
|
+
example_class = Class.new{ include extractor_mod }
|
8
|
+
example_class.new
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#extract_from_env' do
|
12
|
+
context 'default' do
|
13
|
+
let(:env){ Hash.new }
|
14
|
+
|
15
|
+
it 'returns the default value when key is not found' do
|
16
|
+
subject.extract_from_env(env, 'foo', 'default').should eq('default')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context String do
|
21
|
+
let(:env){ { 'foo' => 'bar' } }
|
22
|
+
|
23
|
+
it 'extracts the value using the string as a key' do
|
24
|
+
subject.extract_from_env(env, 'foo').should eq('bar')
|
25
|
+
subject.extract_from_env(env, 'bar').should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'extracts symbolic values' do
|
29
|
+
env = { foo: 'bar' }
|
30
|
+
subject.extract_from_env(env, 'foo').should eq('bar')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context Symbol do
|
35
|
+
let(:env){ { foo: 'bar' } }
|
36
|
+
|
37
|
+
it 'extracts the value using the symbol as a key' do
|
38
|
+
subject.extract_from_env(env, :foo).should eq('bar')
|
39
|
+
subject.extract_from_env(env, :bar).should be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'extracts string values' do
|
43
|
+
env = { 'foo' => 'bar' }
|
44
|
+
subject.extract_from_env(env, :foo).should eq('bar')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context Array do
|
49
|
+
let(:env){ { foo: { bar: 'baz' } } }
|
50
|
+
|
51
|
+
it 'extracts the value using the array as a set of keys' do
|
52
|
+
subject.extract_from_env(env, [:foo, :bar]).should eq('baz')
|
53
|
+
subject.extract_from_env(env, [:bar, :foo]).should be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context Hash do
|
58
|
+
let(:env){ { foo: { bar: 'baz' } } }
|
59
|
+
|
60
|
+
it 'extracts the value using the hash as a set of keys' do
|
61
|
+
subject.extract_from_env(env, { foo: :bar }).should eq('baz')
|
62
|
+
subject.extract_from_env(env, { bar: :foo }).should be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'other' do
|
67
|
+
let(:env){ Hash.new }
|
68
|
+
|
69
|
+
it 'returns the default if given an invalid key' do
|
70
|
+
subject.extract_from_env(env, 123, 'default').should eq('default')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Goliath::Chimp::Rack::ServerMetrics do
|
4
|
+
|
5
|
+
let(:app) { double :app }
|
6
|
+
let(:status) { Hash.new }
|
7
|
+
let(:example_time){ Time.new(2014, 1, 1, 3, 15) }
|
8
|
+
let(:env) { { 'status' => status, endpoint: 'images', start_time: example_time.to_f } }
|
9
|
+
subject { described_class.new(app, env_key: 'endpoint') }
|
10
|
+
|
11
|
+
context '#call', 'when /metrics' do
|
12
|
+
it 'does not call the app' do
|
13
|
+
app.should_not_receive(:call)
|
14
|
+
env['PATH_INFO'] = '/metrics'
|
15
|
+
subject.call env
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns the current status' do
|
19
|
+
env['PATH_INFO'] = '/metrics'
|
20
|
+
subject.call(env).should eq([200, {}, status])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context '#call', 'when /^version' do
|
25
|
+
it 'does nothing' do
|
26
|
+
app.should_receive(:call)
|
27
|
+
subject.should_receive(:post_process)
|
28
|
+
env['PATH_INFO'] = '/foobar'
|
29
|
+
subject.call env
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#post_process' do
|
34
|
+
it 'does not alter the response' do
|
35
|
+
env['REQUEST_METHOD'] = 'GET'
|
36
|
+
subject.post_process(env, 200, {}, {}).should eq([200, {}, {}])
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'attaches request metrics to the status' do
|
40
|
+
env['REQUEST_METHOD'] = 'GET'
|
41
|
+
Time.stub(:now).and_return(example_time + 30)
|
42
|
+
subject.post_process(env, 200, {}, {})
|
43
|
+
status.should eq(requests: { 'images' => { get: { count: 1, total_millis: 30000 } } })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goliath-chimp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: goliath
|
@@ -80,19 +80,25 @@ files:
|
|
80
80
|
- goliath-chimp.gemspec
|
81
81
|
- lib/goliath/chimp.rb
|
82
82
|
- lib/goliath/chimp/handler.rb
|
83
|
+
- lib/goliath/chimp/plugins/activity_monitor.rb
|
83
84
|
- lib/goliath/chimp/rack/api_version.rb
|
84
85
|
- lib/goliath/chimp/rack/control_methods.rb
|
86
|
+
- lib/goliath/chimp/rack/env_extractor.rb
|
85
87
|
- lib/goliath/chimp/rack/force_content_type.rb
|
86
88
|
- lib/goliath/chimp/rack/formatters/json.rb
|
89
|
+
- lib/goliath/chimp/rack/server_metrics.rb
|
87
90
|
- lib/goliath/chimp/rack/validation/required_routes.rb
|
88
91
|
- lib/goliath/chimp/rack/validation/route_handler.rb
|
89
92
|
- lib/goliath/chimp/rack/validation/routes.rb
|
90
93
|
- lib/goliath/chimp/version.rb
|
91
94
|
- spec/handler_spec.rb
|
95
|
+
- spec/plugins/activity_monitor_spec.rb
|
92
96
|
- spec/rack/api_version_spec.rb
|
93
97
|
- spec/rack/control_methods_spec.rb
|
98
|
+
- spec/rack/env_extractor_spec.rb
|
94
99
|
- spec/rack/force_content_type_spec.rb
|
95
100
|
- spec/rack/formatters/json_spec.rb
|
101
|
+
- spec/rack/server_metrics_spec.rb
|
96
102
|
- spec/rack/validations/required_routes_spec.rb
|
97
103
|
- spec/rack/validations/route_handler_spec.rb
|
98
104
|
- spec/rack/validations/routes_spec.rb
|
@@ -112,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
118
|
version: '0'
|
113
119
|
segments:
|
114
120
|
- 0
|
115
|
-
hash:
|
121
|
+
hash: 3845754265470155617
|
116
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
123
|
none: false
|
118
124
|
requirements:
|
@@ -121,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
127
|
version: '0'
|
122
128
|
segments:
|
123
129
|
- 0
|
124
|
-
hash:
|
130
|
+
hash: 3845754265470155617
|
125
131
|
requirements: []
|
126
132
|
rubyforge_project:
|
127
133
|
rubygems_version: 1.8.23
|
@@ -130,10 +136,13 @@ specification_version: 3
|
|
130
136
|
summary: Collection of Chimp-inspired Goliath/Rack utility classes
|
131
137
|
test_files:
|
132
138
|
- spec/handler_spec.rb
|
139
|
+
- spec/plugins/activity_monitor_spec.rb
|
133
140
|
- spec/rack/api_version_spec.rb
|
134
141
|
- spec/rack/control_methods_spec.rb
|
142
|
+
- spec/rack/env_extractor_spec.rb
|
135
143
|
- spec/rack/force_content_type_spec.rb
|
136
144
|
- spec/rack/formatters/json_spec.rb
|
145
|
+
- spec/rack/server_metrics_spec.rb
|
137
146
|
- spec/rack/validations/required_routes_spec.rb
|
138
147
|
- spec/rack/validations/route_handler_spec.rb
|
139
148
|
- spec/rack/validations/routes_spec.rb
|