rack-mini-profiler 3.3.1 → 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 +13 -0
- data/README.md +15 -0
- data/lib/html/includes.css +27 -9
- data/lib/html/includes.js +7 -2
- data/lib/html/includes.scss +42 -16
- data/lib/html/includes.tmpl +42 -27
- data/lib/html/vendor.js +2 -2
- data/lib/mini_profiler/actions.rb +6 -6
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/profiling_methods.rb +3 -2
- data/lib/mini_profiler/storage/memcache_store.rb +1 -1
- data/lib/mini_profiler/timer_struct/page.rb +1 -0
- 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 +1 -1
- data/lib/mini_profiler/views.rb +2 -2
- data/lib/mini_profiler.rb +4 -4
- 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,18 @@
|
|
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
|
+
|
3
16
|
## 3.3.1 - 2024-02-15
|
4
17
|
- [FEATURE] Support dynamic `config.content_security_policy_nonce` [#609](https://github.com/MiniProfiler/rack-mini-profiler/pull/609)
|
5
18
|
- [FEATURE] Add flamgraph path to response header: [#601](https://github.com/MiniProfiler/rack-mini-profiler/pull/601)
|
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:
|
@@ -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:
|
data/lib/html/includes.css
CHANGED
@@ -113,6 +113,8 @@
|
|
113
113
|
float: left; }
|
114
114
|
.profiler-result .profiler-info .profiler-server-time {
|
115
115
|
white-space: nowrap; }
|
116
|
+
.profiler-result .profiler-info .profiler-number {
|
117
|
+
display: block; }
|
116
118
|
.profiler-result .profiler-timings th {
|
117
119
|
background-color: #fff;
|
118
120
|
color: #767676;
|
@@ -133,19 +135,21 @@
|
|
133
135
|
.profiler-result .profiler-timings .profiler-queries-duration {
|
134
136
|
padding-left: 6px; }
|
135
137
|
.profiler-result .profiler-timings .profiler-percent-in-sql {
|
136
|
-
white-space: nowrap;
|
137
|
-
|
138
|
-
.profiler-result .profiler-timings tfoot td {
|
139
|
-
padding-top: 10px;
|
138
|
+
white-space: nowrap; }
|
139
|
+
.profiler-result .profiler-timings tfoot tr td:last-child {
|
140
140
|
text-align: right; }
|
141
|
-
|
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 {
|
142
146
|
font-size: 95%;
|
143
147
|
display: inline-block;
|
144
148
|
margin-left: 12px; }
|
145
|
-
.profiler-result .profiler-timings
|
149
|
+
.profiler-result .profiler-timings-summary a:first-child {
|
146
150
|
float: left;
|
147
151
|
margin-left: 0px; }
|
148
|
-
.profiler-result .profiler-timings
|
152
|
+
.profiler-result .profiler-timings-summary a.profiler-custom-link {
|
149
153
|
float: left; }
|
150
154
|
.profiler-result .profiler-queries {
|
151
155
|
font-family: Helvetica, Arial, sans-serif; }
|
@@ -163,6 +167,12 @@
|
|
163
167
|
background-color: #fdd; }
|
164
168
|
.profiler-result .profiler-queries tr.very-very-slow {
|
165
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; }
|
166
176
|
.profiler-result .profiler-queries pre {
|
167
177
|
font-family: Consolas, monospace, serif;
|
168
178
|
white-space: pre-wrap; }
|
@@ -340,14 +350,22 @@
|
|
340
350
|
text-align: left;
|
341
351
|
line-height: 18px;
|
342
352
|
overflow: auto;
|
353
|
+
max-width: 800px;
|
343
354
|
box-shadow: 0px 1px 15px #555; }
|
344
355
|
.profiler-results .profiler-popup .profiler-info {
|
345
356
|
margin-bottom: 3px;
|
346
357
|
padding-bottom: 2px;
|
347
|
-
border-bottom: 1px solid #ddd;
|
358
|
+
border-bottom: 1px solid #ddd;
|
359
|
+
display: flex;
|
360
|
+
width: inherit; }
|
348
361
|
.profiler-results .profiler-popup .profiler-info .profiler-name {
|
362
|
+
overflow: hidden;
|
363
|
+
text-overflow: ellipsis;
|
364
|
+
text-align: left;
|
365
|
+
white-space: nowrap;
|
349
366
|
font-size: 110%;
|
350
|
-
font-weight: bold;
|
367
|
+
font-weight: bold;
|
368
|
+
padding-right: 10px; }
|
351
369
|
.profiler-results .profiler-popup .profiler-info .profiler-name .profiler-overall-duration {
|
352
370
|
display: none; }
|
353
371
|
.profiler-results .profiler-popup .profiler-info .profiler-server-time {
|
data/lib/html/includes.js
CHANGED
@@ -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
@@ -114,6 +114,9 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
114
114
|
.profiler-server-time {
|
115
115
|
white-space: nowrap;
|
116
116
|
}
|
117
|
+
.profiler-number {
|
118
|
+
display: block;
|
119
|
+
}
|
117
120
|
}
|
118
121
|
|
119
122
|
.profiler-timings {
|
@@ -147,27 +150,28 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
147
150
|
}
|
148
151
|
.profiler-percent-in-sql {
|
149
152
|
white-space: nowrap;
|
153
|
+
}
|
154
|
+
tfoot tr td:last-child {
|
150
155
|
text-align: right;
|
151
156
|
}
|
157
|
+
}
|
152
158
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
159
|
+
.profiler-timings-summary {
|
160
|
+
display: flex;
|
161
|
+
justify-content: space-between;
|
162
|
+
padding-top: 10px;
|
157
163
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
164
|
+
a {
|
165
|
+
font-size: 95%;
|
166
|
+
display: inline-block;
|
167
|
+
margin-left: 12px;
|
162
168
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
}
|
170
|
-
}
|
169
|
+
&:first-child {
|
170
|
+
float: left;
|
171
|
+
margin-left: 0px;
|
172
|
+
}
|
173
|
+
&.profiler-custom-link {
|
174
|
+
float: left;
|
171
175
|
}
|
172
176
|
}
|
173
177
|
}
|
@@ -202,6 +206,18 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
202
206
|
background-color: #fcc;
|
203
207
|
}
|
204
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
|
+
|
205
221
|
pre {
|
206
222
|
font-family: $codeFonts;
|
207
223
|
white-space: pre-wrap;
|
@@ -482,6 +498,7 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
482
498
|
text-align: left;
|
483
499
|
line-height: 18px;
|
484
500
|
overflow: auto;
|
501
|
+
max-width: 800px;
|
485
502
|
|
486
503
|
@include box-shadow(0px, 1px, 15px, $textColor);
|
487
504
|
|
@@ -489,14 +506,23 @@ $zindex: 2147483640; // near 32bit max 2147483647
|
|
489
506
|
margin-bottom: 3px;
|
490
507
|
padding-bottom: 2px;
|
491
508
|
border-bottom: 1px solid #ddd;
|
509
|
+
display: flex;
|
510
|
+
width: inherit;
|
492
511
|
|
493
512
|
.profiler-name {
|
513
|
+
overflow: hidden;
|
514
|
+
text-overflow: ellipsis;
|
515
|
+
text-align: left;
|
516
|
+
white-space: nowrap;
|
494
517
|
font-size: 110%;
|
495
518
|
font-weight: bold;
|
519
|
+
padding-right: 10px;
|
520
|
+
|
496
521
|
.profiler-overall-duration {
|
497
522
|
display: none;
|
498
523
|
}
|
499
524
|
}
|
525
|
+
|
500
526
|
.profiler-server-time {
|
501
527
|
font-size: 95%;
|
502
528
|
}
|
data/lib/html/includes.tmpl
CHANGED
@@ -21,8 +21,8 @@
|
|
21
21
|
|
22
22
|
<div class="profiler-popup">
|
23
23
|
<div class="profiler-info">
|
24
|
-
<span class="profiler-name">
|
25
|
-
{{= 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>
|
26
26
|
</span>
|
27
27
|
<span class="profiler-server-time">{{= it.machine_name}} on {{= MiniProfiler.renderDate(it.started_formatted)}}</span>
|
28
28
|
</div>
|
@@ -45,33 +45,42 @@
|
|
45
45
|
<tbody>
|
46
46
|
{{= MiniProfiler.templates.timingTemplate({timing: it.root, page: it}) }}
|
47
47
|
</tbody>
|
48
|
-
|
49
|
-
<
|
50
|
-
<
|
51
|
-
|
52
|
-
|
53
|
-
{{?}}
|
54
|
-
<a class="profiler-toggle-duration-with-children" title="toggles column with aggregate child durations">show time with children</a>
|
55
|
-
<a
|
56
|
-
class="profiler-snapshots-page-link"
|
57
|
-
title="Go to snapshots page"
|
58
|
-
href="{{= MiniProfiler.options.path }}snapshots">snapshots</a>
|
59
|
-
</td>
|
60
|
-
{{? it.has_sql_timings}}
|
61
|
-
<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">
|
62
|
-
{{= MiniProfiler.formatDuration(it.duration_milliseconds_in_sql / it.duration_milliseconds * 100) }}
|
63
|
-
<span class="profiler-unit">% in sql</span>
|
48
|
+
{{? it.has_sql_timings}}
|
49
|
+
<tfoot>
|
50
|
+
<tr>
|
51
|
+
<td colspan="1">
|
52
|
+
SQL Summary:
|
64
53
|
</td>
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
{{= MiniProfiler.formatDuration(it.
|
69
|
-
|
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>
|
70
59
|
</td>
|
71
|
-
|
72
|
-
</
|
73
|
-
|
60
|
+
</tr>
|
61
|
+
</tfoot>
|
62
|
+
{{?}}
|
74
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
|
+
|
75
84
|
{{? it.client_timings}}
|
76
85
|
<table class="profiler-timings profiler-client-timings">
|
77
86
|
<thead>
|
@@ -124,7 +133,7 @@
|
|
124
133
|
<table>
|
125
134
|
<thead>
|
126
135
|
<tr>
|
127
|
-
<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>
|
128
137
|
<th class="ta-left">call stack<br />query</th>
|
129
138
|
</tr>
|
130
139
|
</thead>
|
@@ -223,10 +232,16 @@
|
|
223
232
|
{{= MiniProfiler.renderExecuteType(it.s.execute_type) }}
|
224
233
|
</div>
|
225
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
|
+
{{?}}
|
226
238
|
</td>
|
227
239
|
<td>
|
228
240
|
<div class="query">
|
229
241
|
<pre class="profiler-stack-trace">{{= it.s.stack_trace_snippet }}</pre>
|
242
|
+
{{? it.s.cached }}
|
243
|
+
<span class="cached"> [CACHE] </span>
|
244
|
+
{{?}}
|
230
245
|
{{? it.s.formatted_command_string}}
|
231
246
|
<pre class="prettyprint lang-sql"><code>{{= it.s.formatted_command_string }}; {{= MiniProfiler.formatParameters(it.s.parameters) }}</code></pre>
|
232
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 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"
|
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)
|
@@ -56,9 +56,9 @@ module Rack
|
|
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, '
|
59
|
+
rack_file = Rack::Files.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
60
60
|
else
|
61
|
-
rack_file = Rack::File.new(resources_root, '
|
61
|
+
rack_file = Rack::File.new(resources_root, 'cache-control' => "max-age=#{cache_control_value}")
|
62
62
|
end
|
63
63
|
|
64
64
|
rack_file.call(resources_env)
|
@@ -93,11 +93,11 @@ module Rack
|
|
93
93
|
# If we're an XMLHttpRequest, serve up the contents as JSON
|
94
94
|
if request.xhr?
|
95
95
|
result_json = page_struct.to_json
|
96
|
-
[200, { '
|
96
|
+
[200, { 'content-type' => 'application/json' }, [result_json]]
|
97
97
|
else
|
98
98
|
# Otherwise give the HTML back
|
99
99
|
html = generate_html(page_struct, env)
|
100
|
-
[200, { '
|
100
|
+
[200, { 'content-type' => 'text/html' }, [html]]
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
@@ -130,7 +130,7 @@ module Rack
|
|
130
130
|
|
131
131
|
unless defined?(MemoryProfiler) && MemoryProfiler.respond_to?(:report)
|
132
132
|
message = "Please install the memory_profiler gem and require it: add gem 'memory_profiler' to your Gemfile"
|
133
|
-
|
133
|
+
_status, headers, body = @app.call(env)
|
134
134
|
body.close if body.respond_to? :close
|
135
135
|
|
136
136
|
return client_settings.handle_cookie(
|
@@ -4,7 +4,7 @@ module Rack
|
|
4
4
|
class MiniProfiler
|
5
5
|
module ProfilingMethods
|
6
6
|
|
7
|
-
def record_sql(query, elapsed_ms, params = nil)
|
7
|
+
def record_sql(query, elapsed_ms, params = nil, cached = nil)
|
8
8
|
return unless current && current.current_timer
|
9
9
|
c = current
|
10
10
|
c.current_timer.add_sql(
|
@@ -13,7 +13,8 @@ module Rack
|
|
13
13
|
c.page_struct,
|
14
14
|
redact_sql_queries? ? nil : params,
|
15
15
|
c.skip_backtrace,
|
16
|
-
c.full_backtrace
|
16
|
+
c.full_backtrace,
|
17
|
+
cached
|
17
18
|
)
|
18
19
|
end
|
19
20
|
|
@@ -42,7 +42,7 @@ module Rack
|
|
42
42
|
@client.add("#{@prefix}-#{user}-v", [], @expires_in_seconds)
|
43
43
|
MAX_RETRIES.times do
|
44
44
|
break if @client.cas("#{@prefix}-#{user}-v", @expires_in_seconds) do |ids|
|
45
|
-
ids << id
|
45
|
+
ids << id if !ids.include?(id)
|
46
46
|
ids
|
47
47
|
end
|
48
48
|
end
|
@@ -101,14 +101,15 @@ module Rack
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false)
|
105
|
-
TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace).tap do |timer|
|
104
|
+
def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false, cached = false)
|
105
|
+
TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace, cached).tap do |timer|
|
106
106
|
self[:sql_timings].push(timer)
|
107
107
|
timer[:parent_timing_id] = self[:id]
|
108
108
|
self[:has_sql_timings] = true
|
109
109
|
self[:sql_timings_duration_milliseconds] += elapsed_ms
|
110
110
|
page[:duration_milliseconds_in_sql] += elapsed_ms
|
111
111
|
page[:sql_count] += 1
|
112
|
+
page[:cached_sql_count] += 1 if cached
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
@@ -10,7 +10,7 @@ module Rack
|
|
10
10
|
class Sql < TimerStruct::Base
|
11
11
|
attr_accessor :parent
|
12
12
|
|
13
|
-
def initialize(query, duration_ms, page, parent, params = nil, skip_backtrace = false, full_backtrace = false)
|
13
|
+
def initialize(query, duration_ms, page, parent, params = nil, skip_backtrace = false, full_backtrace = false, cached = false)
|
14
14
|
|
15
15
|
stack_trace = nil
|
16
16
|
unless skip_backtrace || duration_ms < Rack::MiniProfiler.config.backtrace_threshold_ms
|
@@ -46,7 +46,10 @@ module Rack
|
|
46
46
|
duration_milliseconds: duration_ms,
|
47
47
|
first_fetch_duration_milliseconds: duration_ms,
|
48
48
|
parameters: query ? trim_binds(params) : nil,
|
49
|
+
row_count: 0,
|
50
|
+
class_name: nil,
|
49
51
|
parent_timing_id: nil,
|
52
|
+
cached: cached,
|
50
53
|
is_duplicate: false
|
51
54
|
)
|
52
55
|
end
|
data/lib/mini_profiler/views.rb
CHANGED
@@ -110,7 +110,7 @@ module Rack
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def flamegraph(graph, path, env)
|
113
|
-
headers = { '
|
113
|
+
headers = { 'content-type' => 'text/html' }
|
114
114
|
iframe_src = "#{public_base_path(env)}speedscope/index.html"
|
115
115
|
html = <<~HTML
|
116
116
|
<!DOCTYPE html>
|
@@ -141,7 +141,7 @@ module Rack
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def help(client_settings, env)
|
144
|
-
headers = { '
|
144
|
+
headers = { 'content-type' => 'text/html' }
|
145
145
|
html = <<~HTML
|
146
146
|
<!DOCTYPE html>
|
147
147
|
<html>
|
data/lib/mini_profiler.rb
CHANGED
@@ -296,8 +296,8 @@ module Rack
|
|
296
296
|
|
297
297
|
mode_match_data = action_parameters(env)['flamegraph_mode']
|
298
298
|
|
299
|
-
if mode_match_data && [:cpu, :wall, :object, :custom].include?(mode_match_data
|
300
|
-
mode = mode_match_data
|
299
|
+
if mode_match_data && [:cpu, :wall, :object, :custom].include?(mode_match_data.to_sym)
|
300
|
+
mode = mode_match_data.to_sym
|
301
301
|
else
|
302
302
|
mode = config.flamegraph_mode
|
303
303
|
end
|
@@ -329,7 +329,7 @@ module Rack
|
|
329
329
|
)
|
330
330
|
end
|
331
331
|
elsif path == '/rack-mini-profiler/requests'
|
332
|
-
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html]]
|
332
|
+
status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]] # important to dup here!
|
333
333
|
else
|
334
334
|
status, headers, body = @app.call(env)
|
335
335
|
end
|
@@ -417,7 +417,7 @@ module Rack
|
|
417
417
|
end
|
418
418
|
|
419
419
|
def action_parameters(env)
|
420
|
-
|
420
|
+
Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
421
421
|
end
|
422
422
|
|
423
423
|
def inject_profiler(env, status, headers, body)
|
@@ -111,7 +111,8 @@ module Rack::MiniProfilerRails
|
|
111
111
|
Rack::MiniProfiler.record_sql(
|
112
112
|
payload[:sql],
|
113
113
|
(finish - start) * 1000,
|
114
|
-
Rack::MiniProfiler.binds_to_params(payload[:binds])
|
114
|
+
Rack::MiniProfiler.binds_to_params(payload[:binds]),
|
115
|
+
payload[:cached]
|
115
116
|
)
|
116
117
|
end
|
117
118
|
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PG::Result
|
4
|
+
alias_method :each_without_profiling, :each
|
5
|
+
alias_method :values_without_profiling, :values
|
6
|
+
|
7
|
+
def values(*args, &blk)
|
8
|
+
return values_without_profiling(*args, &blk) unless defined?(@miniprofiler_sql_id)
|
9
|
+
mp_report_sql do
|
10
|
+
values_without_profiling(*args , &blk)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(*args, &blk)
|
15
|
+
return each_without_profiling(*args, &blk) unless defined?(@miniprofiler_sql_id)
|
16
|
+
mp_report_sql do
|
17
|
+
each_without_profiling(*args, &blk)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def mp_report_sql(&block)
|
22
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
|
+
result = yield
|
24
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
25
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class PG::Connection
|
31
|
+
alias_method :exec_without_profiling, :exec
|
32
|
+
alias_method :async_exec_without_profiling, :async_exec
|
33
|
+
alias_method :exec_prepared_without_profiling, :exec_prepared
|
34
|
+
alias_method :send_query_prepared_without_profiling, :send_query_prepared
|
35
|
+
alias_method :prepare_without_profiling, :prepare
|
36
|
+
|
37
|
+
if Gem::Version.new(PG::VERSION) >= Gem::Version.new("1.1.0")
|
38
|
+
alias_method :exec_params_without_profiling, :exec_params
|
39
|
+
end
|
40
|
+
|
41
|
+
def prepare(*args, &blk)
|
42
|
+
# we have no choice but to do this here,
|
43
|
+
# if we do the check for profiling first, our cache may miss critical stuff
|
44
|
+
|
45
|
+
@prepare_map ||= {}
|
46
|
+
@prepare_map[args[0]] = args[1]
|
47
|
+
# dont leak more than 10k ever
|
48
|
+
@prepare_map = {} if @prepare_map.length > 1000
|
49
|
+
|
50
|
+
return prepare_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
51
|
+
prepare_without_profiling(*args, &blk)
|
52
|
+
end
|
53
|
+
|
54
|
+
def exec(*args, &blk)
|
55
|
+
return exec_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
56
|
+
|
57
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
58
|
+
result = exec_without_profiling(*args, &blk)
|
59
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
60
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
61
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
62
|
+
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
if Gem::Version.new(PG::VERSION) >= Gem::Version.new("1.1.0")
|
67
|
+
def exec_params(*args, &blk)
|
68
|
+
return exec_params_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
69
|
+
|
70
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
71
|
+
result = exec_params_without_profiling(*args, &blk)
|
72
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
73
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
74
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
75
|
+
|
76
|
+
result
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def exec_prepared(*args, &blk)
|
81
|
+
return exec_prepared_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
82
|
+
|
83
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
84
|
+
result = exec_prepared_without_profiling(*args, &blk)
|
85
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
86
|
+
mapped = args[0]
|
87
|
+
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
88
|
+
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
89
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
90
|
+
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def send_query_prepared(*args, &blk)
|
95
|
+
return send_query_prepared_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
96
|
+
|
97
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
98
|
+
result = send_query_prepared_without_profiling(*args, &blk)
|
99
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
100
|
+
mapped = args[0]
|
101
|
+
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
102
|
+
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
103
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
104
|
+
|
105
|
+
result
|
106
|
+
end
|
107
|
+
|
108
|
+
def async_exec(*args, &blk)
|
109
|
+
return async_exec_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
110
|
+
|
111
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
112
|
+
result = exec_without_profiling(*args, &blk)
|
113
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
114
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
115
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
116
|
+
|
117
|
+
result
|
118
|
+
end
|
119
|
+
|
120
|
+
alias_method :query, :exec
|
121
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PG::Result
|
4
|
+
module MiniProfiler
|
5
|
+
def values(*args, &blk)
|
6
|
+
return super unless defined?(@miniprofiler_sql_id)
|
7
|
+
mp_report_sql do
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(*args, &blk)
|
13
|
+
return super unless defined?(@miniprofiler_sql_id)
|
14
|
+
mp_report_sql do
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def mp_report_sql(&block)
|
20
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
21
|
+
result = yield
|
22
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
23
|
+
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
prepend MiniProfiler
|
29
|
+
end
|
30
|
+
|
31
|
+
class PG::Connection
|
32
|
+
module MiniProfiler
|
33
|
+
def prepare(*args, &blk)
|
34
|
+
# we have no choice but to do this here,
|
35
|
+
# if we do the check for profiling first, our cache may miss critical stuff
|
36
|
+
|
37
|
+
@prepare_map ||= {}
|
38
|
+
@prepare_map[args[0]] = args[1]
|
39
|
+
# dont leak more than 10k ever
|
40
|
+
@prepare_map = {} if @prepare_map.length > 1000
|
41
|
+
|
42
|
+
return super unless SqlPatches.should_measure?
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def exec(*args, &blk)
|
47
|
+
return super unless SqlPatches.should_measure?
|
48
|
+
|
49
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
50
|
+
result = super
|
51
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
52
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
53
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
54
|
+
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
if Gem::Version.new(PG::VERSION) >= Gem::Version.new("1.1.0")
|
59
|
+
def exec_params(*args, &blk)
|
60
|
+
return super unless SqlPatches.should_measure?
|
61
|
+
|
62
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
63
|
+
result = super
|
64
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
65
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
66
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
67
|
+
|
68
|
+
result
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def exec_prepared(*args, &blk)
|
73
|
+
return super unless SqlPatches.should_measure?
|
74
|
+
|
75
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
76
|
+
result = super
|
77
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
78
|
+
mapped = args[0]
|
79
|
+
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
80
|
+
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
81
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
82
|
+
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_query_prepared(*args, &blk)
|
87
|
+
return super unless SqlPatches.should_measure?
|
88
|
+
|
89
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
90
|
+
result = super
|
91
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
92
|
+
mapped = args[0]
|
93
|
+
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
94
|
+
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
95
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
96
|
+
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
100
|
+
def async_exec(*args, &blk)
|
101
|
+
return super unless SqlPatches.should_measure?
|
102
|
+
|
103
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
104
|
+
result = super
|
105
|
+
elapsed_time = SqlPatches.elapsed_time(start)
|
106
|
+
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
107
|
+
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
108
|
+
|
109
|
+
result
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
prepend MiniProfiler
|
114
|
+
alias_method :query, :exec
|
115
|
+
end
|
data/lib/patches/db/pg.rb
CHANGED
@@ -1,122 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
alias_method
|
7
|
-
|
8
|
-
def values(*args, &blk)
|
9
|
-
return values_without_profiling(*args, &blk) unless defined?(@miniprofiler_sql_id)
|
10
|
-
mp_report_sql do
|
11
|
-
values_without_profiling(*args , &blk)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def each(*args, &blk)
|
16
|
-
return each_without_profiling(*args, &blk) unless defined?(@miniprofiler_sql_id)
|
17
|
-
mp_report_sql do
|
18
|
-
each_without_profiling(*args, &blk)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def mp_report_sql(&block)
|
23
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
24
|
-
result = yield
|
25
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
26
|
-
@miniprofiler_sql_id.report_reader_duration(elapsed_time)
|
27
|
-
result
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class PG::Connection
|
32
|
-
alias_method :exec_without_profiling, :exec
|
33
|
-
alias_method :async_exec_without_profiling, :async_exec
|
34
|
-
alias_method :exec_prepared_without_profiling, :exec_prepared
|
35
|
-
alias_method :send_query_prepared_without_profiling, :send_query_prepared
|
36
|
-
alias_method :prepare_without_profiling, :prepare
|
37
|
-
|
38
|
-
if Gem::Version.new(PG::VERSION) >= Gem::Version.new("1.1.0")
|
39
|
-
alias_method :exec_params_without_profiling, :exec_params
|
40
|
-
end
|
41
|
-
|
42
|
-
def prepare(*args, &blk)
|
43
|
-
# we have no choice but to do this here,
|
44
|
-
# if we do the check for profiling first, our cache may miss critical stuff
|
45
|
-
|
46
|
-
@prepare_map ||= {}
|
47
|
-
@prepare_map[args[0]] = args[1]
|
48
|
-
# dont leak more than 10k ever
|
49
|
-
@prepare_map = {} if @prepare_map.length > 1000
|
50
|
-
|
51
|
-
return prepare_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
52
|
-
prepare_without_profiling(*args, &blk)
|
53
|
-
end
|
54
|
-
|
55
|
-
def exec(*args, &blk)
|
56
|
-
return exec_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
57
|
-
|
58
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
59
|
-
result = exec_without_profiling(*args, &blk)
|
60
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
61
|
-
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
62
|
-
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
63
|
-
|
64
|
-
result
|
65
|
-
end
|
66
|
-
|
67
|
-
if Gem::Version.new(PG::VERSION) >= Gem::Version.new("1.1.0")
|
68
|
-
def exec_params(*args, &blk)
|
69
|
-
return exec_params_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
70
|
-
|
71
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
72
|
-
result = exec_params_without_profiling(*args, &blk)
|
73
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
74
|
-
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
75
|
-
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
76
|
-
|
77
|
-
result
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def exec_prepared(*args, &blk)
|
82
|
-
return exec_prepared_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
83
|
-
|
84
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
85
|
-
result = exec_prepared_without_profiling(*args, &blk)
|
86
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
87
|
-
mapped = args[0]
|
88
|
-
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
89
|
-
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
90
|
-
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
91
|
-
|
92
|
-
result
|
93
|
-
end
|
94
|
-
|
95
|
-
def send_query_prepared(*args, &blk)
|
96
|
-
return send_query_prepared_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
97
|
-
|
98
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
99
|
-
result = send_query_prepared_without_profiling(*args, &blk)
|
100
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
101
|
-
mapped = args[0]
|
102
|
-
mapped = @prepare_map[mapped] || args[0] if @prepare_map
|
103
|
-
record = ::Rack::MiniProfiler.record_sql(mapped, elapsed_time)
|
104
|
-
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
105
|
-
|
106
|
-
result
|
107
|
-
end
|
108
|
-
|
109
|
-
def async_exec(*args, &blk)
|
110
|
-
return async_exec_without_profiling(*args, &blk) unless SqlPatches.should_measure?
|
111
|
-
|
112
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
113
|
-
result = exec_without_profiling(*args, &blk)
|
114
|
-
elapsed_time = SqlPatches.elapsed_time(start)
|
115
|
-
record = ::Rack::MiniProfiler.record_sql(args[0], elapsed_time)
|
116
|
-
result.instance_variable_set("@miniprofiler_sql_id", record) if result
|
117
|
-
|
118
|
-
result
|
119
|
-
end
|
120
|
-
|
121
|
-
alias_method :query, :exec
|
3
|
+
if defined?(Rack::MINI_PROFILER_PREPEND_PG_PATCH)
|
4
|
+
require "patches/db/pg/prepend"
|
5
|
+
else
|
6
|
+
require "patches/db/pg/alias_method"
|
122
7
|
end
|
data/rack-mini-profiler.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
lib = File.expand_path('../lib', __FILE__)
|
4
|
-
$LOAD_PATH.unshift(lib)
|
4
|
+
$LOAD_PATH.unshift(lib) if !$LOAD_PATH.include?(lib)
|
5
5
|
require 'mini_profiler/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
"CHANGELOG.md"
|
22
22
|
]
|
23
23
|
s.add_runtime_dependency 'rack', '>= 1.2.0'
|
24
|
-
s.required_ruby_version = '>=
|
24
|
+
s.required_ruby_version = '>= 3.1.0'
|
25
25
|
|
26
26
|
s.metadata = {
|
27
27
|
'source_code_uri' => Rack::MiniProfiler::SOURCE_CODE_URI,
|
@@ -41,7 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
s.add_development_dependency 'rubocop-discourse'
|
42
42
|
s.add_development_dependency 'listen'
|
43
43
|
s.add_development_dependency 'webpacker'
|
44
|
-
s.add_development_dependency 'rails', '
|
44
|
+
s.add_development_dependency 'rails', '>= 7.1'
|
45
45
|
s.add_development_dependency 'webmock', '3.9.1'
|
46
46
|
s.add_development_dependency 'rubyzip'
|
47
47
|
|
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mini-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
- Robin Ward
|
9
9
|
- Aleks Totic
|
10
|
-
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2025-06-10 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: rack
|
@@ -212,16 +211,16 @@ dependencies:
|
|
212
211
|
name: rails
|
213
212
|
requirement: !ruby/object:Gem::Requirement
|
214
213
|
requirements:
|
215
|
-
- - "
|
214
|
+
- - ">="
|
216
215
|
- !ruby/object:Gem::Version
|
217
|
-
version: '
|
216
|
+
version: '7.1'
|
218
217
|
type: :development
|
219
218
|
prerelease: false
|
220
219
|
version_requirements: !ruby/object:Gem::Requirement
|
221
220
|
requirements:
|
222
|
-
- - "
|
221
|
+
- - ">="
|
223
222
|
- !ruby/object:Gem::Version
|
224
|
-
version: '
|
223
|
+
version: '7.1'
|
225
224
|
- !ruby/object:Gem::Dependency
|
226
225
|
name: webmock
|
227
226
|
requirement: !ruby/object:Gem::Requirement
|
@@ -329,6 +328,8 @@ files:
|
|
329
328
|
- lib/patches/db/nobrainer.rb
|
330
329
|
- lib/patches/db/oracle_enhanced.rb
|
331
330
|
- lib/patches/db/pg.rb
|
331
|
+
- lib/patches/db/pg/alias_method.rb
|
332
|
+
- lib/patches/db/pg/prepend.rb
|
332
333
|
- lib/patches/db/plucky.rb
|
333
334
|
- lib/patches/db/riak.rb
|
334
335
|
- lib/patches/db/rsolr.rb
|
@@ -337,6 +338,7 @@ files:
|
|
337
338
|
- lib/patches/sql_patches.rb
|
338
339
|
- lib/prepend_mysql2_patch.rb
|
339
340
|
- lib/prepend_net_http_patch.rb
|
341
|
+
- lib/prepend_pg_patch.rb
|
340
342
|
- lib/rack-mini-profiler.rb
|
341
343
|
- rack-mini-profiler.gemspec
|
342
344
|
homepage: https://miniprofiler.com
|
@@ -345,7 +347,6 @@ licenses:
|
|
345
347
|
metadata:
|
346
348
|
source_code_uri: https://github.com/MiniProfiler/rack-mini-profiler
|
347
349
|
changelog_uri: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/CHANGELOG.md
|
348
|
-
post_install_message:
|
349
350
|
rdoc_options: []
|
350
351
|
require_paths:
|
351
352
|
- lib
|
@@ -353,15 +354,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
353
354
|
requirements:
|
354
355
|
- - ">="
|
355
356
|
- !ruby/object:Gem::Version
|
356
|
-
version:
|
357
|
+
version: 3.1.0
|
357
358
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
358
359
|
requirements:
|
359
360
|
- - ">="
|
360
361
|
- !ruby/object:Gem::Version
|
361
362
|
version: '0'
|
362
363
|
requirements: []
|
363
|
-
rubygems_version: 3.
|
364
|
-
signing_key:
|
364
|
+
rubygems_version: 3.6.3
|
365
365
|
specification_version: 4
|
366
366
|
summary: Profiles loading speed for rack applications.
|
367
367
|
test_files: []
|