rack-mini-profiler 3.2.1 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +4 -3
- data/lib/html/includes.css +18 -2
- data/lib/html/includes.js +1 -1
- data/lib/html/includes.scss +31 -1
- data/lib/html/includes.tmpl +9 -2
- data/lib/html/vendor.js +1 -1
- data/lib/mini_profiler/actions.rb +6 -1
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +4 -1
- data/lib/mini_profiler/snapshots_transporter.rb +2 -0
- data/lib/mini_profiler/storage/file_store.rb +0 -2
- data/lib/mini_profiler/timer_struct/base.rb +0 -1
- data/lib/mini_profiler/timer_struct/page.rb +10 -3
- data/lib/mini_profiler/version.rb +2 -2
- data/lib/mini_profiler/views.rb +9 -3
- data/lib/mini_profiler.rb +16 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc0d1c8641a30fc59950236e1761ac6a0f1c07cdbc73c5192e256c7bfd84a6af
|
4
|
+
data.tar.gz: 20d8e78c618c96da12676078fff49bb2bf71014e6c8ace339d1fcdc4cd73e8a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 534f1a6ea263d6e9aaea85e14d0aa09ac893081ad350530c8122c241f1cb0f3397cd7fdf1ca5a65e96bca92384e0b38a6ebbcec8b9e94a4cafb4565198cbd743
|
7
|
+
data.tar.gz: 9114e796825cbaa2b88b71c5bfb0a2b5aa0ea6453a4bf4364d6666d37b42fbcdf12991b0df95ceffaefca47907b9d5c36932ada2c77b4d4120889b4f093b2c23
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.3.1 - 2024-02-15
|
4
|
+
- [FEATURE] Support dynamic `config.content_security_policy_nonce` [#609](https://github.com/MiniProfiler/rack-mini-profiler/pull/609)
|
5
|
+
- [FEATURE] Add flamgraph path to response header: [#601](https://github.com/MiniProfiler/rack-mini-profiler/pull/601)
|
6
|
+
|
7
|
+
## 3.3.0 - 2023-12-07
|
8
|
+
- [FEATURE] Use `?pp=flamegraph?ignore_gc=true` or `config.flamegraph_ignore_gc` to ignore gc in flamegraphs. [#599](https://github.com/MiniProfiler/rack-mini-profiler/pull/599)
|
9
|
+
|
3
10
|
## 3.2.1 - 2023-12-07
|
4
11
|
- [FIX] memory_profiler was broken due to an undefined local [#597](https://github.com/MiniProfiler/rack-mini-profiler/pull/597)
|
5
12
|
|
data/README.md
CHANGED
@@ -180,7 +180,7 @@ To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-
|
|
180
180
|
|
181
181
|
Then, to view the flamegraph as a direct HTML response from your request, just visit any page in your app with `?pp=flamegraph` appended to the URL, or add the header `X-Rack-Mini-Profiler` to the request with the value `flamegraph`.
|
182
182
|
|
183
|
-
Conversely, if you want your regular response instead (which is specially useful for JSON and/or XHR requests), just append the `?pp=async-flamegraph` parameter to your request/fetch URL; the request will then return as normal, and the flamegraph data will be stored for later *async* viewing, both for this request and for all subsequent requests made by this page (based on the `REFERER` header). For viewing these async flamegraphs, use the 'flamegraph' link that will appear inside the MiniProfiler UI for these requests.
|
183
|
+
Conversely, if you want your regular response instead (which is specially useful for JSON and/or XHR requests), just append the `?pp=async-flamegraph` parameter to your request/fetch URL; the request will then return as normal, and the flamegraph data will be stored for later *async* viewing, both for this request and for all subsequent requests made by this page (based on the `REFERER` header). For viewing these async flamegraphs, use the 'flamegraph' link that will appear inside the MiniProfiler UI for these requests or path returned in the `X-MiniProfiler-Flamegraph-Path` header.
|
184
184
|
|
185
185
|
Note: Mini Profiler will not record SQL timings for a request if it asks for a flamegraph. The rationale behind this is to keep
|
186
186
|
Mini Profiler's methods that are responsible for generating the timings data out of the flamegraph.
|
@@ -431,6 +431,7 @@ start_hidden | `false`
|
|
431
431
|
backtrace_threshold_ms | `0` | Minimum SQL query elapsed time before a backtrace is recorded.
|
432
432
|
flamegraph_sample_rate | `0.5` | How often to capture stack traces for flamegraphs in milliseconds.
|
433
433
|
flamegraph_mode | `:wall` | The [StackProf mode](https://github.com/tmm1/stackprof#all-options) to pass to `StackProf.run`.
|
434
|
+
flamegraph_ignore_gc | `false` | Whether to ignore garbage collection frames in flamegraphs.
|
434
435
|
base_url_path | `'/mini-profiler-resources/'` | Path for assets; added as a prefix when naming assets and sought when responding to requests.
|
435
436
|
cookie_path | `'/'` | Set-Cookie header path for profile cookie
|
436
437
|
collapse_results | `true` | If multiple timing results exist in a single page, collapse them till clicked.
|
@@ -447,7 +448,7 @@ snapshots_transport_destination_url | `nil`
|
|
447
448
|
snapshots_transport_auth_key | `nil` | `POST` requests made by the snapshots transporter to the destination URL will have a `Mini-Profiler-Transport-Auth` header with the value of this config. Make sure you use a secure and random key for this config.
|
448
449
|
snapshots_redact_sql_queries | `true` | When this is true, SQL queries will be redacted from sampling snapshots, but the backtrace and duration of each SQL query will be saved with the snapshot to keep debugging performance issues possible.
|
449
450
|
snapshots_transport_gzip_requests | `false` | Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
|
450
|
-
content_security_policy_nonce | Rails: Current nonce<br>Rack: nil | Set the content security policy nonce to use when inserting MiniProfiler's script block.
|
451
|
+
content_security_policy_nonce | Rails: Current nonce<br>Rack: nil | Set the content security policy nonce to use when inserting MiniProfiler's script block. Can be set to a static string, or a Proc which receives `env` and `response_headers` as arguments and returns the nonce.
|
451
452
|
enable_hotwire_turbo_drive_support | `false` | Enable support for Hotwire TurboDrive page transitions.
|
452
453
|
profile_parameter | `'pp'` | The query parameter used to interact with this gem.
|
453
454
|
|
@@ -465,7 +466,7 @@ If you are using Heroku Redis, you may need to add the following to your `config
|
|
465
466
|
|
466
467
|
```ruby
|
467
468
|
if Rails.env.production?
|
468
|
-
Rack::MiniProfiler.config.storage_options = {
|
469
|
+
Rack::MiniProfiler.config.storage_options = {
|
469
470
|
url: ENV["REDIS_URL"],
|
470
471
|
ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
|
471
472
|
}
|
data/lib/html/includes.css
CHANGED
@@ -105,7 +105,8 @@
|
|
105
105
|
.profiler-result .profiler-unit {
|
106
106
|
font-family: Consolas, monospace, serif; }
|
107
107
|
.profiler-result .profiler-number {
|
108
|
-
color: #111;
|
108
|
+
color: #111;
|
109
|
+
display: inline-block; }
|
109
110
|
.profiler-result .profiler-info {
|
110
111
|
text-align: right; }
|
111
112
|
.profiler-result .profiler-info .profiler-name {
|
@@ -235,6 +236,19 @@
|
|
235
236
|
.profiler-results {
|
236
237
|
z-index: 2147483643;
|
237
238
|
position: fixed; }
|
239
|
+
.profiler-results.profiler-left .profiler-button {
|
240
|
+
text-align: left; }
|
241
|
+
.profiler-results.profiler-right .profiler-button {
|
242
|
+
text-align: right; }
|
243
|
+
.profiler-results .profiler-button > .profiler-duration-milliseconds {
|
244
|
+
min-width: 70px; }
|
245
|
+
.profiler-results .profiler-button > .profiler-sql-count {
|
246
|
+
min-width: 55px; }
|
247
|
+
.profiler-results .profiler-button > .profiler-name {
|
248
|
+
min-width: 190px;
|
249
|
+
margin-left: 3px; }
|
250
|
+
.profiler-results .profiler-button > .profiler-number {
|
251
|
+
text-align: right; }
|
238
252
|
.profiler-results.profiler-top {
|
239
253
|
top: 0px; }
|
240
254
|
.profiler-results.profiler-top.profiler-left {
|
@@ -285,8 +299,10 @@
|
|
285
299
|
background-color: maroon; }
|
286
300
|
.profiler-results .profiler-button.profiler-button-active .profiler-number,
|
287
301
|
.profiler-results .profiler-button.profiler-button-active .profiler-nuclear,
|
302
|
+
.profiler-results .profiler-button.profiler-button-active .profiler-name,
|
288
303
|
.profiler-results .profiler-controls.profiler-button-active .profiler-number,
|
289
|
-
.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear
|
304
|
+
.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear,
|
305
|
+
.profiler-results .profiler-controls.profiler-button-active .profiler-name {
|
290
306
|
color: #fff;
|
291
307
|
font-weight: bold; }
|
292
308
|
.profiler-results .profiler-button.profiler-button-active .profiler-unit,
|
data/lib/html/includes.js
CHANGED
@@ -220,7 +220,7 @@ var _MiniProfiler = (function() {
|
|
220
220
|
totalSqlCount += parseInt(json.sql_count);
|
221
221
|
reqs++;
|
222
222
|
|
223
|
-
if (!controls &&
|
223
|
+
if (!controls && options.collapseResults && !expandedResults) {
|
224
224
|
if (!totalsControl) {
|
225
225
|
toArray(container.querySelectorAll(".profiler-result")).forEach(
|
226
226
|
function(el) {
|
data/lib/html/includes.scss
CHANGED
@@ -103,6 +103,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
103
103
|
|
104
104
|
.profiler-number {
|
105
105
|
color: $numberColor;
|
106
|
+
display: inline-block;
|
106
107
|
}
|
107
108
|
|
108
109
|
.profiler-info {
|
@@ -317,6 +318,34 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
317
318
|
|
318
319
|
$radius: 10px;
|
319
320
|
|
321
|
+
&.profiler-left {
|
322
|
+
.profiler-button {
|
323
|
+
text-align: left;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
&.profiler-right {
|
328
|
+
.profiler-button {
|
329
|
+
text-align: right;
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
.profiler-button {
|
334
|
+
> .profiler-duration-milliseconds {
|
335
|
+
min-width: 70px;
|
336
|
+
}
|
337
|
+
> .profiler-sql-count {
|
338
|
+
min-width: 55px;
|
339
|
+
}
|
340
|
+
> .profiler-name {
|
341
|
+
min-width: 190px;
|
342
|
+
margin-left: 3px;
|
343
|
+
}
|
344
|
+
> .profiler-number {
|
345
|
+
text-align: right;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
320
349
|
&.profiler-top {
|
321
350
|
top: 0px;
|
322
351
|
|
@@ -399,7 +428,8 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
399
428
|
background-color: maroon;
|
400
429
|
|
401
430
|
.profiler-number,
|
402
|
-
.profiler-nuclear
|
431
|
+
.profiler-nuclear,
|
432
|
+
.profiler-name {
|
403
433
|
color: #fff;
|
404
434
|
font-weight: bold;
|
405
435
|
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -2,14 +2,21 @@
|
|
2
2
|
<div class="profiler-result">
|
3
3
|
<div class="profiler-button {{? it.has_duplicate_sql_timings}}profiler-warning{{?}}">
|
4
4
|
{{? it.has_duplicate_sql_timings}}<span class="profiler-nuclear">!</span>{{?}}
|
5
|
-
<span class="profiler-number">
|
5
|
+
<span class="profiler-number profiler-duration-milliseconds">
|
6
6
|
{{= MiniProfiler.formatDuration(it.duration_milliseconds)}} <span class="profiler-unit">ms</span>
|
7
7
|
</span>
|
8
8
|
{{? MiniProfiler.showTotalSqlCount()}}
|
9
|
-
<span class="profiler-number">
|
9
|
+
<span class="profiler-number profiler-sql-count">
|
10
10
|
{{= it.sql_count}} <span class="profiler-unit">sql</span>
|
11
11
|
</span>
|
12
12
|
{{?}}
|
13
|
+
<span class="profiler-name">
|
14
|
+
{{? it.name.length >= 30 }}
|
15
|
+
{{= it.name.substring(0,15) + "..." + it.name.slice(-15) }}
|
16
|
+
{{??}}
|
17
|
+
{{= it.name}}
|
18
|
+
{{?}}
|
19
|
+
</span>
|
13
20
|
</div>
|
14
21
|
|
15
22
|
<div class="profiler-popup">
|
data/lib/html/vendor.js
CHANGED
@@ -7,7 +7,7 @@
|
|
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_formatted))+'</span> </div> <div class="profiler-output"> <table class="profiler-timings"> <thead> <tr> <th>event</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 class="ta-right">step<br />time from start<br />query type<br />duration</th> <th class="ta-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;
|
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 profiler-duration-milliseconds"> '+( MiniProfiler.formatDuration(it.duration_milliseconds))+' <span class="profiler-unit">ms</span> </span> ';if(MiniProfiler.showTotalSqlCount()){out+=' <span class="profiler-number profiler-sql-count"> '+( it.sql_count)+' <span class="profiler-unit">sql</span> </span> ';}out+=' <span class="profiler-name"> ';if(it.name.length >= 30){out+=' '+( it.name.substring(0,15) + "..." + it.name.slice(-15) )+' ';}else{out+=' '+( it.name)+' ';}out+=' </span> </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_formatted))+'</span> </div> <div class="profiler-output"> <table class="profiler-timings"> <thead> <tr> <th>event</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 class="ta-right">step<br />time from start<br />query type<br />duration</th> <th class="ta-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
|
) {
|
@@ -55,7 +55,12 @@ module Rack
|
|
55
55
|
resources_env = env.dup
|
56
56
|
resources_env['PATH_INFO'] = file_name
|
57
57
|
|
58
|
-
|
58
|
+
if Gem::Version.new(Rack.release) >= Gem::Version.new("2.1.0")
|
59
|
+
rack_file = Rack::Files.new(resources_root, 'Cache-Control' => "max-age=#{cache_control_value}")
|
60
|
+
else
|
61
|
+
rack_file = Rack::File.new(resources_root, 'Cache-Control' => "max-age=#{cache_control_value}")
|
62
|
+
end
|
63
|
+
|
59
64
|
rack_file.call(resources_env)
|
60
65
|
end
|
61
66
|
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -30,6 +30,7 @@ module Rack
|
|
30
30
|
@backtrace_threshold_ms = 0
|
31
31
|
@flamegraph_sample_rate = 0.5
|
32
32
|
@flamegraph_mode = :wall
|
33
|
+
@flamegraph_ignore_gc = false
|
33
34
|
@storage_failure = Proc.new do |exception|
|
34
35
|
if @logger
|
35
36
|
@logger.warn("MiniProfiler storage failure: #{exception.message}")
|
@@ -76,7 +77,7 @@ module Rack
|
|
76
77
|
:storage_options, :user_provider, :enable_advanced_debugging_tools,
|
77
78
|
:skip_sql_param_names, :suppress_encoding, :max_sql_param_length,
|
78
79
|
:content_security_policy_nonce, :enable_hotwire_turbo_drive_support,
|
79
|
-
:flamegraph_mode, :profile_parameter
|
80
|
+
:flamegraph_mode, :flamegraph_ignore_gc, :profile_parameter
|
80
81
|
|
81
82
|
# ui accessors
|
82
83
|
attr_accessor :collapse_results, :max_traces_to_show, :position,
|
@@ -96,7 +97,9 @@ module Rack
|
|
96
97
|
|
97
98
|
# redefined - since the accessor defines it first
|
98
99
|
undef :authorization_mode=
|
100
|
+
# rubocop:disable Lint/DuplicateMethods
|
99
101
|
def authorization_mode=(mode)
|
102
|
+
# rubocop:enable Lint/DuplicateMethods
|
100
103
|
if mode == :whitelist
|
101
104
|
warn "[DEPRECATION] `:whitelist` authorization mode is deprecated. Please use `:allow_authorized` instead."
|
102
105
|
|
@@ -9,9 +9,11 @@ class ::Rack::MiniProfiler::SnapshotsTransporter
|
|
9
9
|
def transported_snapshots_count
|
10
10
|
@@transported_snapshots_count
|
11
11
|
end
|
12
|
+
|
12
13
|
def successful_http_requests_count
|
13
14
|
@@successful_http_requests_count
|
14
15
|
end
|
16
|
+
|
15
17
|
def failed_http_requests_count
|
16
18
|
@@failed_http_requests_count
|
17
19
|
end
|
@@ -59,13 +59,12 @@ module Rack
|
|
59
59
|
|
60
60
|
def initialize(env)
|
61
61
|
timer_id = MiniProfiler.generate_id
|
62
|
-
page_name = env['PATH_INFO']
|
63
62
|
started_at = (Time.now.to_f * 1000).to_i
|
64
63
|
started = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i
|
65
64
|
machine_name = env['SERVER_NAME']
|
66
65
|
super(
|
67
66
|
id: timer_id,
|
68
|
-
name: page_name,
|
67
|
+
name: page_name(env),
|
69
68
|
started: started,
|
70
69
|
started_at: started_at,
|
71
70
|
machine_name: machine_name,
|
@@ -93,7 +92,7 @@ module Rack
|
|
93
92
|
)
|
94
93
|
self[:request_method] = env['REQUEST_METHOD']
|
95
94
|
self[:request_path] = env['PATH_INFO']
|
96
|
-
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env
|
95
|
+
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{page_name(env)}"
|
97
96
|
self[:root] = TimerStruct::Request.createRoot(name, self)
|
98
97
|
end
|
99
98
|
|
@@ -101,6 +100,14 @@ module Rack
|
|
101
100
|
@attributes[:name]
|
102
101
|
end
|
103
102
|
|
103
|
+
def page_name(env)
|
104
|
+
if env['QUERY_STRING'] && env['QUERY_STRING'] != ""
|
105
|
+
env['PATH_INFO'] + "?" + env['QUERY_STRING']
|
106
|
+
else
|
107
|
+
env['PATH_INFO']
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
104
111
|
def duration_ms
|
105
112
|
@attributes[:root][:duration_milliseconds]
|
106
113
|
end
|
data/lib/mini_profiler/views.rb
CHANGED
@@ -28,7 +28,7 @@ module Rack
|
|
28
28
|
# Use it when:
|
29
29
|
# * you have disabled auto append behaviour throught :auto_inject => false flag
|
30
30
|
# * you do not want script to be automatically appended for the current page. You can also call cancel_auto_inject
|
31
|
-
def get_profile_script(env)
|
31
|
+
def get_profile_script(env, response_headers = {})
|
32
32
|
path = public_base_path(env)
|
33
33
|
version = MiniProfiler::ASSET_VERSION
|
34
34
|
if @config.assets_url
|
@@ -39,7 +39,12 @@ module Rack
|
|
39
39
|
url = "#{path}includes.js?v=#{version}" if !url
|
40
40
|
css_url = "#{path}includes.css?v=#{version}" if !css_url
|
41
41
|
|
42
|
-
|
42
|
+
configured_nonce = @config.content_security_policy_nonce
|
43
|
+
if configured_nonce && !configured_nonce.is_a?(String)
|
44
|
+
configured_nonce = configured_nonce.call(env, response_headers)
|
45
|
+
end
|
46
|
+
|
47
|
+
content_security_policy_nonce = configured_nonce ||
|
43
48
|
env["action_dispatch.content_security_policy_nonce"] ||
|
44
49
|
env["secure_headers_content_security_policy_nonce"]
|
45
50
|
|
@@ -77,7 +82,7 @@ module Rack
|
|
77
82
|
script = ::File.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
|
78
83
|
# replace the variables
|
79
84
|
settings.each do |k, v|
|
80
|
-
regex = Regexp.new("\\{#{k
|
85
|
+
regex = Regexp.new("\\{#{k}\\}")
|
81
86
|
script.gsub!(regex, v.to_s)
|
82
87
|
end
|
83
88
|
|
@@ -161,6 +166,7 @@ module Rack
|
|
161
166
|
#{make_link "async-flamegraph", env} : store flamegraph data for this page and all its AJAX requests. Flamegraph links will be available in the mini-profiler UI (requires the stackprof gem).
|
162
167
|
#{make_link "flamegraph&flamegraph_sample_rate=1", env}: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
|
163
168
|
#{make_link "flamegraph&flamegraph_mode=cpu", env}: creates a flamegraph with the specified mode (one of cpu, wall, object, or custom). Overrides value set in config
|
169
|
+
#{make_link "flamegraph&flamegraph_ignore_gc=true", env}: ignore garbage collection frames in flamegraphs. Overrides value set in config
|
164
170
|
#{make_link "flamegraph_embed", env} : a graph representing sampled activity (requires the stackprof gem), embedded resources for use on an intranet.
|
165
171
|
#{make_link "trace-exceptions", env} : will return all the spots where your application raises exceptions
|
166
172
|
#{make_link "analyze-memory", env} : will perform basic memory analysis of heap
|
data/lib/mini_profiler.rb
CHANGED
@@ -302,11 +302,20 @@ module Rack
|
|
302
302
|
mode = config.flamegraph_mode
|
303
303
|
end
|
304
304
|
|
305
|
+
ignore_gc_match_data = action_parameters(env)['flamegraph_ignore_gc']
|
306
|
+
|
307
|
+
if ignore_gc_match_data
|
308
|
+
ignore_gc = ignore_gc_match_data == 'true'
|
309
|
+
else
|
310
|
+
ignore_gc = config.flamegraph_ignore_gc
|
311
|
+
end
|
312
|
+
|
305
313
|
flamegraph = StackProf.run(
|
306
314
|
mode: mode,
|
307
315
|
raw: true,
|
308
316
|
aggregate: false,
|
309
|
-
interval: (sample_rate * 1000).to_i
|
317
|
+
interval: (sample_rate * 1000).to_i,
|
318
|
+
ignore_gc: ignore_gc
|
310
319
|
) do
|
311
320
|
status, headers, body = @app.call(env)
|
312
321
|
end
|
@@ -427,11 +436,12 @@ module Rack
|
|
427
436
|
# inject header
|
428
437
|
if headers.is_a? Hash
|
429
438
|
headers['X-MiniProfiler-Ids'] = ids_comma_separated(env)
|
439
|
+
headers['X-MiniProfiler-Flamegraph-Path'] = flamegraph_path(env) if current.page_struct[:has_flamegraph]
|
430
440
|
end
|
431
441
|
|
432
442
|
if current.inject_js && content_type =~ /text\/html/
|
433
443
|
response = Rack::Response.new([], status, headers)
|
434
|
-
script = self.get_profile_script(env)
|
444
|
+
script = self.get_profile_script(env, headers)
|
435
445
|
|
436
446
|
if String === body
|
437
447
|
response.write inject(body, script)
|
@@ -596,6 +606,10 @@ module Rack
|
|
596
606
|
ids(env).join(",")
|
597
607
|
end
|
598
608
|
|
609
|
+
def flamegraph_path(env)
|
610
|
+
@config.base_url_path + 'flamegraph?id=' + current.page_struct[:id]
|
611
|
+
end
|
612
|
+
|
599
613
|
# cancels automatic injection of profile script for the current page
|
600
614
|
def cancel_auto_inject(env)
|
601
615
|
current.inject_js = false
|
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: 3.
|
4
|
+
version: 3.3.1
|
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:
|
13
|
+
date: 2024-02-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -360,7 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
360
360
|
- !ruby/object:Gem::Version
|
361
361
|
version: '0'
|
362
362
|
requirements: []
|
363
|
-
rubygems_version: 3.4
|
363
|
+
rubygems_version: 3.5.4
|
364
364
|
signing_key:
|
365
365
|
specification_version: 4
|
366
366
|
summary: Profiles loading speed for rack applications.
|