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 CHANGED
@@ -5,8 +5,4 @@ guard 'rspec', :version => 2 do
5
5
  watch(%r{^spec/.+_spec\.rb$})
6
6
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
7
  watch('spec/spec_helper.rb') { "spec" }
8
- end
9
-
10
- guard 'rocco' do
11
- watch(%r{^lib/.*\.rb$})
12
8
  end
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
- Add the following to your `config/environment.rb`
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
- * [https://github.com/reinh](https://github.com/reinh/statsd) for his [statsd](https://github.com/reinh/statsd) Gem.
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 'actionpack'
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
@@ -0,0 +1,11 @@
1
+ require 'fozzie/sniff'
2
+
3
+ class Module
4
+
5
+ def _monitor
6
+ return unless Fozzie.c.sniff?
7
+ self.class_eval { include Fozzie::Sniff }
8
+ @_monitor_flag = true
9
+ end
10
+
11
+ 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
@@ -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 => '127.0.0.1',
48
- :port => 8125,
49
- :config_path => '',
50
- :env => (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'),
51
- :appname => '',
52
- :namespaces => %w{Stats S Statistics Warehouse},
53
- :timeout => 0.5
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
 
@@ -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 = (rails_version == 3 ? ::Rails.application.routes : ::ActionController::Routing::Routes)
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
@@ -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
- true
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
@@ -1,3 +1,3 @@
1
1
  module Fozzie
2
- VERSION = "0.0.20"
2
+ VERSION = "0.0.21"
3
3
  end
@@ -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' && (1000..1200).include?(val) }.twice
41
- subject.time_for('data.bin') { sleep 1 }
42
- subject.time_to_do('data.bin') { sleep 1 }
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') { sleep 1 } }.should_not raise_error
66
- proc { subject.time_to_do('data.bin') { sleep 1 } }.should_not raise_error
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
- UDPSocket.any_instance.stubs(:send).with(any_parameters) { sleep 0.6 }
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