rack-mini-profiler 2.1.0 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +37 -9
- data/lib/html/includes.css +3 -1
- data/lib/html/includes.js +225 -179
- data/lib/html/includes.scss +4 -1
- data/lib/html/includes.tmpl +29 -8
- data/lib/html/profile_handler.js +1 -1
- data/lib/html/speedscope/LICENSE +21 -0
- data/lib/html/speedscope/README.md +3 -0
- data/lib/html/speedscope/demangle-cpp.1768f4cc.js +4 -0
- data/lib/html/speedscope/favicon-16x16.f74b3187.png +0 -0
- data/lib/html/speedscope/favicon-32x32.bc503437.png +0 -0
- data/lib/html/speedscope/file-format-schema.json +324 -0
- data/lib/html/speedscope/fonts/source-code-pro-regular.css +8 -0
- data/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff +0 -0
- data/lib/html/speedscope/fonts/source-code-pro-v13-regular.woff2 +0 -0
- data/lib/html/speedscope/import.cf0fa83f.js +115 -0
- data/lib/html/speedscope/index.html +2 -0
- data/lib/html/speedscope/release.txt +3 -0
- data/lib/html/speedscope/reset.8c46b7a1.css +2 -0
- data/lib/html/speedscope/source-map.438fa06b.js +24 -0
- data/lib/html/speedscope/speedscope.44364064.js +200 -0
- data/lib/html/vendor.js +5 -5
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/client_settings.rb +6 -5
- data/lib/mini_profiler/config.rb +28 -2
- data/lib/mini_profiler/profiler.rb +104 -25
- data/lib/mini_profiler/profiling_methods.rb +11 -2
- data/lib/mini_profiler/snapshots_transporter.rb +109 -0
- data/lib/mini_profiler/storage/abstract_store.rb +14 -8
- data/lib/mini_profiler/timer_struct/page.rb +57 -4
- data/lib/mini_profiler/timer_struct/sql.rb +2 -2
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +1 -1
- data/lib/patches/db/mysql2.rb +4 -27
- data/lib/patches/db/mysql2/alias_method.rb +30 -0
- data/lib/patches/db/mysql2/prepend.rb +34 -0
- data/lib/prepend_mysql2_patch.rb +5 -0
- data/lib/rack-mini-profiler.rb +1 -0
- data/rack-mini-profiler.gemspec +6 -4
- metadata +61 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2984e9dec3cda4d5b86a4ed3b8300725207ba826b6a064402b23dcf04b0d5a51
|
4
|
+
data.tar.gz: 4a05074c124ac23085c08f4ae5389b71ef7b85687c40c5e5594dee4884d87632
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b29d4a47e99d03489251752bfe57a2b64235400b5ffb9f867738bfb9d165f63ddeeb559fc4edf9a60c91fe2d579b04ee473db1b2c7846ab823580d8b9b5e88c1
|
7
|
+
data.tar.gz: 3a0768401c048801bf4adb22792fbdb2643d5b5e0174f92149df96c8ae2ab79d0c504e9e2146d72cc45068df6b88ebc675ef11431d47c17a5b929f375a1e6d74
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 2.3.2 - 2021-04-30
|
4
|
+
|
5
|
+
- [FEATURE] Introduce `pp=async-flamegraph` for asynchronous flamegraphs
|
6
|
+
|
7
|
+
## 2.3.1 - 2021-01-29
|
8
|
+
|
9
|
+
- [FIX] compatability with Ruby 3.0
|
10
|
+
- [FIX] compatability with peek-mysql2
|
11
|
+
|
12
|
+
## 2.3.0 - 2020-12-29
|
13
|
+
|
14
|
+
- [FEATURE] flamegraphs are now based off speedscope
|
15
|
+
|
16
|
+
## 2.2.1 - 2020-12-23
|
17
|
+
|
18
|
+
- [FIX] Turbolinks integration causing increasing number of GET requests
|
19
|
+
- [FEATURE] enahanced log transporter with compression and exponential backoff
|
20
|
+
- [FEATURE] sameSite=Lax added to MiniProfiler cookie
|
21
|
+
|
22
|
+
## 2.2.0 - 2020-10-19
|
23
|
+
|
24
|
+
- [UX] Enhancements to snapshots UI
|
25
|
+
- [FEATURE] Mini Profiler cookie is now sameSite=lax
|
26
|
+
- [FEATURE] Snapshots transporter
|
27
|
+
- [FEATURE] Redact SQL queries in snapshots by default
|
28
|
+
|
3
29
|
## 2.1.0 - 2020-09-17
|
4
30
|
|
5
31
|
- [FEATURE] Allow assets to be precompiled with Sprockets
|
data/README.md
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# rack-mini-profiler
|
2
2
|
|
3
|
-
[![Code Climate](https://codeclimate.com/github/MiniProfiler/rack-mini-profiler/badges/gpa.svg)](https://codeclimate.com/github/MiniProfiler/rack-mini-profiler) [![Build Status](https://travis-ci.org/MiniProfiler/rack-mini-profiler.svg)](https://travis-ci.org/MiniProfiler/rack-mini-profiler)
|
4
|
-
|
5
3
|
Middleware that displays speed badge for every html page. Designed to work both in production and in development.
|
6
4
|
|
7
5
|
#### Features
|
@@ -43,7 +41,6 @@ You can also include optional libraries to enable additional features.
|
|
43
41
|
gem 'memory_profiler'
|
44
42
|
|
45
43
|
# For call-stack profiling flamegraphs
|
46
|
-
gem 'flamegraph'
|
47
44
|
gem 'stackprof'
|
48
45
|
```
|
49
46
|
|
@@ -81,6 +78,16 @@ gem 'rack-mini-profiler', require: ['prepend_net_http_patch']
|
|
81
78
|
|
82
79
|
This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this [ruby issue](https://bugs.ruby-lang.org/issues/11120) for details. The fix is to apply all patches the same way. Mini Profiler by default will apply its patch using method aliasing, but you can change that to module prepend by adding `require: ['prepend_net_http_patch']` to the gem line as shown above.
|
83
80
|
|
81
|
+
#### `peek-mysql2` stack level too deep errors
|
82
|
+
|
83
|
+
If you use peek-mysql2 with Rails >= 5, you'll need to use this gem spec in your Gemfile:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
gem 'rack-mini-profiler', require: ['prepend_mysql2_patch', 'rack-mini-profiler']
|
87
|
+
```
|
88
|
+
|
89
|
+
This should not be necessary with Rails < 5 because peek-mysql2 hooks into mysql2 gem in different ways depending on your Rails version.
|
90
|
+
|
84
91
|
#### Rails and manual initialization
|
85
92
|
|
86
93
|
In case you need to make sure rack_mini_profiler is initialized after all other gems, or you want to execute some code before rack_mini_profiler required:
|
@@ -161,9 +168,13 @@ export RACK_MINI_PROFILER_PATCH="false"
|
|
161
168
|
|
162
169
|
To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler):
|
163
170
|
|
164
|
-
* add the [**
|
171
|
+
* add the [**stackprof**](https://rubygems.org/gems/stackprof) gem to your Gemfile
|
165
172
|
* visit a page in your app with `?pp=flamegraph`
|
166
173
|
|
174
|
+
To store flamegraph data for later viewing, append the `?pp=async-flamegraph` parameter. The request will return as normal.
|
175
|
+
Flamegraph data for this request, and all subsequent requests made by this page (based on the `REFERER` header) will be stored.
|
176
|
+
'flamegraph' links will appear for these requests in the MiniProfiler UI.
|
177
|
+
|
167
178
|
### Memory Profiling
|
168
179
|
|
169
180
|
Memory allocations can be measured (using the [memory_profiler](https://github.com/SamSaffron/memory_profiler) gem)
|
@@ -190,7 +201,7 @@ There are two additional `pp` options that can be used to analyze memory which d
|
|
190
201
|
|
191
202
|
In a complex web application, it's possible for a request to trigger rare conditions that result in poor performance. Mini Profiler ships with a feature to help detect those rare conditions and fix them. It works by enabling invisible profiling on one request every N requests, and saving the performance metrics that are collected during the request (a.k.a snapshot of the request) so that they can be viewed later. To turn this feature on, set the `snapshot_every_n_requests` config to a value larger than 0. The larger the value is, the less frequently requests are profiled.
|
192
203
|
|
193
|
-
Mini Profiler will exclude requests that are made to
|
204
|
+
Mini Profiler will exclude requests that are made to skipped paths (see `skip_paths` config below) from being sampled. Additionally, if profiling is enabled for a request that later finishes with a non-2xx status code, Mini Profiler will discard the snapshot and not save it (this behavior may change in the future).
|
194
205
|
|
195
206
|
After enabling snapshots sampling, you can see the snapshots that have been collected at `/mini-profiler-resources/snapshots` (or if you changed the `base_url_path` config, substitute `mini-profiler-resources` with your value of the config). You'll see on that page a table where each row represents a group of snapshots with the duration of the worst snapshot in that group. The worst snapshot in a group is defined as the snapshot whose request took longer than all of the snapshots in the same group. Snapshots grouped by HTTP method and path of the request, and if your application is a Rails app, Mini Profiler will try to convert the path to `controller#action` and group by that instead of request path. Clicking on a group will display the snapshots of that group sorted from worst to best. From there, you can click on a snapshot's ID to see the snapshot with all the performance metrics that were collected.
|
196
207
|
|
@@ -198,6 +209,18 @@ Access to the snapshots page is restricted to only those who can see the speed b
|
|
198
209
|
|
199
210
|
Mini Profiler will keep a maximum of 1000 snapshots by default, and you can change that via the `snapshots_limit` config. When snapshots reach the configured limit, Mini Profiler will save a new snapshot only if it's worse than at least one of the existing snapshots and delete the best one (i.e. the snapshot whose request took the least time compared to other snapshots).
|
200
211
|
|
212
|
+
#### Snapshots Transporter
|
213
|
+
|
214
|
+
Mini Profiler can be configured so that it sends snapshots over HTTP using the snapshots transporter. The main use-case of the transporter is to allow the aggregation of snapshots from multiple applications/sources in a single place. To enable the snapshots transporter, you need to provide a destination URL to the `snapshots_transport_destination_url` config, and a secure key to the `snapshots_transport_auth_key` config (will be used for authorization). Both of these configs are required for the transporter to be enabled.
|
215
|
+
|
216
|
+
The transporter uses a buffer to temporarily hold snapshots in memory with a limit of 100 snapshots. Every 30 seconds, *if* the buffer is not empty, the transporter will make a `POST` request with the buffer content to the destination URL. Requests made by the transporter will have a `Mini-Profiler-Transport-Auth` header with the value of the `snapshots_transport_auth_key` config. The destination should only accept requests that include this header AND the header's value matches the key you set to the `snapshots_transport_auth_key` config.
|
217
|
+
|
218
|
+
If the specified destination responds with a non-200 status code, the transporter will increase the interval between requests by `2^n` seconds where `n` is the number of failed requests since the last successful request. The base interval between requests is 30 seconds. So if a request fails, the next request will be `30 + 2^1 = 32` seconds later. If the next request fails too, the next one will be `30 + 2^2 = 34` seconds later and so on until a request succeeds at which point the interval will return to 30 seconds. The interval will not go beyond 1 hour.
|
219
|
+
|
220
|
+
Requests made by the transporter can be optionally gzip-compressed by setting the `snapshots_transport_gzip_requests` config to true. The body of the requests (after decompression, if you opt for compression) is a JSON string with a single top-level key called `snapshots` and it has an array of snapshots. The structure of a snapshot is too complex to be explained here, but it has the same structure that Mini Profiler client expects. So if your use-case is to simply be able to view snapshots from multiple sources in one place, you should simply store the snapshots as-is, and then serve them to Mini Profiler client to consume. If the destination application also has Mini Profiler, you can simply use the API of the storage backends to store the incoming snapshots and Mini Profiler will treat them the same as local snapshots (e.g. they'll be grouped and displayed in the same manner described in the previous section).
|
221
|
+
|
222
|
+
Mini Profiler offers an API to add extra fields (a.k.a custom fields) to snapshots. For example, you may want to add whether the request was made by a logged-in or anonymous user, the version of your application or any other things that are specific to your application. To add custom fields to a snapshot, call the `Rack::MiniProfiler.add_snapshot_custom_field(<key>, <value>)` method anywhere during the lifetime of a request, and the snapshot of that request will include the fields you added. If you have a Rails app, you can call that method in an `after_action` callback. Custom fields are cleared between requests.
|
223
|
+
|
201
224
|
## Access control in non-development environments
|
202
225
|
|
203
226
|
rack-mini-profiler is designed with production profiling in mind. To enable that run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
|
@@ -216,21 +239,21 @@ rack-mini-profiler is designed with production profiling in mind. To enable that
|
|
216
239
|
|
217
240
|
Note:
|
218
241
|
|
219
|
-
Out-of-the-box we will initialize the `authorization_mode` to `:
|
242
|
+
Out-of-the-box we will initialize the `authorization_mode` to `:allow_authorized` in production. However, in some cases we may not be able to do it:
|
220
243
|
|
221
|
-
- If you are running in development or test we will not enable
|
244
|
+
- If you are running in development or test we will not enable the explicit authorization mode
|
222
245
|
- If you use `require: false` on rack_mini_profiler we are unlikely to be able to run the railtie
|
223
246
|
- If you are running outside of rails we will not run the railtie
|
224
247
|
|
225
248
|
In those cases use:
|
226
249
|
|
227
250
|
```ruby
|
228
|
-
Rack::MiniProfiler.config.authorization_mode = :
|
251
|
+
Rack::MiniProfiler.config.authorization_mode = :allow_authorized
|
229
252
|
```
|
230
253
|
|
231
254
|
When deciding to fully profile a page mini profiler consults with the `authorization_mode`
|
232
255
|
|
233
|
-
By default in production we attempt to set the authorization mode to `:
|
256
|
+
By default in production we attempt to set the authorization mode to `:allow_authorized` meaning that end user will only be able to see requests where somewhere `Rack::MiniProfiler.authorize_request` is invoked.
|
234
257
|
|
235
258
|
In development we run in the `:allow_all` authorization mode meaning every request is profiled and displayed to the end user.
|
236
259
|
|
@@ -384,6 +407,11 @@ enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that c
|
|
384
407
|
assets_url|`nil`|See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
|
385
408
|
snapshot_every_n_requests|`-1`|Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
|
386
409
|
snapshots_limit|`1000`|Determines how many snapshots Mini Profiler is allowed to keep.
|
410
|
+
snapshot_hidden_custom_fields|`[]`|Each snapshot custom field will have a dedicated column in the UI by default. Use this config to exclude certain custom fields from having their own columns.
|
411
|
+
snapshots_transport_destination_url|`nil`|Set this config to a valid URL to enable snapshots transporter which will `POST` snapshots to the given URL. The transporter requires `snapshots_transport_auth_key` config to be set as well.
|
412
|
+
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
|
+
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
|
+
snapshots_transport_gzip_requests|`false`|Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
|
387
415
|
|
388
416
|
### Using MiniProfiler with `Rack::Deflate` middleware
|
389
417
|
|
data/lib/html/includes.css
CHANGED
@@ -434,7 +434,9 @@
|
|
434
434
|
background: #6a737c;
|
435
435
|
color: #ffffff; }
|
436
436
|
.mp-snapshots .snapshots-table th, .mp-snapshots .snapshots-table td {
|
437
|
-
padding: 5px;
|
437
|
+
padding: 5px 10px;
|
438
438
|
box-sizing: border-box; }
|
439
|
+
.mp-snapshots .snapshots-table th:not(.request-group), .mp-snapshots .snapshots-table td:not(.request-group) {
|
440
|
+
text-align: center; }
|
439
441
|
.mp-snapshots .snapshots-table th {
|
440
442
|
border-right: 1px solid #ffffff; }
|
data/lib/html/includes.js
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
-
var
|
3
|
+
var _MiniProfiler = (function() {
|
4
4
|
var _arguments = arguments;
|
5
5
|
var options,
|
6
6
|
container,
|
7
7
|
controls,
|
8
|
-
fetchedIds = [],
|
9
|
-
fetchingIds =
|
8
|
+
fetchedIds = (window.MiniProfiler && window.MiniProfiler.fetchedIds) || [],
|
9
|
+
fetchingIds =
|
10
|
+
(window.MiniProfiler && window.MiniProfiler.fetchingIds) || [],
|
10
11
|
// so we never pull down a profiler twice
|
11
12
|
ajaxStartTime,
|
12
13
|
totalsControl,
|
@@ -400,7 +401,7 @@ var MiniProfiler = (function() {
|
|
400
401
|
var px = button.offsetTop - 1,
|
401
402
|
// position next to the button we clicked
|
402
403
|
windowHeight = window.innerHeight,
|
403
|
-
maxHeight = windowHeight - 40; // make sure the popup doesn't extend below the fold
|
404
|
+
maxHeight = windowHeight - px - 40; // make sure the popup doesn't extend below the fold
|
404
405
|
|
405
406
|
popup.style[options.renderVerticalPosition] = "".concat(px, "px");
|
406
407
|
popup.style.maxHeight = "".concat(maxHeight, "px");
|
@@ -634,6 +635,10 @@ var MiniProfiler = (function() {
|
|
634
635
|
}
|
635
636
|
};
|
636
637
|
|
638
|
+
var turbolinksSkipResultsFetch = function turbolinksSkipResultsFetch(event) {
|
639
|
+
event.data.xhr.__miniProfilerSkipResultsFetch = true;
|
640
|
+
};
|
641
|
+
|
637
642
|
var bindDocumentEvents = function bindDocumentEvents() {
|
638
643
|
document.addEventListener("click", onClickEvents);
|
639
644
|
document.addEventListener("keyup", onClickEvents);
|
@@ -642,6 +647,10 @@ var MiniProfiler = (function() {
|
|
642
647
|
if (typeof Turbolinks !== "undefined" && Turbolinks.supported) {
|
643
648
|
document.addEventListener("page:change", unbindDocumentEvents);
|
644
649
|
document.addEventListener("turbolinks:load", unbindDocumentEvents);
|
650
|
+
document.addEventListener(
|
651
|
+
"turbolinks:request-start",
|
652
|
+
turbolinksSkipResultsFetch
|
653
|
+
);
|
645
654
|
}
|
646
655
|
};
|
647
656
|
|
@@ -651,6 +660,10 @@ var MiniProfiler = (function() {
|
|
651
660
|
document.removeEventListener("keyup", toggleShortcutEvent);
|
652
661
|
document.removeEventListener("page:change", unbindDocumentEvents);
|
653
662
|
document.removeEventListener("turbolinks:load", unbindDocumentEvents);
|
663
|
+
document.removeEventListener(
|
664
|
+
"turbolinks:request-start",
|
665
|
+
turbolinksSkipResultsFetch
|
666
|
+
);
|
654
667
|
};
|
655
668
|
|
656
669
|
var initFullView = function initFullView() {
|
@@ -679,9 +692,24 @@ var MiniProfiler = (function() {
|
|
679
692
|
if (data.page === "overview") {
|
680
693
|
temp.innerHTML = MiniProfiler.templates.snapshotsGroupsList(data);
|
681
694
|
} else if (data.group_name) {
|
682
|
-
|
695
|
+
var allCustomFieldsNames = [];
|
696
|
+
data.list.forEach(function(snapshot) {
|
697
|
+
Object.keys(snapshot.custom_fields).forEach(function(k) {
|
698
|
+
if (
|
699
|
+
allCustomFieldsNames.indexOf(k) === -1 &&
|
700
|
+
options.hiddenCustomFields.indexOf(k.toLowerCase()) === -1
|
701
|
+
) {
|
702
|
+
allCustomFieldsNames.push(k);
|
703
|
+
}
|
704
|
+
});
|
705
|
+
});
|
706
|
+
allCustomFieldsNames.sort();
|
707
|
+
temp.innerHTML = MiniProfiler.templates.snapshotsList({
|
708
|
+
data: data,
|
709
|
+
allCustomFieldsNames: allCustomFieldsNames
|
710
|
+
});
|
683
711
|
}
|
684
|
-
Array.from(temp.children).forEach(function
|
712
|
+
Array.from(temp.children).forEach(function(child) {
|
685
713
|
document.body.appendChild(child);
|
686
714
|
});
|
687
715
|
};
|
@@ -754,213 +782,220 @@ var MiniProfiler = (function() {
|
|
754
782
|
fetchResults(options.ids);
|
755
783
|
}
|
756
784
|
|
757
|
-
|
758
|
-
|
759
|
-
XMLHttpRequest.prototype.send = function(data) {
|
760
|
-
ajaxStartTime = new Date();
|
761
|
-
this.addEventListener("load", function() {
|
762
|
-
// responseURL isn't available in IE11
|
763
|
-
if (
|
764
|
-
this.responseURL &&
|
765
|
-
this.responseURL.indexOf(window.location.origin) !== 0
|
766
|
-
) {
|
767
|
-
return;
|
768
|
-
}
|
769
|
-
// getAllResponseHeaders isn't available in Edge.
|
770
|
-
var allHeaders = this.getAllResponseHeaders
|
771
|
-
? this.getAllResponseHeaders()
|
772
|
-
: null;
|
773
|
-
if (
|
774
|
-
allHeaders &&
|
775
|
-
allHeaders.toLowerCase().indexOf("x-miniprofiler-ids") === -1
|
776
|
-
) {
|
777
|
-
return;
|
778
|
-
}
|
779
|
-
// should be a string of comma-separated ids
|
780
|
-
var stringIds = this.getResponseHeader("X-MiniProfiler-Ids");
|
781
|
-
|
782
|
-
if (stringIds) {
|
783
|
-
var ids = stringIds.split(",");
|
784
|
-
fetchResults(ids);
|
785
|
-
}
|
786
|
-
});
|
787
|
-
send.call(this, data);
|
788
|
-
}; // fetch results after ASP Ajax calls
|
789
|
-
|
790
|
-
if (
|
791
|
-
typeof Sys != "undefined" &&
|
792
|
-
typeof Sys.WebForms != "undefined" &&
|
793
|
-
typeof Sys.WebForms.PageRequestManager != "undefined"
|
794
|
-
) {
|
795
|
-
// Get the instance of PageRequestManager.
|
796
|
-
var PageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
|
797
|
-
PageRequestManager.add_endRequest(function(sender, args) {
|
798
|
-
if (args) {
|
799
|
-
var response = args.get_response();
|
785
|
+
if (!window.MiniProfiler || !window.MiniProfiler.patchesApplied) {
|
786
|
+
var send = XMLHttpRequest.prototype.send;
|
800
787
|
|
788
|
+
XMLHttpRequest.prototype.send = function(data) {
|
789
|
+
ajaxStartTime = new Date();
|
790
|
+
this.addEventListener("load", function() {
|
791
|
+
// responseURL isn't available in IE11
|
801
792
|
if (
|
802
|
-
|
803
|
-
|
793
|
+
this.responseURL &&
|
794
|
+
this.responseURL.indexOf(window.location.origin) !== 0
|
804
795
|
) {
|
805
|
-
|
806
|
-
.get_response()
|
807
|
-
.getResponseHeader("X-MiniProfiler-Ids");
|
808
|
-
|
809
|
-
if (stringIds) {
|
810
|
-
var ids = stringIds.split(",");
|
811
|
-
fetchResults(ids);
|
812
|
-
}
|
796
|
+
return;
|
813
797
|
}
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
798
|
+
if (this.__miniProfilerSkipResultsFetch) {
|
799
|
+
return;
|
800
|
+
}
|
801
|
+
// getAllResponseHeaders isn't available in Edge.
|
802
|
+
var allHeaders = this.getAllResponseHeaders
|
803
|
+
? this.getAllResponseHeaders()
|
804
|
+
: null;
|
805
|
+
if (
|
806
|
+
allHeaders &&
|
807
|
+
allHeaders.toLowerCase().indexOf("x-miniprofiler-ids") === -1
|
808
|
+
) {
|
809
|
+
return;
|
810
|
+
}
|
811
|
+
// should be a string of comma-separated ids
|
812
|
+
var stringIds = this.getResponseHeader("X-MiniProfiler-Ids");
|
827
813
|
|
828
814
|
if (stringIds) {
|
829
815
|
var ids = stringIds.split(",");
|
830
816
|
fetchResults(ids);
|
831
817
|
}
|
832
|
-
};
|
833
|
-
|
834
|
-
|
818
|
+
});
|
819
|
+
send.call(this, data);
|
820
|
+
}; // fetch results after ASP Ajax calls
|
835
821
|
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
822
|
+
if (
|
823
|
+
typeof Sys != "undefined" &&
|
824
|
+
typeof Sys.WebForms != "undefined" &&
|
825
|
+
typeof Sys.WebForms.PageRequestManager != "undefined"
|
826
|
+
) {
|
827
|
+
// Get the instance of PageRequestManager.
|
828
|
+
var PageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
|
829
|
+
PageRequestManager.add_endRequest(function(sender, args) {
|
830
|
+
if (args) {
|
831
|
+
var response = args.get_response();
|
832
|
+
|
833
|
+
if (
|
834
|
+
response.get_responseAvailable() &&
|
835
|
+
response._xmlHttpRequest !== null
|
836
|
+
) {
|
837
|
+
var stringIds = args
|
838
|
+
.get_response()
|
839
|
+
.getResponseHeader("X-MiniProfiler-Ids");
|
840
|
+
|
841
|
+
if (stringIds) {
|
842
|
+
var ids = stringIds.split(",");
|
843
|
+
fetchResults(ids);
|
844
|
+
}
|
845
|
+
}
|
846
|
+
}
|
847
|
+
});
|
848
|
+
} // more Asp.Net callbacks
|
849
|
+
|
850
|
+
if (typeof WebForm_ExecuteCallback == "function") {
|
851
|
+
WebForm_ExecuteCallback = (function(callbackObject) {
|
852
|
+
// Store original function
|
853
|
+
var original = WebForm_ExecuteCallback;
|
854
|
+
return function(callbackObject) {
|
855
|
+
original(callbackObject);
|
856
|
+
var stringIds = callbackObject.xmlRequest.getResponseHeader(
|
857
|
+
"X-MiniProfiler-Ids"
|
858
|
+
);
|
847
859
|
|
848
|
-
|
860
|
+
if (stringIds) {
|
861
|
+
var ids = stringIds.split(",");
|
862
|
+
fetchResults(ids);
|
863
|
+
}
|
864
|
+
};
|
865
|
+
})();
|
866
|
+
} // also fetch results after ExtJS requests, in case it is being used
|
849
867
|
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
868
|
+
if (
|
869
|
+
typeof Ext != "undefined" &&
|
870
|
+
typeof Ext.Ajax != "undefined" &&
|
871
|
+
typeof Ext.Ajax.on != "undefined"
|
872
|
+
) {
|
873
|
+
// Ext.Ajax is a singleton, so we just have to attach to its 'requestcomplete' event
|
874
|
+
Ext.Ajax.on("requestcomplete", function(e, xhr, settings) {
|
875
|
+
//iframed file uploads don't have headers
|
876
|
+
if (!xhr || !xhr.getResponseHeader) {
|
877
|
+
return;
|
878
|
+
}
|
856
879
|
|
857
|
-
|
858
|
-
Request.prototype.addEvents({
|
859
|
-
onComplete: function onComplete() {
|
860
|
-
var stringIds = this.xhr.getResponseHeader("X-MiniProfiler-Ids");
|
880
|
+
var stringIds = xhr.getResponseHeader("X-MiniProfiler-Ids");
|
861
881
|
|
862
882
|
if (stringIds) {
|
863
883
|
var ids = stringIds.split(",");
|
864
884
|
fetchResults(ids);
|
865
885
|
}
|
866
|
-
}
|
867
|
-
}
|
868
|
-
} // add support for AngularJS, which use the basic XMLHttpRequest object.
|
869
|
-
|
870
|
-
if (window.angular && typeof XMLHttpRequest != "undefined") {
|
871
|
-
var _send = XMLHttpRequest.prototype.send;
|
872
|
-
|
873
|
-
XMLHttpRequest.prototype.send = function sendReplacement(data) {
|
874
|
-
if (this.onreadystatechange) {
|
875
|
-
if (
|
876
|
-
typeof this.miniprofiler == "undefined" ||
|
877
|
-
typeof this.miniprofiler.prev_onreadystatechange == "undefined"
|
878
|
-
) {
|
879
|
-
this.miniprofiler = {
|
880
|
-
prev_onreadystatechange: this.onreadystatechange
|
881
|
-
};
|
886
|
+
});
|
887
|
+
}
|
882
888
|
|
883
|
-
|
884
|
-
|
885
|
-
|
889
|
+
if (typeof MooTools != "undefined" && typeof Request != "undefined") {
|
890
|
+
Request.prototype.addEvents({
|
891
|
+
onComplete: function onComplete() {
|
892
|
+
var stringIds = this.xhr.getResponseHeader("X-MiniProfiler-Ids");
|
886
893
|
|
887
|
-
|
888
|
-
|
889
|
-
|
894
|
+
if (stringIds) {
|
895
|
+
var ids = stringIds.split(",");
|
896
|
+
fetchResults(ids);
|
897
|
+
}
|
898
|
+
}
|
899
|
+
});
|
900
|
+
} // add support for AngularJS, which use the basic XMLHttpRequest object.
|
901
|
+
|
902
|
+
if (window.angular && typeof XMLHttpRequest != "undefined") {
|
903
|
+
var _send = XMLHttpRequest.prototype.send;
|
904
|
+
|
905
|
+
XMLHttpRequest.prototype.send = function sendReplacement(data) {
|
906
|
+
if (this.onreadystatechange) {
|
907
|
+
if (
|
908
|
+
typeof this.miniprofiler == "undefined" ||
|
909
|
+
typeof this.miniprofiler.prev_onreadystatechange == "undefined"
|
910
|
+
) {
|
911
|
+
this.miniprofiler = {
|
912
|
+
prev_onreadystatechange: this.onreadystatechange
|
913
|
+
};
|
914
|
+
|
915
|
+
this.onreadystatechange = function onReadyStateChangeReplacement() {
|
916
|
+
if (this.readyState == 4) {
|
917
|
+
var stringIds = this.getResponseHeader("X-MiniProfiler-Ids");
|
918
|
+
|
919
|
+
if (stringIds) {
|
920
|
+
var ids = stringIds.split(",");
|
921
|
+
fetchResults(ids);
|
922
|
+
}
|
890
923
|
}
|
891
|
-
}
|
892
924
|
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
925
|
+
if (this.miniprofiler.prev_onreadystatechange !== null)
|
926
|
+
return this.miniprofiler.prev_onreadystatechange.apply(
|
927
|
+
this,
|
928
|
+
arguments
|
929
|
+
);
|
930
|
+
};
|
931
|
+
}
|
899
932
|
}
|
900
|
-
}
|
901
|
-
|
902
|
-
return _send.apply(this, arguments);
|
903
|
-
};
|
904
|
-
} // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
905
933
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
) {
|
910
|
-
var __originalFetch = window.fetch;
|
934
|
+
return _send.apply(this, arguments);
|
935
|
+
};
|
936
|
+
} // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
911
937
|
|
912
|
-
window.fetch
|
913
|
-
var
|
938
|
+
if (typeof window.fetch === "function") {
|
939
|
+
var __originalFetch = window.fetch;
|
914
940
|
|
915
|
-
|
916
|
-
|
917
|
-
// look for x-mini-profile-ids
|
918
|
-
var entries = response.headers.entries();
|
919
|
-
var _iteratorNormalCompletion = true;
|
920
|
-
var _didIteratorError = false;
|
921
|
-
var _iteratorError = undefined;
|
941
|
+
window.fetch = function(input, init) {
|
942
|
+
var originalFetchRun = __originalFetch(input, init);
|
922
943
|
|
944
|
+
originalFetchRun.then(function(response) {
|
923
945
|
try {
|
924
|
-
for
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
if (pair[0] && pair[0].toLowerCase() == "x-miniprofiler-ids") {
|
932
|
-
var ids = pair[1].split(",");
|
933
|
-
fetchResults(ids);
|
934
|
-
}
|
935
|
-
}
|
936
|
-
} catch (err) {
|
937
|
-
_didIteratorError = true;
|
938
|
-
_iteratorError = err;
|
939
|
-
} finally {
|
946
|
+
// look for x-mini-profile-ids
|
947
|
+
var entries = response.headers.entries();
|
948
|
+
var _iteratorNormalCompletion = true;
|
949
|
+
var _didIteratorError = false;
|
950
|
+
var _iteratorError = undefined;
|
951
|
+
|
940
952
|
try {
|
941
|
-
|
942
|
-
_iterator.
|
953
|
+
for (
|
954
|
+
var _iterator = entries[Symbol.iterator](), _step;
|
955
|
+
!(_iteratorNormalCompletion = (_step = _iterator.next())
|
956
|
+
.done);
|
957
|
+
_iteratorNormalCompletion = true
|
958
|
+
) {
|
959
|
+
var pair = _step.value;
|
960
|
+
|
961
|
+
if (
|
962
|
+
pair[0] &&
|
963
|
+
pair[0].toLowerCase() == "x-miniprofiler-ids"
|
964
|
+
) {
|
965
|
+
var ids = pair[1].split(",");
|
966
|
+
fetchResults(ids);
|
967
|
+
}
|
943
968
|
}
|
969
|
+
} catch (err) {
|
970
|
+
_didIteratorError = true;
|
971
|
+
_iteratorError = err;
|
944
972
|
} finally {
|
945
|
-
|
946
|
-
|
973
|
+
try {
|
974
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
975
|
+
_iterator.return();
|
976
|
+
}
|
977
|
+
} finally {
|
978
|
+
if (_didIteratorError) {
|
979
|
+
throw _iteratorError;
|
980
|
+
}
|
947
981
|
}
|
948
982
|
}
|
983
|
+
} catch (e) {
|
984
|
+
console.error(e);
|
949
985
|
}
|
950
|
-
}
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
window.fetch.__patchedByMiniProfiler = true;
|
958
|
-
} // some elements want to be hidden on certain doc events
|
986
|
+
});
|
987
|
+
return originalFetchRun;
|
988
|
+
};
|
989
|
+
}
|
990
|
+
window.MiniProfiler.patchesApplied = true;
|
991
|
+
}
|
959
992
|
|
960
993
|
bindDocumentEvents();
|
961
994
|
};
|
962
995
|
|
963
996
|
return {
|
997
|
+
fetchedIds: fetchedIds,
|
998
|
+
fetchingIds: fetchingIds,
|
964
999
|
init: function init() {
|
965
1000
|
var script = document.getElementById("mini-profiler");
|
966
1001
|
if (!script || !script.getAttribute) return;
|
@@ -994,6 +1029,10 @@ var MiniProfiler = (function() {
|
|
994
1029
|
sessionStorage["rack-mini-profiler-start-hidden"] === "true";
|
995
1030
|
var htmlContainer = script.getAttribute("data-html-container");
|
996
1031
|
var cssUrl = script.getAttribute("data-css-url");
|
1032
|
+
var hiddenCustomFields = script
|
1033
|
+
.getAttribute("data-hidden-custom-fields")
|
1034
|
+
.toLowerCase()
|
1035
|
+
.split(",");
|
997
1036
|
return {
|
998
1037
|
ids: ids,
|
999
1038
|
path: path,
|
@@ -1011,7 +1050,8 @@ var MiniProfiler = (function() {
|
|
1011
1050
|
startHidden: startHidden,
|
1012
1051
|
collapseResults: collapseResults,
|
1013
1052
|
htmlContainer: htmlContainer,
|
1014
|
-
cssUrl: cssUrl
|
1053
|
+
cssUrl: cssUrl,
|
1054
|
+
hiddenCustomFields: hiddenCustomFields
|
1015
1055
|
};
|
1016
1056
|
})();
|
1017
1057
|
|
@@ -1173,6 +1213,9 @@ var MiniProfiler = (function() {
|
|
1173
1213
|
shareUrl: function shareUrl(id) {
|
1174
1214
|
return options.path + "results?id=" + id;
|
1175
1215
|
},
|
1216
|
+
flamegraphUrl: function flamegrapgUrl(id) {
|
1217
|
+
return options.path + "flamegraph?id=" + id;
|
1218
|
+
},
|
1176
1219
|
moreUrl: function moreUrl(requestName) {
|
1177
1220
|
var requestParts = requestName.split(" ");
|
1178
1221
|
var linkSrc =
|
@@ -1417,7 +1460,7 @@ var MiniProfiler = (function() {
|
|
1417
1460
|
return options.showTotalSqlCount;
|
1418
1461
|
},
|
1419
1462
|
timestampToRelative: function timestampToRelative(timestamp) {
|
1420
|
-
var now = Math.round(
|
1463
|
+
var now = Math.round(new Date().getTime() / 1000);
|
1421
1464
|
timestamp = Math.round(timestamp / 1000);
|
1422
1465
|
var diff = now - timestamp;
|
1423
1466
|
if (diff < 60) {
|
@@ -1429,7 +1472,7 @@ var MiniProfiler = (function() {
|
|
1429
1472
|
res += "s";
|
1430
1473
|
}
|
1431
1474
|
return res;
|
1432
|
-
}
|
1475
|
+
};
|
1433
1476
|
diff = Math.round(diff / 60);
|
1434
1477
|
if (diff <= 60) {
|
1435
1478
|
return buildDisplayTime(diff, "minute");
|
@@ -1444,5 +1487,8 @@ var MiniProfiler = (function() {
|
|
1444
1487
|
};
|
1445
1488
|
})();
|
1446
1489
|
|
1447
|
-
window.MiniProfiler
|
1448
|
-
MiniProfiler.
|
1490
|
+
if (window.MiniProfiler) {
|
1491
|
+
_MiniProfiler.patchesApplied = window.MiniProfiler.patchesApplied;
|
1492
|
+
}
|
1493
|
+
window.MiniProfiler = _MiniProfiler;
|
1494
|
+
_MiniProfiler.init();
|