rack-mini-profiler 3.3.0 → 4.0.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 +17 -0
- data/README.md +18 -3
- data/lib/html/includes.css +45 -11
- data/lib/html/includes.js +8 -3
- data/lib/html/includes.scss +73 -17
- data/lib/html/includes.tmpl +51 -29
- data/lib/html/vendor.js +2 -2
- data/lib/mini_profiler/actions.rb +10 -5
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/config.rb +2 -0
- data/lib/mini_profiler/profiling_methods.rb +3 -2
- data/lib/mini_profiler/snapshots_transporter.rb +2 -0
- data/lib/mini_profiler/storage/file_store.rb +0 -2
- data/lib/mini_profiler/storage/memcache_store.rb +1 -1
- data/lib/mini_profiler/timer_struct/base.rb +0 -1
- data/lib/mini_profiler/timer_struct/page.rb +11 -3
- data/lib/mini_profiler/timer_struct/request.rb +3 -2
- data/lib/mini_profiler/timer_struct/sql.rb +4 -1
- data/lib/mini_profiler/version.rb +2 -2
- data/lib/mini_profiler/views.rb +10 -5
- data/lib/mini_profiler.rb +10 -5
- data/lib/mini_profiler_rails/railtie.rb +2 -1
- data/lib/patches/db/pg/alias_method.rb +121 -0
- data/lib/patches/db/pg/prepend.rb +115 -0
- data/lib/patches/db/pg.rb +4 -119
- data/lib/prepend_pg_patch.rb +5 -0
- data/rack-mini-profiler.gemspec +3 -3
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb35c9ff911dd9dcc3fc5cccf15d62f21f0c04ed79ff5dec3e88746fcb90a43f
|
4
|
+
data.tar.gz: a8cd39cdb11b4bb2ed154e9c3269de66b6f9c351c8cdcdb775e73e0c25e5d5a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c1123a2fc22b5f20ea63fa089b4914f214a6da3d88dbba2fe1b3f597c92c653a166dc1c97d8a68e51a71893b9787ca11b99db1a68aa19da04cad494d8f33af9
|
7
|
+
data.tar.gz: b21baa7055d5252cf3f57031be17e67810d898e4b40428d12409027454bfa15b414e2346f07e49d09b139945eb177f8951a34f41c765773ac543c49c2f0efd81
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 4.0 - 2025-06-11
|
4
|
+
|
5
|
+
- [BREAKING CHANGE] Ruby version 3.1.0 or later is required. [#632](https://github.com/MiniProfiler/rack-mini-profiler/pull/632)
|
6
|
+
- [FEATURE] Implement prepend patch for postgres [#625](https://github.com/MiniProfiler/rack-mini-profiler/pull/625)
|
7
|
+
- [FEATURE] Show Active Record QueryCache hits in UI. [#640](https://github.com/MiniProfiler/rack-mini-profiler/pull/640)
|
8
|
+
- [FEATURE] Show record type and count in SQL query UI. [#638](https://github.com/MiniProfiler/rack-mini-profiler/pull/638)
|
9
|
+
- [FIX] Requests page fails due to trying to modify a frozen string. [#630](https://github.com/MiniProfiler/rack-mini-profiler/pull/630)
|
10
|
+
- [FIX] alignment of SQL query start times. [#627](https://github.com/MiniProfiler/rack-mini-profiler/pull/627)
|
11
|
+
- [FIX] Lower case HTTP response headers to be compatible with Rack 3 [#628](https://github.com/MiniProfiler/rack-mini-profiler/pull/628)
|
12
|
+
- [FIX] Truncate long profiler name in profiler popup. [#634](https://github.com/MiniProfiler/rack-mini-profiler/pull/634)
|
13
|
+
- [FIX] `flamegraph_mode` query param having no effect. [#635](https://github.com/MiniProfiler/rack-mini-profiler/pull/635)
|
14
|
+
- [FIX] max_traces_to_show had chance to break the profiler frontend [#297](https://github.com/MiniProfiler/rack-mini-profiler/issues/297)
|
15
|
+
|
16
|
+
## 3.3.1 - 2024-02-15
|
17
|
+
- [FEATURE] Support dynamic `config.content_security_policy_nonce` [#609](https://github.com/MiniProfiler/rack-mini-profiler/pull/609)
|
18
|
+
- [FEATURE] Add flamgraph path to response header: [#601](https://github.com/MiniProfiler/rack-mini-profiler/pull/601)
|
19
|
+
|
3
20
|
## 3.3.0 - 2023-12-07
|
4
21
|
- [FEATURE] Use `?pp=flamegraph?ignore_gc=true` or `config.flamegraph_ignore_gc` to ignore gc in flamegraphs. [#599](https://github.com/MiniProfiler/rack-mini-profiler/pull/599)
|
5
22
|
|
data/README.md
CHANGED
@@ -90,6 +90,20 @@ gem 'rack-mini-profiler', require: ['prepend_mysql2_patch', 'rack-mini-profiler'
|
|
90
90
|
|
91
91
|
This should not be necessary with Rails < 5 because peek-mysql2 hooks into mysql2 gem in different ways depending on your Rails version.
|
92
92
|
|
93
|
+
#### `pg` stack level too deep errors
|
94
|
+
|
95
|
+
If you encounter `SystemStackError (stack level too deep)` from PG, you'll need to use this gem spec in your Gemfile:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
gem 'rack-mini-profiler', require: ['prepend_pg_patch', 'rack-mini-profiler']
|
99
|
+
```
|
100
|
+
|
101
|
+
Or if you initially have `require: false`, then use
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
gem 'rack-mini-profiler', require: ['prepend_pg_patch']
|
105
|
+
```
|
106
|
+
|
93
107
|
#### Rails and manual initialization
|
94
108
|
|
95
109
|
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:
|
@@ -180,7 +194,7 @@ To generate [flamegraphs](http://samsaffron.com/archive/2013/03/19/flame-graphs-
|
|
180
194
|
|
181
195
|
Then, to view the flamegraph as a direct HTML response from your request, just visit any page in your app with `?pp=flamegraph` appended to the URL, or add the header `X-Rack-Mini-Profiler` to the request with the value `flamegraph`.
|
182
196
|
|
183
|
-
Conversely, if you want your regular response instead (which is specially useful for JSON and/or XHR requests), just append the `?pp=async-flamegraph` parameter to your request/fetch URL; the request will then return as normal, and the flamegraph data will be stored for later *async* viewing, both for this request and for all subsequent requests made by this page (based on the `REFERER` header). For viewing these async flamegraphs, use the 'flamegraph' link that will appear inside the MiniProfiler UI for these requests.
|
197
|
+
Conversely, if you want your regular response instead (which is specially useful for JSON and/or XHR requests), just append the `?pp=async-flamegraph` parameter to your request/fetch URL; the request will then return as normal, and the flamegraph data will be stored for later *async* viewing, both for this request and for all subsequent requests made by this page (based on the `REFERER` header). For viewing these async flamegraphs, use the 'flamegraph' link that will appear inside the MiniProfiler UI for these requests or path returned in the `X-MiniProfiler-Flamegraph-Path` header.
|
184
198
|
|
185
199
|
Note: Mini Profiler will not record SQL timings for a request if it asks for a flamegraph. The rationale behind this is to keep
|
186
200
|
Mini Profiler's methods that are responsible for generating the timings data out of the flamegraph.
|
@@ -245,6 +259,7 @@ rack-mini-profiler is designed with production profiling in mind. To enable that
|
|
245
259
|
end
|
246
260
|
```
|
247
261
|
|
262
|
+
> [!WARNING]
|
248
263
|
> If your production application is running on more than one server (or more than one dyno) you will need to configure rack mini profiler's storage to use Redis or Memcache. See [storage](#storage) for information on changing the storage backend.
|
249
264
|
|
250
265
|
Note:
|
@@ -448,7 +463,7 @@ snapshots_transport_destination_url | `nil`
|
|
448
463
|
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.
|
449
464
|
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.
|
450
465
|
snapshots_transport_gzip_requests | `false` | Make the snapshots transporter gzip the requests it makes to `snapshots_transport_destination_url`.
|
451
|
-
content_security_policy_nonce | Rails: Current nonce<br>Rack: nil | Set the content security policy nonce to use when inserting MiniProfiler's script block.
|
466
|
+
content_security_policy_nonce | Rails: Current nonce<br>Rack: nil | Set the content security policy nonce to use when inserting MiniProfiler's script block. Can be set to a static string, or a Proc which receives `env` and `response_headers` as arguments and returns the nonce.
|
452
467
|
enable_hotwire_turbo_drive_support | `false` | Enable support for Hotwire TurboDrive page transitions.
|
453
468
|
profile_parameter | `'pp'` | The query parameter used to interact with this gem.
|
454
469
|
|
@@ -466,7 +481,7 @@ If you are using Heroku Redis, you may need to add the following to your `config
|
|
466
481
|
|
467
482
|
```ruby
|
468
483
|
if Rails.env.production?
|
469
|
-
Rack::MiniProfiler.config.storage_options = {
|
484
|
+
Rack::MiniProfiler.config.storage_options = {
|
470
485
|
url: ENV["REDIS_URL"],
|
471
486
|
ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
|
472
487
|
}
|
data/lib/html/includes.css
CHANGED
@@ -105,13 +105,16 @@
|
|
105
105
|
.profiler-result .profiler-unit {
|
106
106
|
font-family: Consolas, monospace, serif; }
|
107
107
|
.profiler-result .profiler-number {
|
108
|
-
color: #111;
|
108
|
+
color: #111;
|
109
|
+
display: inline-block; }
|
109
110
|
.profiler-result .profiler-info {
|
110
111
|
text-align: right; }
|
111
112
|
.profiler-result .profiler-info .profiler-name {
|
112
113
|
float: left; }
|
113
114
|
.profiler-result .profiler-info .profiler-server-time {
|
114
115
|
white-space: nowrap; }
|
116
|
+
.profiler-result .profiler-info .profiler-number {
|
117
|
+
display: block; }
|
115
118
|
.profiler-result .profiler-timings th {
|
116
119
|
background-color: #fff;
|
117
120
|
color: #767676;
|
@@ -132,19 +135,21 @@
|
|
132
135
|
.profiler-result .profiler-timings .profiler-queries-duration {
|
133
136
|
padding-left: 6px; }
|
134
137
|
.profiler-result .profiler-timings .profiler-percent-in-sql {
|
135
|
-
white-space: nowrap;
|
136
|
-
|
137
|
-
.profiler-result .profiler-timings tfoot td {
|
138
|
-
padding-top: 10px;
|
138
|
+
white-space: nowrap; }
|
139
|
+
.profiler-result .profiler-timings tfoot tr td:last-child {
|
139
140
|
text-align: right; }
|
140
|
-
|
141
|
+
.profiler-result .profiler-timings-summary {
|
142
|
+
display: flex;
|
143
|
+
justify-content: space-between;
|
144
|
+
padding-top: 10px; }
|
145
|
+
.profiler-result .profiler-timings-summary a {
|
141
146
|
font-size: 95%;
|
142
147
|
display: inline-block;
|
143
148
|
margin-left: 12px; }
|
144
|
-
.profiler-result .profiler-timings
|
149
|
+
.profiler-result .profiler-timings-summary a:first-child {
|
145
150
|
float: left;
|
146
151
|
margin-left: 0px; }
|
147
|
-
.profiler-result .profiler-timings
|
152
|
+
.profiler-result .profiler-timings-summary a.profiler-custom-link {
|
148
153
|
float: left; }
|
149
154
|
.profiler-result .profiler-queries {
|
150
155
|
font-family: Helvetica, Arial, sans-serif; }
|
@@ -162,6 +167,12 @@
|
|
162
167
|
background-color: #fdd; }
|
163
168
|
.profiler-result .profiler-queries tr.very-very-slow {
|
164
169
|
background-color: #fcc; }
|
170
|
+
.profiler-result .profiler-queries tr.cached {
|
171
|
+
background-color: #f2f0ef; }
|
172
|
+
.profiler-result .profiler-queries span.cached {
|
173
|
+
color: #818589; }
|
174
|
+
.profiler-result .profiler-queries span.cached + pre {
|
175
|
+
display: inline; }
|
165
176
|
.profiler-result .profiler-queries pre {
|
166
177
|
font-family: Consolas, monospace, serif;
|
167
178
|
white-space: pre-wrap; }
|
@@ -235,6 +246,19 @@
|
|
235
246
|
.profiler-results {
|
236
247
|
z-index: 2147483643;
|
237
248
|
position: fixed; }
|
249
|
+
.profiler-results.profiler-left .profiler-button {
|
250
|
+
text-align: left; }
|
251
|
+
.profiler-results.profiler-right .profiler-button {
|
252
|
+
text-align: right; }
|
253
|
+
.profiler-results .profiler-button > .profiler-duration-milliseconds {
|
254
|
+
min-width: 70px; }
|
255
|
+
.profiler-results .profiler-button > .profiler-sql-count {
|
256
|
+
min-width: 55px; }
|
257
|
+
.profiler-results .profiler-button > .profiler-name {
|
258
|
+
min-width: 190px;
|
259
|
+
margin-left: 3px; }
|
260
|
+
.profiler-results .profiler-button > .profiler-number {
|
261
|
+
text-align: right; }
|
238
262
|
.profiler-results.profiler-top {
|
239
263
|
top: 0px; }
|
240
264
|
.profiler-results.profiler-top.profiler-left {
|
@@ -285,8 +309,10 @@
|
|
285
309
|
background-color: maroon; }
|
286
310
|
.profiler-results .profiler-button.profiler-button-active .profiler-number,
|
287
311
|
.profiler-results .profiler-button.profiler-button-active .profiler-nuclear,
|
312
|
+
.profiler-results .profiler-button.profiler-button-active .profiler-name,
|
288
313
|
.profiler-results .profiler-controls.profiler-button-active .profiler-number,
|
289
|
-
.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear
|
314
|
+
.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear,
|
315
|
+
.profiler-results .profiler-controls.profiler-button-active .profiler-name {
|
290
316
|
color: #fff;
|
291
317
|
font-weight: bold; }
|
292
318
|
.profiler-results .profiler-button.profiler-button-active .profiler-unit,
|
@@ -324,14 +350,22 @@
|
|
324
350
|
text-align: left;
|
325
351
|
line-height: 18px;
|
326
352
|
overflow: auto;
|
353
|
+
max-width: 800px;
|
327
354
|
box-shadow: 0px 1px 15px #555; }
|
328
355
|
.profiler-results .profiler-popup .profiler-info {
|
329
356
|
margin-bottom: 3px;
|
330
357
|
padding-bottom: 2px;
|
331
|
-
border-bottom: 1px solid #ddd;
|
358
|
+
border-bottom: 1px solid #ddd;
|
359
|
+
display: flex;
|
360
|
+
width: inherit; }
|
332
361
|
.profiler-results .profiler-popup .profiler-info .profiler-name {
|
362
|
+
overflow: hidden;
|
363
|
+
text-overflow: ellipsis;
|
364
|
+
text-align: left;
|
365
|
+
white-space: nowrap;
|
333
366
|
font-size: 110%;
|
334
|
-
font-weight: bold;
|
367
|
+
font-weight: bold;
|
368
|
+
padding-right: 10px; }
|
335
369
|
.profiler-results .profiler-popup .profiler-info .profiler-name .profiler-overall-duration {
|
336
370
|
display: none; }
|
337
371
|
.profiler-results .profiler-popup .profiler-info .profiler-server-time {
|
data/lib/html/includes.js
CHANGED
@@ -220,7 +220,7 @@ var _MiniProfiler = (function() {
|
|
220
220
|
totalSqlCount += parseInt(json.sql_count);
|
221
221
|
reqs++;
|
222
222
|
|
223
|
-
if (!controls &&
|
223
|
+
if (!controls && options.collapseResults && !expandedResults) {
|
224
224
|
if (!totalsControl) {
|
225
225
|
toArray(container.querySelectorAll(".profiler-result")).forEach(
|
226
226
|
function(el) {
|
@@ -285,10 +285,10 @@ var _MiniProfiler = (function() {
|
|
285
285
|
}); // limit count
|
286
286
|
|
287
287
|
if (
|
288
|
-
container.querySelectorAll(".profiler-result").length >
|
288
|
+
container.querySelectorAll(".profiler-result:not(:has(.profiler-totals))").length >
|
289
289
|
options.maxTracesToShow
|
290
290
|
) {
|
291
|
-
var elem = container.querySelector(
|
291
|
+
var elem = container.querySelector('.profiler-result:not(:has(.profiler-totals))');
|
292
292
|
|
293
293
|
if (elem) {
|
294
294
|
elem.parentElement.removeChild(elem);
|
@@ -1179,6 +1179,7 @@ var _MiniProfiler = (function() {
|
|
1179
1179
|
|
1180
1180
|
reqs = 0;
|
1181
1181
|
totalTime = 0;
|
1182
|
+
totalSqlCount = 0;
|
1182
1183
|
expandedResults = false;
|
1183
1184
|
toArray(
|
1184
1185
|
document.querySelectorAll(".profiler-results .profiler-result")
|
@@ -1288,6 +1289,10 @@ var _MiniProfiler = (function() {
|
|
1288
1289
|
|
1289
1290
|
sqlTiming.parent_timing_name = timing.name;
|
1290
1291
|
|
1292
|
+
if (sqlTiming.cached) {
|
1293
|
+
sqlTiming.row_class = "cached";
|
1294
|
+
}
|
1295
|
+
|
1291
1296
|
if (sqlTiming.duration_milliseconds > 50) {
|
1292
1297
|
sqlTiming.row_class = "slow";
|
1293
1298
|
}
|
data/lib/html/includes.scss
CHANGED
@@ -103,6 +103,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
103
103
|
|
104
104
|
.profiler-number {
|
105
105
|
color: $numberColor;
|
106
|
+
display: inline-block;
|
106
107
|
}
|
107
108
|
|
108
109
|
.profiler-info {
|
@@ -113,6 +114,9 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
113
114
|
.profiler-server-time {
|
114
115
|
white-space: nowrap;
|
115
116
|
}
|
117
|
+
.profiler-number {
|
118
|
+
display: block;
|
119
|
+
}
|
116
120
|
}
|
117
121
|
|
118
122
|
.profiler-timings {
|
@@ -146,27 +150,28 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
146
150
|
}
|
147
151
|
.profiler-percent-in-sql {
|
148
152
|
white-space: nowrap;
|
153
|
+
}
|
154
|
+
tfoot tr td:last-child {
|
149
155
|
text-align: right;
|
150
156
|
}
|
157
|
+
}
|
151
158
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
159
|
+
.profiler-timings-summary {
|
160
|
+
display: flex;
|
161
|
+
justify-content: space-between;
|
162
|
+
padding-top: 10px;
|
156
163
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
164
|
+
a {
|
165
|
+
font-size: 95%;
|
166
|
+
display: inline-block;
|
167
|
+
margin-left: 12px;
|
161
168
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
}
|
169
|
-
}
|
169
|
+
&:first-child {
|
170
|
+
float: left;
|
171
|
+
margin-left: 0px;
|
172
|
+
}
|
173
|
+
&.profiler-custom-link {
|
174
|
+
float: left;
|
170
175
|
}
|
171
176
|
}
|
172
177
|
}
|
@@ -201,6 +206,18 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
201
206
|
background-color: #fcc;
|
202
207
|
}
|
203
208
|
|
209
|
+
tr.cached {
|
210
|
+
background-color: #f2f0ef;
|
211
|
+
}
|
212
|
+
|
213
|
+
span.cached {
|
214
|
+
color: #818589;
|
215
|
+
}
|
216
|
+
|
217
|
+
span.cached + pre {
|
218
|
+
display: inline;
|
219
|
+
}
|
220
|
+
|
204
221
|
pre {
|
205
222
|
font-family: $codeFonts;
|
206
223
|
white-space: pre-wrap;
|
@@ -317,6 +334,34 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
317
334
|
|
318
335
|
$radius: 10px;
|
319
336
|
|
337
|
+
&.profiler-left {
|
338
|
+
.profiler-button {
|
339
|
+
text-align: left;
|
340
|
+
}
|
341
|
+
}
|
342
|
+
|
343
|
+
&.profiler-right {
|
344
|
+
.profiler-button {
|
345
|
+
text-align: right;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
.profiler-button {
|
350
|
+
> .profiler-duration-milliseconds {
|
351
|
+
min-width: 70px;
|
352
|
+
}
|
353
|
+
> .profiler-sql-count {
|
354
|
+
min-width: 55px;
|
355
|
+
}
|
356
|
+
> .profiler-name {
|
357
|
+
min-width: 190px;
|
358
|
+
margin-left: 3px;
|
359
|
+
}
|
360
|
+
> .profiler-number {
|
361
|
+
text-align: right;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
320
365
|
&.profiler-top {
|
321
366
|
top: 0px;
|
322
367
|
|
@@ -399,7 +444,8 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
399
444
|
background-color: maroon;
|
400
445
|
|
401
446
|
.profiler-number,
|
402
|
-
.profiler-nuclear
|
447
|
+
.profiler-nuclear,
|
448
|
+
.profiler-name {
|
403
449
|
color: #fff;
|
404
450
|
font-weight: bold;
|
405
451
|
}
|
@@ -452,6 +498,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
452
498
|
text-align: left;
|
453
499
|
line-height: 18px;
|
454
500
|
overflow: auto;
|
501
|
+
max-width: 800px;
|
455
502
|
|
456
503
|
@include box-shadow(0px, 1px, 15px, $textColor);
|
457
504
|
|
@@ -459,14 +506,23 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
459
506
|
margin-bottom: 3px;
|
460
507
|
padding-bottom: 2px;
|
461
508
|
border-bottom: 1px solid #ddd;
|
509
|
+
display: flex;
|
510
|
+
width: inherit;
|
462
511
|
|
463
512
|
.profiler-name {
|
513
|
+
overflow: hidden;
|
514
|
+
text-overflow: ellipsis;
|
515
|
+
text-align: left;
|
516
|
+
white-space: nowrap;
|
464
517
|
font-size: 110%;
|
465
518
|
font-weight: bold;
|
519
|
+
padding-right: 10px;
|
520
|
+
|
466
521
|
.profiler-overall-duration {
|
467
522
|
display: none;
|
468
523
|
}
|
469
524
|
}
|
525
|
+
|
470
526
|
.profiler-server-time {
|
471
527
|
font-size: 95%;
|
472
528
|
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -2,20 +2,27 @@
|
|
2
2
|
<div class="profiler-result">
|
3
3
|
<div class="profiler-button {{? it.has_duplicate_sql_timings}}profiler-warning{{?}}">
|
4
4
|
{{? it.has_duplicate_sql_timings}}<span class="profiler-nuclear">!</span>{{?}}
|
5
|
-
<span class="profiler-number">
|
5
|
+
<span class="profiler-number profiler-duration-milliseconds">
|
6
6
|
{{= MiniProfiler.formatDuration(it.duration_milliseconds)}} <span class="profiler-unit">ms</span>
|
7
7
|
</span>
|
8
8
|
{{? MiniProfiler.showTotalSqlCount()}}
|
9
|
-
<span class="profiler-number">
|
9
|
+
<span class="profiler-number profiler-sql-count">
|
10
10
|
{{= it.sql_count}} <span class="profiler-unit">sql</span>
|
11
11
|
</span>
|
12
12
|
{{?}}
|
13
|
+
<span class="profiler-name">
|
14
|
+
{{? it.name.length >= 30 }}
|
15
|
+
{{= it.name.substring(0,15) + "..." + it.name.slice(-15) }}
|
16
|
+
{{??}}
|
17
|
+
{{= it.name}}
|
18
|
+
{{?}}
|
19
|
+
</span>
|
13
20
|
</div>
|
14
21
|
|
15
22
|
<div class="profiler-popup">
|
16
23
|
<div class="profiler-info">
|
17
|
-
<span class="profiler-name">
|
18
|
-
{{= it.name}} <span class="profiler-overall-duration">({{= MiniProfiler.formatDuration(it.duration_milliseconds)}} ms)</span>
|
24
|
+
<span class="profiler-name" title="{{= it.name}}">
|
25
|
+
{{= it.name }} <span class="profiler-overall-duration">({{= MiniProfiler.formatDuration(it.duration_milliseconds)}} ms)</span>
|
19
26
|
</span>
|
20
27
|
<span class="profiler-server-time">{{= it.machine_name}} on {{= MiniProfiler.renderDate(it.started_formatted)}}</span>
|
21
28
|
</div>
|
@@ -38,33 +45,42 @@
|
|
38
45
|
<tbody>
|
39
46
|
{{= MiniProfiler.templates.timingTemplate({timing: it.root, page: it}) }}
|
40
47
|
</tbody>
|
41
|
-
|
42
|
-
<
|
43
|
-
<
|
44
|
-
|
45
|
-
|
46
|
-
{{?}}
|
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>
|
52
|
-
</td>
|
53
|
-
{{? it.has_sql_timings}}
|
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">
|
55
|
-
{{= MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) }}
|
56
|
-
<span class="profiler-unit">% in sql</span>
|
48
|
+
{{? it.has_sql_timings}}
|
49
|
+
<tfoot>
|
50
|
+
<tr>
|
51
|
+
<td colspan="1">
|
52
|
+
SQL Summary:
|
57
53
|
</td>
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
{{= MiniProfiler.formatDuration(it.
|
62
|
-
|
54
|
+
<td colspan="5" title="percent of total request time spent in SQL">
|
55
|
+
{{=it.sql_count}} {{? it.cached_sql_count > 0 }} ({{=it.cached_sql_count}} cached) {{?}}
|
56
|
+
<span class="profiler-unit" title="percent of total request time spent in SQL"> -
|
57
|
+
{{= MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) }}% in sql
|
58
|
+
</span>
|
63
59
|
</td>
|
64
|
-
|
65
|
-
</
|
66
|
-
|
60
|
+
</tr>
|
61
|
+
</tfoot>
|
62
|
+
{{?}}
|
67
63
|
</table>
|
64
|
+
|
65
|
+
<div class="profiler-timings-summary">
|
66
|
+
<div>
|
67
|
+
{{? !it.client_timings}}
|
68
|
+
{{= MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) }}
|
69
|
+
{{?}}
|
70
|
+
<a class="profiler-toggle-duration-with-children" title="toggles column with aggregate child durations">show time with children</a>
|
71
|
+
<a
|
72
|
+
class="profiler-snapshots-page-link"
|
73
|
+
title="Go to snapshots page"
|
74
|
+
href="{{= MiniProfiler.options.path }}snapshots">snapshots</a>
|
75
|
+
</div>
|
76
|
+
{{~ it.custom_timing_names :value}}
|
77
|
+
<div 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">
|
78
|
+
{{= MiniProfiler.formatDuration(it.custom_timing_stats[value].duration / it.duration_milliseconds * 100) }}
|
79
|
+
<span class="profiler-unit">% in {{= value.toLowerCase() }}</span>
|
80
|
+
</div>
|
81
|
+
{{~}}
|
82
|
+
</div>
|
83
|
+
|
68
84
|
{{? it.client_timings}}
|
69
85
|
<table class="profiler-timings profiler-client-timings">
|
70
86
|
<thead>
|
@@ -117,7 +133,7 @@
|
|
117
133
|
<table>
|
118
134
|
<thead>
|
119
135
|
<tr>
|
120
|
-
<th class="ta-right">step<br />time from start<br />query type<br />duration</th>
|
136
|
+
<th class="ta-right">step<br />time from start<br />query type<br />duration<br />records</th>
|
121
137
|
<th class="ta-left">call stack<br />query</th>
|
122
138
|
</tr>
|
123
139
|
</thead>
|
@@ -216,10 +232,16 @@
|
|
216
232
|
{{= MiniProfiler.renderExecuteType(it.s.execute_type) }}
|
217
233
|
</div>
|
218
234
|
<div title="{{? it.s.execute_type == 3}}first result fetched: {{= it.s.first_fetch_duration_milliseconds }}ms{{?}}">{{= MiniProfiler.formatDuration(it.s.duration_milliseconds) }} <span class="profiler-unit">ms</span></div>
|
235
|
+
{{? it.s.row_count > 0 }}
|
236
|
+
<div title="number and type of records instantiated by query">{{= it.s.class_name }}: {{= it.s.row_count }}</div>
|
237
|
+
{{?}}
|
219
238
|
</td>
|
220
239
|
<td>
|
221
240
|
<div class="query">
|
222
241
|
<pre class="profiler-stack-trace">{{= it.s.stack_trace_snippet }}</pre>
|
242
|
+
{{? it.s.cached }}
|
243
|
+
<span class="cached"> [CACHE] </span>
|
244
|
+
{{?}}
|
223
245
|
{{? it.s.formatted_command_string}}
|
224
246
|
<pre class="prettyprint lang-sql"><code>{{= it.s.formatted_command_string }}; {{= MiniProfiler.formatParameters(it.s.parameters) }}</code></pre>
|
225
247
|
{{??}}
|
data/lib/html/vendor.js
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
MiniProfiler.templates = {};
|
8
8
|
MiniProfiler.templates["profilerTemplate"] = function anonymous(it
|
9
9
|
) {
|
10
|
-
var out=' <div class="profiler-result"> <div class="profiler-button ';if(it.has_duplicate_sql_timings){out+='profiler-warning';}out+='"> ';if(it.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' <span class="profiler-number"> '+( MiniProfiler.formatDuration(it.duration_milliseconds))+' <span class="profiler-unit">ms</span> </span> ';if(MiniProfiler.showTotalSqlCount()){out+=' <span class="profiler-number"> '+( it.sql_count)+' <span class="profiler-unit">sql</span> </span> ';}out+=' </div> <div class="profiler-popup"> <div class="profiler-info"> <span class="profiler-name"
|
10
|
+
var out=' <div class="profiler-result"> <div class="profiler-button ';if(it.has_duplicate_sql_timings){out+='profiler-warning';}out+='"> ';if(it.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' <span class="profiler-number profiler-duration-milliseconds"> '+( MiniProfiler.formatDuration(it.duration_milliseconds))+' <span class="profiler-unit">ms</span> </span> ';if(MiniProfiler.showTotalSqlCount()){out+=' <span class="profiler-number profiler-sql-count"> '+( it.sql_count)+' <span class="profiler-unit">sql</span> </span> ';}out+=' <span class="profiler-name"> ';if(it.name.length >= 30){out+=' '+( it.name.substring(0,15) + "..." + it.name.slice(-15) )+' ';}else{out+=' '+( it.name)+' ';}out+=' </span> </div> <div class="profiler-popup"> <div class="profiler-info"> <span class="profiler-name" title="'+( it.name)+'"> '+( it.name )+' <span class="profiler-overall-duration">('+( MiniProfiler.formatDuration(it.duration_milliseconds))+' ms)</span> </span> <span class="profiler-server-time">'+( it.machine_name)+' on '+( MiniProfiler.renderDate(it.started_formatted))+'</span> </div> <div class="profiler-output"> <table class="profiler-timings"> <thead> <tr> <th>event</th> <th>duration (ms)</th> <th class="profiler-duration-with-children">with children (ms)</th> <th class="time-from-start">from start (ms)</th> ';if(it.has_sql_timings){out+=' <th colspan="2">query time (ms)</th> ';}out+=' ';var arr1=it.custom_timing_names;if(arr1){var value,i1=-1,l1=arr1.length-1;while(i1<l1){value=arr1[i1+=1];out+=' <th colspan="2">'+( value.toLowerCase() )+' (ms)</th> ';} } out+=' </tr> </thead> <tbody> '+( MiniProfiler.templates.timingTemplate({timing: it.root, page: it}) )+' </tbody> ';if(it.has_sql_timings){out+=' <tfoot> <tr> <td colspan="1"> SQL Summary: </td> <td colspan="5" title="percent of total request time spent in SQL"> '+(it.sql_count)+' ';if(it.cached_sql_count > 0){out+=' ('+(it.cached_sql_count)+' cached) ';}out+=' <span class="profiler-unit" title="percent of total request time spent in SQL"> - '+( MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) )+'% in sql </span> </td> </tr> </tfoot> ';}out+=' </table> <div class="profiler-timings-summary"> <div> ';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> </div> ';var arr2=it.custom_timing_names;if(arr2){var value,i2=-1,l2=arr2.length-1;while(i2<l2){value=arr2[i2+=1];out+=' <div 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> </div> ';} } out+=' </div> ';if(it.client_timings){out+=' <table class="profiler-timings profiler-client-timings"> <thead> <tr> <th>client event</th> <th>duration (ms)</th> <th>from start (ms)</th> </tr> </thead> <tbody> ';var arr3=MiniProfiler.getClientTimings(it.client_timings);if(arr3){var value,i3=-1,l3=arr3.length-1;while(i3<l3){value=arr3[i3+=1];out+=' <tr class="';if(value.isTrivial){out+='profiler-trivial';}out+='"> <td class="profiler-label">'+( value.name )+'</td> <td class="profiler-duration"> ';if(value.duration >= 0){out+=' <span class="profiler-unit"></span>'+( MiniProfiler.formatDuration(value.duration) )+' ';}out+=' </td> <td class="profiler-duration time-from-start"> <span class="profiler-unit">+</span>'+( MiniProfiler.formatDuration(value.start) )+' </td> </tr> ';} } out+=' </tbody> <tfoot> <td colspan="3"> '+( MiniProfiler.templates.linksTemplate({timing: it.root, page: it}) )+' </td> </tfoot> </table> ';}out+=' ';if(it.custom_fields && Object.keys(it.custom_fields).length > 0){out+=' <p class="custom-fields-title">Snapshot custom fields</p> <table class="profiler-timings"> <tbody> ';var arr4=Object.keys(it.custom_fields);if(arr4){var key,i4=-1,l4=arr4.length-1;while(i4<l4){key=arr4[i4+=1];out+=' <tr> <td class="profiler-label">'+( key )+'</td> <td class="profiler-label">'+( it.custom_fields[key] )+'</td> </tr> ';} } out+=' </tbody> </table> ';}out+=' </div> </div> ';if(it.has_sql_timings){out+=' <div class="profiler-queries"> <table> <thead> <tr> <th class="ta-right">step<br />time from start<br />query type<br />duration<br />records</th> <th class="ta-left">call stack<br />query</th> </tr> </thead> <tbody> ';var arr5=MiniProfiler.getSqlTimings(it.root);if(arr5){var value,index=-1,l5=arr5.length-1;while(index<l5){value=arr5[index+=1];out+=' '+( MiniProfiler.templates.sqlGapTemplate({g: value.prevGap}) )+' '+( MiniProfiler.templates.sqlTimingTemplate({i: index, s: value}) )+' ';if(value.nextGap){out+=' '+( MiniProfiler.templates.sqlGapTemplate({g: value.nextGap}) )+' ';}out+=' ';} } out+=' </tbody> </table> <p class="profiler-trivial-gap-container"> <a class="profiler-toggle-trivial-gaps">show trivial gaps</a> </p> </div> ';}out+=' </div>';return out;
|
11
11
|
}
|
12
12
|
MiniProfiler.templates["linksTemplate"] = function anonymous(it
|
13
13
|
) {
|
@@ -19,7 +19,7 @@ var out=' <tr class="';if(it.timing.is_trivial){out+='profiler-trivial';}out+='"
|
|
19
19
|
}
|
20
20
|
MiniProfiler.templates["sqlTimingTemplate"] = function anonymous(it
|
21
21
|
) {
|
22
|
-
var out=' <tr class="'+( it.s.row_class || '' )+'" data-timing-id="'+( it.s.parent_timing_id )+'"> <td class="profiler-info"> <div>'+( it.s.parent_timing_name )+'</div> <div class="profiler-number"><span class="profiler-unit">T+</span>'+( MiniProfiler.formatDuration(it.s.start_milliseconds) )+' <span class="profiler-unit">ms</span></div> <div> ';if(it.s.is_duplicate){out+='<span class="profiler-warning">DUPLICATE</span>';}out+=' '+( MiniProfiler.renderExecuteType(it.s.execute_type) )+' </div> <div title="';if(it.s.execute_type == 3){out+='first result fetched: '+( it.s.first_fetch_duration_milliseconds )+'ms';}out+='">'+( MiniProfiler.formatDuration(it.s.duration_milliseconds) )+' <span class="profiler-unit">ms</span></div> </td> <td> <div class="query"> <pre class="profiler-stack-trace">'+( it.s.stack_trace_snippet )+'</pre> ';if(it.s.formatted_command_string){out+=' <pre class="prettyprint lang-sql"><code>'+( it.s.formatted_command_string )+'; '+( MiniProfiler.formatParameters(it.s.parameters) )+'</code></pre> ';}else{out+=' <i>Query redacted</i> ';}out+=' </div> </td> </tr>';return out;
|
22
|
+
var out=' <tr class="'+( it.s.row_class || '' )+'" data-timing-id="'+( it.s.parent_timing_id )+'"> <td class="profiler-info"> <div>'+( it.s.parent_timing_name )+'</div> <div class="profiler-number"><span class="profiler-unit">T+</span>'+( MiniProfiler.formatDuration(it.s.start_milliseconds) )+' <span class="profiler-unit">ms</span></div> <div> ';if(it.s.is_duplicate){out+='<span class="profiler-warning">DUPLICATE</span>';}out+=' '+( MiniProfiler.renderExecuteType(it.s.execute_type) )+' </div> <div title="';if(it.s.execute_type == 3){out+='first result fetched: '+( it.s.first_fetch_duration_milliseconds )+'ms';}out+='">'+( MiniProfiler.formatDuration(it.s.duration_milliseconds) )+' <span class="profiler-unit">ms</span></div> ';if(it.s.row_count > 0){out+=' <div title="number and type of records instantiated by query">'+( it.s.class_name )+': '+( it.s.row_count )+'</div> ';}out+=' </td> <td> <div class="query"> <pre class="profiler-stack-trace">'+( it.s.stack_trace_snippet )+'</pre> ';if(it.s.cached){out+=' <span class="cached"> [CACHE] </span> ';}out+=' ';if(it.s.formatted_command_string){out+=' <pre class="prettyprint lang-sql"><code>'+( it.s.formatted_command_string )+'; '+( MiniProfiler.formatParameters(it.s.parameters) )+'</code></pre> ';}else{out+=' <i>Query redacted</i> ';}out+=' </div> </td> </tr>';return out;
|
23
23
|
}
|
24
24
|
MiniProfiler.templates["sqlGapTemplate"] = function anonymous(it
|
25
25
|
) {
|
@@ -5,7 +5,7 @@ module Rack
|
|
5
5
|
def serve_snapshot(env)
|
6
6
|
MiniProfiler.authorize_request
|
7
7
|
status = 200
|
8
|
-
headers = { '
|
8
|
+
headers = { 'content-type' => 'text/html' }
|
9
9
|
qp = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
10
10
|
if group_name = qp["group_name"]
|
11
11
|
list = @storage.snapshots_group(group_name)
|
@@ -55,7 +55,12 @@ module Rack
|
|
55
55
|
resources_env = env.dup
|
56
56
|
resources_env['PATH_INFO'] = file_name
|
57
57
|
|
58
|
-
|
58
|
+
if Gem::Version.new(Rack.release) >= Gem::Version.new("2.1.0")
|
59
|
+
rack_file = Rack::Files.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
60
|
+
else
|
61
|
+
rack_file = Rack::File.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
62
|
+
end
|
63
|
+
|
59
64
|
rack_file.call(resources_env)
|
60
65
|
end
|
61
66
|
|
@@ -88,11 +93,11 @@ module Rack
|
|
88
93
|
# If we're an XMLHttpRequest, serve up the contents as JSON
|
89
94
|
if request.xhr?
|
90
95
|
result_json = page_struct.to_json
|
91
|
-
[200, { '
|
96
|
+
[200, { 'content-type' => 'application/json' }, [result_json]]
|
92
97
|
else
|
93
98
|
# Otherwise give the HTML back
|
94
99
|
html = generate_html(page_struct, env)
|
95
|
-
[200, { '
|
100
|
+
[200, { 'content-type' => 'text/html' }, [html]]
|
96
101
|
end
|
97
102
|
end
|
98
103
|
|
@@ -125,7 +130,7 @@ module Rack
|
|
125
130
|
|
126
131
|
unless defined?(MemoryProfiler) && MemoryProfiler.respond_to?(:report)
|
127
132
|
message = "Please install the memory_profiler gem and require it: add gem 'memory_profiler' to your Gemfile"
|
128
|
-
|
133
|
+
_status, headers, body = @app.call(env)
|
129
134
|
body.close if body.respond_to? :close
|
130
135
|
|
131
136
|
return client_settings.handle_cookie(
|
data/lib/mini_profiler/config.rb
CHANGED
@@ -97,7 +97,9 @@ module Rack
|
|
97
97
|
|
98
98
|
# redefined - since the accessor defines it first
|
99
99
|
undef :authorization_mode=
|
100
|
+
# rubocop:disable Lint/DuplicateMethods
|
100
101
|
def authorization_mode=(mode)
|
102
|
+
# rubocop:enable Lint/DuplicateMethods
|
101
103
|
if mode == :whitelist
|
102
104
|
warn "[DEPRECATION] `:whitelist` authorization mode is deprecated. Please use `:allow_authorized` instead."
|
103
105
|
|