rack-mini-profiler 2.0.0 → 2.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +59 -28
- data/lib/enable_rails_patches.rb +1 -3
- data/lib/html/includes.css +40 -9
- data/lib/html/includes.js +90 -29
- data/lib/html/includes.scss +32 -4
- data/lib/html/includes.tmpl +74 -2
- data/lib/html/profile_handler.js +1 -1
- data/lib/html/rack-mini-profiler.css +3 -0
- data/lib/html/rack-mini-profiler.js +2 -0
- data/lib/html/vendor.js +10 -2
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +13 -1
- data/lib/mini_profiler/profiler.rb +166 -23
- data/lib/mini_profiler/storage/abstract_store.rb +72 -0
- data/lib/mini_profiler/storage/memory_store.rb +54 -5
- data/lib/mini_profiler/storage/redis_store.rb +136 -2
- data/lib/mini_profiler/timer_struct/page.rb +4 -1
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +13 -0
- data/lib/mini_profiler_rails/railtie_methods.rb +6 -0
- data/lib/patches/net_patches.rb +19 -6
- data/lib/prepend_net_http_patch.rb +5 -0
- data/rack-mini-profiler.gemspec +4 -2
- metadata +51 -20
data/lib/html/includes.scss
CHANGED
@@ -14,9 +14,10 @@ $codeFonts: Consolas, monospace, serif;
|
|
14
14
|
$zindex: 2147483640; // near 32bit max 2147483647
|
15
15
|
|
16
16
|
// do some resets
|
17
|
+
.mp-snapshots,
|
17
18
|
.profiler-result,
|
18
19
|
.profiler-queries {
|
19
|
-
color:
|
20
|
+
color: $textColor;
|
20
21
|
line-height: 1;
|
21
22
|
font-size: 12px;
|
22
23
|
|
@@ -55,6 +56,11 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
55
56
|
text-decoration: underline;
|
56
57
|
}
|
57
58
|
}
|
59
|
+
.custom-fields-title {
|
60
|
+
color: $textColor;
|
61
|
+
font: $normalFonts;
|
62
|
+
font-size: 14px;
|
63
|
+
}
|
58
64
|
}
|
59
65
|
|
60
66
|
// styles shared between popup view and full view
|
@@ -62,6 +68,9 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
62
68
|
.profiler-toggle-duration-with-children {
|
63
69
|
float: right;
|
64
70
|
}
|
71
|
+
.profiler-snapshots-page-link {
|
72
|
+
float: left;
|
73
|
+
}
|
65
74
|
table.profiler-client-timings {
|
66
75
|
margin-top: 10px;
|
67
76
|
}
|
@@ -199,7 +208,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
199
208
|
|
200
209
|
th {
|
201
210
|
background-color: #fff;
|
202
|
-
border-bottom: 1px solid
|
211
|
+
border-bottom: 1px solid $textColor;
|
203
212
|
font-weight: bold;
|
204
213
|
padding: 15px;
|
205
214
|
white-space: nowrap;
|
@@ -452,7 +461,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
452
461
|
line-height: 18px;
|
453
462
|
overflow: auto;
|
454
463
|
|
455
|
-
@include box-shadow(0px, 1px, 15px,
|
464
|
+
@include box-shadow(0px, 1px, 15px, $textColor);
|
456
465
|
|
457
466
|
.profiler-info {
|
458
467
|
margin-bottom: 3px;
|
@@ -592,7 +601,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
592
601
|
}
|
593
602
|
th {
|
594
603
|
font-size: 16px;
|
595
|
-
color:
|
604
|
+
color: $textColor;
|
596
605
|
line-height: 20px;
|
597
606
|
}
|
598
607
|
|
@@ -617,3 +626,22 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
617
626
|
background: #fff;
|
618
627
|
}
|
619
628
|
}
|
629
|
+
|
630
|
+
.mp-snapshots {
|
631
|
+
font-family: $normalFonts;
|
632
|
+
font-size: 16px;
|
633
|
+
|
634
|
+
.snapshots-table {
|
635
|
+
thead {
|
636
|
+
background: #6a737c;
|
637
|
+
color: #ffffff;
|
638
|
+
}
|
639
|
+
th, td {
|
640
|
+
padding: 5px;
|
641
|
+
box-sizing: border-box;
|
642
|
+
}
|
643
|
+
th {
|
644
|
+
border-right: 1px solid #ffffff;
|
645
|
+
}
|
646
|
+
}
|
647
|
+
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -45,6 +45,10 @@
|
|
45
45
|
{{= MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) }}
|
46
46
|
{{?}}
|
47
47
|
<a class="profiler-toggle-duration-with-children" title="toggles column with aggregate child durations">show time with children</a>
|
48
|
+
<a
|
49
|
+
class="profiler-snapshots-page-link"
|
50
|
+
title="Go to snapshots page"
|
51
|
+
href="{{= MiniProfiler.options.path }}snapshots">snapshots</a>
|
48
52
|
</td>
|
49
53
|
{{? it.has_sql_timings}}
|
50
54
|
<td colspan="2" class="profiler-number profiler-percent-in-sql" title="{{= MiniProfiler.getSqlTimingsCount(it.root) }} queries spent {{= MiniProfiler.formatDuration(it.duration_milliseconds_in_sql) }} ms of total request time">
|
@@ -92,6 +96,19 @@
|
|
92
96
|
</tfoot>
|
93
97
|
</table>
|
94
98
|
{{?}}
|
99
|
+
{{? it.custom_fields && Object.keys(it.custom_fields).length > 0 }}
|
100
|
+
<p class="custom-fields-title">Snapshot custom fields</p>
|
101
|
+
<table class="profiler-timings">
|
102
|
+
<tbody>
|
103
|
+
{{~ Object.keys(it.custom_fields) :key }}
|
104
|
+
<tr>
|
105
|
+
<td class="profiler-label">{{= key }}</td>
|
106
|
+
<td class="profiler-label">{{= it.custom_fields[key] }}</td>
|
107
|
+
</tr>
|
108
|
+
{{~}}
|
109
|
+
</tbody>
|
110
|
+
</table>
|
111
|
+
{{?}}
|
95
112
|
</div>
|
96
113
|
</div>
|
97
114
|
|
@@ -128,8 +145,8 @@
|
|
128
145
|
{{? it.custom_link}}
|
129
146
|
<a href="{{= it.custom_link }}" class="profiler-custom-link" target="_blank">{{= it.custom_link_name }}</a>
|
130
147
|
{{?}}
|
131
|
-
{{? it.has_trivial_timings}}
|
132
|
-
<a class="profiler-toggle-trivial" data-show-on-load="{{= it.has_all_trivial_timings }}" title="toggles any rows with < {{= it.trivial_duration_threshold_milliseconds }} ms">
|
148
|
+
{{? it.page.has_trivial_timings}}
|
149
|
+
<a class="profiler-toggle-trivial" data-show-on-load="{{= it.page.has_all_trivial_timings }}" title="toggles any rows with < {{= it.page.trivial_duration_threshold_milliseconds }} ms">
|
133
150
|
show trivial
|
134
151
|
</a>
|
135
152
|
{{?}}
|
@@ -216,3 +233,58 @@
|
|
216
233
|
</td>
|
217
234
|
</tr>
|
218
235
|
</script>
|
236
|
+
|
237
|
+
<script id="snapshotsGroupsList" type="text/x-dot-tmpl">
|
238
|
+
{{? it.list && it.list.length }}
|
239
|
+
<table class="snapshots-table">
|
240
|
+
<thead>
|
241
|
+
<tr>
|
242
|
+
<th>Requests Group</th>
|
243
|
+
<th>Worst Time (ms)</th>
|
244
|
+
</tr>
|
245
|
+
</thead>
|
246
|
+
<tbody>
|
247
|
+
{{~ it.list :row}}
|
248
|
+
<tr>
|
249
|
+
<td><a href="{{= row.url }}">{{= row.name }}</a></td>
|
250
|
+
<td>{{= MiniProfiler.formatDuration(row.worst_score) }}</td>
|
251
|
+
</tr>
|
252
|
+
{{~}}
|
253
|
+
</tbody>
|
254
|
+
</table>
|
255
|
+
{{??}}
|
256
|
+
<h2>No snapshots exist</h2>
|
257
|
+
{{?}}
|
258
|
+
</script>
|
259
|
+
|
260
|
+
<script id="snapshotsList" type="text/x-dot-tmpl">
|
261
|
+
{{? it.list && it.list.length }}
|
262
|
+
<h2>Snapshots for {{= it.group_name }}</h2>
|
263
|
+
<table class="snapshots-table">
|
264
|
+
<thead>
|
265
|
+
<tr>
|
266
|
+
<th>ID</th>
|
267
|
+
<th>Duration (ms)</th>
|
268
|
+
<th>Age</th>
|
269
|
+
</tr>
|
270
|
+
</thead>
|
271
|
+
<tbody>
|
272
|
+
{{~ it.list :row}}
|
273
|
+
<tr>
|
274
|
+
<td><a href="{{= row.url }}">
|
275
|
+
{{= row.id }}
|
276
|
+
</a></td>
|
277
|
+
<td>{{= MiniProfiler.formatDuration(row.duration) }}</td>
|
278
|
+
<td>
|
279
|
+
{{? row.timestamp }}
|
280
|
+
{{= MiniProfiler.timestampToRelative(row.timestamp) }}
|
281
|
+
{{?}}
|
282
|
+
</td>
|
283
|
+
</tr>
|
284
|
+
{{~}}
|
285
|
+
</tbody>
|
286
|
+
</table>
|
287
|
+
{{??}}
|
288
|
+
<h2>No snapshots for {{= it.group_name }}</h2>
|
289
|
+
{{?}}
|
290
|
+
</script>
|
data/lib/html/profile_handler.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
<script async type="text/javascript" id="mini-profiler" src="{
|
1
|
+
<script async type="text/javascript" id="mini-profiler" src="{url}" data-css-url="{cssUrl}" data-version="{version}" data-path="{path}" data-current-id="{currentId}" data-ids="{ids}" data-horizontal-position="{horizontalPosition}" data-vertical-position="{verticalPosition}" data-trivial="{showTrivial}" data-children="{showChildren}" data-max-traces="{maxTracesToShow}" data-controls="{showControls}" data-total-sql-count="{showTotalSqlCount}" data-authorized="{authorized}" data-toggle-shortcut="{toggleShortcut}" data-start-hidden="{startHidden}" data-collapse-results="{collapseResults}" data-html-container="{htmlContainer}"></script>
|
data/lib/html/vendor.js
CHANGED
@@ -7,11 +7,11 @@
|
|
7
7
|
MiniProfiler.templates = {};
|
8
8
|
MiniProfiler.templates["profilerTemplate"] = function anonymous(it
|
9
9
|
) {
|
10
|
-
var out=' <div class="profiler-result"> <div class="profiler-button ';if(it.has_duplicate_sql_timings){out+='profiler-warning';}out+='"> ';if(it.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' <span class="profiler-number"> '+( MiniProfiler.formatDuration(it.duration_milliseconds))+' <span class="profiler-unit">ms</span> </span> ';if(MiniProfiler.showTotalSqlCount()){out+=' <span class="profiler-number"> '+( it.sql_count)+' <span class="profiler-unit">sql</span> </span> ';}out+=' </div> <div class="profiler-popup"> <div class="profiler-info"> <span class="profiler-name"> '+( it.name)+' <span class="profiler-overall-duration">('+( MiniProfiler.formatDuration(it.duration_milliseconds))+' ms)</span> </span> <span class="profiler-server-time">'+( it.machine_name)+' on '+( MiniProfiler.renderDate(it.started))+'</span> </div> <div class="profiler-output"> <table class="profiler-timings"> <thead> <tr> <th></th> <th>duration (ms)</th> <th class="profiler-duration-with-children">with children (ms)</th> <th class="time-from-start">from start (ms)</th> ';if(it.has_sql_timings){out+=' <th colspan="2">query time (ms)</th> ';}out+=' ';var arr1=it.custom_timing_names;if(arr1){var value,i1=-1,l1=arr1.length-1;while(i1<l1){value=arr1[i1+=1];out+=' <th colspan="2">'+( value.toLowerCase() )+' (ms)</th> ';} } out+=' </tr> </thead> <tbody> '+( MiniProfiler.templates.timingTemplate({timing: it.root, page: it}) )+' </tbody> <tfoot> <tr> <td colspan="3"> ';if(!it.client_timings){out+=' '+( MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) )+' ';}out+=' <a class="profiler-toggle-duration-with-children" title="toggles column with aggregate child durations">show time with children</a> </td> ';if(it.has_sql_timings){out+=' <td colspan="2" class="profiler-number profiler-percent-in-sql" title="'+( MiniProfiler.getSqlTimingsCount(it.root) )+' queries spent '+( MiniProfiler.formatDuration(it.duration_milliseconds_in_sql) )+' ms of total request time"> '+( MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) )+' <span class="profiler-unit">% in sql</span> </td> ';}out+=' ';var arr2=it.custom_timing_names;if(arr2){var value,i2=-1,l2=arr2.length-1;while(i2<l2){value=arr2[i2+=1];out+=' <td colspan="2" class="profiler-number profiler-percentage-in-sql" title="'+( it.custom_timing_stats[value].count )+' '+( value.toLowerCase() )+' invocations spent '+( MiniProfiler.formatDuration(it.custom_timing_stats[value].duration) )+' ms of total request time"> '+( MiniProfiler.formatDuration(it.custom_timing_stats[value].duration / it.duration_milliseconds * 100) )+' <span class="profiler-unit">% in '+( value.toLowerCase() )+'</span> </td> ';} } out+=' </tr> </tfoot> </table> ';if(it.client_timings){out+=' <table class="profiler-timings profiler-client-timings"> <thead> <tr> <th>client event</th> <th>duration (ms)</th> <th>from start (ms)</th> </tr> </thead> <tbody> ';var arr3=MiniProfiler.getClientTimings(it.client_timings);if(arr3){var value,i3=-1,l3=arr3.length-1;while(i3<l3){value=arr3[i3+=1];out+=' <tr class="';if(value.isTrivial){out+='profiler-trivial';}out+='"> <td class="profiler-label">'+( value.name )+'</td> <td class="profiler-duration"> ';if(value.duration >= 0){out+=' <span class="profiler-unit"></span>'+( MiniProfiler.formatDuration(value.duration) )+' ';}out+=' </td> <td class="profiler-duration time-from-start"> <span class="profiler-unit">+</span>'+( MiniProfiler.formatDuration(value.start) )+' </td> </tr> ';} } out+=' </tbody> <tfoot> <td colspan="3"> '+( MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) )+' </td> </tfoot> </table> ';}out+=' </div> </div> ';if(it.has_sql_timings){out+=' <div class="profiler-queries"> <table> <thead> <tr> <th style="text-align:right">step<br />time from start<br />query type<br />duration</th> <th style="text-align:left">call stack<br />query</th> </tr> </thead> <tbody> ';var
|
10
|
+
var out=' <div class="profiler-result"> <div class="profiler-button ';if(it.has_duplicate_sql_timings){out+='profiler-warning';}out+='"> ';if(it.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' <span class="profiler-number"> '+( MiniProfiler.formatDuration(it.duration_milliseconds))+' <span class="profiler-unit">ms</span> </span> ';if(MiniProfiler.showTotalSqlCount()){out+=' <span class="profiler-number"> '+( it.sql_count)+' <span class="profiler-unit">sql</span> </span> ';}out+=' </div> <div class="profiler-popup"> <div class="profiler-info"> <span class="profiler-name"> '+( it.name)+' <span class="profiler-overall-duration">('+( MiniProfiler.formatDuration(it.duration_milliseconds))+' ms)</span> </span> <span class="profiler-server-time">'+( it.machine_name)+' on '+( MiniProfiler.renderDate(it.started))+'</span> </div> <div class="profiler-output"> <table class="profiler-timings"> <thead> <tr> <th></th> <th>duration (ms)</th> <th class="profiler-duration-with-children">with children (ms)</th> <th class="time-from-start">from start (ms)</th> ';if(it.has_sql_timings){out+=' <th colspan="2">query time (ms)</th> ';}out+=' ';var arr1=it.custom_timing_names;if(arr1){var value,i1=-1,l1=arr1.length-1;while(i1<l1){value=arr1[i1+=1];out+=' <th colspan="2">'+( value.toLowerCase() )+' (ms)</th> ';} } out+=' </tr> </thead> <tbody> '+( MiniProfiler.templates.timingTemplate({timing: it.root, page: it}) )+' </tbody> <tfoot> <tr> <td colspan="3"> ';if(!it.client_timings){out+=' '+( MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) )+' ';}out+=' <a class="profiler-toggle-duration-with-children" title="toggles column with aggregate child durations">show time with children</a> <a class="profiler-snapshots-page-link" title="Go to snapshots page" href="'+( MiniProfiler.options.path )+'snapshots">snapshots</a> </td> ';if(it.has_sql_timings){out+=' <td colspan="2" class="profiler-number profiler-percent-in-sql" title="'+( MiniProfiler.getSqlTimingsCount(it.root) )+' queries spent '+( MiniProfiler.formatDuration(it.duration_milliseconds_in_sql) )+' ms of total request time"> '+( MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) )+' <span class="profiler-unit">% in sql</span> </td> ';}out+=' ';var arr2=it.custom_timing_names;if(arr2){var value,i2=-1,l2=arr2.length-1;while(i2<l2){value=arr2[i2+=1];out+=' <td colspan="2" class="profiler-number profiler-percentage-in-sql" title="'+( it.custom_timing_stats[value].count )+' '+( value.toLowerCase() )+' invocations spent '+( MiniProfiler.formatDuration(it.custom_timing_stats[value].duration) )+' ms of total request time"> '+( MiniProfiler.formatDuration(it.custom_timing_stats[value].duration / it.duration_milliseconds * 100) )+' <span class="profiler-unit">% in '+( value.toLowerCase() )+'</span> </td> ';} } out+=' </tr> </tfoot> </table> ';if(it.client_timings){out+=' <table class="profiler-timings profiler-client-timings"> <thead> <tr> <th>client event</th> <th>duration (ms)</th> <th>from start (ms)</th> </tr> </thead> <tbody> ';var arr3=MiniProfiler.getClientTimings(it.client_timings);if(arr3){var value,i3=-1,l3=arr3.length-1;while(i3<l3){value=arr3[i3+=1];out+=' <tr class="';if(value.isTrivial){out+='profiler-trivial';}out+='"> <td class="profiler-label">'+( value.name )+'</td> <td class="profiler-duration"> ';if(value.duration >= 0){out+=' <span class="profiler-unit"></span>'+( MiniProfiler.formatDuration(value.duration) )+' ';}out+=' </td> <td class="profiler-duration time-from-start"> <span class="profiler-unit">+</span>'+( MiniProfiler.formatDuration(value.start) )+' </td> </tr> ';} } out+=' </tbody> <tfoot> <td colspan="3"> '+( MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) )+' </td> </tfoot> </table> ';}out+=' ';if(it.custom_fields && Object.keys(it.custom_fields).length > 0){out+=' <p class="custom-fields-title">Snapshot custom fields</p> <table class="profiler-timings"> <tbody> ';var arr4=Object.keys(it.custom_fields);if(arr4){var key,i4=-1,l4=arr4.length-1;while(i4<l4){key=arr4[i4+=1];out+=' <tr> <td class="profiler-label">'+( key )+'</td> <td class="profiler-label">'+( it.custom_fields[key] )+'</td> </tr> ';} } out+=' </tbody> </table> ';}out+=' </div> </div> ';if(it.has_sql_timings){out+=' <div class="profiler-queries"> <table> <thead> <tr> <th style="text-align:right">step<br />time from start<br />query type<br />duration</th> <th style="text-align:left">call stack<br />query</th> </tr> </thead> <tbody> ';var arr5=MiniProfiler.getSqlTimings(it.root);if(arr5){var value,index=-1,l5=arr5.length-1;while(index<l5){value=arr5[index+=1];out+=' '+( MiniProfiler.templates.sqlGapTemplate({g: value.prevGap}) )+' '+( MiniProfiler.templates.sqlTimingTemplate({i: index, s: value}) )+' ';if(value.nextGap){out+=' '+( MiniProfiler.templates.sqlGapTemplate({g: value.nextGap}) )+' ';}out+=' ';} } out+=' </tbody> </table> <p class="profiler-trivial-gap-container"> <a class="profiler-toggle-trivial-gaps">show trivial gaps</a> </p> </div> ';}out+=' </div>';return out;
|
11
11
|
}
|
12
12
|
MiniProfiler.templates["linksTemplate"] = function anonymous(it
|
13
13
|
) {
|
14
|
-
var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.has_all_trivial_timings )+'" title="toggles any rows with < '+( it.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
|
14
|
+
var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.page.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.page.has_all_trivial_timings )+'" title="toggles any rows with < '+( it.page.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
|
15
15
|
}
|
16
16
|
MiniProfiler.templates["timingTemplate"] = function anonymous(it
|
17
17
|
) {
|
@@ -25,6 +25,14 @@ MiniProfiler.templates["sqlGapTemplate"] = function anonymous(it
|
|
25
25
|
) {
|
26
26
|
var out=' <tr class="profiler-gap-info';if(it.g.duration < 4){out+=' profiler-trivial-gaps';}out+='"> <td class="profiler-info"> '+( it.g.duration )+' <span class="profiler-unit">ms</span> </td> <td class="query"> <div>'+( it.g.topReason.name )+' — '+( it.g.topReason.duration.toFixed(2) )+' <span class="profiler-unit">ms</span></div> </td> </tr>';return out;
|
27
27
|
}
|
28
|
+
MiniProfiler.templates["snapshotsGroupsList"] = function anonymous(it
|
29
|
+
) {
|
30
|
+
var out=' ';if(it.list && it.list.length){out+=' <table class="snapshots-table"> <thead> <tr> <th>Requests Group</th> <th>Worst Time (ms)</th> </tr> </thead> <tbody> ';var arr1=it.list;if(arr1){var row,i1=-1,l1=arr1.length-1;while(i1<l1){row=arr1[i1+=1];out+=' <tr> <td><a href="'+( row.url )+'">'+( row.name )+'</a></td> <td>'+( MiniProfiler.formatDuration(row.worst_score) )+'</td> </tr> ';} } out+=' </tbody> </table> ';}else{out+=' <h2>No snapshots exist</h2> ';}return out;
|
31
|
+
}
|
32
|
+
MiniProfiler.templates["snapshotsList"] = function anonymous(it
|
33
|
+
) {
|
34
|
+
var out=' ';if(it.list && it.list.length){out+=' <h2>Snapshots for '+( it.group_name )+'</h2> <table class="snapshots-table"> <thead> <tr> <th>ID</th> <th>Duration (ms)</th> <th>Age</th> </tr> </thead> <tbody> ';var arr1=it.list;if(arr1){var row,i1=-1,l1=arr1.length-1;while(i1<l1){row=arr1[i1+=1];out+=' <tr> <td><a href="'+( row.url )+'"> '+( row.id )+' </a></td> <td>'+( MiniProfiler.formatDuration(row.duration) )+'</td> <td> ';if(row.timestamp){out+=' '+( MiniProfiler.timestampToRelative(row.timestamp) )+' ';}out+=' </td> </tr> ';} } out+=' </tbody> </table> ';}else{out+=' <h2>No snapshots for '+( it.group_name )+'</h2> ';}return out;
|
35
|
+
}
|
28
36
|
|
29
37
|
if (typeof prettyPrint === "undefined") {
|
30
38
|
// prettify.js
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -37,6 +37,8 @@ module Rack
|
|
37
37
|
@max_sql_param_length = 0 # disable sql parameter collection by default
|
38
38
|
@skip_sql_param_names = /password/ # skips parameters with the name password by default
|
39
39
|
@enable_advanced_debugging_tools = false
|
40
|
+
@snapshot_every_n_requests = -1
|
41
|
+
@snapshots_limit = 1000
|
40
42
|
|
41
43
|
# ui parameters
|
42
44
|
@autorized = true
|
@@ -60,7 +62,8 @@ module Rack
|
|
60
62
|
:base_url_path, :disable_caching, :enabled,
|
61
63
|
:flamegraph_sample_rate, :logger, :pre_authorize_cb, :skip_paths,
|
62
64
|
:skip_schema_queries, :storage, :storage_failure, :storage_instance,
|
63
|
-
:storage_options, :user_provider, :enable_advanced_debugging_tools
|
65
|
+
:storage_options, :user_provider, :enable_advanced_debugging_tools,
|
66
|
+
:snapshot_every_n_requests, :snapshots_limit
|
64
67
|
attr_accessor :skip_sql_param_names, :suppress_encoding, :max_sql_param_length
|
65
68
|
|
66
69
|
# ui accessors
|
@@ -71,6 +74,15 @@ module Rack
|
|
71
74
|
# Deprecated options
|
72
75
|
attr_accessor :use_existing_jquery
|
73
76
|
|
77
|
+
attr_reader :assets_url
|
78
|
+
|
79
|
+
def assets_url=(lmbda)
|
80
|
+
if defined?(Rack::MiniProfilerRails)
|
81
|
+
Rack::MiniProfilerRails.create_engine
|
82
|
+
end
|
83
|
+
@assets_url = lmbda
|
84
|
+
end
|
85
|
+
|
74
86
|
def vertical_position
|
75
87
|
position.include?('bottom') ? 'bottom' : 'top'
|
76
88
|
end
|
@@ -8,7 +8,7 @@ module Rack
|
|
8
8
|
attr_accessor :subscribe_sql_active_record
|
9
9
|
|
10
10
|
def patch_rails?
|
11
|
-
!!defined?(
|
11
|
+
!!defined?(Rack::MINI_PROFILER_ENABLE_RAILS_PATCHES)
|
12
12
|
end
|
13
13
|
|
14
14
|
def generate_id
|
@@ -38,9 +38,20 @@ module Rack
|
|
38
38
|
|
39
39
|
def current=(c)
|
40
40
|
# we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
|
41
|
+
Thread.current[:mini_profiler_snapshot_custom_fields] = nil
|
41
42
|
Thread.current[:mini_profiler_private] = c
|
42
43
|
end
|
43
44
|
|
45
|
+
def add_snapshot_custom_field(key, value)
|
46
|
+
thread_var_key = :mini_profiler_snapshot_custom_fields
|
47
|
+
Thread.current[thread_var_key] ||= {}
|
48
|
+
Thread.current[thread_var_key][key] = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_snapshot_custom_fields
|
52
|
+
Thread.current[:mini_profiler_snapshot_custom_fields]
|
53
|
+
end
|
54
|
+
|
44
55
|
# discard existing results, don't track this request
|
45
56
|
def discard_results
|
46
57
|
self.current.discard = true if current
|
@@ -106,14 +117,23 @@ module Rack
|
|
106
117
|
def serve_results(env)
|
107
118
|
request = Rack::Request.new(env)
|
108
119
|
id = request.params['id']
|
109
|
-
|
110
|
-
|
120
|
+
is_snapshot = request.params['snapshot']
|
121
|
+
is_snapshot = [true, "true"].include?(is_snapshot)
|
122
|
+
if is_snapshot
|
123
|
+
page_struct = @storage.load_snapshot(id)
|
124
|
+
else
|
125
|
+
page_struct = @storage.load(id)
|
126
|
+
end
|
127
|
+
if !page_struct && is_snapshot
|
128
|
+
id = ERB::Util.html_escape(id)
|
129
|
+
return [404, {}, ["Snapshot with id '#{id}' not found"]]
|
130
|
+
elsif !page_struct
|
111
131
|
@storage.set_viewed(user(env), id)
|
112
|
-
id = ERB::Util.html_escape(
|
132
|
+
id = ERB::Util.html_escape(id)
|
113
133
|
user_info = ERB::Util.html_escape(user(env))
|
114
134
|
return [404, {}, ["Request not found: #{id} - user #{user_info}"]]
|
115
135
|
end
|
116
|
-
|
136
|
+
if !page_struct[:has_user_viewed] && !is_snapshot
|
117
137
|
page_struct[:client_timings] = TimerStruct::Client.init_from_form_data(env, page_struct)
|
118
138
|
page_struct[:has_user_viewed] = true
|
119
139
|
@storage.save(page_struct)
|
@@ -148,11 +168,12 @@ module Rack
|
|
148
168
|
file_name = path.sub(@config.base_url_path, '')
|
149
169
|
|
150
170
|
return serve_results(env) if file_name.eql?('results')
|
171
|
+
return handle_snapshots_request(env) if file_name.eql?('snapshots')
|
151
172
|
|
152
173
|
resources_env = env.dup
|
153
174
|
resources_env['PATH_INFO'] = file_name
|
154
175
|
|
155
|
-
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' =>
|
176
|
+
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age=#{cache_control_value}")
|
156
177
|
rack_file.call(resources_env)
|
157
178
|
end
|
158
179
|
|
@@ -177,7 +198,6 @@ module Rack
|
|
177
198
|
end
|
178
199
|
|
179
200
|
def call(env)
|
180
|
-
|
181
201
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
182
202
|
client_settings = ClientSettings.new(env, @storage, start)
|
183
203
|
MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
|
@@ -189,15 +209,31 @@ module Rack
|
|
189
209
|
# Someone (e.g. Rails engine) could change the SCRIPT_NAME so we save it
|
190
210
|
env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME'] = ENV['PASSENGER_BASE_URI'] || env['SCRIPT_NAME']
|
191
211
|
|
192
|
-
skip_it = (
|
193
|
-
|
194
|
-
|
212
|
+
skip_it = /pp=skip/.match?(query_string) || (
|
213
|
+
@config.skip_paths &&
|
214
|
+
@config.skip_paths.any? do |p|
|
215
|
+
if p.instance_of?(String)
|
216
|
+
path.start_with?(p)
|
217
|
+
elsif p.instance_of?(Regexp)
|
218
|
+
p.match?(path)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
)
|
222
|
+
if skip_it
|
223
|
+
return client_settings.handle_cookie(@app.call(env))
|
224
|
+
end
|
225
|
+
|
226
|
+
skip_it = (@config.pre_authorize_cb && !@config.pre_authorize_cb.call(env))
|
195
227
|
|
196
228
|
if skip_it || (
|
197
229
|
@config.authorization_mode == :whitelist &&
|
198
230
|
!client_settings.has_valid_cookie?
|
199
231
|
)
|
200
|
-
|
232
|
+
if take_snapshot?(path)
|
233
|
+
return client_settings.handle_cookie(take_snapshot(env, start))
|
234
|
+
else
|
235
|
+
return client_settings.handle_cookie(@app.call(env))
|
236
|
+
end
|
201
237
|
end
|
202
238
|
|
203
239
|
# handle all /mini-profiler requests here
|
@@ -308,6 +344,15 @@ module Rack
|
|
308
344
|
status, headers, body = @app.call(env)
|
309
345
|
end
|
310
346
|
end
|
347
|
+
elsif path == '/rack-mini-profiler/requests'
|
348
|
+
blank_page_html = <<~HTML
|
349
|
+
<html>
|
350
|
+
<head></head>
|
351
|
+
<body></body>
|
352
|
+
</html>
|
353
|
+
HTML
|
354
|
+
|
355
|
+
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]]
|
311
356
|
else
|
312
357
|
status, headers, body = @app.call(env)
|
313
358
|
end
|
@@ -364,17 +409,6 @@ module Rack
|
|
364
409
|
return client_settings.handle_cookie(self.flamegraph(flamegraph))
|
365
410
|
end
|
366
411
|
|
367
|
-
if path == '/rack-mini-profiler/requests'
|
368
|
-
blank_page_html = <<~HTML
|
369
|
-
<html>
|
370
|
-
<head></head>
|
371
|
-
<body></body>
|
372
|
-
</html>
|
373
|
-
HTML
|
374
|
-
|
375
|
-
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]]
|
376
|
-
end
|
377
|
-
|
378
412
|
begin
|
379
413
|
@storage.save(page_struct)
|
380
414
|
# no matter what it is, it should be unviewed, otherwise we will miss POST
|
@@ -630,10 +664,20 @@ Append the following to your query string:
|
|
630
664
|
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
631
665
|
def get_profile_script(env)
|
632
666
|
path = "#{env['RACK_MINI_PROFILER_ORIGINAL_SCRIPT_NAME']}#{@config.base_url_path}"
|
667
|
+
version = MiniProfiler::ASSET_VERSION
|
668
|
+
if @config.assets_url
|
669
|
+
url = @config.assets_url.call('rack-mini-profiler.js', version, env)
|
670
|
+
css_url = @config.assets_url.call('rack-mini-profiler.css', version, env)
|
671
|
+
end
|
672
|
+
|
673
|
+
url = "#{path}includes.js?v=#{version}" if !url
|
674
|
+
css_url = "#{path}includes.css?v=#{version}" if !css_url
|
633
675
|
|
634
676
|
settings = {
|
635
677
|
path: path,
|
636
|
-
|
678
|
+
url: url,
|
679
|
+
cssUrl: css_url,
|
680
|
+
version: version,
|
637
681
|
verticalPosition: @config.vertical_position,
|
638
682
|
horizontalPosition: @config.horizontal_position,
|
639
683
|
showTrivial: @config.show_trivial,
|
@@ -673,5 +717,104 @@ Append the following to your query string:
|
|
673
717
|
current.inject_js = false
|
674
718
|
end
|
675
719
|
|
720
|
+
def cache_control_value
|
721
|
+
86400
|
722
|
+
end
|
723
|
+
|
724
|
+
private
|
725
|
+
|
726
|
+
def handle_snapshots_request(env)
|
727
|
+
self.current = nil
|
728
|
+
MiniProfiler.authorize_request
|
729
|
+
status = 200
|
730
|
+
headers = { 'Content-Type' => 'text/html' }
|
731
|
+
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
732
|
+
if group_name = qp["group_name"]
|
733
|
+
list = @storage.find_snapshots_group(group_name)
|
734
|
+
list.each do |snapshot|
|
735
|
+
snapshot[:url] = url_for_snapshot(snapshot[:id])
|
736
|
+
end
|
737
|
+
data = {
|
738
|
+
group_name: group_name,
|
739
|
+
list: list
|
740
|
+
}
|
741
|
+
else
|
742
|
+
list = @storage.snapshot_groups_overview
|
743
|
+
list.each do |group|
|
744
|
+
group[:url] = url_for_snapshots_group(group[:name])
|
745
|
+
end
|
746
|
+
data = {
|
747
|
+
page: "overview",
|
748
|
+
list: list
|
749
|
+
}
|
750
|
+
end
|
751
|
+
data_html = <<~HTML
|
752
|
+
<div style="display: none;" id="snapshots-data">
|
753
|
+
#{data.to_json}
|
754
|
+
</div>
|
755
|
+
HTML
|
756
|
+
response = Rack::Response.new([], status, headers)
|
757
|
+
|
758
|
+
response.write <<~HTML
|
759
|
+
<html>
|
760
|
+
<head></head>
|
761
|
+
<body class="mp-snapshots">
|
762
|
+
HTML
|
763
|
+
response.write(data_html)
|
764
|
+
script = self.get_profile_script(env)
|
765
|
+
response.write(script)
|
766
|
+
response.write <<~HTML
|
767
|
+
</body>
|
768
|
+
</html>
|
769
|
+
HTML
|
770
|
+
response.finish
|
771
|
+
end
|
772
|
+
|
773
|
+
def rails_route_from_path(path, method)
|
774
|
+
if defined?(Rails) && defined?(ActionController::RoutingError)
|
775
|
+
hash = Rails.application.routes.recognize_path(path, method: method)
|
776
|
+
if hash && hash[:controller] && hash[:action]
|
777
|
+
"#{method} #{hash[:controller]}##{hash[:action]}"
|
778
|
+
end
|
779
|
+
end
|
780
|
+
rescue ActionController::RoutingError
|
781
|
+
nil
|
782
|
+
end
|
783
|
+
|
784
|
+
def url_for_snapshots_group(group_name)
|
785
|
+
qs = Rack::Utils.build_query({ group_name: group_name })
|
786
|
+
"/#{@config.base_url_path.gsub('/', '')}/snapshots?#{qs}"
|
787
|
+
end
|
788
|
+
|
789
|
+
def url_for_snapshot(id)
|
790
|
+
qs = Rack::Utils.build_query({ id: id, snapshot: true })
|
791
|
+
"/#{@config.base_url_path.gsub('/', '')}/results?#{qs}"
|
792
|
+
end
|
793
|
+
|
794
|
+
def take_snapshot?(path)
|
795
|
+
@config.snapshot_every_n_requests > 0 &&
|
796
|
+
!path.start_with?(@config.base_url_path) &&
|
797
|
+
@storage.should_take_snapshot?(@config.snapshot_every_n_requests)
|
798
|
+
end
|
799
|
+
|
800
|
+
def take_snapshot(env, start)
|
801
|
+
MiniProfiler.create_current(env, @config)
|
802
|
+
results = @app.call(env)
|
803
|
+
status = results[0].to_i
|
804
|
+
if status >= 200 && status < 300
|
805
|
+
page_struct = current.page_struct
|
806
|
+
page_struct[:root].record_time(
|
807
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000
|
808
|
+
)
|
809
|
+
custom_fields = MiniProfiler.get_snapshot_custom_fields
|
810
|
+
page_struct[:custom_fields] = custom_fields if custom_fields
|
811
|
+
@storage.push_snapshot(
|
812
|
+
page_struct,
|
813
|
+
@config
|
814
|
+
)
|
815
|
+
end
|
816
|
+
self.current = nil
|
817
|
+
results
|
818
|
+
end
|
676
819
|
end
|
677
820
|
end
|