fozzie 0.0.15 → 0.0.16

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