miniprofiler 0.1.7.1

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 +4 -0
  2. data/CHANGELOG +32 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +64 -0
  5. data/README.md +110 -0
  6. data/Rakefile +40 -0
  7. data/autotest/discover.rb +2 -0
  8. data/lib/mini_profiler/body_add_proxy.rb +45 -0
  9. data/lib/mini_profiler/client_timer_struct.rb +76 -0
  10. data/lib/mini_profiler/config.rb +52 -0
  11. data/lib/mini_profiler/context.rb +10 -0
  12. data/lib/mini_profiler/page_timer_struct.rb +53 -0
  13. data/lib/mini_profiler/profiler.rb +405 -0
  14. data/lib/mini_profiler/profiling_methods.rb +73 -0
  15. data/lib/mini_profiler/request_timer_struct.rb +96 -0
  16. data/lib/mini_profiler/sql_timer_struct.rb +48 -0
  17. data/lib/mini_profiler/storage/abstract_store.rb +27 -0
  18. data/lib/mini_profiler/storage/file_store.rb +108 -0
  19. data/lib/mini_profiler/storage/memory_store.rb +68 -0
  20. data/lib/mini_profiler/storage/redis_store.rb +44 -0
  21. data/lib/mini_profiler/timer_struct.rb +31 -0
  22. data/lib/mini_profiler_rails/railtie.rb +84 -0
  23. data/lib/patches/sql_patches.rb +185 -0
  24. data/lib/rack-mini-profiler.rb +6 -0
  25. data/rack-mini-profiler.gemspec +23 -0
  26. data/spec/components/body_add_proxy_spec.rb +90 -0
  27. data/spec/components/client_timer_struct_spec.rb +165 -0
  28. data/spec/components/file_store_spec.rb +47 -0
  29. data/spec/components/memory_store_spec.rb +40 -0
  30. data/spec/components/page_timer_struct_spec.rb +33 -0
  31. data/spec/components/profiler_spec.rb +126 -0
  32. data/spec/components/redis_store_spec.rb +44 -0
  33. data/spec/components/request_timer_struct_spec.rb +148 -0
  34. data/spec/components/sql_timer_struct_spec.rb +63 -0
  35. data/spec/components/timer_struct_spec.rb +47 -0
  36. data/spec/fixtures/simple_client_request.yml +17 -0
  37. data/spec/fixtures/weird_client_request.yml +13 -0
  38. data/spec/integration/mini_profiler_spec.rb +142 -0
  39. data/spec/spec_helper.rb +31 -0
  40. data/spec/support/expand_each_to_matcher.rb +8 -0
  41. data/test_old/config.ru +41 -0
  42. metadata +155 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ lib/html
2
+ pkg/
3
+ tmp/
4
+ *.gem
data/CHANGELOG ADDED
@@ -0,0 +1,32 @@
1
+ 28-June-2012 - Sam
2
+
3
+ * Started change log
4
+ * Corrected profiler so it properly captures POST requests (was supressing non 200s)
5
+ * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
6
+ * Fixed bug where unviewed missing ids never got cleared
7
+ * Supress all '/assets/' in the rails tie (makes debugging easier)
8
+ * record_sql was mega buggy
9
+
10
+ 9-July-2012 - Sam
11
+
12
+ * Cleaned up mechanism for profiling in production, all you need to do now
13
+ is call Rack::MiniProfiler.authorize_request to get profiling working in
14
+ production
15
+ * Added option to display full backtraces pp=full-backtrace
16
+ * Cleaned up railties, got rid of the post authorize callback
17
+ * Version 0.1.3
18
+
19
+ 12-July-2012 - Sam
20
+
21
+ * Fixed incorrect profiling steps (was not indenting or measuring start time right
22
+ * Implemented native PG and MySql2 interceptors, this gives way more accurate times
23
+ * Refactored context so its a proper class and not a hash
24
+ * Added some more client probing built in to rails
25
+ * More tests
26
+
27
+ 18-July-2012 - Sam
28
+
29
+ * Added First Paint time for chrome
30
+ * Bug fix to ensure non Rails installs have mini profiler
31
+ * Version 0.1.7
32
+
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'therubyracer', :require => 'v8'
6
+ gem 'less'
7
+
8
+ group :test do
9
+ gem 'rake'
10
+ gem 'rack'
11
+ gem 'rspec'
12
+ gem 'ZenTest'
13
+ gem 'autotest'
14
+ gem 'redis'
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ miniprofiler (0.1.7.1)
5
+ rack (>= 1.1.3)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ ZenTest (4.8.1)
11
+ activemodel (3.2.6)
12
+ activesupport (= 3.2.6)
13
+ builder (~> 3.0.0)
14
+ activerecord (3.2.6)
15
+ activemodel (= 3.2.6)
16
+ activesupport (= 3.2.6)
17
+ arel (~> 3.0.2)
18
+ tzinfo (~> 0.3.29)
19
+ activesupport (3.2.6)
20
+ i18n (~> 0.6)
21
+ multi_json (~> 1.0)
22
+ arel (3.0.2)
23
+ autotest (4.4.6)
24
+ ZenTest (>= 4.4.1)
25
+ builder (3.0.0)
26
+ commonjs (0.2.6)
27
+ diff-lcs (1.1.3)
28
+ i18n (0.6.0)
29
+ less (2.2.1)
30
+ commonjs (~> 0.2.6)
31
+ libv8 (3.3.10.4)
32
+ multi_json (1.3.6)
33
+ rack (1.4.1)
34
+ rack-test (0.6.1)
35
+ rack (>= 1.0)
36
+ rake (0.9.2.2)
37
+ redis (3.0.1)
38
+ rspec (2.11.0)
39
+ rspec-core (~> 2.11.0)
40
+ rspec-expectations (~> 2.11.0)
41
+ rspec-mocks (~> 2.11.0)
42
+ rspec-core (2.11.0)
43
+ rspec-expectations (2.11.1)
44
+ diff-lcs (~> 1.1.3)
45
+ rspec-mocks (2.11.1)
46
+ therubyracer (0.10.1)
47
+ libv8 (~> 3.3.10)
48
+ tzinfo (0.3.33)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ ZenTest
55
+ activerecord (~> 3.0)
56
+ autotest
57
+ less
58
+ miniprofiler!
59
+ rack
60
+ rack-test
61
+ rake
62
+ redis
63
+ rspec
64
+ therubyracer
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # NOTE FOR THIS GEM FORK
2
+
3
+ This is a fork the Ruby folder of https://github.com/SamSaffron/MiniProfiler which includes some quick fixes to make the original rack-mini-profiler gem a little more usable. I will consolidate any useful changes here into pull requests for the original repo.
4
+
5
+ * Add `gem 'miniprofiler'` to your Gemfile (install of 'rack-mini-profiler')
6
+ * `miniprofiler`'s version uses `x.y.z.t` syntax in order to more easily sync with `rack-mini-profiler`'s `x.y.z` SemVer versioning. I will increase `t` whenever I make a patch
7
+ * This gem fork will work in staging, not just development and production
8
+ * Gem release date will be correct (as of now 'rack-mini-profiler' hardcodes the release date to be 04/02/2012, which is very confusing: https://rubygems.org/gems/rack-mini-profiler -- I will report to them)
9
+ * Allow turning off loading jquery if the app already uses jquery
10
+ * More to come
11
+
12
+ # Original README below:
13
+
14
+ # rack-mini-profiler
15
+
16
+ Middleware that displays speed badge for every html page. Designed to work both in production and in development.
17
+
18
+ ## Using rack-mini-profiler in your app
19
+
20
+ Install/add to Gemfile
21
+
22
+ ```ruby
23
+ gem 'rack-mini-profiler'
24
+ ```
25
+ Using Rails:
26
+
27
+ All you have to do is include the Gem and you're good to go in development.
28
+
29
+ rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
30
+
31
+ For example:
32
+
33
+ ```ruby
34
+ # A hook in your ApplicationController
35
+ def authorize
36
+ if current_user.is_admin?
37
+ Rack::MiniProfiler.authorize_request
38
+ end
39
+ end
40
+ ````
41
+
42
+
43
+ Using Builder:
44
+
45
+ ```ruby
46
+ require 'rack-mini-profiler'
47
+ builder = Rack::Builder.new do
48
+ use Rack::MiniProfiler
49
+
50
+ map('/') { run get }
51
+ end
52
+ ```
53
+
54
+ Using Sinatra:
55
+
56
+ ```ruby
57
+ require 'rack-mini-profiler'
58
+ class MyApp < Sinatra::Base
59
+ use Rack::MiniProfiler
60
+ end
61
+ ```
62
+
63
+ ## Storage
64
+
65
+ By default, rack-mini-profiler stores its results in a memory store:
66
+
67
+ ```ruby
68
+ # our default
69
+ Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
70
+ ```
71
+
72
+ There are 2 other available storage engines, `RedisStore` and `FileStore`.
73
+
74
+ MemoryStore is stores results in a processes heap - something that does not work well in a multi process environment.
75
+ FileStore stores results in the file system - something that may not work well in a multi machine environment.
76
+
77
+ Additionally you may implement an AbstractStore for your own provider.
78
+
79
+ Rails hooks up a FileStore for all environments.
80
+
81
+ ## Running the Specs
82
+
83
+ ```
84
+ $ rake build
85
+ $ rake spec
86
+ ```
87
+
88
+ Additionally you can also run `autotest` if you like.
89
+
90
+ ## Configuration Options
91
+
92
+ You can set configuration options using the configuration accessor on Rack::MiniProfiler:
93
+
94
+ ```
95
+ # Have Mini Profiler show up on the right
96
+ Rack::MiniProfiler.config.position = 'right'
97
+ ```
98
+
99
+ In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
100
+
101
+ ## Available Options
102
+
103
+ * pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
104
+ * position - Can either be 'right' or 'left'. Default is 'left'.
105
+ * skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
106
+
107
+ ## Special query strings
108
+
109
+ If you include the query string `pp=help` at the end of your request you will see the various option you have. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
110
+
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ # Rakefile
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ Bundler.setup(:default, :test)
5
+ require "bundler/gem_tasks"
6
+
7
+ task :default => [:spec]
8
+
9
+ require 'rspec/core'
10
+ require 'rspec/core/rake_task'
11
+ RSpec::Core::RakeTask.new(:spec) do |spec|
12
+ spec.pattern = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ desc "builds a gem"
16
+ task :build => :compile_less do
17
+ `gem build rack-mini-profiler.gemspec 1>&2`
18
+ end
19
+
20
+ desc "compile less"
21
+ task :compile_less => :copy_files do
22
+ `lessc lib/html/includes.less > lib/html/includes.css`
23
+ end
24
+
25
+
26
+ desc "copy files from other parts of the tree"
27
+ task :copy_files do
28
+ `rm -R -f lib/html && mkdir lib/html 1>&2`
29
+ path = File.expand_path('../StackExchange.Profiling/UI')
30
+ `ln -s #{path}/includes.less lib/html/includes.less`
31
+ `ln -s #{path}/includes.js lib/html/includes.js`
32
+ `ln -s #{path}/includes.tmpl lib/html/includes.tmpl`
33
+ `ln -s #{path}/jquery.1.7.1.js lib/html/jquery.1.7.1.js`
34
+ `ln -s #{path}/jquery.tmpl.js lib/html/jquery.tmpl.js`
35
+ `ln -s #{path}/list.css lib/html/list.css`
36
+ `ln -s #{path}/list.js lib/html/list.js`
37
+ `ln -s #{path}/list.tmpl lib/html/list.tmpl`
38
+ `ln -s #{path}/include.partial.html lib/html/profile_handler.js`
39
+ end
40
+
@@ -0,0 +1,2 @@
1
+ Autotest.add_discovery { "rspec2" }
2
+
@@ -0,0 +1,45 @@
1
+ module Rack
2
+ class MiniProfiler
3
+
4
+ # This class acts as a proxy to the Body so that we can
5
+ # safely append to the end without knowing about the internals
6
+ # of the body class.
7
+ class BodyAddProxy
8
+ def initialize(body, additional_text)
9
+ @body = body
10
+ @additional_text = additional_text
11
+ end
12
+
13
+ def respond_to?(*args)
14
+ super or @body.respond_to?(*args)
15
+ end
16
+
17
+ def method_missing(*args, &block)
18
+ @body.__send__(*args, &block)
19
+ end
20
+
21
+ # In the case of to_str we don't want to use method_missing as it might avoid
22
+ # a call to each (such as in Rack::Test)
23
+ def to_str
24
+ result = ""
25
+ each {|token| result << token}
26
+ result
27
+ end
28
+
29
+ def each(&block)
30
+
31
+ # In ruby 1.9 we don't support String#each
32
+ if @body.is_a?(String)
33
+ yield @body
34
+ else
35
+ @body.each(&block)
36
+ end
37
+
38
+ yield @additional_text
39
+ self
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,76 @@
1
+ require 'mini_profiler/timer_struct'
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+
6
+ # This class holds the client timings
7
+ class ClientTimerStruct < TimerStruct
8
+
9
+ def self.init_instrumentation
10
+ "<script type=\"text/javascript\">mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()</script>"
11
+ end
12
+
13
+ def self.instrument(name,orig)
14
+ probe = "<script>mPt.probe('#{name}')</script>"
15
+ wrapped = probe
16
+ wrapped << orig
17
+ wrapped << probe
18
+ wrapped
19
+ end
20
+
21
+
22
+ def initialize(env={})
23
+ super
24
+ end
25
+
26
+ def init_from_form_data(env, page_struct)
27
+ timings = []
28
+ clientTimes, clientPerf, baseTime = nil
29
+ form = env['rack.request.form_hash']
30
+
31
+ clientPerf = form['clientPerformance'] if form
32
+ clientTimes = clientPerf['timing'] if clientPerf
33
+
34
+ baseTime = clientTimes['navigationStart'].to_i if clientTimes
35
+ return unless clientTimes && baseTime
36
+
37
+ probes = form['clientProbes']
38
+ translated = {}
39
+ if probes && probes != "null"
40
+ probes.each do |id, val|
41
+ name = val["n"]
42
+ translated[name] ||= {}
43
+ if translated[name][:start]
44
+ translated[name][:finish] = val["d"]
45
+ else
46
+ translated[name][:start] = val["d"]
47
+ end
48
+ end
49
+ end
50
+
51
+ translated.each do |name, data|
52
+ h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
53
+ h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
54
+ timings.push(h)
55
+ end
56
+
57
+ clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
58
+ start = clientTimes[k].to_i - baseTime
59
+ finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
60
+ duration = 0
61
+ duration = finish - start if finish > start
62
+ name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
63
+ timings.push({"Name" => name, "Start" => start, "Duration" => duration}) if start >= 0
64
+ end
65
+
66
+ clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
67
+ timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
68
+ end
69
+
70
+ self['RedirectCount'] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirectCount']
71
+ self['Timings'] = timings
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,52 @@
1
+ module Rack
2
+ class MiniProfiler
3
+ class Config
4
+
5
+ def self.attr_accessor(*vars)
6
+ @attributes ||= []
7
+ @attributes.concat vars
8
+ super(*vars)
9
+ end
10
+
11
+ def self.attributes
12
+ @attributes
13
+ end
14
+
15
+ attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
16
+ :backtrace_remove, :backtrace_filter, :skip_schema_queries,
17
+ :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode, :use_existing_jquery
18
+
19
+ def self.default
20
+ new.instance_eval {
21
+ @auto_inject = true # automatically inject on every html page
22
+ @base_url_path = "/mini-profiler-resources/"
23
+
24
+ # called prior to rack chain, to ensure we are allowed to profile
25
+ @pre_authorize_cb = lambda {|env| true}
26
+
27
+ # called after rack chain, to ensure we are REALLY allowed to profile
28
+ @position = 'left' # Where it is displayed
29
+ @skip_schema_queries = false
30
+ @storage = MiniProfiler::MemoryStore
31
+ @user_provider = Proc.new{|env| Rack::Request.new(env).ip}
32
+ @authorization_mode = :allow_all
33
+ @use_existing_jquery = false
34
+ self
35
+ }
36
+ end
37
+
38
+ def merge!(config)
39
+ return unless config
40
+ if Hash === config
41
+ config.each{|k,v| instance_variable_set "@#{k}",v}
42
+ else
43
+ self.class.attributes.each{ |k|
44
+ v = config.send k
45
+ instance_variable_set "@#{k}", v if v
46
+ }
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ class Rack::MiniProfiler::Context
2
+ attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,:full_backtrace,:discard, :mpt_init
3
+
4
+ def initialize(opts = {})
5
+ opts.each do |k,v|
6
+ self.instance_variable_set('@' + k, v)
7
+ end
8
+ end
9
+
10
+ end
@@ -0,0 +1,53 @@
1
+ require 'mini_profiler/timer_struct'
2
+
3
+ module Rack
4
+ class MiniProfiler
5
+
6
+ # PageTimerStruct
7
+ # Root: RequestTimer
8
+ # :has_many RequestTimer children
9
+ # :has_many SqlTimer children
10
+ class PageTimerStruct < TimerStruct
11
+ def initialize(env)
12
+ super("Id" => MiniProfiler.generate_id,
13
+ "Name" => env['PATH_INFO'],
14
+ "Started" => (Time.now.to_f * 1000).to_i,
15
+ "MachineName" => env['SERVER_NAME'],
16
+ "Level" => 0,
17
+ "User" => "unknown user",
18
+ "HasUserViewed" => false,
19
+ "ClientTimings" => ClientTimerStruct.new,
20
+ "DurationMilliseconds" => 0,
21
+ "HasTrivialTimings" => true,
22
+ "HasAllTrivialTimigs" => false,
23
+ "TrivialDurationThresholdMilliseconds" => 2,
24
+ "Head" => nil,
25
+ "DurationMillisecondsInSql" => 0,
26
+ "HasSqlTimings" => true,
27
+ "HasDuplicateSqlTimings" => false,
28
+ "ExecutedReaders" => 0,
29
+ "ExecutedScalars" => 0,
30
+ "ExecutedNonQueries" => 0)
31
+ name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
32
+ self['Root'] = RequestTimerStruct.createRoot(name, self)
33
+ end
34
+
35
+ def duration_ms
36
+ @attributes['Root']['DurationMilliseconds']
37
+ end
38
+
39
+ def root
40
+ @attributes['Root']
41
+ end
42
+
43
+ def to_json(*a)
44
+ attribs = @attributes.merge(
45
+ "Started" => '/Date(%d)/' % @attributes['Started'],
46
+ "DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
47
+ )
48
+ ::JSON.generate(attribs, :max_nesting => 100)
49
+ end
50
+ end
51
+
52
+ end
53
+ end