rack-mini-profiler 0.9.3 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-mini-profiler might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 915cfa4f71cab960c3f73128fa213cce013f0b65
4
- data.tar.gz: 3465e843ef28ed50178f1f76255de96b474903f6
3
+ metadata.gz: 661dc965f1499d7a00baa35f5699e91e1552ff68
4
+ data.tar.gz: 7bbccf4b17154aab14f7bd2012afcf2dac0efe25
5
5
  SHA512:
6
- metadata.gz: 7c232ba029fb050b15d6b4cdb08b9dc3a6079ed723c06beb7b99dcbc7b6d009822aa2686895661a8e57497a0c5e529a691bf7507c0a895639be9b0e0346ddf29
7
- data.tar.gz: 45b90274fd6828441a1a4ca14017a6d3b1f1292b65c157dbd03964748b7fd648b39f06484b36d56fd0340b52498bd9c8b0242fe6063e6e23dd115d4b99c10040
6
+ metadata.gz: 903fe47823489fae4a2b18aff1cddca204130c1e41b2132c5db951007a5c1fe85b7b110f3264b8abc2f22c07d438071b81c93251bc31cbe26db725b868ca0a2a
7
+ data.tar.gz: 400bb2bf85475461de90d238eb95cbac925710f758f576c7371cb7b25ef0998bf39d637835520b42dd96fbbeb7cd53f36fe531f83e9e9c8c1cf36022795fcb8c
@@ -1,4 +1,33 @@
1
1
  # CHANGELOG
2
+
3
+ ## 0.9.8 - 2015-11-27 (Sam Saffron)
4
+
5
+ - [FEATURE] disable_env_dump config setting (@mathias)
6
+ - [FEATURE] set X-MiniProfiler-Ids for all 2XX reqs (@tymagu2)
7
+ - [FEATURE] add support for NoBrainer (rethinkdb) profiling (@niv)
8
+ - [FEATURE] add oracle enhanced adapter profiling (@rrooding)
9
+ - [FEATURE] pp=profile-memory can now parse query params (@dgynn)
10
+
11
+
12
+ ## 0.9.7 - 2015-08-03 (Sam Saffron)
13
+
14
+ - [FEATURE] remove confusing pp=profile-gc-time (Nate Berkopec)
15
+ - [FEATURE] truncate strings in pp=analyze-memory (Nate Berkopec)
16
+ - [FEATURE] rename pp=profile-gc-ruby-head to pp=profile-memory (Nate Berkopec)
17
+
18
+ ## 0.9.6 - 2015-07-08 (Sam Saffron)
19
+
20
+ - [FIX] incorrect truncation in pp=analyze-memory
21
+
22
+ ## 0.9.5 - 2015-07-08 (Sam Saffron)
23
+
24
+ - [FEATURE] improve pp=analyze-memory
25
+
26
+ ## 0.9.4 - 2015-07-08 (Sam Saffron)
27
+ - [UX] added a link to "more" actions in profiler
28
+ - [FEATURE] pp=help now displays links
29
+ - [FEATURE] simple memory report with pp=analyze-memory
30
+
2
31
  ## 0.9.2 - 2014-06-26 (Sam Saffron)
3
32
  - [CHANGE] staging and other environments behave like production (Cedric Felizard)
4
33
  - [DOC] CHANGELOG reorg (Olivier Lacan)
data/README.md CHANGED
@@ -6,7 +6,7 @@ Middleware that displays speed badge for every html page. Designed to work both
6
6
 
7
7
  #### Features
8
8
 
9
- * database profiling. Currently supports Mysql2, Postgres, and Mongoid3 (with fallback support to ActiveRecord)
9
+ * database profiling. Currently supports Mysql2, Postgres, Oracle (oracle_enhanced ~> 1.5.0) and Mongoid3 (with fallback support to ActiveRecord)
10
10
 
11
11
  #### Learn more
12
12
 
@@ -23,7 +23,6 @@ We have decided to restructure our repository so there is a central UI repo and
23
23
 
24
24
  - Setting up a build that reuses https://github.com/MiniProfiler/ui
25
25
  - Migrating the internal data structures [per the spec](https://github.com/MiniProfiler/ui)
26
- - Cleaning up the [horrendous class structure that is using strings as keys and crazy non-objects](https://github.com/MiniProfiler/rack-mini-profiler/blob/master/lib/mini_profiler/timer_struct/sql.rb#L36-L44)
27
26
 
28
27
  If you feel like taking on any of this start an issue and update us on your progress.
29
28
 
@@ -90,20 +89,21 @@ To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-
90
89
  * add the [**flamegraph**](https://github.com/SamSaffron/flamegraph) gem to your Gemfile
91
90
  * visit a page in your app with `?pp=flamegraph`
92
91
 
93
- Flamegraph generation is supported in MRI 2.0 and 2.1 only.
92
+ Flamegraph generation is supported in MRI 2.0, 2.1, and 2.2 only.
94
93
 
95
94
 
96
- ## Access control in production
95
+ ## Access control in non-development environments
97
96
 
98
97
  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.
99
98
 
100
99
  ```ruby
101
- # A hook in your ApplicationController
102
- def authorize
103
- if current_user.is_admin?
104
- Rack::MiniProfiler.authorize_request
100
+ # inside your ApplicationController
101
+
102
+ before_action do
103
+ if current_user && current_user.is_admin?
104
+ Rack::MiniProfiler.authorize_request
105
+ end
105
106
  end
106
- end
107
107
  ```
108
108
 
109
109
  ## Configuration
@@ -122,7 +122,7 @@ To disable this behavior, use the following config setting:
122
122
 
123
123
  ```ruby
124
124
  # Do not let rack-mini-profiler disable caching
125
- Rack::MiniProfiler.config.disable_caching = false # defaults to true
125
+ Rack::MiniProfiler.config.disable_caching = false # defaults to true
126
126
  ```
127
127
 
128
128
  ### Storage
@@ -183,11 +183,14 @@ The available configuration options are:
183
183
  * skip_paths - Specifies path list that can be skipped.
184
184
  * 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.
185
185
  * auto_inject (default true) - when false the miniprofiler script is not injected in the page
186
- * backtrace_filter - a regex you can use to filter out unwanted lines from the backtraces
187
- * toggle_shortcut (default Alt+P) - a jquery.hotkeys.js-style keyboard shortcut, used to toggle the mini_profiler's visibility. See http://code.google.com/p/js-hotkeys/ for more info.
186
+ * backtrace_ignores (default nil) - an array of regexes you can use to filter out unwanted lines from the backtraces
187
+ * backtrace_includes (default nil, or [/^\/?(app|config|lib|test)/] in rails) - an array of regexes you can use to keep lines in the backtrace
188
+ * backtrace_remove (default nil, or Rails.root in rails) - A string or regex to remove part of each line in the backtrace
189
+ * toggle_shortcut (default Alt+P) - a jquery.hotkeys.js-style keyboard shortcut, used to toggle the mini_profiler's visibility. See https://github.com/jeresig/jquery.hotkeys for more info.
188
190
  * start_hidden (default false) - Whether or not you want the mini_profiler to be visible when loading a page
189
191
  * backtrace_threshold_ms (default zero) - Minimum SQL query elapsed time before a backtrace is recorded. Backtrace recording can take a couple of milliseconds on rubies earlier than 2.0, impacting performance for very small queries.
190
192
  * flamegraph_sample_rate (default 0.5ms) - How often fast_stack should get stack trace info to generate flamegraphs
193
+ * disable_env_dump (default false) - When enabled, disables the `?pp=env` which can be useful when you are concerned about not sending ENV vars out over HTTP.
191
194
 
192
195
  ### Custom middleware ordering (required if using `Rack::Deflate` with Rails)
193
196
 
@@ -395,6 +395,9 @@
395
395
  padding: 0px;
396
396
  margin: 0px;
397
397
  }
398
+ .profiler-results .profiler-more-actions {
399
+ float: left;
400
+ }
398
401
  .profiler-queries-bg {
399
402
  z-index: 2147483642;
400
403
  display: none;
@@ -663,7 +663,7 @@ var MiniProfiler = (function () {
663
663
  // jquery.hotkeys.js
664
664
  // https://github.com/jeresig/jquery.hotkeys/blob/master/jquery.hotkeys.js
665
665
 
666
- if (jQuery.hotkeys === undefined) {
666
+ if (MiniProfiler.jQuery.hotkeys === undefined) {
667
667
  (function(d){function h(g){if("string"===typeof g.data){var h=g.handler,j=g.data.toLowerCase().split(" ");g.handler=function(b){if(!(this!==b.target&&(/textarea|select/i.test(b.target.nodeName)||"text"===b.target.type))){var c="keypress"!==b.type&&d.hotkeys.specialKeys[b.which],e=String.fromCharCode(b.which).toLowerCase(),a="",f={};b.altKey&&"alt"!==c&&(a+="alt+");b.ctrlKey&&"ctrl"!==c&&(a+="ctrl+");b.metaKey&&(!b.ctrlKey&&"meta"!==c)&&(a+="meta+");b.shiftKey&&"shift"!==c&&(a+="shift+");c?f[a+c]=
668
668
  !0:(f[a+e]=!0,f[a+d.hotkeys.shiftNums[e]]=!0,"shift+"===a&&(f[d.hotkeys.shiftNums[e]]=!0));c=0;for(e=j.length;c<e;c++)if(f[j[c]])return h.apply(this,arguments)}}}}d.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",
669
669
  109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(","0":")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"}};d.each(["keydown","keyup","keypress"],function(){d.event.special[this]={add:h}})})(MiniProfiler.jQuery);
@@ -748,6 +748,12 @@ var MiniProfiler = (function () {
748
748
  return options.path + 'results?id=' + id;
749
749
  },
750
750
 
751
+ moreUrl: function () {
752
+ var loc = window.location.href;
753
+ loc = loc.indexOf("?") > 0 ? loc + "&pp=help" : "?pp=help";
754
+ return loc;
755
+ },
756
+
751
757
  getClientTimings: function (clientTimings) {
752
758
  var list = [];
753
759
  var t;
@@ -399,6 +399,9 @@
399
399
  padding: 0px;
400
400
  margin: 0px;
401
401
  }
402
+
403
+ .profiler-more-actions { float: left; }
404
+
402
405
  }
403
406
 
404
407
  // popup results' queries will be displayed in front of this
@@ -413,6 +416,7 @@
413
416
  min-width:100%;
414
417
  }
415
418
 
419
+
416
420
  // used when viewing a shared, full page result
417
421
  .profiler-result-full {
418
422
  .profiler-result {
@@ -484,4 +488,5 @@
484
488
  }
485
489
  }
486
490
  }
491
+
487
492
  }
@@ -123,6 +123,7 @@
123
123
 
124
124
  <script id="linksTemplate" type="text/x-jquery-tmpl">
125
125
  <a href="${MiniProfiler.shareUrl(id)}" class="profiler-share-profiler-results" target="_blank">share</a>
126
+ <a href="${MiniProfiler.moreUrl()}" class="profiler-more-actions">more</a>
126
127
  {{if custom_link}}
127
128
  <a href="${custom_link}" class="profiler-custom-link" target="_blank">${custom_link_name}</a>
128
129
  {{/if}}
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class MiniProfiler
3
- ASSET_VERSION = '9db1f8db18400644bd5c7449e5295620'.freeze
3
+ ASSET_VERSION = 'e0e50f551fa464f9d95c2e082bb2d45b'.freeze
4
4
  end
5
5
  end
@@ -12,10 +12,13 @@ module Rack
12
12
  @attributes
13
13
  end
14
14
 
15
- attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores, :backtrace_includes, :backtrace_remove,
16
- :backtrace_threshold_ms, :base_url_path, :disable_caching, :enabled, :flamegraph_sample_rate, :logger, :position,
17
- :pre_authorize_cb, :skip_paths, :skip_schema_queries, :start_hidden, :storage, :storage_failure,
18
- :storage_instance, :storage_options, :toggle_shortcut, :user_provider
15
+ attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores,
16
+ :backtrace_includes, :backtrace_remove, :backtrace_threshold_ms,
17
+ :base_url_path, :disable_caching, :disable_env_dump, :enabled,
18
+ :flamegraph_sample_rate, :logger, :position, :pre_authorize_cb,
19
+ :skip_paths, :skip_schema_queries, :start_hidden, :storage,
20
+ :storage_failure, :storage_instance, :storage_options, :toggle_shortcut,
21
+ :user_provider
19
22
 
20
23
  # Deprecated options
21
24
  attr_accessor :use_existing_jquery
@@ -44,6 +47,7 @@ module Rack
44
47
  end
45
48
  end
46
49
  @enabled = true
50
+ @disable_env_dump = false
47
51
  self
48
52
  }
49
53
  end
@@ -92,26 +92,6 @@ class Rack::MiniProfiler::GCProfiler
92
92
  [objects,memory_allocated]
93
93
  end
94
94
 
95
- def profile_gc_time(app, env)
96
- body = []
97
-
98
- begin
99
- GC::Profiler.clear
100
- prev_profiler_state = GC::Profiler.enabled?
101
- prev_gc_state = GC.enable
102
- GC::Profiler.enable
103
- b = app.call(env)[2]
104
- b.close if b.respond_to? :close
105
- body << "GC Profiler ran during this request, if it fired you will see the cost below:\n\n"
106
- body << GC::Profiler.result
107
- ensure
108
- prev_gc_state ? GC.disable : GC.enable
109
- GC::Profiler.disable unless prev_profiler_state
110
- end
111
-
112
- return [200, {'Content-Type' => 'text/plain'}, body]
113
- end
114
-
115
95
  def profile_gc(app, env)
116
96
 
117
97
  # for memsize_of
@@ -100,7 +100,7 @@ module Rack
100
100
 
101
101
  # Otherwise give the HTML back
102
102
  html = MiniProfiler.share_template.dup
103
- html.gsub!(/\{path\}/, "#{env['SCRIPT_NAME']}#{@config.base_url_path}")
103
+ html.gsub!(/\{path\}/, "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}")
104
104
  html.gsub!(/\{version\}/, MiniProfiler::ASSET_VERSION)
105
105
  html.gsub!(/\{json\}/, result_json)
106
106
  html.gsub!(/\{includes\}/, get_profile_script(env))
@@ -157,8 +157,11 @@ module Rack
157
157
  query_string = env['QUERY_STRING']
158
158
  path = env['PATH_INFO']
159
159
 
160
+ # Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
161
+ env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = env['SCRIPT_NAME']
162
+
160
163
  skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env)) ||
161
- (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
164
+ (@config.skip_paths && @config.skip_paths.any?{ |p| path.start_with?(p) }) ||
162
165
  query_string =~ /pp=skip/
163
166
 
164
167
  has_profiling_cookie = client_settings.has_cookie?
@@ -196,20 +199,23 @@ module Rack
196
199
 
197
200
  if query_string =~ /pp=profile-gc/
198
201
  current.measure = false if current
199
-
200
- if query_string =~ /pp=profile-gc-time/
201
- return Rack::MiniProfiler::GCProfiler.new.profile_gc_time(@app, env)
202
- elsif query_string =~ /pp=profile-gc-ruby-head/
203
- result = StringIO.new
204
- report = MemoryProfiler.report do
205
- _,_,body = @app.call(env)
206
- body.close if body.respond_to? :close
207
- end
208
- report.pretty_print(result)
209
- return text_result(result.string)
210
- else
211
- return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
202
+ return Rack::MiniProfiler::GCProfiler.new.profile_gc(@app, env)
203
+ end
204
+
205
+ if query_string =~ /pp=profile-memory/
206
+ query_params = Rack::Utils.parse_nested_query(query_string)
207
+ options = {
208
+ :ignore_files => query_params['memory_profiler_ignore_files'],
209
+ :allow_files => query_params['memory_profiler_allow_files'],
210
+ }
211
+ options[:top]= Integer(query_params['memory_profiler_top']) if query_params.key?('memory_profiler_top')
212
+ result = StringIO.new
213
+ report = MemoryProfiler.report(options) do
214
+ _,_,body = @app.call(env)
215
+ body.close if body.respond_to? :close
212
216
  end
217
+ report.pretty_print(result)
218
+ return text_result(result.string)
213
219
  end
214
220
 
215
221
  MiniProfiler.create_current(env, @config)
@@ -285,7 +291,7 @@ module Rack
285
291
  if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
286
292
  # this is non-obvious, don't kill the profiling cookie on errors or short requests
287
293
  # this ensures that stuff that never reaches the rails stack does not kill profiling
288
- if status == 200 && ((Time.now - start) > 0.1)
294
+ if status >= 200 && status < 300 && ((Time.now - start) > 0.1)
289
295
  client_settings.discard_cookie!(headers)
290
296
  end
291
297
  skip_it = true
@@ -299,14 +305,19 @@ module Rack
299
305
  return dump_exceptions exceptions
300
306
  end
301
307
 
302
- if query_string =~ /pp=env/
308
+ if query_string =~ /pp=env/ && !config.disable_env_dump
303
309
  body.close if body.respond_to? :close
304
310
  return dump_env env
305
311
  end
306
312
 
313
+ if query_string =~ /pp=analyze-memory/
314
+ body.close if body.respond_to? :close
315
+ return analyze_memory
316
+ end
317
+
307
318
  if query_string =~ /pp=help/
308
319
  body.close if body.respond_to? :close
309
- return help(client_settings)
320
+ return help(client_settings, env)
310
321
  end
311
322
 
312
323
  page_struct = current.page_struct
@@ -325,7 +336,7 @@ module Rack
325
336
  @storage.save(page_struct)
326
337
 
327
338
  # inject headers, script
328
- if headers['Content-Type'] && status == 200
339
+ if status >= 200 && status < 300
329
340
  client_settings.write!(headers)
330
341
  result = inject_profiler(env,status,headers,body)
331
342
  return result if result
@@ -446,30 +457,112 @@ module Rack
446
457
  text_result(body)
447
458
  end
448
459
 
460
+ def trim_strings(strings, max_size)
461
+ strings.sort!{|a,b| b[1] <=> a[1]}
462
+ i = 0
463
+ strings.delete_if{|_| (i+=1) > max_size}
464
+ end
465
+
466
+ def analyze_memory
467
+ require 'objspace'
468
+
469
+ utf8 = "utf-8"
470
+
471
+ GC.start
472
+
473
+ trunc = lambda do |str|
474
+ str = str.length > 200 ? str : str[0..200]
475
+
476
+ if str.encoding != Encoding::UTF_8
477
+ str = str.dup
478
+ str.force_encoding(utf8)
479
+
480
+ unless str.valid_encoding?
481
+ # work around bust string with a double conversion
482
+ str.encode!("utf-16","utf-8",:invalid => :replace)
483
+ str.encode!("utf-8","utf-16")
484
+ end
485
+ end
486
+
487
+ str
488
+ end
489
+
490
+ body = "ObjectSpace stats:\n\n"
491
+
492
+ counts = ObjectSpace.count_objects
493
+ total_strings = counts[:T_STRING]
494
+
495
+ body << counts
496
+ .sort{|a,b| b[1] <=> a[1]}
497
+ .map{|k,v| "#{k}: #{v}"}
498
+ .join("\n")
499
+
500
+ strings = []
501
+ string_counts = Hash.new(0)
502
+ sample_strings = []
503
+
504
+ max_size = 1000
505
+ sample_every = total_strings / max_size
506
+
507
+ i = 0
508
+ ObjectSpace.each_object(String) do |str|
509
+ i += 1
510
+ string_counts[str] += 1
511
+ strings << [trunc.call(str), str.length]
512
+ sample_strings << [trunc.call(str), str.length] if i % sample_every == 0
513
+ if strings.length > max_size * 2
514
+ trim_strings(strings, max_size)
515
+ end
516
+ end
517
+
518
+ trim_strings(strings, max_size)
519
+
520
+ body << "\n\n\n1000 Largest strings:\n\n"
521
+ body << strings.map{|s,len| "#{s[0..1000]}\n(len: #{len})\n\n"}.join("\n")
522
+
523
+ body << "\n\n\n1000 Sample strings:\n\n"
524
+ body << sample_strings.map{|s,len| "#{s[0..1000]}\n(len: #{len})\n\n"}.join("\n")
525
+
526
+ body << "\n\n\n1000 Most common strings:\n\n"
527
+ body << string_counts.sort{|a,b| b[1] <=> a[1]}[0..max_size].map{|s,len| "#{trunc.call(s)}\n(x #{len})\n\n"}.join("\n")
528
+
529
+ text_result(body)
530
+ end
531
+
449
532
  def text_result(body)
450
533
  headers = {'Content-Type' => 'text/plain'}
451
534
  [200, headers, [body]]
452
535
  end
453
536
 
454
- def help(client_settings)
455
- headers = {'Content-Type' => 'text/plain'}
456
- body = "Append the following to your query string:
457
-
458
- pp=help : display this screen
459
- pp=env : display the rack environment
460
- pp=skip : skip mini profiler for this request
461
- pp=no-backtrace #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
462
- pp=normal-backtrace #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
463
- pp=full-backtrace #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
464
- pp=disable : disable profiling for this session
465
- pp=enable : enable profiling for this session (if previously disabled)
466
- pp=profile-gc: perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
467
- pp=profile-gc-time: perform built-in gc profiling on this request (ruby 1.9.3 only)
468
- pp=profile-gc-ruby-head: requires the memory_profiler gem, new location based report
469
- pp=flamegraph: works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem).
470
- pp=flamegraph&flamegraph_sample_rate=1: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
471
- pp=flamegraph_embed: works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem), embedded resources for use on an intranet.
472
- pp=trace-exceptions: requires Ruby 2.0, will return all the spots where your application raises execptions
537
+ def make_link(postfix, env)
538
+ link = env["PATH_INFO"] + "?" + env["QUERY_STRING"].sub("pp=help", "pp=#{postfix}")
539
+ "pp=<a href='#{link}'>#{postfix}</a>"
540
+ end
541
+
542
+ def help(client_settings, env)
543
+ headers = {'Content-Type' => 'text/html'}
544
+ body = "<html><body>
545
+ <pre style='line-height: 30px; font-size: 16px;'>
546
+ Append the following to your query string:
547
+
548
+ #{make_link "help", env} : display this screen
549
+ #{make_link "env", env} : display the rack environment
550
+ #{make_link "skip", env} : skip mini profiler for this request
551
+ #{make_link "no-backtrace", env} #{"(*) " if client_settings.backtrace_none?}: don't collect stack traces from all the SQL executed (sticky, use pp=normal-backtrace to enable)
552
+ #{make_link "normal-backtrace", env} #{"(*) " if client_settings.backtrace_default?}: collect stack traces from all the SQL executed and filter normally
553
+ #{make_link "full-backtrace", env} #{"(*) " if client_settings.backtrace_full?}: enable full backtraces for SQL executed (use pp=normal-backtrace to disable)
554
+ #{make_link "disable", env} : disable profiling for this session
555
+ #{make_link "enable", env} : enable profiling for this session (if previously disabled)
556
+ #{make_link "profile-gc", env} : perform gc profiling on this request, analyzes ObjectSpace generated by request (ruby 1.9.3 only)
557
+ #{make_link "profile-memory", env} : requires the memory_profiler gem, new location based report
558
+ #{make_link "flamegraph", env} : works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem).
559
+ #{make_link "flamegraph&flamegraph_sample_rate=1", env}: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
560
+ #{make_link "flamegraph_embed", env} : works best on Ruby 2.0, a graph representing sampled activity (requires the flamegraph gem), embedded resources for use on an intranet.
561
+ #{make_link "trace-exceptions", env} : requires Ruby 2.0, will return all the spots where your application raises exceptions
562
+ #{make_link "analyze-memory", env} : requires Ruby 2.0, will perform basic memory analysis of heap
563
+ </pre>
564
+ </body>
565
+ </html>
473
566
  "
474
567
 
475
568
  client_settings.write!(headers)
@@ -501,7 +594,11 @@ module Rack
501
594
  # * you have disabled auto append behaviour throught :auto_inject => false flag
502
595
  # * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
503
596
  def get_profile_script(env)
504
- path = "#{env['SCRIPT_NAME']}#{@config.base_url_path}"
597
+ path = if env["action_controller.instance"]
598
+ env["action_controller.instance"].url_for("#{@config.base_url_path}")
599
+ else
600
+ "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}"
601
+ end
505
602
 
506
603
  settings = {
507
604
  :path => path,
@@ -26,8 +26,14 @@ module Rack
26
26
  end
27
27
 
28
28
  private
29
- def path(key)
30
- @path + "/" + @prefix + "_" + key
29
+ if RUBY_PLATFORM =~ /mswin(?!ce)|mingw|cygwin|bccwin/
30
+ def path(key)
31
+ @path + "/" + @prefix + "_" + key.gsub(/:/, '_')
32
+ end
33
+ else
34
+ def path(key)
35
+ @path + "/" + @prefix + "_" + key
36
+ end
31
37
  end
32
38
  end
33
39
 
@@ -9,6 +9,7 @@ module Rack
9
9
  require 'dalli' unless defined? Dalli
10
10
  args ||= {}
11
11
  @prefix = args[:prefix] || "MPMemcacheStore"
12
+ @prefix += "-#{Rack::MiniProfiler::VERSION}"
12
13
  @client = args[:client] || Dalli::Client.new
13
14
  @expires_in_seconds = args[:expires_in] || EXPIRES_IN_SECONDS
14
15
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class MiniProfiler
3
- VERSION = '0.9.3'
3
+ VERSION = '0.9.8'
4
4
  end
5
5
  end
@@ -9,7 +9,12 @@ module Rack::MiniProfilerRails
9
9
 
10
10
  c = Rack::MiniProfiler.config
11
11
 
12
- # By default, only show the MiniProfiler in development mode, in production allow profiling if post_authorize_cb is set
12
+ # By default, only show the MiniProfiler in development mode.
13
+ # To use the MiniProfiler in production, call Rack::MiniProfiler.authorize_request
14
+ # from a hook in your ApplicationController
15
+ #
16
+ # Example:
17
+ # before_action { Rack::MiniProfiler.authorize_request if current_user.is_admin? }
13
18
  #
14
19
  # NOTE: this must be set here with = and not ||=
15
20
  # The out of the box default is "true"
@@ -19,8 +24,9 @@ module Rack::MiniProfilerRails
19
24
 
20
25
  c.skip_paths ||= []
21
26
 
27
+ c.skip_paths << app.config.assets.prefix if serves_static_assets?(app)
28
+
22
29
  if Rails.env.development?
23
- c.skip_paths << app.config.assets.prefix if app.respond_to? :assets
24
30
  c.skip_schema_queries = true
25
31
  end
26
32
 
@@ -59,6 +65,16 @@ module Rack::MiniProfilerRails
59
65
  @already_initialized = true
60
66
  end
61
67
 
68
+ def self.serves_static_assets?(app)
69
+ return false if !app.respond_to?(:assets)
70
+ # Rails 4.2 deprecates serve_static_assets in favor of serve_static_files
71
+ if app.config.respond_to?(:serve_static_files)
72
+ app.config.serve_static_files
73
+ else
74
+ app.config.serve_static_assets
75
+ end
76
+ end
77
+
62
78
  class Railtie < ::Rails::Railtie
63
79
 
64
80
  initializer "rack_mini_profiler.configure_rails_initialization" do |app|
@@ -0,0 +1,12 @@
1
+ # Mongo/Mongoid 5 patches
2
+ class Mongo::Server::Connection
3
+ def dispatch_with_timing(*args, &blk)
4
+ return dispatch_without_timing(*args, &blk) unless SqlPatches.should_measure?
5
+
6
+ result, _record = SqlPatches.record_sql(args[0][0].payload.inspect) do
7
+ dispatch_without_timing(*args, &blk)
8
+ end
9
+ return result
10
+ end
11
+ alias_method_chain :dispatch, :timing
12
+ end
@@ -0,0 +1,29 @@
1
+ class Rack::MiniProfiler::NoBrainerProfiler
2
+
3
+ def on_query(env)
4
+ if SqlPatches.should_measure?
5
+ not_indexed = env[:criteria] && env[:criteria].where_present? &&
6
+ !env[:criteria].where_indexed? &&
7
+ !env[:criteria].model.try(:perf_warnings_disabled)
8
+
9
+ query = ""
10
+
11
+ # per-model/query database overrides
12
+ query << "[#{env[:options][:db]}] " if env[:options][:db]
13
+
14
+ # "read", "write" prefix
15
+ # query << "(#{NoBrainer::RQL.type_of(env[:query]).to_s}) "
16
+
17
+ query << "NOT USING INDEX: " if not_indexed
18
+ query << env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ') + " "
19
+
20
+ if env[:exception]
21
+ query << "exception: #{env[:exception].class} #{env[:exception].message.split("\n").first} "
22
+ end
23
+
24
+ ::Rack::MiniProfiler.record_sql query.strip, env[:duration] * 1000.0
25
+ end
26
+ end
27
+
28
+ NoBrainer::Profiler.register self.new
29
+ end
@@ -0,0 +1,70 @@
1
+ class ActiveRecord::Result
2
+ alias_method :each_without_profiling, :each
3
+ def each(&blk)
4
+ return each_without_profiling(&blk) unless @miniprofiler_sql_id
5
+
6
+ start = Time.now
7
+ result = each_without_profiling(&blk)
8
+ elapsed_time = SqlPatches.elapsed_time(start)
9
+ @miniprofiler_sql_id.report_reader_duration(elapsed_time)
10
+
11
+ result
12
+ end
13
+ end
14
+
15
+ class ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
16
+ SCHEMA_QUERY_TYPES = ["Sequence", "Primary Key", "Primary Key Trigger", nil].freeze
17
+
18
+ alias_method :execute_without_profiling, :execute
19
+ def execute(sql, name = nil)
20
+ mp_profile_sql(sql, name) { execute_without_profiling(sql, name) }
21
+ end
22
+
23
+ alias_method :exec_query_without_profiling, :exec_query
24
+ def exec_query(sql, name = 'SQL', binds = [])
25
+ mp_profile_sql(sql, name) { exec_query_without_profiling(sql, name, binds) }
26
+ end
27
+
28
+ alias_method :exec_insert_without_profiling, :exec_insert
29
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
30
+ mp_profile_sql(sql, name) { exec_insert_without_profiling(sql, name, binds, pk, sequence_name) }
31
+ end
32
+
33
+ alias_method :exec_update_without_profiling, :exec_update
34
+ def exec_update(sql, name, binds)
35
+ mp_profile_sql(sql, name) { exec_update_without_profiling(sql, name, binds) }
36
+ end
37
+
38
+ # See oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb:183
39
+ # where the exec delete method is aliased in the same way. We just have to do it again here to make sure
40
+ # the new exec_delete alias is linked to our profiling-enabled version.
41
+ alias :exec_delete :exec_update
42
+
43
+ private
44
+
45
+ def mp_profile_sql(sql, name, &blk)
46
+ return yield unless mp_should_measure?(name)
47
+
48
+ start = Time.now
49
+ result = yield
50
+ elapsed_time = SqlPatches.elapsed_time(start)
51
+ record = ::Rack::MiniProfiler.record_sql(sql, elapsed_time)
52
+
53
+ # Some queries return the row count as a Fixnum and will be frozen, don't save a record
54
+ # for those.
55
+ result.instance_variable_set("@miniprofiler_sql_id", record) if (result && !result.frozen?)
56
+
57
+ result
58
+ end
59
+
60
+ # Only measure when profiling is enabled
61
+ # When skip_schema_queries is set to true, it will ignore any query of the types
62
+ # in the schema_query_types array
63
+ def mp_should_measure?(name)
64
+ return false unless SqlPatches.should_measure?
65
+
66
+ !(Rack::MiniProfiler.config.skip_schema_queries && SCHEMA_QUERY_TYPES.include?(name))
67
+ end
68
+ end
69
+
70
+ SqlPatches.patched = true
@@ -14,6 +14,12 @@ class SqlPatches
14
14
  false
15
15
  end
16
16
 
17
+ def self.correct_version?(required_version, klass)
18
+ Gem::Dependency.new('', required_version).match?('', klass::VERSION)
19
+ rescue NameError
20
+ false
21
+ end
22
+
17
23
  def self.module_exists?(name)
18
24
  eval(name + ".class").to_s.eql?('Module')
19
25
  rescue NameError
@@ -37,10 +43,13 @@ class SqlPatches
37
43
  end
38
44
  end
39
45
 
40
- require 'patches/db/mysql2' if SqlPatches.class_exists? "Mysql2::Client"
41
- require 'patches/db/pg' if SqlPatches.class_exists? "PG::Result"
42
- require 'patches/db/moped' if SqlPatches.class_exists?("Moped::Node")
43
- require 'patches/db/plucky' if SqlPatches.class_exists?("Plucky::Query")
44
- require 'patches/db/rsolr' if SqlPatches.class_exists?("RSolr::Connection") && RSolr::VERSION[0] != "0"
45
- require 'patches/db/sequel' if !SqlPatches.patched? && SqlPatches.class_exists?("Sequel::Database")
46
- require 'patches/db/activerecord' if !SqlPatches.patched? && SqlPatches.module_exists?("ActiveRecord")
46
+ require 'patches/db/mysql2' if defined?(Mysql2::Client) && SqlPatches.class_exists?("Mysql2::Client")
47
+ require 'patches/db/pg' if defined?(PG::Result) && SqlPatches.class_exists?("PG::Result")
48
+ require 'patches/db/oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) && SqlPatches.class_exists?("ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter") && SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
49
+ require 'patches/db/mongo' if defined?(Mongo) &&!SqlPatches.patched? && SqlPatches.module_exists?("Mongo")
50
+ require 'patches/db/moped' if defined?(Moped::Node) && SqlPatches.class_exists?("Moped::Node")
51
+ require 'patches/db/plucky' if defined?(Plucky::Query) && SqlPatches.class_exists?("Plucky::Query")
52
+ require 'patches/db/rsolr' if defined?(RSolr::Connection) && SqlPatches.class_exists?("RSolr::Connection") && RSolr::VERSION[0] != "0"
53
+ require 'patches/db/sequel' if defined?(Sequel::Database) && !SqlPatches.patched? && SqlPatches.class_exists?("Sequel::Database")
54
+ require 'patches/db/activerecord' if defined?(ActiveRecord) &&!SqlPatches.patched? && SqlPatches.module_exists?("ActiveRecord")
55
+ require 'patches/db/nobrainer' if defined?(NoBrainer) && SqlPatches.module_exists?("NoBrainer")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-mini-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-02-27 00:00:00.000000000 Z
13
+ date: 2015-11-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -223,8 +223,11 @@ files:
223
223
  - lib/mini_profiler/version.rb
224
224
  - lib/mini_profiler_rails/railtie.rb
225
225
  - lib/patches/db/activerecord.rb
226
+ - lib/patches/db/mongo.rb
226
227
  - lib/patches/db/moped.rb
227
228
  - lib/patches/db/mysql2.rb
229
+ - lib/patches/db/nobrainer.rb
230
+ - lib/patches/db/oracle_enhanced.rb
228
231
  - lib/patches/db/pg.rb
229
232
  - lib/patches/db/plucky.rb
230
233
  - lib/patches/db/rsolr.rb
@@ -253,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
256
  version: '0'
254
257
  requirements: []
255
258
  rubyforge_project:
256
- rubygems_version: 2.2.2
259
+ rubygems_version: 2.4.5.1
257
260
  signing_key:
258
261
  specification_version: 4
259
262
  summary: Profiles loading speed for rack applications.