fozzie 0.0.20 → 0.0.21
Sign up to get free protection for your applications and to get access to all the features.
- data/Guardfile +0 -4
- data/README.md +6 -6
- data/fozzie.gemspec +6 -3
- data/lib/core_ext/module/monitor.rb +11 -0
- data/lib/fozzie.rb +5 -1
- data/lib/fozzie/configuration.rb +14 -8
- data/lib/fozzie/mill.rb +50 -0
- data/lib/fozzie/rails/engine.rb +15 -0
- data/lib/fozzie/rails/middleware.rb +7 -4
- data/lib/fozzie/railtie.rb +15 -0
- data/lib/fozzie/sniff.rb +53 -0
- data/lib/fozzie/socket.rb +3 -2
- data/lib/fozzie/version.rb +1 -1
- data/resources/mill.js.example +27 -0
- data/resources/mill.min.js.example +1 -0
- data/spec/lib/fozzie/configuration_spec.rb +34 -0
- data/spec/lib/fozzie/interface_spec.rb +7 -6
- data/spec/lib/fozzie/mill_spec.rb +43 -0
- data/spec/lib/fozzie/rails/middleware_spec.rb +14 -20
- data/spec/lib/fozzie/sniff_spec.rb +107 -0
- metadata +144 -44
- data/doc/lib/core_ext/hash.html +0 -79
- data/doc/lib/fozzie.html +0 -152
- data/doc/lib/fozzie/configuration.html +0 -175
- data/doc/lib/fozzie/interface.html +0 -286
- data/doc/lib/fozzie/rack/middleware.html +0 -97
- data/doc/lib/fozzie/rails/middleware.html +0 -97
- data/doc/lib/fozzie/socket.html +0 -129
- data/doc/lib/fozzie/version.html +0 -36
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -144,11 +144,7 @@ To time and register the controller actions within your Rails application, Fozzi
|
|
144
144
|
|
145
145
|
Based on the Rack middleware above, but is more involved in it's construction of the bucket value.
|
146
146
|
|
147
|
-
|
148
|
-
|
149
|
-
Rails::Initializer.run do |config|
|
150
|
-
config.middleware.use 'Fozzie::Rails::Middleware'
|
151
|
-
end
|
147
|
+
Fozzie::Rails::Middleware will automatically be invoked on Rails initialization.
|
152
148
|
|
153
149
|
## Bucket name prefixes
|
154
150
|
|
@@ -176,6 +172,10 @@ Fozzie will try to log these errors, but only if a logger has been applied (whic
|
|
176
172
|
|
177
173
|
This may change, depending on feedback and more production experience.
|
178
174
|
|
175
|
+
## Rails User Interface Performance Measuring
|
176
|
+
|
177
|
+
If you also require UI metrics, you can also include the Mill script in the bottom of any page you would like to measure (see `resources/mill.js` and `resources/mill.min.js`) and you start receiving measurements on page performance.
|
178
|
+
|
179
179
|
## Credits
|
180
180
|
|
181
181
|
Currently supported and maintained by [Marc Watts](marc.watts@lonelyplanet.co.uk) @Lonely Planet Online.
|
@@ -186,7 +186,7 @@ Big thanks to:
|
|
186
186
|
|
187
187
|
* [Etsy](http://codeascraft.etsy.com/) who's [Statsd](https://github.com/etsy/statsd) product has enabled us to come such a long way in a very short period of time. We love Etsy.
|
188
188
|
|
189
|
-
* [
|
189
|
+
* [reinh](https://github.com/reinh/statsd) for his [statsd](https://github.com/reinh/statsd) Gem.
|
190
190
|
|
191
191
|
## Comments and Feedback
|
192
192
|
|
data/fozzie.gemspec
CHANGED
@@ -21,19 +21,22 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
23
|
s.add_dependency 'sys-uname'
|
24
|
+
s.add_dependency 'facets'
|
24
25
|
|
25
26
|
s.add_development_dependency 'rake'
|
26
27
|
s.add_development_dependency 'rspec'
|
27
28
|
s.add_development_dependency 'mocha'
|
28
29
|
s.add_development_dependency 'syntax'
|
29
|
-
|
30
|
-
s.add_development_dependency 'rack-test'
|
31
30
|
s.add_development_dependency 'simplecov'
|
31
|
+
|
32
32
|
s.add_development_dependency 'sinatra'
|
33
|
-
s.add_development_dependency '
|
33
|
+
s.add_development_dependency 'rack-test'
|
34
|
+
s.add_development_dependency 'actionpack', '2.3.14'
|
34
35
|
|
35
36
|
s.add_development_dependency 'guard'
|
36
37
|
s.add_development_dependency 'guard-rspec'
|
37
38
|
s.add_development_dependency 'guard-rocco'
|
38
39
|
s.add_development_dependency 'fl-rocco'
|
40
|
+
|
41
|
+
s.add_development_dependency 'ruby_gntp'
|
39
42
|
end
|
data/lib/fozzie.rb
CHANGED
@@ -9,6 +9,8 @@
|
|
9
9
|
#
|
10
10
|
module Fozzie
|
11
11
|
|
12
|
+
require 'core_ext/module/monitor'
|
13
|
+
|
12
14
|
require 'fozzie/configuration'
|
13
15
|
require "fozzie/interface"
|
14
16
|
require "fozzie/version"
|
@@ -16,6 +18,8 @@ module Fozzie
|
|
16
18
|
require "fozzie/rack/middleware"
|
17
19
|
require "fozzie/rails/middleware"
|
18
20
|
|
21
|
+
require 'fozzie/railtie' if defined?(::Rails)
|
22
|
+
|
19
23
|
class << self
|
20
24
|
|
21
25
|
# Shortcut for `Fozzie.config`
|
@@ -57,4 +61,4 @@ module Fozzie
|
|
57
61
|
Kernel.const_set(klas, Interface.instance) unless const_defined?(klas)
|
58
62
|
end
|
59
63
|
|
60
|
-
end
|
64
|
+
end
|
data/lib/fozzie/configuration.rb
CHANGED
@@ -10,7 +10,7 @@ module Fozzie
|
|
10
10
|
class Configuration
|
11
11
|
include Sys
|
12
12
|
|
13
|
-
attr_accessor :env, :config_path, :host, :port, :appname, :namespaces, :timeout
|
13
|
+
attr_accessor :env, :config_path, :host, :port, :appname, :namespaces, :timeout, :monitor_classes, :sniff_envs
|
14
14
|
|
15
15
|
def initialize(args = {})
|
16
16
|
merge_and_assign_config(args)
|
@@ -30,6 +30,10 @@ module Fozzie
|
|
30
30
|
@origin_name ||= Uname.uname.nodename
|
31
31
|
end
|
32
32
|
|
33
|
+
def sniff?
|
34
|
+
self.sniff_envs.collect(&:to_sym).include?(self.env.to_sym)
|
35
|
+
end
|
36
|
+
|
33
37
|
private
|
34
38
|
|
35
39
|
# Handle the merging of the given configuaration, and the default config.
|
@@ -44,13 +48,15 @@ module Fozzie
|
|
44
48
|
# Default configuration settings
|
45
49
|
def self.default_configuration
|
46
50
|
{
|
47
|
-
:host
|
48
|
-
:port
|
49
|
-
:config_path
|
50
|
-
:env
|
51
|
-
:appname
|
52
|
-
:namespaces
|
53
|
-
:timeout
|
51
|
+
:host => '127.0.0.1',
|
52
|
+
:port => 8125,
|
53
|
+
:config_path => '',
|
54
|
+
:env => (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'),
|
55
|
+
:appname => '',
|
56
|
+
:namespaces => %w{Stats S Statistics Warehouse},
|
57
|
+
:timeout => 0.5,
|
58
|
+
:monitor_classes => [],
|
59
|
+
:sniff_envs => [:development, :staging, :production]
|
54
60
|
}.dup
|
55
61
|
end
|
56
62
|
|
data/lib/fozzie/mill.rb
ADDED
@@ -0,0 +1,50 @@
|
|
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
|
@@ -0,0 +1,15 @@
|
|
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
|
@@ -13,22 +13,25 @@ module Fozzie
|
|
13
13
|
return nil unless path_str
|
14
14
|
|
15
15
|
begin
|
16
|
-
routing =
|
16
|
+
routing = routing_lookup
|
17
17
|
path = routing.recognize_path(path_str)
|
18
18
|
stat = [path[:controller], path[:action], "render"].join('.')
|
19
19
|
stat
|
20
|
-
rescue ActionController::RoutingError => exc
|
21
|
-
S.increment "routing.error"
|
22
|
-
nil
|
23
20
|
rescue => exc
|
21
|
+
S.increment "routing.error"
|
24
22
|
nil
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
26
|
+
def routing_lookup
|
27
|
+
(rails_version == 3 ? ::Rails.application.routes : ::ActionController::Routing::Routes)
|
28
|
+
end
|
29
|
+
|
28
30
|
def rails_version
|
29
31
|
::Rails.version.to_i
|
30
32
|
end
|
31
33
|
|
32
34
|
end
|
35
|
+
|
33
36
|
end
|
34
37
|
end
|
@@ -0,0 +1,15 @@
|
|
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
|
data/lib/fozzie/sniff.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'core_ext/module/monitor'
|
2
|
+
require 'facets/module/alias_method_chain' unless Module.methods.include?(:alias_method_chain)
|
3
|
+
require 'facets/string/snakecase'
|
4
|
+
|
5
|
+
module Fozzie
|
6
|
+
module Sniff
|
7
|
+
|
8
|
+
def self.included(klass)
|
9
|
+
return if klass.include?(ClassMethods)
|
10
|
+
|
11
|
+
klass.class_eval { extend ClassMethods }
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def _monitor
|
17
|
+
@_monitor_flag = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def _monitor_meth(target, &blk)
|
21
|
+
return if @_monitor_flag.nil? || !@_monitor_flag
|
22
|
+
|
23
|
+
@_monitor_flag, feature, bin = false, :monitor, [self.name.snakecase, target.to_s.snakecase]
|
24
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
25
|
+
|
26
|
+
with = "#{aliased_target}_with_#{feature}#{punctuation}"
|
27
|
+
without = "#{aliased_target}_without_#{feature}#{punctuation}"
|
28
|
+
|
29
|
+
blk.call(with, without, feature, bin)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_added(target)
|
33
|
+
_monitor_meth(target) do |with, without, feature, bin|
|
34
|
+
define_method(with) do |*args|
|
35
|
+
S.time_for(bin) { args.empty? ? self.send(without) : self.send(without, *args) }
|
36
|
+
end
|
37
|
+
self.alias_method_chain(target, feature)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def singleton_method_added(target)
|
42
|
+
_monitor_meth(target) do |with, without, feature, bin|
|
43
|
+
define_singleton_method(with) do |*args|
|
44
|
+
S.time_for(bin) { args.empty? ? send(without) : send(without, *args) }
|
45
|
+
end
|
46
|
+
self.singleton_class.class_eval { alias_method_chain target, feature }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/fozzie/socket.rb
CHANGED
@@ -33,8 +33,9 @@ module Fozzie
|
|
33
33
|
begin
|
34
34
|
Fozzie.logger.debug {"Statsd: #{message}"} if Fozzie.logger
|
35
35
|
Timeout.timeout(Fozzie.c.timeout) {
|
36
|
-
socket.send(message, 0, Fozzie.c.host, Fozzie.c.port)
|
37
|
-
|
36
|
+
res = socket.send(message, 0, Fozzie.c.host, Fozzie.c.port)
|
37
|
+
Fozzie.logger.debug {"Statsd sent: #{res}"} if Fozzie.logger
|
38
|
+
(res.to_i == message.length)
|
38
39
|
}
|
39
40
|
rescue => exc
|
40
41
|
Fozzie.logger.debug {"Statsd Failure: #{exc.message}\n#{exc.backtrace}"} if Fozzie.logger
|
data/lib/fozzie/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
// Mill-Snippet_0.0.1
|
2
|
+
(function($){
|
3
|
+
var __mill__ = function() {
|
4
|
+
var c=[window.navigator,window.performance.timing], d=[], t, a, script, s;
|
5
|
+
for(n in c){
|
6
|
+
if(c[n]){
|
7
|
+
t=c[n];
|
8
|
+
for(a in c[n]){
|
9
|
+
if(a && (typeof(t[a])!=='function'&& typeof(t[a])!=='object')){
|
10
|
+
d.push(a);
|
11
|
+
d.push(escape(t[a]));
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
d.push('href');
|
17
|
+
d.push(window.location.href);
|
18
|
+
script = document.createElement('script');
|
19
|
+
script.src = "/mill?d=" + d.join(';');
|
20
|
+
script.async = true;
|
21
|
+
s = document.getElementsByTagName('script')[0];
|
22
|
+
s.parentNode.insertBefore(script, s);
|
23
|
+
};
|
24
|
+
if(window.addEventListener){
|
25
|
+
window.addEventListener('load', function(){__mill__();}, false);
|
26
|
+
}
|
27
|
+
}).call($);
|
@@ -0,0 +1 @@
|
|
1
|
+
(function(b){var a=function(){var j=[window.navigator,window.performance.timing],i=[],g,e,f,h;for(n in j){if(j[n]){g=j[n];for(e in j[n]){if(e&&(typeof(g[e])!=="function"&&typeof(g[e])!=="object")){i.push(e);i.push(escape(g[e]))}}}}i.push("href");i.push(window.location.href);f=document.createElement("script");f.src="/mill?d="+i.join(";");f.async=true;h=document.getElementsByTagName("script")[0];h.parentNode.insertBefore(f,h)};if(window.addEventListener){window.addEventListener("load",function(){a()},false)}}).call($);
|
@@ -51,4 +51,38 @@ describe Fozzie::Configuration do
|
|
51
51
|
subject.namespaces.should include("S")
|
52
52
|
end
|
53
53
|
|
54
|
+
describe "#sniff?" do
|
55
|
+
|
56
|
+
it "defaults to false for testing" do
|
57
|
+
subject.stubs(:env).returns('test')
|
58
|
+
subject.sniff?.should be_false
|
59
|
+
end
|
60
|
+
|
61
|
+
it "defaults true when in development" do
|
62
|
+
subject.stubs(:env).returns('development')
|
63
|
+
subject.sniff?.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "defaults true when in production" do
|
67
|
+
subject.stubs(:env).returns('production')
|
68
|
+
subject.sniff?.should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#sniff_envs allows configuration for #sniff?" do
|
74
|
+
let!(:sniff_envs) { subject.stubs(:sniff_envs).returns(['test']) }
|
75
|
+
|
76
|
+
it "scopes to return false" do
|
77
|
+
subject.stubs(:env).returns('development')
|
78
|
+
subject.sniff?.should be_false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "scopes to return true" do
|
82
|
+
subject.stubs(:env).returns('test')
|
83
|
+
subject.sniff?.should be_true
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
54
88
|
end
|
@@ -37,9 +37,9 @@ describe Fozzie::Interface do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it "times a given block" do
|
40
|
-
subject.expects(:timing).with() {|b, val, timing| b == 'data.bin' && (
|
41
|
-
subject.time_for('data.bin') { sleep
|
42
|
-
subject.time_to_do('data.bin') { sleep
|
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
43
|
end
|
44
44
|
|
45
45
|
it "registers a commit" do
|
@@ -62,8 +62,8 @@ describe Fozzie::Interface do
|
|
62
62
|
|
63
63
|
it "ensures block is called on socket error" do
|
64
64
|
UDPSocket.any_instance.stubs(:send).raises(SocketError)
|
65
|
-
proc { subject.time_for('data.bin')
|
66
|
-
proc { subject.time_to_do('data.bin') { sleep
|
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
67
|
end
|
68
68
|
|
69
69
|
it "raises exception if natural exception from block" do
|
@@ -79,7 +79,8 @@ describe Fozzie::Interface do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
it "raises Timeout on slow lookup" do
|
82
|
-
|
82
|
+
Fozzie.c.timeout = 0.01
|
83
|
+
UDPSocket.any_instance.stubs(:send).with(any_parameters) { sleep 0.4 }
|
83
84
|
subject.increment('data.bin').should eq false
|
84
85
|
end
|
85
86
|
|