fozzie 0.0.27 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +2 -1
  2. data/Guardfile +2 -2
  3. data/README.md +22 -11
  4. data/Rakefile +1 -1
  5. data/fozzie.gemspec +8 -14
  6. data/lib/core_ext/module/monitor.rb +4 -3
  7. data/lib/fozzie.rb +6 -7
  8. data/lib/fozzie/adapter.rb +1 -0
  9. data/lib/fozzie/adapter/statsd.rb +86 -0
  10. data/lib/fozzie/bulk_dsl.rb +28 -0
  11. data/lib/fozzie/configuration.rb +32 -13
  12. data/lib/fozzie/dsl.rb +19 -0
  13. data/lib/fozzie/exception.rb +5 -0
  14. data/lib/fozzie/interface.rb +30 -15
  15. data/lib/fozzie/rack/middleware.rb +3 -1
  16. data/lib/fozzie/sniff.rb +28 -33
  17. data/lib/fozzie/version.rb +1 -1
  18. data/spec/config/fozzie.yml +2 -1
  19. data/spec/lib/core_ext/module/monitor_spec.rb +9 -0
  20. data/spec/lib/fozzie/adapter/statsd_spec.rb +82 -0
  21. data/spec/lib/fozzie/bulk_dsl_spec.rb +47 -0
  22. data/spec/lib/fozzie/configuration_spec.rb +34 -20
  23. data/spec/lib/fozzie/dsl_spec.rb +16 -0
  24. data/spec/lib/fozzie/rack/middleware_spec.rb +6 -36
  25. data/spec/lib/fozzie/rack/sinatra_spec.rb +31 -0
  26. data/spec/lib/fozzie/sniff_spec.rb +37 -37
  27. data/spec/lib/fozzie/version_spec.rb +1 -1
  28. data/spec/lib/fozzie_spec.rb +19 -3
  29. data/spec/shared_examples/fozzie_adapter.rb +7 -0
  30. data/spec/shared_examples/interface.rb +160 -0
  31. data/spec/spec_helper.rb +20 -10
  32. metadata +31 -74
  33. data/lib/core_ext/hash.rb +0 -16
  34. data/lib/fozzie/mill.rb +0 -50
  35. data/lib/fozzie/rails/engine.rb +0 -15
  36. data/lib/fozzie/rails/middleware.rb +0 -39
  37. data/lib/fozzie/railtie.rb +0 -15
  38. data/lib/fozzie/socket.rb +0 -53
  39. data/spec/lib/core_ext/hash_spec.rb +0 -33
  40. data/spec/lib/fozzie/interface_spec.rb +0 -180
  41. data/spec/lib/fozzie/mill_spec.rb +0 -43
  42. data/spec/lib/fozzie/rails/middleware_spec.rb +0 -125
@@ -1,8 +1,10 @@
1
+ require 'fozzie'
2
+
1
3
  module Fozzie
2
4
  module Rack
3
5
 
4
6
  # Time and record each request through a given Rack app
5
- # This middlewware times server processing for a resource, not view render.
7
+ # This middleware times server processing for a resource, not view render.
6
8
  class Middleware
7
9
 
8
10
  attr_reader :app
@@ -5,53 +5,48 @@ require 'facets/string/snakecase'
5
5
  module Fozzie
6
6
  module Sniff
7
7
 
8
- def self.included(klass)
9
- return if klass.include?(ClassMethods)
10
-
11
- klass.class_eval { extend ClassMethods }
8
+ def _monitor(bucket_name = nil)
9
+ @_monitor_flag = true
10
+ @_bucket_name = bucket_name
12
11
  end
13
12
 
14
- module ClassMethods
15
-
16
- def _monitor
17
- @_monitor_flag = true
18
- end
13
+ def _monitor_meth(target, &blk)
14
+ return if @_monitor_flag.nil? || !@_monitor_flag
19
15
 
20
- def _monitor_meth(target, &blk)
21
- return if @_monitor_flag.nil? || !@_monitor_flag
16
+ @_monitor_flag = false
17
+ bin = @_bucket_name || [self.name.snakecase, target.to_s.snakecase]
18
+ feature = :monitor
19
+ aliased_target = target.to_s.sub(/([?!=])$/, '')
20
+ punctuation = $1
22
21
 
23
- @_monitor_flag, feature, bin = false, :monitor, [self.name.snakecase, target.to_s.snakecase]
24
- aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
22
+ with = "#{aliased_target}_with_#{feature}#{punctuation}"
23
+ without = "#{aliased_target}_without_#{feature}#{punctuation}"
25
24
 
26
- with = "#{aliased_target}_with_#{feature}#{punctuation}"
27
- without = "#{aliased_target}_without_#{feature}#{punctuation}"
28
-
29
- blk.call(with, without, feature, bin)
30
- end
25
+ blk.call(with, without, feature, bin)
26
+ end
31
27
 
32
- def method_added(target)
33
- _monitor_meth(target) do |with, without, feature, bin|
34
- define_method(with) do |*args, &blk|
35
- S.time_for(bin) do
36
- args.empty? ? self.send(without, &blk) : self.send(without, *args, &blk)
37
- end
28
+ def method_added(target)
29
+ _monitor_meth(target) do |with, without, feature, bin|
30
+ define_method(with) do |*args, &blk|
31
+ S.time_for(bin) do
32
+ args.empty? ? self.send(without, &blk) : self.send(without, *args, &blk)
38
33
  end
39
- self.alias_method_chain(target, feature)
40
34
  end
35
+ self.alias_method_chain(target, feature)
41
36
  end
37
+ end
42
38
 
43
- def singleton_method_added(target)
44
- _monitor_meth(target) do |with, without, feature, bin|
45
- define_singleton_method(with) do |*args, &blk|
46
- S.time_for(bin) do
47
- args.empty? ? send(without, &blk) : send(without, *args, &blk)
48
- end
39
+ def singleton_method_added(target)
40
+ _monitor_meth(target) do |with, without, feature, bin|
41
+ define_singleton_method(with) do |*args, &blk|
42
+ S.time_for(bin) do
43
+ args.empty? ? send(without, &blk) : send(without, *args, &blk)
49
44
  end
50
- self.singleton_class.class_eval { alias_method_chain target, feature }
51
45
  end
46
+ self.singleton_class.class_eval { alias_method_chain target, feature }
52
47
  end
53
-
54
48
  end
55
49
 
56
50
  end
51
+
57
52
  end
@@ -1,3 +1,3 @@
1
1
  module Fozzie
2
- VERSION = "0.0.27"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -2,4 +2,5 @@
2
2
  test:
3
3
  appname: 'fozzie'
4
4
  host: '1.1.1.1'
5
- port: 9876
5
+ port: 9876
6
+ enable_rails_middleware: false
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Module do
4
+
5
+ it "includes _monitor method when required" do
6
+ Module.should respond_to(:_monitor)
7
+ end
8
+
9
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'fozzie/adapter/statsd'
3
+
4
+ module Fozzie::Adapter
5
+ describe Statsd do
6
+ it_behaves_like "fozzie adapter"
7
+
8
+ # Switch to Statsd adapter for the duration of this test
9
+ before(:all) do
10
+ Fozzie.c.adapter = :Statsd
11
+ end
12
+
13
+ after(:all) do
14
+ Fozzie.c.adapter = :TestAdapter
15
+ end
16
+
17
+ it "downcases any stat value" do
18
+ subject.should_receive(:send_to_socket).with {|bin| bin.match /\.foo/ }
19
+
20
+ subject.register(:bin => "FOO", :value => 1, :type => :gauge, :sample_rate => 1)
21
+ end
22
+
23
+ describe "#format_bucket" do
24
+ it "accepts arrays" do
25
+ subject.format_bucket([:foo, '2']).should match /foo.2$/
26
+ subject.format_bucket([:foo, '2']).should match /foo.2$/
27
+ subject.format_bucket(%w{foo bar}).should match /foo.bar$/
28
+ end
29
+
30
+ it "converts any values to strings for stat value, ignoring nil" do
31
+ subject.format_bucket([:foo, 1, nil, "@", "BAR"]).should =~ /foo.1._.bar/
32
+ end
33
+
34
+ it "replaces invalid chracters" do
35
+ subject.format_bucket([:foo, ':']).should match /foo.#{subject.class::RESERVED_CHARS_REPLACEMENT}$/
36
+ subject.format_bucket([:foo, '@']).should match /foo.#{subject.class::RESERVED_CHARS_REPLACEMENT}$/
37
+ subject.format_bucket('foo.bar.|').should match /foo.bar.#{subject.class::RESERVED_CHARS_REPLACEMENT}$/
38
+ end
39
+ end
40
+
41
+ describe "#format_value" do
42
+ it "defaults type to gauge when type is not mapped" do
43
+ subject.format_value(1, :foo, 1).should eq '1|g'
44
+ end
45
+
46
+ it "converts basic values to string" do
47
+ subject.format_value(1, :count, 1).should eq '1|c'
48
+ end
49
+ end
50
+
51
+ it "ensures block is called on socket error" do
52
+ subject.socket.stub(:send) { raise SocketError }
53
+
54
+ proc { subject.register(:bin => 'data.bin', :value => 1, :type => :gauge, :sample_rate => 1) { sleep 0.01 } }.should_not raise_error
55
+ proc { subject.register(:bin => 'data.bin', :value => 1, :type => :gauge, :sample_rate => 1) { sleep 0.01 } }.should_not raise_error
56
+ end
57
+
58
+ it "raises Timeout on slow lookup" do
59
+ Fozzie.c.timeout = 0.01
60
+ subject.socket.stub(:send).with(any_args) { sleep 0.4 }
61
+
62
+ subject.register(:bin => 'data.bin', :value => 1, :type => :gauge, :sample_rate => 1).should eq false
63
+ end
64
+
65
+ describe "multiple stats in a single call" do
66
+
67
+ it "collects stats together with delimeter" do
68
+ Fozzie.c.disable_prefix
69
+
70
+ stats = [
71
+ { :bin => 'foo', :value => 1, :type => :count, :sample_rate => 1 },
72
+ { :bin => 'bar', :value => 1, :type => :gauge, :sample_rate => 1 },
73
+ { :bin => %w{foo bar}, :value => 100, :type => :timing, :sample_rate => 1 }
74
+ ]
75
+
76
+ subject.should_receive(:send_to_socket).with "foo:1|c\nbar:1|g\nfoo.bar:100|ms"
77
+
78
+ subject.register(stats)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module Fozzie
4
+ describe BulkDsl do
5
+
6
+ it_behaves_like "interface"
7
+
8
+ describe "#initialize" do
9
+
10
+ it "accepts and performs block" do
11
+ BulkDsl.any_instance.should_receive(:foo)
12
+
13
+ BulkDsl.new { foo }
14
+ end
15
+
16
+ end
17
+
18
+ it "sends statistics in one call" do
19
+ Fozzie.c.adapter.should_receive(:register).once
20
+
21
+ BulkDsl.new do
22
+ increment :foo
23
+ decrement :bar
24
+ end
25
+ end
26
+
27
+ it "scopes given block when arity provided" do
28
+ Fozzie.c.adapter.should_receive(:register).once
29
+
30
+ class Foo
31
+
32
+ def send_stats
33
+ BulkDsl.new do |s|
34
+ s.increment random_value
35
+ s.decrement random_value
36
+ end
37
+ end
38
+
39
+ def random_value; rand end
40
+
41
+ end
42
+
43
+ Foo.new.send_stats
44
+ end
45
+
46
+ end
47
+ end
@@ -2,7 +2,6 @@ require 'spec_helper'
2
2
  require 'resolv'
3
3
 
4
4
  describe Fozzie::Configuration do
5
-
6
5
  it "#host" do
7
6
  subject.host.should be_kind_of(String)
8
7
  end
@@ -12,27 +11,47 @@ describe Fozzie::Configuration do
12
11
  end
13
12
 
14
13
  it "attempts to load configuration from yaml" do
15
- c = Fozzie::Configuration.new({:env => 'test', :config_path => 'spec/'})
16
- c.stubs(:origin_name).returns ""
14
+ c = Fozzie::Configuration.new({
15
+ env: 'test',
16
+ config_path: 'spec/',
17
+ adapter: :TestAdapter
18
+ })
19
+ c.stub(:origin_name => "")
17
20
  c.host.should eq '1.1.1.1'
18
21
  c.port.should eq 9876
19
22
  c.appname.should eq 'fozzie'
20
- c.data_prefix.should eq 'fozzie.test'
23
+ c.data_prefix.should eq "fozzie#{c.safe_separator}test"
21
24
  end
22
25
 
23
26
  it "defaults env" do
24
27
  subject.env.should eq 'test'
25
28
  end
26
29
 
27
- describe "#prefix and #data_prefix" do
30
+ describe "#adapter" do
31
+ it "throw error on incorrect assignment" do
32
+ -> { Fozzie::Configuration.new({:env => 'test', :adapter => 'foo'}) }.should raise_error(Fozzie::AdapterMissing)
33
+ end
34
+
35
+ it "defaults adapter to Statsd" do
36
+ subject.adapter.should be_kind_of(Fozzie::Adapter::Statsd)
37
+ end
38
+ end
39
+
40
+ describe "#disable_prefix" do
41
+ it "sets the data_prefix to nil" do
42
+ subject.disable_prefix
43
+ subject.data_prefix.should be_nil
44
+ end
45
+ end
28
46
 
47
+ describe "#prefix and #data_prefix" do
29
48
  it "creates a #data_prefix" do
30
- subject.stubs(:origin_name).returns("")
49
+ subject.stub(:origin_name => "")
31
50
  subject.data_prefix.should eq 'test'
32
51
  end
33
52
 
34
53
  it "creates a #data_prefix with appname when set" do
35
- subject.stubs(:origin_name).returns("")
54
+ subject.stub(:origin_name => "")
36
55
  subject.appname = 'astoria'
37
56
  subject.data_prefix.should eq 'astoria.test'
38
57
  end
@@ -48,11 +67,10 @@ describe Fozzie::Configuration do
48
67
  end
49
68
 
50
69
  it "allows dynamic injection of value to prefix" do
51
- subject.stubs(:origin_name).returns("")
70
+ subject.stub(:origin_name => "")
52
71
  subject.prefix << 'git-sha-1234'
53
72
  subject.data_prefix.should eq 'test.git-sha-1234'
54
73
  end
55
-
56
74
  end
57
75
 
58
76
  it "handles missing configuration namespace" do
@@ -66,46 +84,42 @@ describe Fozzie::Configuration do
66
84
  end
67
85
 
68
86
  describe "#sniff?" do
69
-
70
87
  it "defaults to false for testing" do
71
- subject.stubs(:env).returns('test')
88
+ subject.stub(:env => "test")
72
89
  subject.sniff?.should be_false
73
90
  end
74
91
 
75
92
  it "defaults true when in development" do
76
- subject.stubs(:env).returns('development')
93
+ subject.stub(:env => "development")
77
94
  subject.sniff?.should be_true
78
95
  end
79
96
 
80
97
  it "defaults true when in production" do
81
- subject.stubs(:env).returns('production')
98
+ subject.stub(:env => "production")
82
99
  subject.sniff?.should be_true
83
100
  end
84
-
85
101
  end
86
102
 
87
103
  describe "#sniff_envs allows configuration for #sniff?" do
88
- let!(:sniff_envs) { subject.stubs(:sniff_envs).returns(['test']) }
104
+ let!(:sniff_envs) { subject.stub(:sniff_envs => ['test']) }
89
105
 
90
106
  it "scopes to return false" do
91
- subject.stubs(:env).returns('development')
107
+ subject.stub(:env => "development")
92
108
  subject.sniff?.should be_false
93
109
  end
94
110
 
95
111
  it "scopes to return true" do
96
- subject.stubs(:env).returns('test')
112
+ subject.stub(:env => "test")
97
113
  subject.sniff?.should be_true
98
114
  end
99
115
 
100
116
  end
101
117
 
102
118
  describe "ignoring prefix" do
103
-
104
119
  it "does not use prefix when set to ignore" do
105
120
  subject.disable_prefix
106
121
  subject.ignore_prefix.should eq(true)
107
122
  end
108
-
109
123
  end
110
124
 
111
- end
125
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'fozzie/interface'
3
+
4
+ describe Fozzie::Dsl do
5
+
6
+ subject { Fozzie::Dsl.instance }
7
+
8
+ it_behaves_like "interface"
9
+
10
+ it "acts an a singleton" do
11
+ Fozzie.c.namespaces.each do |k|
12
+ Kernel.const_get(k).should eq Fozzie::Dsl.instance
13
+ end
14
+ end
15
+
16
+ end
@@ -1,12 +1,11 @@
1
1
  require 'spec_helper'
2
- require 'sinatra/base'
3
- require 'rack/test'
2
+ require 'fozzie/rack/middleware'
4
3
 
5
4
  describe Fozzie::Rack::Middleware do
6
5
 
7
6
  subject do
8
7
  unless defined?(RackApp)
9
- RackApp = Class.new { def call(env); env end }
8
+ RackApp = Class.new { def call(env); env end }
10
9
  end
11
10
  Fozzie::Rack::Middleware.new RackApp.new
12
11
  end
@@ -24,30 +23,29 @@ describe Fozzie::Rack::Middleware do
24
23
 
25
24
  it "ignored stats request when path not valid" do
26
25
  fake_env = { 'PATH_INFO' => '' }
27
- subject.expects(:call_without_timer).with(fake_env)
26
+ subject.should_receive(:call_without_timer).with(fake_env)
28
27
  subject.call(fake_env)
29
28
  end
30
29
 
31
30
  it "passes request with timer on index" do
32
31
  fake_env = { 'PATH_INFO' => '/' }
33
- subject.expects(:call_with_timer).with('index.render', fake_env)
32
+ subject.should_receive(:call_with_timer).with('index.render', fake_env)
34
33
  subject.call(fake_env)
35
34
  end
36
35
 
37
36
  it "passes request with timer on full path" do
38
37
  fake_env = { 'PATH_INFO' => '/somewhere/nice' }
39
- subject.expects(:call_with_timer).with('somewhere.nice.render', fake_env)
38
+ subject.should_receive(:call_with_timer).with('somewhere.nice.render', fake_env)
40
39
  subject.call(fake_env)
41
40
  end
42
41
 
43
42
  it "passes request onto app" do
44
43
  envs = ['', '/', '/somewhere/nice'].each do |p|
45
44
  fake_env = { 'PATH_INFO' => p }
46
- subject.app.expects(:call).with(fake_env)
45
+ subject.app.should_receive(:call).with(fake_env)
47
46
  subject.call(fake_env)
48
47
  end
49
48
  end
50
-
51
49
  end
52
50
 
53
51
  describe "#generate_key" do
@@ -69,32 +67,4 @@ describe Fozzie::Rack::Middleware do
69
67
 
70
68
  end
71
69
 
72
- end
73
-
74
- describe "Sinatra Server with Middleware" do
75
- include Rack::Test::Methods
76
-
77
- def app
78
- Sinatra.new do
79
- set :environment, :test
80
- use Fozzie::Rack::Middleware
81
- get('/') { "echo" }
82
- get('/somewhere/nice') { "echo" }
83
- end
84
- end
85
-
86
- it "sends stats request on root" do
87
- S.expects(:timing).with('index.render', any_parameters)
88
- get '/'
89
- last_response.should be_ok
90
- last_response.body.should == 'echo'
91
- end
92
-
93
- it "sends stats request on nested path" do
94
- S.expects(:timing).with('somewhere.nice.render', any_parameters)
95
- get '/somewhere/nice'
96
- last_response.should be_ok
97
- last_response.body.should == 'echo'
98
- end
99
-
100
70
  end