rack-mini-profiler 2.0.4 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +39 -1
- data/lib/html/includes.css +38 -0
- data/lib/html/includes.js +65 -3
- data/lib/html/includes.scss +35 -4
- data/lib/html/includes.tmpl +92 -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/client_settings.rb +1 -0
- data/lib/mini_profiler/config.rb +22 -2
- data/lib/mini_profiler/profiler.rb +171 -12
- data/lib/mini_profiler/profiling_methods.rb +8 -1
- data/lib/mini_profiler/snapshots_transporter.rb +84 -0
- data/lib/mini_profiler/storage/abstract_store.rb +78 -0
- data/lib/mini_profiler/storage/memory_store.rb +54 -5
- data/lib/mini_profiler/storage/redis_store.rb +134 -0
- data/lib/mini_profiler/timer_struct/page.rb +52 -2
- 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 +11 -0
- data/lib/rack-mini-profiler.rb +1 -0
- data/rack-mini-profiler.gemspec +1 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42690a86bcd4e4cfd8bf17289dc7b2f583bdd4ec286b4bbcdd50454f0f73551b
|
4
|
+
data.tar.gz: 723291f0ae196b09f585a331fc4e33b54bfd4be7fba527b623d851dc77524cce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca4bd7e3f35d42171bf2e39775a4a8fb565b907ba1eab40002886f7933bb4e9d30bfb8d9a87a7cb408e68b3f7e1a5dbbd360fa633f8e5c94793218abb40b32d5
|
7
|
+
data.tar.gz: 877164b6caf03efc6fa4183bed8f6dfce09c691e98c9ac3588fae8307d6b1632be8e666140c4a581280b6f0bfb69c2f5d4cb100ecca0f4afe1d3a9cd770b8e7f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 2.2.0 - 2020-10-19
|
4
|
+
|
5
|
+
- [UX] Enhancements to snapshots UI
|
6
|
+
- [FEATURE] Mini Profiler cookie is now sameSite=lax
|
7
|
+
- [FEATURE] Snapshots transporter
|
8
|
+
- [FEATURE] Redact SQL queries in snapshots by default
|
9
|
+
|
10
|
+
## 2.1.0 - 2020-09-17
|
11
|
+
|
12
|
+
- [FEATURE] Allow assets to be precompiled with Sprockets
|
13
|
+
- [FEATURE] Snapshots sampling (see README in repo)
|
14
|
+
- [FEATURE] Allow `skip_paths` config to contain regular expressions
|
15
|
+
|
3
16
|
## 2.0.4 - 2020-08-04
|
4
17
|
|
5
18
|
- [FIX] webpacker may exist with no config, allow for that
|
data/README.md
CHANGED
@@ -186,6 +186,28 @@ There are two additional `pp` options that can be used to analyze memory which d
|
|
186
186
|
* Use `?pp=profile-gc` to report on Garbage Collection statistics
|
187
187
|
* Use `?pp=analyze-memory` to report on ObjectSpace statistics
|
188
188
|
|
189
|
+
### Snapshots Sampling
|
190
|
+
|
191
|
+
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
|
+
|
193
|
+
Mini Profiler will exclude requests that are made to skippd 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
|
+
|
195
|
+
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
|
+
|
197
|
+
Access to the snapshots page is restricted to only those who can see the speed badge on their own requests, see the section below this one about access control.
|
198
|
+
|
199
|
+
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
|
+
|
201
|
+
#### Snapshots Transporter
|
202
|
+
|
203
|
+
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.
|
204
|
+
|
205
|
+
The transporter uses a buffer to temporarily hold snapshots in memory with a limit of 100 snapshots. Every 10 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.
|
206
|
+
|
207
|
+
The body of the requests made by the transporter 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).
|
208
|
+
|
209
|
+
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.
|
210
|
+
|
189
211
|
## Access control in non-development environments
|
190
212
|
|
191
213
|
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.
|
@@ -329,6 +351,15 @@ _Note:_ The GUID (`data-version` and the `?v=` parameter on the `src`) will chan
|
|
329
351
|
#### Using MiniProfiler's built in route for apps without HTML responses
|
330
352
|
MiniProfiler also ships with a `/rack-mini-profiler/requests` route that displays the speed badge on a blank HTML page. This can be useful when profiling an application that does not render HTML.
|
331
353
|
|
354
|
+
#### Register MiniProfiler's assets in the Rails assets pipeline
|
355
|
+
MiniProfiler can be configured so it registers its assets in the assets pipeline. To do that, you'll need to provide a lambda (or proc) to the `assets_url` config (see the below section). The callback will receive 3 arguments which are: `name` represents asset name (currently it's either `rack-mini-profiling.js` or `rack-mini-profiling.css`), `assets_version` is a 32 characters long hash of MiniProfiler's assets, and `env` which is the `env` object of the request. MiniProfiler expects the `assets_url` callback to return a URL from which the asset can be loaded (the return value will be used as a `href`/`src` attribute in the DOM). If the `assets_url` callback is not set (the default) or it returns a non-truthy value, MiniProfiler will fallback to loading assets from its own middleware (`/mini-profiler-resources/*`). The following callback should work for most applications:
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
Rack::MiniProfiler.config.assets_url = ->(name, version, env) {
|
359
|
+
ActionController::Base.helpers.asset_path(name)
|
360
|
+
}
|
361
|
+
```
|
362
|
+
|
332
363
|
### Configuration Options
|
333
364
|
|
334
365
|
You can set configuration options using the configuration accessor on `Rack::MiniProfiler`.
|
@@ -344,7 +375,7 @@ Option|Default|Description
|
|
344
375
|
-------|---|--------
|
345
376
|
pre_authorize_cb|Rails: dev only<br>Rack: always on|A lambda callback that returns true to make mini_profiler visible on a given request.
|
346
377
|
position|`'top-left'`|Display mini_profiler on `'top-right'`, `'top-left'`, `'bottom-right'` or `'bottom-left'`.
|
347
|
-
skip_paths|`[]`|
|
378
|
+
skip_paths|`[]`|An array of paths that skip profiling. Both `String` and `Regexp` are acceptable in the array.
|
348
379
|
skip_schema_queries|Rails dev: `true`<br>Othwerwise: `false`|`true` to skip schema queries.
|
349
380
|
auto_inject|`true`|`true` to inject the miniprofiler script in the page.
|
350
381
|
backtrace_ignores|`[]`|Regexes of lines to be removed from backtraces.
|
@@ -360,6 +391,13 @@ max_traces_to_show|20|Maximum number of mini profiler timing blocks to show on o
|
|
360
391
|
html_container|`body`|The HTML container (as a jQuery selector) to inject the mini_profiler UI into
|
361
392
|
show_total_sql_count|`false`|Displays the total number of SQL executions.
|
362
393
|
enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords.
|
394
|
+
assets_url|`nil`|See the "Register MiniProfiler's assets in the Rails assets pipeline" section above.
|
395
|
+
snapshot_every_n_requests|`-1`|Determines how frequently snapshots are taken. See the "Snapshots Sampling" above for more details.
|
396
|
+
snapshots_limit|`1000`|Determines how many snapshots Mini Profiler is allowed to keep.
|
397
|
+
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.
|
398
|
+
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.
|
399
|
+
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.
|
400
|
+
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.
|
363
401
|
|
364
402
|
### Using MiniProfiler with `Rack::Deflate` middleware
|
365
403
|
|
data/lib/html/includes.css
CHANGED
@@ -1,9 +1,20 @@
|
|
1
1
|
@charset "UTF-8";
|
2
|
+
.mp-snapshots,
|
2
3
|
.profiler-result,
|
3
4
|
.profiler-queries {
|
4
5
|
color: #555;
|
5
6
|
line-height: 1;
|
6
7
|
font-size: 12px; }
|
8
|
+
.mp-snapshots pre,
|
9
|
+
.mp-snapshots code,
|
10
|
+
.mp-snapshots label,
|
11
|
+
.mp-snapshots table,
|
12
|
+
.mp-snapshots tbody,
|
13
|
+
.mp-snapshots thead,
|
14
|
+
.mp-snapshots tfoot,
|
15
|
+
.mp-snapshots tr,
|
16
|
+
.mp-snapshots th,
|
17
|
+
.mp-snapshots td,
|
7
18
|
.profiler-result pre,
|
8
19
|
.profiler-result code,
|
9
20
|
.profiler-result label,
|
@@ -33,27 +44,40 @@
|
|
33
44
|
background-color: transparent;
|
34
45
|
overflow: visible;
|
35
46
|
max-height: none; }
|
47
|
+
.mp-snapshots table,
|
36
48
|
.profiler-result table,
|
37
49
|
.profiler-queries table {
|
38
50
|
border-collapse: collapse;
|
39
51
|
border-spacing: 0; }
|
52
|
+
.mp-snapshots a,
|
53
|
+
.mp-snapshots a:hover,
|
40
54
|
.profiler-result a,
|
41
55
|
.profiler-result a:hover,
|
42
56
|
.profiler-queries a,
|
43
57
|
.profiler-queries a:hover {
|
44
58
|
cursor: pointer;
|
45
59
|
color: #0077cc; }
|
60
|
+
.mp-snapshots a,
|
46
61
|
.profiler-result a,
|
47
62
|
.profiler-queries a {
|
48
63
|
text-decoration: none; }
|
64
|
+
.mp-snapshots a:hover,
|
49
65
|
.profiler-result a:hover,
|
50
66
|
.profiler-queries a:hover {
|
51
67
|
text-decoration: underline; }
|
68
|
+
.mp-snapshots .custom-fields-title,
|
69
|
+
.profiler-result .custom-fields-title,
|
70
|
+
.profiler-queries .custom-fields-title {
|
71
|
+
color: #555;
|
72
|
+
font: Helvetica, Arial, sans-serif;
|
73
|
+
font-size: 14px; }
|
52
74
|
|
53
75
|
.profiler-result {
|
54
76
|
font-family: Helvetica, Arial, sans-serif; }
|
55
77
|
.profiler-result .profiler-toggle-duration-with-children {
|
56
78
|
float: right; }
|
79
|
+
.profiler-result .profiler-snapshots-page-link {
|
80
|
+
float: left; }
|
57
81
|
.profiler-result table.profiler-client-timings {
|
58
82
|
margin-top: 10px; }
|
59
83
|
.profiler-result .profiler-label {
|
@@ -402,3 +426,17 @@
|
|
402
426
|
background: #ffffbb; }
|
403
427
|
100% {
|
404
428
|
background: #fff; } }
|
429
|
+
|
430
|
+
.mp-snapshots {
|
431
|
+
font-family: Helvetica, Arial, sans-serif;
|
432
|
+
font-size: 16px; }
|
433
|
+
.mp-snapshots .snapshots-table thead {
|
434
|
+
background: #6a737c;
|
435
|
+
color: #ffffff; }
|
436
|
+
.mp-snapshots .snapshots-table th, .mp-snapshots .snapshots-table td {
|
437
|
+
padding: 5px 10px;
|
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; }
|
441
|
+
.mp-snapshots .snapshots-table th {
|
442
|
+
border-right: 1px solid #ffffff; }
|
data/lib/html/includes.js
CHANGED
@@ -673,6 +673,32 @@ var MiniProfiler = (function() {
|
|
673
673
|
});
|
674
674
|
};
|
675
675
|
|
676
|
+
var initSnapshots = function initSnapshots(dataElement) {
|
677
|
+
var data = JSON.parse(dataElement.textContent);
|
678
|
+
var temp = document.createElement("DIV");
|
679
|
+
if (data.page === "overview") {
|
680
|
+
temp.innerHTML = MiniProfiler.templates.snapshotsGroupsList(data);
|
681
|
+
} else if (data.group_name) {
|
682
|
+
var allCustomFieldsNames = [];
|
683
|
+
data.list.forEach(function (snapshot) {
|
684
|
+
Object.keys(snapshot.custom_fields).forEach(function (k) {
|
685
|
+
if (allCustomFieldsNames.indexOf(k) === -1 &&
|
686
|
+
options.hiddenCustomFields.indexOf(k.toLowerCase()) === -1) {
|
687
|
+
allCustomFieldsNames.push(k);
|
688
|
+
}
|
689
|
+
});
|
690
|
+
});
|
691
|
+
allCustomFieldsNames.sort();
|
692
|
+
temp.innerHTML = MiniProfiler.templates.snapshotsList({
|
693
|
+
data: data,
|
694
|
+
allCustomFieldsNames: allCustomFieldsNames
|
695
|
+
});
|
696
|
+
}
|
697
|
+
Array.from(temp.children).forEach(function (child) {
|
698
|
+
document.body.appendChild(child);
|
699
|
+
});
|
700
|
+
};
|
701
|
+
|
676
702
|
var initControls = function initControls(container) {
|
677
703
|
if (options.showControls) {
|
678
704
|
var _controls = document.createElement("div");
|
@@ -952,7 +978,7 @@ var MiniProfiler = (function() {
|
|
952
978
|
var script = document.getElementById("mini-profiler");
|
953
979
|
if (!script || !script.getAttribute) return;
|
954
980
|
|
955
|
-
options = (function() {
|
981
|
+
this.options = options = (function() {
|
956
982
|
var version = script.getAttribute("data-version");
|
957
983
|
var path = script.getAttribute("data-path");
|
958
984
|
var currentId = script.getAttribute("data-current-id");
|
@@ -980,6 +1006,8 @@ var MiniProfiler = (function() {
|
|
980
1006
|
script.getAttribute("data-start-hidden") === "true" ||
|
981
1007
|
sessionStorage["rack-mini-profiler-start-hidden"] === "true";
|
982
1008
|
var htmlContainer = script.getAttribute("data-html-container");
|
1009
|
+
var cssUrl = script.getAttribute("data-css-url");
|
1010
|
+
var hiddenCustomFields = script.getAttribute("data-hidden-custom-fields").toLowerCase().split(",");
|
983
1011
|
return {
|
984
1012
|
ids: ids,
|
985
1013
|
path: path,
|
@@ -996,11 +1024,19 @@ var MiniProfiler = (function() {
|
|
996
1024
|
toggleShortcut: toggleShortcut,
|
997
1025
|
startHidden: startHidden,
|
998
1026
|
collapseResults: collapseResults,
|
999
|
-
htmlContainer: htmlContainer
|
1027
|
+
htmlContainer: htmlContainer,
|
1028
|
+
cssUrl: cssUrl,
|
1029
|
+
hiddenCustomFields: hiddenCustomFields
|
1000
1030
|
};
|
1001
1031
|
})();
|
1002
1032
|
|
1003
1033
|
var doInit = function doInit() {
|
1034
|
+
var snapshotsElement = document.getElementById("snapshots-data");
|
1035
|
+
if (snapshotsElement != null) {
|
1036
|
+
initSnapshots(snapshotsElement);
|
1037
|
+
return;
|
1038
|
+
}
|
1039
|
+
|
1004
1040
|
// when rendering a shared, full page, this div will exist
|
1005
1041
|
container = document.querySelectorAll(".profiler-result-full");
|
1006
1042
|
|
@@ -1056,7 +1092,7 @@ var MiniProfiler = (function() {
|
|
1056
1092
|
|
1057
1093
|
var init = function init() {
|
1058
1094
|
if (options.authorized) {
|
1059
|
-
var url = options.
|
1095
|
+
var url = options.cssUrl;
|
1060
1096
|
|
1061
1097
|
if (document.createStyleSheet) {
|
1062
1098
|
document.createStyleSheet(url);
|
@@ -1394,8 +1430,34 @@ var MiniProfiler = (function() {
|
|
1394
1430
|
},
|
1395
1431
|
showTotalSqlCount: function showTotalSqlCount() {
|
1396
1432
|
return options.showTotalSqlCount;
|
1433
|
+
},
|
1434
|
+
timestampToRelative: function timestampToRelative(timestamp) {
|
1435
|
+
var now = Math.round((new Date()).getTime() / 1000);
|
1436
|
+
timestamp = Math.round(timestamp / 1000);
|
1437
|
+
var diff = now - timestamp;
|
1438
|
+
if (diff < 60) {
|
1439
|
+
return "< 1 minute";
|
1440
|
+
}
|
1441
|
+
var buildDisplayTime = function buildDisplayTime(num, unit) {
|
1442
|
+
var res = num + " " + unit;
|
1443
|
+
if (num !== 1) {
|
1444
|
+
res += "s";
|
1445
|
+
}
|
1446
|
+
return res;
|
1447
|
+
}
|
1448
|
+
diff = Math.round(diff / 60);
|
1449
|
+
if (diff <= 60) {
|
1450
|
+
return buildDisplayTime(diff, "minute");
|
1451
|
+
}
|
1452
|
+
diff = Math.round(diff / 60);
|
1453
|
+
if (diff <= 24) {
|
1454
|
+
return buildDisplayTime(diff, "hour");
|
1455
|
+
}
|
1456
|
+
diff = Math.round(diff / 24);
|
1457
|
+
return buildDisplayTime(diff, "day");
|
1397
1458
|
}
|
1398
1459
|
};
|
1399
1460
|
})();
|
1400
1461
|
|
1462
|
+
window.MiniProfiler = MiniProfiler;
|
1401
1463
|
MiniProfiler.init();
|
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,25 @@ $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 10px;
|
641
|
+
box-sizing: border-box;
|
642
|
+
&:not(.request-group) {
|
643
|
+
text-align: center;
|
644
|
+
}
|
645
|
+
}
|
646
|
+
th {
|
647
|
+
border-right: 1px solid #ffffff;
|
648
|
+
}
|
649
|
+
}
|
650
|
+
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
<span class="profiler-name">
|
18
18
|
{{= it.name}} <span class="profiler-overall-duration">({{= MiniProfiler.formatDuration(it.duration_milliseconds)}} ms)</span>
|
19
19
|
</span>
|
20
|
-
<span class="profiler-server-time">{{= it.machine_name}} on {{= MiniProfiler.renderDate(it.
|
20
|
+
<span class="profiler-server-time">{{= it.machine_name}} on {{= MiniProfiler.renderDate(it.started_formatted)}}</span>
|
21
21
|
</div>
|
22
22
|
<div class="profiler-output">
|
23
23
|
<table class="profiler-timings">
|
@@ -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
|
|
@@ -200,7 +217,11 @@
|
|
200
217
|
<td>
|
201
218
|
<div class="query">
|
202
219
|
<pre class="profiler-stack-trace">{{= it.s.stack_trace_snippet }}</pre>
|
203
|
-
|
220
|
+
{{? it.s.formatted_command_string}}
|
221
|
+
<pre class="prettyprint lang-sql"><code>{{= it.s.formatted_command_string }}; {{= MiniProfiler.formatParameters(it.s.parameters) }}</code></pre>
|
222
|
+
{{??}}
|
223
|
+
<i>Query redacted</i>
|
224
|
+
{{?}}
|
204
225
|
</div>
|
205
226
|
</td>
|
206
227
|
</tr>
|
@@ -216,3 +237,72 @@
|
|
216
237
|
</td>
|
217
238
|
</tr>
|
218
239
|
</script>
|
240
|
+
|
241
|
+
<script id="snapshotsGroupsList" type="text/x-dot-tmpl">
|
242
|
+
{{? it.list && it.list.length }}
|
243
|
+
<table class="snapshots-table">
|
244
|
+
<thead>
|
245
|
+
<tr>
|
246
|
+
<th>Requests Group</th>
|
247
|
+
<th>Worst Time (ms)</th>
|
248
|
+
<th>Best Time (ms)</th>
|
249
|
+
<th>No. of Snapshots</th>
|
250
|
+
</tr>
|
251
|
+
</thead>
|
252
|
+
<tbody>
|
253
|
+
{{~ it.list :row}}
|
254
|
+
<tr>
|
255
|
+
<td class="request-group"><a href="{{= row.url }}">{{= row.name }}</a></td>
|
256
|
+
<td>{{= MiniProfiler.formatDuration(row.worst_score) }}</td>
|
257
|
+
<td>{{= MiniProfiler.formatDuration(row.best_score) }}</td>
|
258
|
+
<td>{{= row.snapshots_count }}</td>
|
259
|
+
</tr>
|
260
|
+
{{~}}
|
261
|
+
</tbody>
|
262
|
+
</table>
|
263
|
+
{{??}}
|
264
|
+
<h2>No snapshots exist</h2>
|
265
|
+
{{?}}
|
266
|
+
</script>
|
267
|
+
|
268
|
+
<script id="snapshotsList" type="text/x-dot-tmpl">
|
269
|
+
{{ var data = it.data; }}
|
270
|
+
{{ var customFieldsNames = it.allCustomFieldsNames; }}
|
271
|
+
{{? data.list && data.list.length }}
|
272
|
+
<h2>Snapshots for {{= data.group_name }}</h2>
|
273
|
+
<table class="snapshots-table">
|
274
|
+
<thead>
|
275
|
+
<tr>
|
276
|
+
<th>ID</th>
|
277
|
+
<th>Duration (ms)</th>
|
278
|
+
<th>SQL Count</th>
|
279
|
+
{{~ customFieldsNames :name }}
|
280
|
+
<th>{{= name }}</th>
|
281
|
+
{{~}}
|
282
|
+
<th>Age</th>
|
283
|
+
</tr>
|
284
|
+
</thead>
|
285
|
+
<tbody>
|
286
|
+
{{~ data.list :row}}
|
287
|
+
<tr>
|
288
|
+
<td><a href="{{= row.url }}">
|
289
|
+
{{= row.id }}
|
290
|
+
</a></td>
|
291
|
+
<td>{{= MiniProfiler.formatDuration(row.duration) }}</td>
|
292
|
+
<td>{{= row.sql_count }}</td>
|
293
|
+
{{~ customFieldsNames :name }}
|
294
|
+
<td>{{= row.custom_fields[name] || "" }}</td>
|
295
|
+
{{~}}
|
296
|
+
<td>
|
297
|
+
{{? row.timestamp }}
|
298
|
+
{{= MiniProfiler.timestampToRelative(row.timestamp) }}
|
299
|
+
{{?}}
|
300
|
+
</td>
|
301
|
+
</tr>
|
302
|
+
{{~}}
|
303
|
+
</tbody>
|
304
|
+
</table>
|
305
|
+
{{??}}
|
306
|
+
<h2>No snapshots for {{= data.group_name }}</h2>
|
307
|
+
{{?}}
|
308
|
+
</script>
|