rack-mini-profiler 2.3.2 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2984e9dec3cda4d5b86a4ed3b8300725207ba826b6a064402b23dcf04b0d5a51
4
- data.tar.gz: 4a05074c124ac23085c08f4ae5389b71ef7b85687c40c5e5594dee4884d87632
3
+ metadata.gz: 6ff9200889e574b3f90f901b90b88fe05f0c48993c945fc5dac37c2f1d86e693
4
+ data.tar.gz: bdca4fb3ada1b7c56525753b9eaa6430389f309a76152e3f8a7e0c9297463bea
5
5
  SHA512:
6
- metadata.gz: b29d4a47e99d03489251752bfe57a2b64235400b5ffb9f867738bfb9d165f63ddeeb559fc4edf9a60c91fe2d579b04ee473db1b2c7846ab823580d8b9b5e88c1
7
- data.tar.gz: 3a0768401c048801bf4adb22792fbdb2643d5b5e0174f92149df96c8ae2ab79d0c504e9e2146d72cc45068df6b88ebc675ef11431d47c17a5b929f375a1e6d74
6
+ metadata.gz: 43b8c69b37ba90c80c31d8d410b05dda421322d5c6d1f45654807b526f7d990bec54eaa711aa34bbf43141831453a75543b3a27189103ea3f3bcbac7f3f4101a
7
+ data.tar.gz: bed607b319c32d85838767f7de608031411d7513fcbd8866d2d67feb44253b997e389671a903b192be07e04d1c018069ab835536de47ada14ad3af8bf89739d9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.3.3 - 2021-08-30
4
+
5
+ - [FEATURE] Introduce `pp=flamegraph_mode`
6
+ - [FEATURE] Richer CSP configuration options
7
+ - [FEATURE] Add support for Hotwire Turbo Drive
8
+
3
9
  ## 2.3.2 - 2021-04-30
4
10
 
5
11
  - [FEATURE] Introduce `pp=async-flamegraph` for asynchronous flamegraphs
data/README.md CHANGED
@@ -175,6 +175,9 @@ To store flamegraph data for later viewing, append the `?pp=async-flamegraph` pa
175
175
  Flamegraph data for this request, and all subsequent requests made by this page (based on the `REFERER` header) will be stored.
176
176
  'flamegraph' links will appear for these requests in the MiniProfiler UI.
177
177
 
178
+ Note: Mini Profiler will not record SQL timings for a request if it asks for a flamegraph. The rationale behind this is to keep
179
+ Mini Profiler's methods that are responsible for generating the timings data out of the flamegraph.
180
+
178
181
  ### Memory Profiling
179
182
 
180
183
  Memory allocations can be measured (using the [memory_profiler](https://github.com/SamSaffron/memory_profiler) gem)
@@ -398,6 +401,7 @@ toggle_shortcut|Alt+P|Keyboard shortcut to toggle the mini_profiler's visibility
398
401
  start_hidden|`false`|`false` to make mini_profiler visible on page load.
399
402
  backtrace_threshold_ms|`0`|Minimum SQL query elapsed time before a backtrace is recorded.
400
403
  flamegraph_sample_rate|`0.5`|How often to capture stack traces for flamegraphs in milliseconds.
404
+ flamegraph_mode|`:wall`|The [StackProf mode](https://github.com/tmm1/stackprof#all-options) to pass to `StackProf.run`.
401
405
  base_url_path|`'/mini-profiler-resources/'`|Path for assets; added as a prefix when naming assets and sought when responding to requests.
402
406
  collapse_results|`true`|If multiple timing results exist in a single page, collapse them till clicked.
403
407
  max_traces_to_show|20|Maximum number of mini profiler timing blocks to show on one page
@@ -412,13 +416,15 @@ snapshots_transport_destination_url|`nil`|Set this config to a valid URL to enab
412
416
  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.
413
417
  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.
414
418
  snapshots_transport_gzip_requests|`false`|Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
419
+ content_security_policy_nonce|Rails: Current nonce<br>Rack: nil|Set the content security policy nonce to use when inserting MiniProfiler's script block.
420
+ enable_hotwire_turbo_drive_support| `false` | Enable support for Hotwire TurboDrive page transitions.
415
421
 
416
422
  ### Using MiniProfiler with `Rack::Deflate` middleware
417
423
 
418
424
  If you are using `Rack::Deflate` with Rails and `rack-mini-profiler` in its default configuration,
419
425
  `Rack::MiniProfiler` will be injected (as always) at position 0 in the middleware stack,
420
426
  which means it will run after `Rack::Deflate` on response processing. To prevent attempting to inject
421
- HTML in already compressed response body MiniProfiler will suppress compression by setting
427
+ HTML in already compressed response body MiniProfiler will suppress compression by setting
422
428
  `identity` encoding in `Accept-Encoding` request header.
423
429
 
424
430
  ## Special query strings
@@ -461,20 +467,23 @@ end
461
467
  If you want to contribute to this project, that's great, thank you! You can run the following rake task:
462
468
 
463
469
  ```
470
+ $ BUNDLE_GEMFILE=website/Gemfile bundle install
464
471
  $ bundle exec rake client_dev
465
472
  ```
466
473
 
467
- which will start a local Sinatra server at `http://localhost:9292` where you'll be able to preview your changes. Refreshing the page should be enough to see any changes you make to files in the `lib/html` directory.
474
+ This will start a local Sinatra server at `http://localhost:9292` where you'll be able to preview your changes. Refreshing the page should be enough to see any changes you make to files in the `lib/html` directory.
475
+
476
+ Make sure to prepend `bundle exec` before any Rake tasks you run.
468
477
 
469
478
  ## Running the Specs
470
479
 
480
+ You need Memcached and Redis services running for the specs.
481
+
471
482
  ```
472
483
  $ rake build
473
484
  $ rake spec
474
485
  ```
475
486
 
476
- Additionally you can also run `autotest` if you like.
477
-
478
487
  ## Licence
479
488
 
480
489
  The MIT License (MIT)
@@ -69,8 +69,16 @@
69
69
  .profiler-result .custom-fields-title,
70
70
  .profiler-queries .custom-fields-title {
71
71
  color: #555;
72
- font: Helvetica, Arial, sans-serif;
72
+ font-family: Helvetica, Arial, sans-serif;
73
73
  font-size: 14px; }
74
+ .mp-snapshots .ta-left,
75
+ .profiler-result .ta-left,
76
+ .profiler-queries .ta-left {
77
+ text-align: left; }
78
+ .mp-snapshots .ta-right,
79
+ .profiler-result .ta-right,
80
+ .profiler-queries .ta-right {
81
+ text-align: right; }
74
82
 
75
83
  .profiler-result {
76
84
  font-family: Helvetica, Arial, sans-serif; }
@@ -233,8 +241,6 @@
233
241
  left: 0px; }
234
242
  .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
235
243
  .profiler-results.profiler-top.profiler-left .profiler-controls {
236
- -webkit-border-bottom-right-radius: 10px;
237
- -moz-border-radius-bottomright: 10px;
238
244
  border-bottom-right-radius: 10px; }
239
245
  .profiler-results.profiler-top.profiler-left .profiler-button,
240
246
  .profiler-results.profiler-top.profiler-left .profiler-controls {
@@ -243,8 +249,6 @@
243
249
  right: 0px; }
244
250
  .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
245
251
  .profiler-results.profiler-top.profiler-right .profiler-controls {
246
- -webkit-border-bottom-left-radius: 10px;
247
- -moz-border-radius-bottomleft: 10px;
248
252
  border-bottom-left-radius: 10px; }
249
253
  .profiler-results.profiler-top.profiler-right .profiler-button,
250
254
  .profiler-results.profiler-top.profiler-right .profiler-controls {
@@ -255,8 +259,6 @@
255
259
  left: 0px; }
256
260
  .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-result:first-child .profiler-button,
257
261
  .profiler-results.profiler-bottom.profiler-left .profiler-controls {
258
- -webkit-border-top-right-radius: 10px;
259
- -moz-border-radius-topright: 10px;
260
262
  border-top-right-radius: 10px; }
261
263
  .profiler-results.profiler-bottom.profiler-left .profiler-button,
262
264
  .profiler-results.profiler-bottom.profiler-left .profiler-controls {
@@ -265,8 +267,6 @@
265
267
  right: 0px; }
266
268
  .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-result:first-child .profiler-button,
267
269
  .profiler-results.profiler-bottom.profiler-right .profiler-controls {
268
- -webkit-border-bottom-top-radius: 10px;
269
- -moz-border-radius-topleft: 10px;
270
270
  border-top-left-radius: 10px; }
271
271
  .profiler-results.profiler-bottom.profiler-right .profiler-button,
272
272
  .profiler-results.profiler-bottom.profiler-right .profiler-controls {
@@ -324,8 +324,6 @@
324
324
  text-align: left;
325
325
  line-height: 18px;
326
326
  overflow: auto;
327
- -moz-box-shadow: 0px 1px 15px #555;
328
- -webkit-box-shadow: 0px 1px 15px #555;
329
327
  box-shadow: 0px 1px 15px #555; }
330
328
  .profiler-results .profiler-popup .profiler-info {
331
329
  margin-bottom: 3px;
data/lib/html/includes.js CHANGED
@@ -495,6 +495,19 @@ var _MiniProfiler = (function() {
495
495
  }, 3000);
496
496
  };
497
497
 
498
+ var onTurboBeforeVisit = function onTurboBeforeVisit(e) {
499
+ if(!e.defaultPrevented) {
500
+ window.MiniProfilerContainer = document.querySelector('body > .profiler-results')
501
+ window.MiniProfiler.pageTransition()
502
+ }
503
+ }
504
+
505
+ var onTurboLoad = function onTurboLoad(e) {
506
+ if(window.MiniProfilerContainer) {
507
+ document.body.appendChild(window.MiniProfilerContainer)
508
+ }
509
+ }
510
+
498
511
  var onClickEvents = function onClickEvents(e) {
499
512
  // this happens on every keystroke, and :visible is crazy expensive in IE <9
500
513
  // and in this case, the display:none check is sufficient.
@@ -652,6 +665,11 @@ var _MiniProfiler = (function() {
652
665
  turbolinksSkipResultsFetch
653
666
  );
654
667
  }
668
+
669
+ if (options.hotwireTurboDriveSupport) {
670
+ document.addEventListener("turbo:before-visit", onTurboBeforeVisit)
671
+ document.addEventListener("turbo:load", onTurboLoad)
672
+ }
655
673
  };
656
674
 
657
675
  var unbindDocumentEvents = function unbindDocumentEvents() {
@@ -664,6 +682,8 @@ var _MiniProfiler = (function() {
664
682
  "turbolinks:request-start",
665
683
  turbolinksSkipResultsFetch
666
684
  );
685
+ document.removeEventListener("turbo:before-visit", onTurboBeforeVisit);
686
+ document.removeEventListener("turbo:load", onTurboLoad);
667
687
  };
668
688
 
669
689
  var initFullView = function initFullView() {
@@ -1033,6 +1053,8 @@ var _MiniProfiler = (function() {
1033
1053
  .getAttribute("data-hidden-custom-fields")
1034
1054
  .toLowerCase()
1035
1055
  .split(",");
1056
+ var hotwireTurboDriveSupport = script
1057
+ .getAttribute('data-turbo-permanent') === "true";
1036
1058
  return {
1037
1059
  ids: ids,
1038
1060
  path: path,
@@ -1051,7 +1073,8 @@ var _MiniProfiler = (function() {
1051
1073
  collapseResults: collapseResults,
1052
1074
  htmlContainer: htmlContainer,
1053
1075
  cssUrl: cssUrl,
1054
- hiddenCustomFields: hiddenCustomFields
1076
+ hiddenCustomFields: hiddenCustomFields,
1077
+ hotwireTurboDriveSupport: hotwireTurboDriveSupport
1055
1078
  };
1056
1079
  })();
1057
1080
 
@@ -1,6 +1,4 @@
1
1
  @mixin box-shadow($dx, $dy, $radius, $color) {
2
- -moz-box-shadow: $dx $dy $radius $color;
3
- -webkit-box-shadow: $dx $dy $radius $color;
4
2
  box-shadow: $dx $dy $radius $color;
5
3
  }
6
4
 
@@ -58,9 +56,11 @@ $zindex: 2147483640; // near 32bit max 2147483647
58
56
  }
59
57
  .custom-fields-title {
60
58
  color: $textColor;
61
- font: $normalFonts;
59
+ font-family: $normalFonts;
62
60
  font-size: 14px;
63
61
  }
62
+ .ta-left { text-align: left; }
63
+ .ta-right { text-align: right; }
64
64
  }
65
65
 
66
66
  // styles shared between popup view and full view
@@ -326,8 +326,6 @@ $zindex: 2147483640; // near 32bit max 2147483647
326
326
  &.profiler-no-controls .profiler-totals,
327
327
  &.profiler-no-controls .profiler-result:last-child .profiler-button,
328
328
  .profiler-controls {
329
- -webkit-border-bottom-right-radius: $radius;
330
- -moz-border-radius-bottomright: $radius;
331
329
  border-bottom-right-radius: $radius;
332
330
  }
333
331
 
@@ -343,8 +341,6 @@ $zindex: 2147483640; // near 32bit max 2147483647
343
341
  &.profiler-no-controls .profiler-totals,
344
342
  &.profiler-no-controls .profiler-result:last-child .profiler-button,
345
343
  .profiler-controls {
346
- -webkit-border-bottom-left-radius: $radius;
347
- -moz-border-radius-bottomleft: $radius;
348
344
  border-bottom-left-radius: $radius;
349
345
  }
350
346
 
@@ -364,8 +360,6 @@ $zindex: 2147483640; // near 32bit max 2147483647
364
360
  &.profiler-no-controls .profiler-totals,
365
361
  &.profiler-no-controls .profiler-result:first-child .profiler-button,
366
362
  .profiler-controls {
367
- -webkit-border-top-right-radius: $radius;
368
- -moz-border-radius-topright: $radius;
369
363
  border-top-right-radius: $radius;
370
364
  }
371
365
 
@@ -381,8 +375,6 @@ $zindex: 2147483640; // near 32bit max 2147483647
381
375
  &.profiler-no-controls .profiler-totals,
382
376
  &.profiler-no-controls .profiler-result:first-child .profiler-button,
383
377
  .profiler-controls {
384
- -webkit-border-bottom-top-radius: $radius;
385
- -moz-border-radius-topleft: $radius;
386
378
  border-top-left-radius: $radius;
387
379
  }
388
380
 
@@ -117,8 +117,8 @@
117
117
  <table>
118
118
  <thead>
119
119
  <tr>
120
- <th style="text-align:right">step<br />time from start<br />query type<br />duration</th>
121
- <th style="text-align:left">call stack<br />query</th>
120
+ <th class="ta-right">step<br />time from start<br />query type<br />duration</th>
121
+ <th class="ta-left">call stack<br />query</th>
122
122
  </tr>
123
123
  </thead>
124
124
  <tbody>
@@ -1 +1 @@
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}" data-hidden-custom-fields="{hiddenCustomFields}"></script>
1
+ <script async nonce="{cspNonce}" 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}" data-hidden-custom-fields="{hiddenCustomFields}" data-turbo-permanent="{hotwireTurboDriveSupport}"></script>
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 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;
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;
11
11
  }
12
12
  MiniProfiler.templates["linksTemplate"] = function anonymous(it
13
13
  ) {
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Rack
3
3
  class MiniProfiler
4
- ASSET_VERSION = '8cccf9fed3d62814b1d70f252554501b'
4
+ ASSET_VERSION = '35a79b300ab5afa978cb59af0b05e059'
5
5
  end
6
6
  end
@@ -28,6 +28,7 @@ module Rack
28
28
  @authorization_mode = :allow_all
29
29
  @backtrace_threshold_ms = 0
30
30
  @flamegraph_sample_rate = 0.5
31
+ @flamegraph_mode = :wall
31
32
  @storage_failure = Proc.new do |exception|
32
33
  if @logger
33
34
  @logger.warn("MiniProfiler storage failure: #{exception.message}")
@@ -57,6 +58,7 @@ module Rack
57
58
  @snapshots_transport_auth_key = nil
58
59
  @snapshots_redact_sql_queries = true
59
60
  @snapshots_transport_gzip_requests = false
61
+ @enable_hotwire_turbo_drive_support = false
60
62
 
61
63
  self
62
64
  }
@@ -68,7 +70,9 @@ module Rack
68
70
  :flamegraph_sample_rate, :logger, :pre_authorize_cb, :skip_paths,
69
71
  :skip_schema_queries, :storage, :storage_failure, :storage_instance,
70
72
  :storage_options, :user_provider, :enable_advanced_debugging_tools,
71
- :skip_sql_param_names, :suppress_encoding, :max_sql_param_length
73
+ :skip_sql_param_names, :suppress_encoding, :max_sql_param_length,
74
+ :content_security_policy_nonce, :enable_hotwire_turbo_drive_support,
75
+ :flamegraph_mode
72
76
 
73
77
  # ui accessors
74
78
  attr_accessor :collapse_results, :max_traces_to_show, :position,
@@ -362,8 +362,17 @@ module Rack
362
362
  else
363
363
  sample_rate = config.flamegraph_sample_rate
364
364
  end
365
+
366
+ mode_match_data = query_string.match(/flamegraph_mode=([a-zA-Z]+)/)
367
+
368
+ if mode_match_data && [:cpu, :wall, :object, :custom].include?(mode_match_data[1].to_sym)
369
+ mode = mode_match_data[1].to_sym
370
+ else
371
+ mode = config.flamegraph_mode
372
+ end
373
+
365
374
  flamegraph = StackProf.run(
366
- mode: :wall,
375
+ mode: mode,
367
376
  raw: true,
368
377
  aggregate: false,
369
378
  interval: (sample_rate * 1000).to_i
@@ -630,7 +639,7 @@ module Rack
630
639
  end
631
640
 
632
641
  def text_result(body)
633
- headers = { 'Content-Type' => 'text/plain' }
642
+ headers = { 'Content-Type' => 'text/plain; charset=utf-8' }
634
643
  [200, headers, [body]]
635
644
  end
636
645
 
@@ -643,7 +652,7 @@ module Rack
643
652
  headers = { 'Content-Type' => 'text/html' }
644
653
  body = "<html><body>
645
654
  <pre style='line-height: 30px; font-size: 16px;'>
646
- Append the following to your query string:
655
+ This is the help menu of the <a href='#{Rack::MiniProfiler::SOURCE_CODE_URI}'>rack-mini-profiler</a> gem, append the following to your query string for more options:
647
656
 
648
657
  #{make_link "help", env} : display this screen
649
658
  #{make_link "env", env} : display the rack environment
@@ -658,6 +667,7 @@ Append the following to your query string:
658
667
  #{make_link "flamegraph", env} : a graph representing sampled activity (requires the stackprof gem).
659
668
  #{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).
660
669
  #{make_link "flamegraph&flamegraph_sample_rate=1", env}: creates a flamegraph with the specified sample rate (in ms). Overrides value set in config
670
+ #{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
661
671
  #{make_link "flamegraph_embed", env} : a graph representing sampled activity (requires the stackprof gem), embedded resources for use on an intranet.
662
672
  #{make_link "trace-exceptions", env} : will return all the spots where your application raises exceptions
663
673
  #{make_link "analyze-memory", env} : will perform basic memory analysis of heap
@@ -728,6 +738,10 @@ Append the following to your query string:
728
738
  url = "#{path}includes.js?v=#{version}" if !url
729
739
  css_url = "#{path}includes.css?v=#{version}" if !css_url
730
740
 
741
+ content_security_policy_nonce = @config.content_security_policy_nonce ||
742
+ env["action_dispatch.content_security_policy_nonce"] ||
743
+ env["secure_headers_content_security_policy_nonce"]
744
+
731
745
  settings = {
732
746
  path: path,
733
747
  url: url,
@@ -745,7 +759,9 @@ Append the following to your query string:
745
759
  startHidden: @config.start_hidden,
746
760
  collapseResults: @config.collapse_results,
747
761
  htmlContainer: @config.html_container,
748
- hiddenCustomFields: @config.snapshot_hidden_custom_fields.join(',')
762
+ hiddenCustomFields: @config.snapshot_hidden_custom_fields.join(','),
763
+ cspNonce: content_security_policy_nonce,
764
+ hotwireTurboDriveSupport: @config.enable_hotwire_turbo_drive_support,
749
765
  }
750
766
 
751
767
  if current && current.page_struct
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Rack
4
4
  class MiniProfiler
5
- VERSION = '2.3.2'
5
+ VERSION = '2.3.3'
6
+ SOURCE_CODE_URI = 'https://github.com/MiniProfiler/rack-mini-profiler'
6
7
  end
7
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # riak-client 2.2.2 patches
4
4
  class Riak::Multiget
5
- class <<self
5
+ class << self
6
6
  alias_method :get_all_without_profiling, :get_all
7
7
  def get_all(client, fetch_list)
8
8
  return get_all_without_profiling(client, fetch_list) unless SqlPatches.should_measure?
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
  s.required_ruby_version = '>= 2.4.0'
25
25
 
26
26
  s.metadata = {
27
- 'source_code_uri' => 'https://github.com/MiniProfiler/rack-mini-profiler',
27
+ 'source_code_uri' => Rack::MiniProfiler::SOURCE_CODE_URI,
28
28
  'changelog_uri' => 'https://github.com/MiniProfiler/rack-mini-profiler/blob/master/CHANGELOG.md'
29
29
  }
30
30
 
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: 2.3.2
4
+ version: 2.3.3
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: 2021-04-29 00:00:00.000000000 Z
13
+ date: 2021-08-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -354,7 +354,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
354
354
  - !ruby/object:Gem::Version
355
355
  version: '0'
356
356
  requirements: []
357
- rubygems_version: 3.2.2
357
+ rubygems_version: 3.1.6
358
358
  signing_key:
359
359
  specification_version: 4
360
360
  summary: Profiles loading speed for rack applications.