fozzie 0.0.27 → 1.0.0

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