rack-mini-profiler 0.9.3 → 0.9.8

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.

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.