goliath-chimp 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,5 +1,5 @@
1
1
  module Goliath
2
2
  module Chimp
3
- VERSION = '0.0.1'
3
+ VERSION = '0.0.2'
4
4
  end
5
5
  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
@@ -1,3 +1,6 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+
1
4
  if ENV['RACK_COVERAGE']
2
5
  require 'simplecov'
3
6
  SimpleCov.start
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.1
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-01-30 00:00:00.000000000 Z
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: -3557348802184007792
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: -3557348802184007792
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