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 +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.
|