rack-mini-profiler 0.1.23 → 0.1.28

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.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dd8393bb0afbc59162f9ece7f63b47218f742127
4
+ data.tar.gz: 4f29c3a0407eb08c0b5146eea018919dc6620db8
5
+ SHA512:
6
+ metadata.gz: 0276de7ece6f3f8a70a4ce085ddb36a209385b1e3892c147057d6629922b4ea90e33177fc1093c72c08bf2de156585cff6b95576733c1cc906cf7cd4cd16000f
7
+ data.tar.gz: f9accaad19036dde64fdacb1c86ddd15892d97e778ce079bd579bdbe78df508b41716fecc17e0b3176887002caef63f6cc9f37c6384f12ecc233bb170a384863
@@ -1,77 +1,77 @@
1
- 28-June-2012 - Sam
2
-
1
+ 28-June-2012 - Sam
2
+
3
3
  * Started change log
4
4
  * Corrected profiler so it properly captures POST requests (was supressing non 200s)
5
- * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
5
+ * Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
6
6
  * Fixed bug where unviewed missing ids never got cleared
7
7
  * Supress all '/assets/' in the rails tie (makes debugging easier)
8
8
  * record_sql was mega buggy
9
9
  * added MemcacheStore
10
10
 
11
- 9-July-2012 - Sam
11
+ 9-July-2012 - Sam
12
12
 
13
13
  * Cleaned up mechanism for profiling in production, all you need to do now
14
- is call Rack::MiniProfiler.authorize_request to get profiling working in
15
- production
16
- * Added option to display full backtraces pp=full-backtrace
17
- * Cleaned up railties, got rid of the post authorize callback
18
- * Version 0.1.3
14
+ is call Rack::MiniProfiler.authorize_request to get profiling working in
15
+ production
16
+ * Added option to display full backtraces pp=full-backtrace
17
+ * Cleaned up railties, got rid of the post authorize callback
18
+ * Version 0.1.3
19
19
 
20
- 12-July-2012 - Sam
20
+ 12-July-2012 - Sam
21
21
 
22
22
  * Fixed incorrect profiling steps (was not indenting or measuring start time right
23
- * Implemented native PG and MySql2 interceptors, this gives way more accurate times
24
- * Refactored context so its a proper class and not a hash
25
- * Added some more client probing built in to rails
26
- * More tests
23
+ * Implemented native PG and MySql2 interceptors, this gives way more accurate times
24
+ * Refactored context so its a proper class and not a hash
25
+ * Added some more client probing built in to rails
26
+ * More tests
27
+
28
+ 18-July-2012 - Sam
27
29
 
28
- 18-July-2012 - Sam
29
-
30
30
  * Added First Paint time for chrome
31
31
  * Bug fix to ensure non Rails installs have mini profiler
32
32
  * Version 0.1.7
33
33
 
34
- 30-July-2012 - Sam
35
-
34
+ 30-July-2012 - Sam
35
+
36
36
  * Made compliant with ancient versions of Rack (including Rack used by Rails2)
37
- * Fixed broken share link
37
+ * Fixed broken share link
38
38
  * Fixed crashes on startup (in MemoryStore and FileStore)
39
39
  * Version 0.1.8
40
- * Unicode fix
40
+ * Unicode fix
41
41
  * Version 0.1.9
42
42
 
43
43
  7-August-2012 - Sam
44
44
 
45
45
  * Added option to disable profiler for the current session (pp=disable / pp=enable)
46
- * yajl compatability contributed by Sven Riedel
46
+ * yajl compatability contributed by Sven Riedel
47
47
 
48
- 10-August-2012 - Sam
48
+ 10-August-2012 - Sam
49
49
 
50
- * Added basic prepared statement profiling for postgres
50
+ * Added basic prepared statement profiling for postgres
51
51
 
52
- 20-August-2012 - Sam
53
-
54
- * 1.12.pre
52
+ 20-August-2012 - Sam
53
+
54
+ * 1.12.pre
55
55
  * Cap X-MiniProfiler-Ids at 10, otherwise the header can get killed
56
56
 
57
57
  3-September-2012 - Sam
58
58
 
59
59
  * 1.13.pre
60
- * pg gem prepared statements were not being logged correctly
60
+ * pg gem prepared statements were not being logged correctly
61
61
  * added setting config.backtrace_ignores = [] - an array of regexes that match on caller lines that get ignored
62
- * added setting config.backtrace_includes = [] - an array of regexes that get included in the trace by default
62
+ * added setting config.backtrace_includes = [] - an array of regexes that get included in the trace by default
63
63
  * cleaned up the way client settings are stored
64
64
  * made pp=full-backtrace "sticky"
65
65
  * added pp=normal-backtrace to clear the "sticky" state
66
66
  * change "pp=sample" to work with "caller" no need for stack trace gem
67
67
 
68
- 4-September-2012 - Sam
68
+ 4-September-2012 - Sam
69
69
 
70
70
  * 1.15.pre
71
- * fixed annoying bug where client settings were not sticking
71
+ * fixed annoying bug where client settings were not sticking
72
72
  * fixed long standing issue with Rack::ConditionalGet stopping MiniProfiler from working properly
73
73
 
74
- 5-September-2012 - Sam
74
+ 5-September-2012 - Sam
75
75
 
76
76
  * 1.16
77
77
  * fixed long standing problem specs (issue with memory store)
@@ -83,25 +83,65 @@
83
83
  * 1.17
84
84
  * pp=sample was bust unless stacktrace was installed
85
85
 
86
- 10-September-2012 - Sam
86
+ 10-September-2012 - Sam
87
87
 
88
88
  * 1.19
89
89
  * fix compat issue with 1.8.7
90
90
 
91
91
  12-September-2012 - Sam
92
-
92
+
93
93
  * 1.20
94
94
  * Added pp=profile-gc , it allows you to profile the GC in Ruby 1.9.3
95
95
 
96
96
  17-September-2012
97
97
  * 1.21
98
- * New MemchacedStore
98
+ * New MemchacedStore
99
99
  * Rails 4 support
100
100
 
101
101
  17-September-2012
102
102
  * Allow rack-mini-profiler to be sourced from github
103
103
  * Extracted the pp=profile-gc-time out, the object space profiler needs to disable gc
104
104
 
105
- 20-September-2010
105
+ 20-September-2012
106
106
  * 1.22
107
107
  * Fix permission issue in the gem
108
+
109
+ 8-April-2013
110
+ * 1.24
111
+ * Flame Graph Support see: http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler
112
+ * Fix file retention leak in file_store
113
+ * New toggle_shortcut and start_hidden options
114
+ * Fix for AngularJS support and MooTools
115
+ * More robust gc profiling
116
+ * Mongoid support
117
+ * Fix for html5 implicit body tags
118
+ * script tag initialized via data-attributes
119
+ * new - Rack::MiniProfiler.counter counter_name {}
120
+ * Allow usage of existing jQuery if its already loaded
121
+ * Fix pp=enable
122
+ * 1.8.7 support ... grrr
123
+ * Net:HTTP profiling
124
+ * pre authorize to run in all non development? and production? modes
125
+
126
+ 8-April-2013
127
+ * 1.25
128
+ * Missed flamegraph.html from build
129
+
130
+ 11-April-2013
131
+ * 1.26
132
+ * (minor) allow Rack::MiniProfilerRails.initialize!(Rails.application), for post config intialization
133
+
134
+ 26-June-2013
135
+ * 1.27
136
+ * Disable global ajax handlers on MP requests @JP
137
+ * Add Rack::MiniProfiler.config.backtrace_threshold_ms
138
+ * jQuery 2.0 support
139
+
140
+ 18-July-2013
141
+ * 1.28
142
+ * diagnostics in abstract storage was raising not implemented killing
143
+ ?pp=env and others
144
+ * SOLR xml unescaped by mistake
145
+
146
+
147
+
@@ -79,6 +79,22 @@ RedisStore/MemcacheStore work in multi process and multi machine environments (R
79
79
 
80
80
  Additionally you may implement an AbstractStore for your own provider.
81
81
 
82
+ ## User result segregation
83
+
84
+ MiniProfiler will attempt to keep all user results isolated, out-of-the-box the user provider uses the ip address:
85
+
86
+ ```ruby
87
+ Rack::MiniProfiler.config.user_provider = Proc.new{|env| Rack::Request.new(env).ip}
88
+ ```
89
+
90
+ You can override (something that is very important in a multi-machine production setup):
91
+
92
+ ```ruby
93
+ Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) }
94
+ ```
95
+
96
+ The string this function returns should be unique for each user on the system (for anonymous you may need to fall back to ip address)
97
+
82
98
  ## Running the Specs
83
99
 
84
100
  ```
@@ -95,8 +111,14 @@ You can set configuration options using the configuration accessor on Rack::Mini
95
111
  ```
96
112
  # Have Mini Profiler show up on the right
97
113
  Rack::MiniProfiler.config.position = 'right'
114
+ # Have Mini Profiler start in hidden mode - display with short cut (defaulted to 'Alt+P')
115
+ Rack::MiniProfiler.config.start_hidden = true
116
+ # Don't collect backtraces on SQL queries that take less than 5 ms to execute
117
+ # (necessary on Rubies earlier than 2.0)
118
+ Rack::MiniProfiler.config.backtrace_threshold_ms = 5
98
119
  ```
99
120
 
121
+
100
122
  In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
101
123
 
102
124
  ## Rails 2.X support
@@ -129,6 +151,10 @@ if JSON.const_defined?(:Pure)
129
151
  end
130
152
  ```
131
153
 
154
+ ## Notes
155
+
156
+ - Be sure to require rack_mini_profiler last in your Gemfile, when it is required it will monkey patch pg and mysql gems to insert instrumentation. If included to early no SQL will show up.
157
+
132
158
  ## Available Options
133
159
 
134
160
  * pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
@@ -136,8 +162,11 @@ end
136
162
  * 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.
137
163
  * auto_inject (default true) - when false the miniprofiler script is not injected in the page
138
164
  * backtrace_filter - a regex you can use to filter out unwanted lines from the backtraces
165
+ * 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.
166
+ * start_hidden (default false) - Whether or not you want the mini_profiler to be visible when loading a page
167
+ * 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.
139
168
 
140
169
  ## Special query strings
141
170
 
142
- If you include the query string `pp=help` at the end of your request you will see the various option you have. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
171
+ If you include the query string `pp=help` at the end of your request you will see the various options available. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
143
172
 
@@ -0,0 +1,351 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
5
+ <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.8/d3.min.js"></script>
6
+ <!-- <script src="http://cdnjs.cloudflare.com/ajax/libs/sugar/1.3.8/sugar.min.js"></script> -->
7
+ <meta charset=utf-8 />
8
+ <title>Flame Graph of Page</title>
9
+ <style>
10
+ .info {height: 40px;}
11
+ .legend div {
12
+ display: block;
13
+ float: left;
14
+ width: 150px;
15
+ margin: 0 8px 8px;
16
+ padding: 4px;
17
+ height: 50px;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div class="graph"></div>
23
+ <div class="info"></div>
24
+ <div class="legend"></div>
25
+
26
+ <script>
27
+
28
+ var data = /*DATA*/;
29
+ var maxX = 0;
30
+ var maxY = 0;
31
+
32
+ debounce = function(func, wait, trickle) {
33
+ var timeout;
34
+
35
+ timeout = null;
36
+ return function() {
37
+ var args, context, currentWait, later;
38
+ context = this;
39
+ args = arguments;
40
+ later = function() {
41
+ timeout = null;
42
+ return func.apply(context, args);
43
+ };
44
+
45
+ if (timeout && trickle) {
46
+ // already queued, let it through
47
+ return;
48
+ }
49
+
50
+ if (typeof wait === "function") {
51
+ currentWait = wait();
52
+ } else {
53
+ currentWait = wait;
54
+ }
55
+
56
+ if (timeout) {
57
+ clearTimeout(timeout);
58
+ }
59
+
60
+ timeout = setTimeout(later, currentWait);
61
+ return timeout;
62
+ };
63
+ };
64
+
65
+
66
+ var guessGem = function(frame)
67
+ {
68
+ var split = frame.split('/gems/');
69
+ if(split.length == 1) {
70
+ split = frame.split('/app/');
71
+ if(split.length == 1) {
72
+ split = frame.split('/lib/');
73
+ }
74
+
75
+ split = split[Math.max(split.length-2,0)].split('/');
76
+ return split[split.length-1];
77
+ }
78
+ else
79
+ {
80
+ return split[split.length -1].split('/')[0];
81
+ }
82
+ }
83
+
84
+ var guessMethod = function(frame) {
85
+ var split = frame.split('`');
86
+ if(split.length == 2) {
87
+ return split[1].split("'")[0];
88
+ }
89
+ return '?';
90
+ }
91
+
92
+ var guessFile = function(frame) {
93
+ var split = frame.split(".rb:");
94
+ if(split.length == 2) {
95
+ split = split[0].split('/');
96
+ return split[split.length - 1];
97
+ }
98
+ return "";
99
+ }
100
+
101
+ $.each(data, function(){
102
+ maxX = Math.max(maxX, this.x + this.width);
103
+ maxY = Math.max(maxY, this.y);
104
+ this.shortName = /* guessGem(this.frame) + " " + guessFile(this.frame) + " " */ guessMethod(this.frame);
105
+ });
106
+
107
+ var width = $(window).width();
108
+ var height = $(window).height() / 1.2;
109
+
110
+ $('.graph').width(width).height(height);
111
+
112
+ var xScale = d3.scale.linear()
113
+ .domain([0, maxX])
114
+ .range([0, width]);
115
+
116
+ var yScale = d3.scale.linear()
117
+ .domain([0, maxY])
118
+ .range([0,height]);
119
+
120
+ var realHeight = 0;
121
+ var debouncedHeightCheck = debounce(function(){
122
+ if (realHeight > 15) {
123
+ svg.selectAll('text').attr('display','show');
124
+ } else {
125
+ svg.selectAll('text').attr('display','none');
126
+ }
127
+ }, 200);
128
+
129
+ function zoom()
130
+ {
131
+ svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
132
+
133
+ realHeight = yScale(1) * d3.event.scale;
134
+ debouncedHeightCheck();
135
+ }
136
+
137
+ var svg = d3.select(".graph")
138
+ .append("svg")
139
+ .attr("width", "100%")
140
+ .attr("height", "100%")
141
+ .attr("pointer-events", "all")
142
+ .append('svg:g')
143
+ .call(d3.behavior.zoom().on("zoom", zoom))
144
+ .append('svg:g');
145
+
146
+
147
+ // so zoom works everywhere
148
+ svg.append("rect")
149
+ .attr("x",function(d) { return xScale(0); })
150
+ .attr("y",function(d) { return yScale(0);})
151
+ .attr("width", function(d){return xScale(maxX);})
152
+ .attr("height", yScale(maxY))
153
+ .attr("fill", "white");
154
+
155
+ var color = function() {
156
+ var r = parseInt(205 + Math.random() * 50);
157
+ var g = parseInt(Math.random() * 230);
158
+ var b = parseInt(Math.random() * 55);
159
+ return "rgb(" + r + "," + g + "," + b + ")";
160
+ }
161
+ var info = {};
162
+
163
+ // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
164
+ Array.prototype.getUnique = function() {
165
+ var o = {}, a = []
166
+ for (var i = 0; i < this.length; i++) o[this[i]] = 1
167
+ for (var e in o) a.push(e)
168
+ return a
169
+ }
170
+
171
+ var samplePercent = function(samples, exclusive){
172
+ var info = " (" + samples +
173
+ " sample" + (samples == 1 ? "" : "s") + " - " +
174
+ ((samples / maxX) * 100).toFixed(2) + "%) ";
175
+ if (exclusive) {
176
+ info += " (" + exclusive +
177
+ " exclusive - " +
178
+ ((exclusive / maxX) * 100).toFixed(2) + "%) ";
179
+ }
180
+ return info;
181
+ }
182
+
183
+ var mouseover = function(d) {
184
+ var i = info[d.frame];
185
+ $('.info').text( d.frame + " " + samplePercent(i.samples.length, d.topFrame ? d.topFrame.exclusiveCount : 0));
186
+ d3.selectAll(i.nodes)
187
+ .attr('opacity',0.5);
188
+ };
189
+
190
+ var mouseout = function(d) {
191
+ var i = info[d.frame];
192
+ $('.info').text("");
193
+ d3.selectAll(i.nodes)
194
+ .attr('opacity',1);
195
+ };
196
+
197
+ // http://stackoverflow.com/a/7419630
198
+ var rainbow = function(numOfSteps, step) {
199
+ // This function generates vibrant, "evenly spaced" colours (i.e. no clustering). This is ideal for creating easily distiguishable vibrant markers in Google Maps and other apps.
200
+ // Adam Cole, 2011-Sept-14
201
+ // HSV to RBG adapted from: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
202
+ var r, g, b;
203
+ var h = step / numOfSteps;
204
+ var i = ~~(h * 6);
205
+ var f = h * 6 - i;
206
+ var q = 1 - f;
207
+ switch(i % 6){
208
+ case 0: r = 1, g = f, b = 0; break;
209
+ case 1: r = q, g = 1, b = 0; break;
210
+ case 2: r = 0, g = 1, b = f; break;
211
+ case 3: r = 0, g = q, b = 1; break;
212
+ case 4: r = f, g = 0, b = 1; break;
213
+ case 5: r = 1, g = 0, b = q; break;
214
+ }
215
+ var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2);
216
+ return (c);
217
+ }
218
+
219
+ // assign some colors, analyze samples per gem
220
+ var gemStats = {}
221
+ var topFrames = {}
222
+ var lastFrame = {frame: 'd52e04d-df28-41ed-a215-b6ec840a8ea5', x: -1}
223
+
224
+ $.each(data, function(){
225
+
226
+ var gem = guessGem(this.frame);
227
+ var stat = gemStats[gem];
228
+
229
+ if(!stat) {
230
+ gemStats[gem] = stat = {samples: [], frames: []};
231
+ }
232
+
233
+ stat.frames.push(this.frame);
234
+ for(var j=0; j < this.width; j++){
235
+ stat.samples.push(this.x + j);
236
+ }
237
+ // This assumes the traversal is in order
238
+ if (lastFrame.x != this.x) {
239
+ var topFrame = topFrames[lastFrame.frame]
240
+ if (!topFrame) {
241
+ topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
242
+ }
243
+ topFrame.exclusiveCount += 1;
244
+ lastFrame.topFrame = topFrame;
245
+ }
246
+ lastFrame = this;
247
+
248
+ });
249
+
250
+ var topFrame = topFrames[lastFrame.frame]
251
+ if (!topFrame) {
252
+ topFrames[lastFrame.frame] = topFrame = {exclusiveCount: 0}
253
+ }
254
+ topFrame.exclusiveCount += 1;
255
+ lastFrame.topFrame = topFrame;
256
+
257
+ var totalGems = 0;
258
+ $.each(gemStats, function(){totalGems++;});
259
+
260
+
261
+ var currentIndex = 0;
262
+ $.each(gemStats, function(k,stat){
263
+
264
+ stat.color = rainbow(totalGems, currentIndex);
265
+ stat.samples = stat.samples.getUnique();
266
+
267
+ for(var x=0; x < stat.frames.length; x++) {
268
+ info[stat.frames[x]] = {nodes: [], samples: [], color: stat.color};
269
+ }
270
+
271
+ currentIndex += 1;
272
+ });
273
+
274
+
275
+ // see: http://bl.ocks.org/mundhradevang/1387786
276
+ function fontSize(d,i) {
277
+ var size = yScale(1) / 3;
278
+ // var words = d.shortName.split(' ');
279
+ var word = d.shortName; // words[0];
280
+ var width = xScale(d.width+100);
281
+ var height = yScale(1);
282
+ var length = 0;
283
+ d3.select(this).style("font-size", size + "px").text(word);
284
+ while(((this.getBBox().width >= width) || (this.getBBox().height >= height)) && (size > 12))
285
+ {
286
+ size -= 0.1;
287
+ d3.select(this).style("font-size", size + "px");
288
+ }
289
+
290
+ d3.select(this).attr("dy", size);
291
+ }
292
+
293
+ svg.selectAll("g")
294
+ .data(data)
295
+ .enter()
296
+ .append("g")
297
+ .each(function(){
298
+ d3.select(this)
299
+ .append("rect")
300
+ .attr("x",function(d) { return xScale(d.x-1); })
301
+ .attr("y",function(d) { return yScale(maxY - d.y);})
302
+ .attr("width", function(d){return xScale(d.width);})
303
+ .attr("height", yScale(1))
304
+ .attr("fill", function(d){
305
+ var i = info[d.frame];
306
+ if(!i) {
307
+ info[d.frame] = i = {nodes: [], samples: [], color: color()};
308
+ }
309
+ i.nodes.push(this);
310
+ for(var j=0; j < d.width; j++){
311
+ i.samples.push(d.x + j);
312
+ }
313
+ return i.color;
314
+ })
315
+ .on("mouseover", mouseover)
316
+ .on("mouseout", mouseout);
317
+
318
+ d3.select(this)
319
+ .append("text")
320
+ .attr("x",function(d) { return xScale(d.x - 0.98); })
321
+ .attr("y",function(d) { return yScale(maxY - d.y);})
322
+ .on("mouseover", mouseover)
323
+ .on("mouseout", mouseout)
324
+ .each(fontSize)
325
+ .attr("display", "none");
326
+
327
+ });
328
+
329
+
330
+ // Samples may overlap on the same line
331
+ for (var r in info) {
332
+ if (info[r].samples) {
333
+ info[r].samples = info[r].samples.getUnique();
334
+ }
335
+ };
336
+
337
+
338
+ // render the legend
339
+ $.each(gemStats, function(k,v){
340
+ var node = $("<div></div>")
341
+ .css("background-color", v.color)
342
+ .text(k + " " + samplePercent(v.samples.length)) ;
343
+ $('.legend').append(node);
344
+ });
345
+
346
+
347
+
348
+ </script>
349
+ </body>
350
+ </html>
351
+