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
         |