fozzie 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,121 @@
1
+ require 'singleton'
2
+ require 'fozzie/socket'
3
+
4
+ module Fozzie
5
+ class Interface
6
+ include Fozzie::Socket, Singleton
7
+
8
+ # Increments the given stat by one, with an optional sample rate
9
+ #
10
+ # `Stats.increment 'wat'`
11
+ def increment(stat, sample_rate=1)
12
+ count(stat, 1, sample_rate)
13
+ end
14
+
15
+ # Decrements the given stat by one, with an optional sample rate
16
+ #
17
+ # `Stats.decrement 'wat'`
18
+ def decrement(stat, sample_rate=1)
19
+ count(stat, -1, sample_rate)
20
+ end
21
+
22
+ # Registers a count for the given stat, with an optional sample rate
23
+ #
24
+ # `Stats.count 'wat', 500`
25
+ def count(stat, count, sample_rate=1)
26
+ send(stat, count, 'c', sample_rate)
27
+ end
28
+
29
+ # Registers a timing (in ms) for the given stat, with an optional sample rate
30
+ #
31
+ # `Stats.timing 'wat', 500`
32
+ def timing(stat, ms, sample_rate=1)
33
+ send(stat, ms, 'ms', sample_rate)
34
+ end
35
+
36
+ # Registers the time taken to complete a given block (in ms), with an optional sample rate
37
+ #
38
+ # `Stats.time 'wat' { # Do something... }`
39
+ def time(stat, sample_rate=1)
40
+ stat = stat.flatten.join('.') if stat.kind_of?(Array)
41
+ start = Time.now
42
+ result = yield
43
+ timing(stat, ((Time.now - start) * 1000).round, sample_rate)
44
+ result
45
+ end
46
+
47
+ # Registers the time taken to complete a given block (in ms), with an optional sample rate
48
+ #
49
+ # `Stats.time_to_do 'wat' { # Do something, again... }`
50
+ def time_to_do(stat, sample_rate=1, &block)
51
+ time_for(stat, sample_rate, &block)
52
+ end
53
+
54
+ # Registers the time taken to complete a given block (in ms), with an optional sample rate
55
+ #
56
+ # `Stats.time_for 'wat' { # Do something, grrr... }`
57
+ def time_for(stat, sample_rate=1, &block)
58
+ time(stat, sample_rate, &block)
59
+ end
60
+
61
+ # Registers a commit
62
+ #
63
+ # `Stats.commit`
64
+ def commit
65
+ event :commit
66
+ end
67
+
68
+ # Registers a commit
69
+ #
70
+ # `Stats.commit`
71
+ def committed
72
+ commit
73
+ end
74
+
75
+ # Registers that the app has been built
76
+ #
77
+ # `Stats.built`
78
+ def built
79
+ event :build
80
+ end
81
+
82
+ # Registers a build for the app
83
+ #
84
+ # `Stats.build`
85
+ def build
86
+ built
87
+ end
88
+
89
+ # Registers a deployed status for the given app
90
+ #
91
+ # `Stats.deployed 'watapp'`
92
+ def deployed(app = nil)
93
+ event :deploy, app
94
+ end
95
+
96
+ # Registers a deployment for the given app
97
+ #
98
+ # `Stats.deploy 'watapp'`
99
+ def deploy(app = nil)
100
+ deployed(app)
101
+ end
102
+
103
+ # Registers an increment on the result of the given boolean
104
+ #
105
+ # `Stats.increment_on 'wat', wat.random?`
106
+ def increment_on(stat, perf, sample_rate=1)
107
+ key = "#{stat}.%s" % (perf ? "success" : "fail")
108
+ increment(key, sample_rate)
109
+ perf
110
+ end
111
+
112
+ # Register an event of any type
113
+ #
114
+ # `Stats.event 'wat', 'app'`
115
+ def event(type, app = nil)
116
+ stat = ["event", type.to_s, app].compact.join('.')
117
+ timing stat, Time.now.usec
118
+ end
119
+
120
+ end
121
+ end
@@ -1,5 +1,8 @@
1
1
  module Fozzie
2
2
  module Rack
3
+
4
+ # Time and record each request through a given Rack app
5
+ # This middlewware times server processing for a resource, not view render.
3
6
  class Middleware
4
7
 
5
8
  attr_reader :app
@@ -2,8 +2,12 @@ require 'fozzie/rack/middleware'
2
2
 
3
3
  module Fozzie
4
4
  module Rails
5
+
6
+ # Time and record each request through a given Rails app
7
+ # This middlewware times server processing for a resource, not view render.
5
8
  class Middleware < Fozzie::Rack::Middleware
6
9
 
10
+ # Generates the statistics key for the current path
7
11
  def generate_key(env)
8
12
  path_str = env['PATH_INFO']
9
13
  return nil unless path_str
@@ -0,0 +1,44 @@
1
+ module Fozzie
2
+ module Socket
3
+
4
+ RESERVED_CHARS_REGEX = /[\:\|\@]/
5
+
6
+ private
7
+
8
+ # Send the statistic to the server
9
+ def send(stat, delta, type, sample_rate)
10
+ prefix = "%s." % Fozzie.c.data_prefix unless Fozzie.c.data_prefix.nil?
11
+ stat = stat.to_s.gsub('::', '.').gsub(RESERVED_CHARS_REGEX, '_')
12
+
13
+ k = "%s%s:%s|%s" % [prefix, stat, delta, type]
14
+ k << '|@%s' % sample_rate.to_s if sample_rate < 1
15
+
16
+ sampled(sample_rate) { send_to_socket(k.strip) }
17
+ end
18
+
19
+ # If the statistic is sampled, generate a condition to check if it's good to send
20
+ def sampled(sample_rate)
21
+ yield unless sample_rate < 1 and rand > sample_rate
22
+ end
23
+
24
+ # Send data to the server via the socket
25
+ def send_to_socket(message)
26
+ begin
27
+ Fozzie.logger.debug {"Statsd: #{message}"} if Fozzie.logger
28
+ Timeout.timeout(Fozzie.c.timeout) {
29
+ socket.send(message, 0, Fozzie.c.host, Fozzie.c.port)
30
+ true
31
+ }
32
+ rescue => exc
33
+ Fozzie.ogger.debug {"Statsd Failure: #{exc.message}\n#{exc.backtrace}"} if Fozzie.logger
34
+ false
35
+ end
36
+ end
37
+
38
+ # The Socket we want to use to send data
39
+ def socket
40
+ @socket ||= UDPSocket.new
41
+ end
42
+
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Fozzie
2
- VERSION = "0.0.15"
2
+ VERSION = "0.0.16"
3
3
  end
@@ -13,32 +13,32 @@ describe Fozzie::Configuration do
13
13
 
14
14
  it "attempts to load configuration from yaml" do
15
15
  c = Fozzie::Configuration.new({:env => 'test', :config_path => 'spec/'})
16
- c.stubs(:origin_name).returns("")
17
- c.host.should == '1.1.1.1'
18
- c.port.should == 9876
19
- c.appname.should == 'fozzie'
20
- c.data_prefix.should == 'fozzie.test'
16
+ c.stubs(:origin_name).returns ""
17
+ c.host.should eq '1.1.1.1'
18
+ c.port.should eq 9876
19
+ c.appname.should eq 'fozzie'
20
+ c.data_prefix.should eq 'fozzie.test'
21
21
  end
22
22
 
23
23
  it "defaults env" do
24
- subject.env.should == 'development'
24
+ subject.env.should eq 'development'
25
25
  end
26
26
 
27
27
  it "creates a data prefix" do
28
28
  subject.stubs(:origin_name).returns("")
29
- subject.data_prefix.should == 'development'
29
+ subject.data_prefix.should eq 'development'
30
30
  end
31
31
 
32
32
  it "creates a data prefix with appname when set" do
33
33
  subject.stubs(:origin_name).returns("")
34
34
  subject.appname = 'astoria'
35
- subject.data_prefix.should == 'astoria.development'
35
+ subject.data_prefix.should eq 'astoria.development'
36
36
  end
37
37
 
38
38
  it "creates a prefix with origin" do
39
39
  subject.stubs(:origin_name).returns("app.server.local")
40
40
  subject.appname = 'astoria'
41
- subject.data_prefix.should == 'astoria.app-server-local.development'
41
+ subject.data_prefix.should eq 'astoria.app-server-local.development'
42
42
  end
43
43
 
44
44
  it "handles missing configuration namespace" do
@@ -51,37 +51,4 @@ describe Fozzie::Configuration do
51
51
  subject.namespaces.should include("S")
52
52
  end
53
53
 
54
- describe "#ip_from_host" do
55
-
56
- it "returns host if host an ip" do
57
- c = Fozzie::Configuration.new({:env => 'test', :config_path => nil, :host => '127.0.0.1'})
58
- c.ip_from_host.should == '127.0.0.1'
59
- end
60
-
61
- it "assigns nil on miss" do
62
- Resolv.expects(:getaddresses).with('some.awesome.log.server').returns([])
63
- c = Fozzie::Configuration.new({:env => 'test', :config_path => nil, :host => 'some.awesome.log.server'})
64
- c.ip_from_host.should == ""
65
- c.ip_from_host.should == ""
66
- end
67
-
68
- it "looks up ip from host" do
69
- c = Fozzie::Configuration.new({:env => 'test', :config_path => nil, :host => 'lonelyplanet.com'})
70
- c.ip_from_host.should match(/^(?:\d{1,3}\.){3}\d{1,3}$/)
71
- end
72
-
73
- it "caches the ip once it is retrieved" do
74
- Resolv.expects(:getaddresses).with('lonelyplanet.com').returns(["1.1.1.1"])
75
- c = Fozzie::Configuration.new({:env => 'test', :config_path => nil, :host => 'lonelyplanet.com'})
76
- c.ip_from_host.should == "1.1.1.1"
77
- end
78
-
79
- it "raises Timeout on slow lookup" do
80
- Resolv.stubs(:getaddresses).with('lonelyplanet.com') {|val| sleep 0.6; [] }
81
- c = Fozzie::Configuration.new(:env => 'test', :config_path => nil, :host => 'lonelyplanet.com', :timeout => 0.5)
82
- c.ip_from_host.should == ""
83
- end
84
-
85
- end
86
-
87
54
  end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+ require 'fozzie/interface'
3
+
4
+ describe Fozzie::Interface do
5
+
6
+ subject { Fozzie::Interface.instance }
7
+
8
+ it "acts an a singleton" do
9
+ Fozzie.c.namespaces.each do |k|
10
+ Kernel.const_get(k).should eq Fozzie::Interface.instance
11
+ end
12
+ end
13
+
14
+ it "times a given block" do
15
+ subject.expects(:timing).with() {|b, val, timing| b == 'data.bin' && (1000..1200).include?(val) }.twice
16
+ subject.time_for('data.bin') { sleep 1 }
17
+ subject.time_to_do('data.bin') { sleep 1 }
18
+ end
19
+
20
+ it "registers a commit" do
21
+ subject.expects(:timing).with('event.commit', anything).twice
22
+ subject.commit
23
+ subject.committed
24
+ end
25
+
26
+ it "registers a build" do
27
+ subject.expects(:timing).with('event.build', anything).twice
28
+ subject.build
29
+ subject.built
30
+ end
31
+
32
+ it "registers a deploy" do
33
+ subject.expects(:timing).with('event.deploy', anything).twice
34
+ subject.deploy
35
+ subject.deployed
36
+ end
37
+
38
+ it "ensures block is called on socket error" do
39
+ UDPSocket.any_instance.stubs(:send).raises(SocketError)
40
+ proc { subject.time_for('data.bin') { sleep 1 } }.should_not raise_error
41
+ proc { subject.time_to_do('data.bin') { sleep 1 } }.should_not raise_error
42
+ end
43
+
44
+ it "raises exception if natural exception from block" do
45
+ proc { subject.time_for('data.bin') { raise ArgumentError, "testing" } }.should raise_error(ArgumentError)
46
+ end
47
+
48
+ it "only calls the block once on SocketError" do
49
+ UDPSocket.any_instance.stubs(:send).raises(SocketError)
50
+ i = 0
51
+ p = proc {|n| (n + 1) }
52
+ val = subject.time_for('data.bin') { i+= p.call(i) }
53
+ val.should == 1
54
+ end
55
+
56
+ it "raises Timeout on slow lookup" do
57
+ UDPSocket.any_instance.stubs(:send).with(any_parameters) { sleep 0.6 }
58
+ subject.increment('data.bin').should eq false
59
+ end
60
+
61
+ describe "#increment_on" do
62
+
63
+ it "registers success" do
64
+ subject.expects(:increment).with("event.increment.success", 1)
65
+ subject.increment_on('event.increment', true).should == true
66
+ end
67
+
68
+ it "registers failure" do
69
+ subject.expects(:increment).with("event.increment.fail", 1)
70
+ subject.increment_on('event.increment', false).should == false
71
+ end
72
+
73
+ it "simply questions the passed val with if" do
74
+ a = mock()
75
+ a.expects(:save).returns({})
76
+ subject.expects(:increment).with("event.increment.success", 1)
77
+ subject.increment_on('event.increment', a.save).should == {}
78
+ end
79
+
80
+ it "registers fail on nil return" do
81
+ a = mock()
82
+ a.expects(:save).returns(nil)
83
+ subject.expects(:increment).with("event.increment.fail", 1)
84
+ subject.increment_on('event.increment', a.save).should == nil
85
+ end
86
+
87
+ describe "performing actions" do
88
+
89
+ it "registers success" do
90
+ a = mock()
91
+ a.expects(:save).returns(true)
92
+ subject.expects(:increment).with("event.increment.success", 1)
93
+ subject.increment_on('event.increment', a.save).should == true
94
+ end
95
+
96
+ it "registers failure" do
97
+ a = mock()
98
+ a.expects(:save).returns(false)
99
+ subject.expects(:increment).with("event.increment.fail", 1)
100
+ subject.increment_on('event.increment', a.save).should == false
101
+ end
102
+
103
+ it "registers positive even when nested" do
104
+ a = mock()
105
+ a.expects(:save).returns(true)
106
+ subject.expects(:timing).with('event.run', any_parameters)
107
+ subject.expects(:increment).with("event.increment.success", 1)
108
+
109
+ res = subject.time_to_do "event.run" do
110
+ subject.increment_on('event.increment', a.save)
111
+ end
112
+ res.should == true
113
+ end
114
+
115
+ it "registers negative even when nested" do
116
+ a = mock()
117
+ a.expects(:save).returns(false)
118
+ subject.expects(:timing).with('event.run', any_parameters)
119
+ subject.expects(:increment).with("event.increment.fail", 1)
120
+
121
+ res = subject.time_to_do "event.run" do
122
+ subject.increment_on('event.increment', a.save)
123
+ end
124
+ res.should == false
125
+ end
126
+
127
+ it "allows passing of arrays for stat key" do
128
+ subject.expects(:timing).with('event.commit', any_parameters)
129
+ subject.time_to_do %w{event commit} do; end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ end
@@ -5,11 +5,9 @@ require 'rack/test'
5
5
  describe Fozzie::Rack::Middleware do
6
6
 
7
7
  subject do
8
- RackApp = Class.new do
9
- def call(env)
10
- env
11
- end
12
- end unless defined?(RackApp)
8
+ unless defined?(RackApp)
9
+ RackApp = Class.new { def call(env); env end }
10
+ end
13
11
  Fozzie::Rack::Middleware.new RackApp.new
14
12
  end
15
13
 
@@ -2,9 +2,13 @@ require 'spec_helper'
2
2
  require 'action_controller'
3
3
 
4
4
  describe Fozzie::Rails::Middleware do
5
+ let(:path_info) { '/somewhere/railsy' }
6
+ let(:fake_env) { ({ 'PATH_INFO' => path_info }) }
5
7
 
6
8
  before :each do
7
- ActionController::RoutingError = Class.new(StandardError) unless defined?(ActionController::RoutingError)
9
+ unless defined?(ActionController::RoutingError)
10
+ ActionController::RoutingError = Class.new(StandardError)
11
+ end
8
12
  end
9
13
 
10
14
  subject do
@@ -24,55 +28,57 @@ describe Fozzie::Rails::Middleware do
24
28
  end
25
29
 
26
30
  describe "rails 2" do
27
-
28
- before :each do
29
- RailsApp.stubs(:version).returns("2.3.1")
30
- end
31
+ let!(:version) { RailsApp.stubs(:version).returns("2.3.1") }
31
32
 
32
33
  it "#generate_key" do
33
- s = '/somewhere/railsy'
34
- fake_env = { 'PATH_INFO' => s }
35
- ActionController::Routing::Routes.expects(:recognize_path).with(s).returns({:controller => 'somewhere', :action => 'railsy'})
34
+ ActionController::Routing::Routes.expects(:recognize_path)
35
+ .with(path_info)
36
+ .returns({:controller => 'somewhere', :action => 'railsy'})
37
+
36
38
  subject.generate_key(fake_env).should == 'somewhere.railsy.render'
37
39
  end
38
40
 
39
41
  it "returns nil on routing error" do
40
- s = '/somewhere/railsy'
41
- fake_env = { 'PATH_INFO' => s }
42
- ActionController::Routing::Routes.expects(:recognize_path).with(s).raises(ArgumentError)
42
+ ActionController::Routing::Routes.expects(:recognize_path)
43
+ .with(path_info)
44
+ .raises(ArgumentError)
45
+
43
46
  subject.generate_key(fake_env).should == nil
44
47
  end
45
48
 
46
49
  end
47
50
 
48
51
  describe "rails 3" do
49
-
50
- before :each do
51
- RailsApp.stubs(:version).returns("3.1.1")
52
- @app, @routing = Class.new, Class.new
53
- @app.stubs(:routes).returns(@routing)
54
- RailsApp.stubs(:application).returns(@app)
55
- end
52
+ let(:app) { Class.new }
53
+ let(:routing) { Class.new }
54
+ let!(:rails) { RailsApp.stubs(:application).returns(app) }
55
+ let!(:version) { RailsApp.stubs(:version).returns("3.1.1") }
56
+ let!(:routes) { app.stubs(:routes).returns(routing)}
56
57
 
57
58
  it "#generate_key" do
58
- s = '/somewhere/railsy'
59
- fake_env = { 'PATH_INFO' => s }
60
- @routing.expects(:recognize_path).with(s).returns({:controller => 'somewhere', :action => 'railsy'})
59
+ routing.expects(:recognize_path)
60
+ .with(path_info)
61
+ .returns({:controller => 'somewhere', :action => 'railsy'})
62
+
61
63
  subject.generate_key(fake_env).should == 'somewhere.railsy.render'
62
64
  end
63
65
 
64
66
  it "returns nil on error" do
65
- s = '/somewhere/railsy'
66
- fake_env = { 'PATH_INFO' => s }
67
- @routing.expects(:recognize_path).with(s).raises(ArgumentError)
67
+ routing.expects(:recognize_path)
68
+ .with(path_info)
69
+ .raises(ArgumentError)
70
+
68
71
  subject.generate_key(fake_env).should == nil
69
72
  end
70
-
73
+
71
74
  it "returns nil on routing error" do
72
- s = '/somewhere/railsy'
73
- fake_env = { 'PATH_INFO' => s }
74
- @routing.expects(:recognize_path).with(s).raises(ActionController::RoutingError)
75
- S.expects(:increment).with('routing.error')
75
+ routing.expects(:recognize_path)
76
+ .with(path_info)
77
+ .raises(ActionController::RoutingError)
78
+
79
+ S.expects(:increment)
80
+ .with('routing.error')
81
+
76
82
  subject.generate_key(fake_env).should == nil
77
83
  end
78
84