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