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,16 +0,0 @@
1
- class Hash
2
-
3
- # Return self as symbolized keys hash
4
- def symbolize_keys
5
- self.dup.inject({}) do |hsh, (k,v)|
6
- hsh[k.to_s.to_sym] = (v.respond_to?(:symbolize_keys) ? v.symbolize_keys : v)
7
- hsh
8
- end
9
- end
10
-
11
- # Replace self with symbolized keys hash
12
- def symbolize_keys!
13
- self.replace(self.symbolize_keys)
14
- end
15
-
16
- end
@@ -1,50 +0,0 @@
1
- require 'uri'
2
-
3
- module Fozzie
4
- class Mill
5
-
6
- DELIMETER = ';'
7
- METRICS = %w{ttfb load}
8
-
9
- attr_reader :str, :args
10
-
11
- def initialize(str = "")
12
- @str = str
13
- escaped_split = str.split(DELIMETER).map!{|x| URI.unescape(x) }
14
- @args = Hash[*escaped_split]
15
- end
16
-
17
- def self.register(str = "")
18
- new(str).register
19
- end
20
-
21
- def register
22
- return self unless self.has_href?
23
- METRICS.each do |k|
24
- next unless self.respond_to?(k.to_sym)
25
- S.timing((namespace << ['page', k]).flatten, self.send(k.to_sym))
26
- end
27
-
28
- self
29
- end
30
-
31
- def load
32
- @load ||= @args['domComplete'].to_i - @args['fetchStart'].to_i
33
- end
34
-
35
- def ttfb
36
- @ttfb ||= @args['responseStart'].to_i - @args['fetchStart'].to_i
37
- end
38
-
39
- def has_href?
40
- !@args['href'].nil?
41
- end
42
-
43
- def namespace
44
- @uri ||= URI(@args['href'])
45
- @path ||= @uri.path.strip.split('/').reject(&:empty?)
46
- @path.dup
47
- end
48
-
49
- end
50
- end
@@ -1,15 +0,0 @@
1
- require 'json'
2
- require 'fozzie/mill'
3
-
4
- module Fozzie
5
- module Rails
6
- class Engine < ::Rails::Engine
7
-
8
- endpoint Proc.new { |env|
9
- Fozzie::Mill.register(env['QUERY_STRING'].gsub('d=', ''))
10
- [201, {"Content-Type" => "text/html"}, [""]]
11
- }
12
-
13
- end
14
- end
15
- end
@@ -1,39 +0,0 @@
1
- require 'fozzie/rack/middleware'
2
-
3
- module Fozzie
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.
8
- class Middleware < Fozzie::Rack::Middleware
9
-
10
- # Generates the statistics key for the current path
11
- def generate_key(env)
12
- path_str = env['PATH_INFO']
13
- request_method = env['REQUEST_METHOD']
14
-
15
- return nil unless path_str
16
-
17
- begin
18
- routing = routing_lookup
19
- path = routing.recognize_path(path_str, :method => request_method)
20
- stat = [path[:controller], path[:action], "render"].join('.')
21
- stat
22
- rescue => exc
23
- S.increment "routing.error"
24
- nil
25
- end
26
- end
27
-
28
- def routing_lookup
29
- (rails_version == 3 ? ::Rails.application.routes : ::ActionController::Routing::Routes)
30
- end
31
-
32
- def rails_version
33
- ::Rails.version.to_i
34
- end
35
-
36
- end
37
-
38
- end
39
- end
@@ -1,15 +0,0 @@
1
- require 'fozzie/rails/engine'
2
-
3
- class FozzieRailtie < Rails::Railtie
4
- initializer "fozzie_railtie.configure_rails_initialization" do |app|
5
-
6
- # Load up the middleware
7
- app.middleware.use Fozzie::Rails::Middleware
8
-
9
- # Add the Mill route
10
- app.routes.prepend do
11
- mount Fozzie::Rails::Engine => '/mill'
12
- end
13
-
14
- end
15
- end
@@ -1,53 +0,0 @@
1
- require 'socket'
2
-
3
- module Fozzie
4
- module Socket
5
-
6
- RESERVED_CHARS_REGEX = /[\:\|\@\s]/
7
- DELIMETER = '.'
8
-
9
- private
10
-
11
- # Send the statistic to the server
12
- #
13
- # Creates the Statsd key from the given values, and sends to socket (depending on sample rate)
14
- #
15
- def send(stat, delta, type, sample_rate)
16
- stat = [stat].flatten.compact.collect(&:to_s).join(DELIMETER).downcase
17
- stat = stat.gsub('::', DELIMETER).gsub(RESERVED_CHARS_REGEX, '_')
18
-
19
- k = [Fozzie.c.data_prefix, stat].compact.join(DELIMETER)
20
- k << ":"
21
- k << [delta, type].join('|')
22
- k << '@%s' % sample_rate.to_s if sample_rate < 1
23
-
24
- sampled(sample_rate) { send_to_socket(k.strip) }
25
- end
26
-
27
- # If the statistic is sampled, generate a condition to check if it's good to send
28
- def sampled(sample_rate)
29
- yield unless sample_rate < 1 and rand > sample_rate
30
- end
31
-
32
- # Send data to the server via the socket
33
- def send_to_socket(message)
34
- begin
35
- Fozzie.logger.debug {"Statsd: #{message}"} if Fozzie.logger
36
- Timeout.timeout(Fozzie.c.timeout) {
37
- res = socket.send(message, 0, Fozzie.c.host, Fozzie.c.port)
38
- Fozzie.logger.debug {"Statsd sent: #{res}"} if Fozzie.logger
39
- (res.to_i == message.length)
40
- }
41
- rescue => exc
42
- Fozzie.logger.debug {"Statsd Failure: #{exc.message}\n#{exc.backtrace}"} if Fozzie.logger
43
- false
44
- end
45
- end
46
-
47
- # The Socket we want to use to send data
48
- def socket
49
- @socket ||= ::UDPSocket.new
50
- end
51
-
52
- end
53
- end
@@ -1,33 +0,0 @@
1
- require 'spec_helper'
2
- require 'core_ext/hash'
3
-
4
- describe Hash do
5
-
6
- it { should respond_to(:symbolize_keys) }
7
- it { should respond_to(:symbolize_keys!) }
8
-
9
- it "manipulates keys correctly" do
10
- {
11
- '1' => 1,
12
- '2' => 2,
13
- '3' => 3
14
- }.symbolize_keys.should == {
15
- :"1" => 1,
16
- :"2" => 2,
17
- :"3" => 3
18
- }
19
- end
20
-
21
- it "returns copy when bang not provided" do
22
- hsh = { '1' => 1, '2' => 2, '3' => 3 }
23
- hsh.symbolize_keys
24
- hsh.should == { '1' => 1, '2' => 2, '3' => 3 }
25
- end
26
-
27
- it "replaces self when bang provided" do
28
- hsh = { '1' => 1, '2' => 2, '3' => 3 }
29
- hsh.symbolize_keys!
30
- hsh.should == { :"1" => 1, :"2" => 2, :"3" => 3 }
31
- end
32
-
33
- end
@@ -1,180 +0,0 @@
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 "downcases any stat value" do
15
- subject.expects(:send_to_socket)
16
- .with {|bin| bin.match /\.foo/ }
17
-
18
- subject.increment("FOO")
19
- end
20
-
21
- it "replaces invalid stat value chars" do
22
- subject.expects(:send_to_socket)
23
- .with {|bin| bin.match /\.foo_/ }
24
- .times(4)
25
-
26
- subject.increment("FOO:")
27
- subject.increment("FOO@")
28
- subject.increment("FOO|")
29
- subject.increment(["FOO|"])
30
- end
31
-
32
- it "converts any values to strings for stat value, ignoring nil" do
33
- subject.expects(:send_to_socket)
34
- .with {|bin| bin.match /\.foo.1._.bar/ }
35
-
36
- subject.increment([:foo, 1, nil, "@", "BAR"])
37
- end
38
-
39
- it "times a given block" do
40
- subject.expects(:timing).with() {|b, val, timing| b == 'data.bin' && (1..11).include?(val) }.twice
41
- subject.time_for('data.bin') { sleep 0.01 }
42
- subject.time_to_do('data.bin') { sleep 0.01 }
43
- end
44
-
45
- it "registers a commit" do
46
- subject.expects(:gauge).with(['event', 'commit', nil], anything).twice
47
- subject.commit
48
- subject.committed
49
- end
50
-
51
- it "registers a build" do
52
- subject.expects(:gauge).with(['event', 'build', nil], anything).twice
53
- subject.build
54
- subject.built
55
- end
56
-
57
- it "registers a deploy" do
58
- subject.expects(:gauge).with(['event', 'deploy', nil], anything).twice
59
- subject.deploy
60
- subject.deployed
61
- end
62
-
63
- it "ensures block is called on socket error" do
64
- UDPSocket.any_instance.stubs(:send).raises(SocketError)
65
- proc { subject.time_for('data.bin') { sleep 0.01 } }.should_not raise_error
66
- proc { subject.time_to_do('data.bin') { sleep 0.01 } }.should_not raise_error
67
- end
68
-
69
- it "raises exception if natural exception from block" do
70
- proc { subject.time_for('data.bin') { raise ArgumentError, "testing" } }.should raise_error(ArgumentError)
71
- end
72
-
73
- it "only calls the block once on SocketError" do
74
- UDPSocket.any_instance.stubs(:send).raises(SocketError)
75
- i = 0
76
- p = proc {|n| (n + 1) }
77
- val = subject.time_for('data.bin') { i+= p.call(i) }
78
- val.should == 1
79
- end
80
-
81
- it "raises Timeout on slow lookup" do
82
- Fozzie.c.timeout = 0.01
83
- UDPSocket.any_instance.stubs(:send).with(any_parameters) { sleep 0.4 }
84
- subject.increment('data.bin').should eq false
85
- end
86
-
87
- describe "#increment_on" do
88
-
89
- it "registers success" do
90
- subject.expects(:increment).with(["event.increment", "success"], 1)
91
- subject.increment_on('event.increment', true).should == true
92
- end
93
-
94
- it "registers failure" do
95
- subject.expects(:increment).with(["event.increment", "fail"], 1)
96
- subject.increment_on('event.increment', false).should == false
97
- end
98
-
99
- it "simply questions the passed val with if" do
100
- a = mock()
101
- a.expects(:save).returns({})
102
- subject.expects(:increment).with(["event.increment", "success"], 1)
103
- subject.increment_on('event.increment', a.save).should == {}
104
- end
105
-
106
- it "registers fail on nil return" do
107
- a = mock()
108
- a.expects(:save).returns(nil)
109
- subject.expects(:increment).with(["event.increment", "fail"], 1)
110
- subject.increment_on('event.increment', a.save).should == nil
111
- end
112
-
113
- describe "performing actions" do
114
-
115
- it "registers success" do
116
- a = mock()
117
- a.expects(:save).returns(true)
118
- subject.expects(:increment).with(["event.increment", "success"], 1)
119
- subject.increment_on('event.increment', a.save).should == true
120
- end
121
-
122
- it "registers failure" do
123
- a = mock()
124
- a.expects(:save).returns(false)
125
- subject.expects(:increment).with(["event.increment", "fail"], 1)
126
- subject.increment_on('event.increment', a.save).should == false
127
- end
128
-
129
- it "registers positive even when nested" do
130
- a = mock()
131
- a.expects(:save).returns(true)
132
- subject.expects(:timing).with('event.run', any_parameters)
133
- subject.expects(:increment).with(["event.increment", "success"], 1)
134
-
135
- res = subject.time_to_do "event.run" do
136
- subject.increment_on('event.increment', a.save)
137
- end
138
- res.should == true
139
- end
140
-
141
- it "registers negative even when nested" do
142
- a = mock()
143
- a.expects(:save).returns(false)
144
- subject.expects(:timing).with('event.run', any_parameters)
145
- subject.expects(:increment).with(["event.increment", "fail"], 1)
146
-
147
- res = subject.time_to_do "event.run" do
148
- subject.increment_on('event.increment', a.save)
149
- end
150
- res.should == false
151
- end
152
-
153
- it "allows passing of arrays for stat key" do
154
- subject.expects(:timing).with(['event', 'commit'], any_parameters)
155
- subject.time_to_do %w{event commit} do; end
156
- end
157
-
158
- end
159
-
160
- end
161
-
162
- describe "#gauge" do
163
- it "registers a gauge measurement" do
164
- subject.expects(:send).with("mystat", 99, "g", 1)
165
- subject.gauge("mystat", 99)
166
- end
167
- end
168
-
169
- describe "without prefix" do
170
-
171
- it "registers stats without app, etc" do
172
- Fozzie.c.disable_prefix
173
- subject.expects(:send_to_socket).with {|bin| bin.match(/^mystat/) }
174
-
175
- subject.gauge("mystat", 99)
176
- end
177
-
178
- end
179
-
180
- end
@@ -1,43 +0,0 @@
1
- require 'spec_helper'
2
- require 'fozzie/mill'
3
-
4
- module Fozzie
5
- describe Mill do
6
- let(:str) { "userAgent;Mozilla/5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_7_3%29%20AppleWebKit/535.19%20%28KHTML%2C%20like%20Gecko%29%20Chrome/18.0.1025.165%20Safari/535.19;vendorSub;;vendor;Google%20Inc.;onLine;true;appCodeName;Mozilla;cookieEnabled;true;product;Gecko;language;en-US;appVersion;5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_7_3%29%20AppleWebKit/535.19%20%28KHTML%2C%20like%20Gecko%29%20Chrome/18.0.1025.165%20Safari/535.19;appName;Netscape;productSub;20030107;platform;MacIntel;domainLookupStart;1335538507546;domInteractive;1335538508237;domComplete;1335538508323;loadEventEnd;0;requestStart;1335538507546;connectEnd;1335538507546;domContentLoadedEventEnd;1335538508237;unloadEventStart;1335538508050;domainLookupEnd;1335538507546;fetchStart;1335538507545;unloadEventEnd;1335538508050;connectStart;1335538507546;secureConnectionStart;0;redirectEnd;0;domContentLoadedEventStart;1335538508237;loadEventStart;1335538508323;responseStart;1335538508049;redirectStart;0;domLoading;1335538508182;responseEnd;1335538508050;navigationStart;1335538507545;href;http://localhost:8080/" }
7
- subject { Mill }
8
-
9
- describe "with environment arguments" do
10
-
11
- it "creates args hash from string" do
12
- inst = subject.register(str)
13
- inst.args['appCodeName'].should eq 'Mozilla'
14
- inst.args['cookieEnabled'].should eq 'true'
15
- end
16
-
17
- it "handles escaped values" do
18
- inst = subject.register(str)
19
- inst.args['userAgent'].should eq 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.165 Safari/535.19'
20
- inst.args['appVersion'].should eq '5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.165 Safari/535.19'
21
- end
22
-
23
- it "skips if not passed" do
24
- S.expects(:timing).never
25
- subject.register("")
26
- end
27
-
28
- describe "registers all mapped metrics" do
29
- let!(:expects) {
30
- S.expects(:timing).with(["page", "ttfb"], 504)
31
- S.expects(:timing).with(["page", "load"], 778)
32
- }
33
-
34
- it "dom complete" do
35
- subject.register(str)
36
- end
37
-
38
- end
39
-
40
- end
41
-
42
- end
43
- end