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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +15 -12
- data/lib/html/includes.css +3 -0
- data/lib/html/includes.js +7 -1
- data/lib/html/includes.less +5 -0
- data/lib/html/includes.tmpl +1 -0
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +8 -4
- data/lib/mini_profiler/gc_profiler.rb +0 -20
- data/lib/mini_profiler/profiler.rb +136 -39
- data/lib/mini_profiler/storage/file_store.rb +8 -2
- data/lib/mini_profiler/storage/memcache_store.rb +1 -0
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +18 -2
- data/lib/patches/db/mongo.rb +12 -0
- data/lib/patches/db/nobrainer.rb +29 -0
- data/lib/patches/db/oracle_enhanced.rb +70 -0
- data/lib/patches/sql_patches.rb +16 -7
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 661dc965f1499d7a00baa35f5699e91e1552ff68
|
4
|
+
data.tar.gz: 7bbccf4b17154aab14f7bd2012afcf2dac0efe25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 903fe47823489fae4a2b18aff1cddca204130c1e41b2132c5db951007a5c1fe85b7b110f3264b8abc2f22c07d438071b81c93251bc31cbe26db725b868ca0a2a
|
7
|
+
data.tar.gz: 400bb2bf85475461de90d238eb95cbac925710f758f576c7371cb7b25ef0998bf39d637835520b42dd96fbbeb7cd53f36fe531f83e9e9c8c1cf36022795fcb8c
|
data/CHANGELOG.md
CHANGED
@@ -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.
|
92
|
+
Flamegraph generation is supported in MRI 2.0, 2.1, and 2.2 only.
|
94
93
|
|
95
94
|
|
96
|
-
## Access control in
|
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
|
-
#
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
*
|
187
|
-
*
|
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
|
|
data/lib/html/includes.css
CHANGED
data/lib/html/includes.js
CHANGED
@@ -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;
|
data/lib/html/includes.less
CHANGED
@@ -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
|
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -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}}
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -12,10 +12,13 @@ module Rack
|
|
12
12
|
@attributes
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_accessor :authorization_mode, :auto_inject, :backtrace_ignores,
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
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['
|
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
|
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
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
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
|
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
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
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
|
-
|
30
|
-
|
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
|
@@ -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
|
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
|
data/lib/patches/sql_patches.rb
CHANGED
@@ -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'
|
41
|
-
require 'patches/db/pg'
|
42
|
-
require 'patches/db/
|
43
|
-
require 'patches/db/
|
44
|
-
require 'patches/db/
|
45
|
-
require 'patches/db/
|
46
|
-
require 'patches/db/
|
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.
|
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-
|
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.
|
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.
|