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 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