rack-mini-profiler 1.1.4 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +24 -0
- data/lib/enable_rails_patches.rb +5 -0
- data/lib/html/includes.js +62 -43
- data/lib/html/includes.tmpl +3 -3
- data/lib/html/vendor.js +2 -2
- data/lib/mini_profiler/asset_version.rb +1 -1
- data/lib/mini_profiler/client_settings.rb +10 -3
- data/lib/mini_profiler/profiler.rb +20 -1
- data/lib/mini_profiler/timer_struct/custom.rb +1 -0
- data/lib/mini_profiler/timer_struct/request.rb +53 -11
- data/lib/mini_profiler/timer_struct/sql.rb +2 -0
- data/lib/mini_profiler/version.rb +1 -1
- data/lib/mini_profiler_rails/railtie.rb +73 -7
- data/lib/mini_profiler_rails/railtie_methods.rb +55 -0
- data/lib/patches/db/activerecord.rb +1 -12
- data/lib/patches/sql_patches.rb +12 -4
- data/lib/rack-mini-profiler.rb +0 -1
- data/rack-mini-profiler.gemspec +3 -1
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7fcca7a28de2898e8e320f2f94db5d96eee3128b9be7a48053e472036a6ef95
|
4
|
+
data.tar.gz: 3a21bb993d77ef33baca78135716ab0e44dfbd28eefbb8874bae0b9a715ccb3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b4a31ef4e964fbd76646425d1ff16007c56d77360c6ad1f6923e753cd8830614098867ce06d5a554810564674b654b8868b1cdddc9c815942e25314459b3de4
|
7
|
+
data.tar.gz: fb3a9d99bb23a5e5d7b1518cd122a4240658b6c0c5d0c06f1852ddf472e9761cf9e46db5ce6cdcd92d574ec81ec3789f42aee51fe9ee20d8f587b51059cd55e1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 2.0.2 - 2020-05-25
|
4
|
+
|
5
|
+
- [FIX] client timings were not showing up when you clicked show trivial
|
6
|
+
|
7
|
+
## 2.0.1 - 2020-03-17
|
8
|
+
|
9
|
+
- [REVERT] Prepend Net::HTTP patch instead of class_eval and aliasing (#429) (technique clashes with New Relic and Skylight agents)
|
10
|
+
|
11
|
+
## 2.0.0 - 2020-03-11
|
12
|
+
|
13
|
+
- [FEATURE] Prepend Net::HTTP patch instead of class_eval and aliasing (#429)
|
14
|
+
- [FEATURE] Stop patching Rails and use `ActiveSupport::Notifications` by default (see README.md for details)
|
15
|
+
|
16
|
+
## 1.1.6 - 2020-01-30
|
17
|
+
|
18
|
+
- [FIX] edge condition on page transition function could lead to exceptions
|
19
|
+
|
20
|
+
## 1.1.5 - 2020-01-28
|
21
|
+
|
22
|
+
- [FIX] correct custom counter regression
|
23
|
+
- [FIX] respect max_traces_to_show
|
24
|
+
- [FIX] handle storage engine failures in whitelist mode
|
25
|
+
|
3
26
|
## 1.1.4 - 2019-12-12
|
4
27
|
|
5
28
|
- [SECURITY] carefully crafted SQL could cause an XSS on sites that do not use CSPs
|
data/README.md
CHANGED
@@ -51,6 +51,20 @@ gem 'stackprof'
|
|
51
51
|
|
52
52
|
All you have to do is to include the Gem and you're good to go in development. See notes below for use in production.
|
53
53
|
|
54
|
+
#### Upgrading to version 2.0.0
|
55
|
+
|
56
|
+
Prior to version 2.0.0, Mini Profiler patched various Rails methods to get the information it needed such as template rendering time. Starting from version 2.0.0, Mini Profiler doesn't patch any Rails methods by default and relies on `ActiveSupport::Notifications` to get the information it needs from Rails. If you want Mini Profiler to keep using its patches in version 2.0.0 and later, change the gem line in your `Gemfile` to the following:
|
57
|
+
|
58
|
+
If you want to manually require Mini Profiler:
|
59
|
+
```ruby
|
60
|
+
gem 'rack-mini-profiler', require: ['enable_rails_patches']
|
61
|
+
```
|
62
|
+
|
63
|
+
If you don't want to manually require Mini Profiler:
|
64
|
+
```ruby
|
65
|
+
gem 'rack-mini-profiler', require: ['enable_rails_patches', 'rack-mini-profiler']
|
66
|
+
```
|
67
|
+
|
54
68
|
#### Rails and manual initialization
|
55
69
|
|
56
70
|
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:
|
@@ -393,6 +407,16 @@ if JSON.const_defined?(:Pure)
|
|
393
407
|
end
|
394
408
|
```
|
395
409
|
|
410
|
+
## Development
|
411
|
+
|
412
|
+
If you want to contribute to this project, that's great, thank you! You can run the following rake task:
|
413
|
+
|
414
|
+
```
|
415
|
+
$ bundle exec rake client_dev
|
416
|
+
```
|
417
|
+
|
418
|
+
which will start a local Sinatra server at `http://localhost:9292` where you'll be able to preview your changes. Refreshing the page should be enough to see any changes you make to files in the `lib/html` directory.
|
419
|
+
|
396
420
|
## Running the Specs
|
397
421
|
|
398
422
|
```
|
data/lib/html/includes.js
CHANGED
@@ -79,20 +79,6 @@ var MiniProfiler = (function() {
|
|
79
79
|
return localStorage[getVersionedKey(keyPrefix)];
|
80
80
|
};
|
81
81
|
|
82
|
-
var compileTemplates = function compileTemplates(data) {
|
83
|
-
var element = document.createElement("DIV");
|
84
|
-
element.innerHTML = data;
|
85
|
-
var templates = {};
|
86
|
-
var children = element.children;
|
87
|
-
|
88
|
-
for (var i = 0; i < children.length; i++) {
|
89
|
-
var child = children[i];
|
90
|
-
templates[child.id] = doT.compile(child.innerHTML);
|
91
|
-
}
|
92
|
-
|
93
|
-
MiniProfiler.templates = templates;
|
94
|
-
};
|
95
|
-
|
96
82
|
var getClientPerformance = function getClientPerformance() {
|
97
83
|
return window.performance === null ? null : window.performance;
|
98
84
|
};
|
@@ -122,18 +108,8 @@ var MiniProfiler = (function() {
|
|
122
108
|
// ie is buggy strip out functions
|
123
109
|
var copy = {
|
124
110
|
navigation: {},
|
125
|
-
timing:
|
111
|
+
timing: clientPerformance.timing.toJSON()
|
126
112
|
};
|
127
|
-
var timing = extend({}, clientPerformance.timing);
|
128
|
-
|
129
|
-
for (p in timing) {
|
130
|
-
if (
|
131
|
-
timing.hasOwnProperty(p) &&
|
132
|
-
!(typeof timing[p] === "function")
|
133
|
-
) {
|
134
|
-
copy.timing[p] = timing[p];
|
135
|
-
}
|
136
|
-
}
|
137
113
|
|
138
114
|
if (clientPerformance.navigation) {
|
139
115
|
copy.navigation.redirectCount =
|
@@ -161,10 +137,13 @@ var MiniProfiler = (function() {
|
|
161
137
|
(function() {
|
162
138
|
var request = new XMLHttpRequest();
|
163
139
|
var url = options.path + "results";
|
164
|
-
var params =
|
165
|
-
|
166
|
-
|
167
|
-
|
140
|
+
var params = {
|
141
|
+
id: id,
|
142
|
+
clientPerformance: clientPerformance,
|
143
|
+
clientProbes: clientProbes,
|
144
|
+
popup: 1
|
145
|
+
};
|
146
|
+
var queryParam = toQueryString(params);
|
168
147
|
request.open("POST", url, true);
|
169
148
|
|
170
149
|
request.onload = function() {
|
@@ -186,24 +165,45 @@ var MiniProfiler = (function() {
|
|
186
165
|
"Content-Type",
|
187
166
|
"application/x-www-form-urlencoded"
|
188
167
|
);
|
189
|
-
request.send(
|
168
|
+
request.send(queryParam);
|
190
169
|
})();
|
191
170
|
}
|
192
171
|
}
|
193
172
|
};
|
194
173
|
|
195
|
-
var
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
174
|
+
var toQueryString = function toQueryString(data, parentKey) {
|
175
|
+
var result = [];
|
176
|
+
for (var key in data) {
|
177
|
+
var val = data[key];
|
178
|
+
var newKey = !parentKey ? key : parentKey + "[" + key + "]";
|
179
|
+
if (
|
180
|
+
typeof val === "object" &&
|
181
|
+
!Array.isArray(val) &&
|
182
|
+
val !== null &&
|
183
|
+
val !== undefined
|
184
|
+
) {
|
185
|
+
result[result.length] = toQueryString(val, newKey);
|
186
|
+
} else {
|
187
|
+
if (Array.isArray(val)) {
|
188
|
+
val.forEach(function(v) {
|
189
|
+
result[result.length] =
|
190
|
+
encodeURIComponent(newKey + "[]") + "=" + encodeURIComponent(v);
|
191
|
+
});
|
192
|
+
} else if (val === null || val === undefined) {
|
193
|
+
result[result.length] = encodeURIComponent(newKey) + "=";
|
194
|
+
} else {
|
195
|
+
result[result.length] =
|
196
|
+
encodeURIComponent(newKey) +
|
197
|
+
"=" +
|
198
|
+
encodeURIComponent(val.toString());
|
199
|
+
}
|
203
200
|
}
|
204
201
|
}
|
205
|
-
|
206
|
-
|
202
|
+
return result
|
203
|
+
.filter(function(element) {
|
204
|
+
return element && element.length > 0;
|
205
|
+
})
|
206
|
+
.join("&");
|
207
207
|
};
|
208
208
|
|
209
209
|
var renderTemplate = function renderTemplate(json) {
|
@@ -284,7 +284,7 @@ var MiniProfiler = (function() {
|
|
284
284
|
}); // limit count
|
285
285
|
|
286
286
|
if (
|
287
|
-
container.
|
287
|
+
container.querySelectorAll(".profiler-result").length >
|
288
288
|
options.maxTracesToShow
|
289
289
|
) {
|
290
290
|
var elem = container.querySelector(".profiler-result");
|
@@ -746,7 +746,24 @@ var MiniProfiler = (function() {
|
|
746
746
|
XMLHttpRequest.prototype.send = function(data) {
|
747
747
|
ajaxStartTime = new Date();
|
748
748
|
this.addEventListener("load", function() {
|
749
|
-
//
|
749
|
+
// responseURL isn't available in IE11
|
750
|
+
if (
|
751
|
+
this.responseURL &&
|
752
|
+
this.responseURL.indexOf(window.location.origin) !== 0
|
753
|
+
) {
|
754
|
+
return;
|
755
|
+
}
|
756
|
+
// getAllResponseHeaders isn't available in Edge.
|
757
|
+
var allHeaders = this.getAllResponseHeaders
|
758
|
+
? this.getAllResponseHeaders()
|
759
|
+
: null;
|
760
|
+
if (
|
761
|
+
allHeaders &&
|
762
|
+
allHeaders.toLowerCase().indexOf("x-miniprofiler-ids") === -1
|
763
|
+
) {
|
764
|
+
return;
|
765
|
+
}
|
766
|
+
// should be a string of comma-separated ids
|
750
767
|
var stringIds = this.getResponseHeader("X-MiniProfiler-Ids");
|
751
768
|
|
752
769
|
if (stringIds) {
|
@@ -1069,7 +1086,9 @@ var MiniProfiler = (function() {
|
|
1069
1086
|
},
|
1070
1087
|
pageTransition: function pageTransition() {
|
1071
1088
|
if (totalsControl) {
|
1072
|
-
totalsControl.parentElement
|
1089
|
+
if (totalsControl.parentElement) {
|
1090
|
+
totalsControl.parentElement.removeChild(totalsControl);
|
1091
|
+
}
|
1073
1092
|
totalsControl = null;
|
1074
1093
|
}
|
1075
1094
|
|
data/lib/html/includes.tmpl
CHANGED
@@ -128,8 +128,8 @@
|
|
128
128
|
{{? it.custom_link}}
|
129
129
|
<a href="{{= it.custom_link }}" class="profiler-custom-link" target="_blank">{{= it.custom_link_name }}</a>
|
130
130
|
{{?}}
|
131
|
-
{{? it.has_trivial_timings}}
|
132
|
-
<a class="profiler-toggle-trivial" data-show-on-load="{{= it.has_all_trivial_timings }}" title="toggles any rows with < {{= it.trivial_duration_threshold_milliseconds }} ms">
|
131
|
+
{{? it.page.has_trivial_timings}}
|
132
|
+
<a class="profiler-toggle-trivial" data-show-on-load="{{= it.page.has_all_trivial_timings }}" title="toggles any rows with < {{= it.page.trivial_duration_threshold_milliseconds }} ms">
|
133
133
|
show trivial
|
134
134
|
</a>
|
135
135
|
{{?}}
|
@@ -181,7 +181,7 @@
|
|
181
181
|
|
182
182
|
{{? it.timing.has_children}}
|
183
183
|
{{~ it.timing.children :value}}
|
184
|
-
{{= MiniProfiler.templates.timingTemplate({timing: value, page: it}) }}
|
184
|
+
{{= MiniProfiler.templates.timingTemplate({timing: value, page: it.page}) }}
|
185
185
|
{{~}}
|
186
186
|
{{?}}
|
187
187
|
</script>
|
data/lib/html/vendor.js
CHANGED
@@ -11,11 +11,11 @@ var out=' |
}
|
|
12
12
|
MiniProfiler.templates["linksTemplate"] = function anonymous(it
|
|
13
13
|
) {
|
|
14
|
-
var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.has_all_trivial_timings )+'" title="toggles any rows with < '+( it.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
|
|
14
|
+
var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.page.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.page.has_all_trivial_timings )+'" title="toggles any rows with < '+( it.page.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
|
|
15
15
|
}
|
|
16
16
|
MiniProfiler.templates["timingTemplate"] = function anonymous(it
|
|
17
17
|
) {
|
|
18
|
-
var out=' <tr class="';if(it.timing.is_trivial){out+='profiler-trivial';}out+='" data-timing-id="'+( it.timing.id )+'"> <td class="profiler-label" title="';if(it.timing.name && it.timing.name.length > 45){out+=''+( it.timing.name );}out+='"> <span class="profiler-indent">'+( MiniProfiler.renderIndent(it.timing.depth) )+'</span> '+( it.timing.name.slice(0,45) );if(it.timing.name && it.timing.name.length > 45){out+='...';}out+=' </td> <td class="profiler-duration" title="duration of this step without any children\'s durations"> '+( MiniProfiler.formatDuration(it.timing.duration_without_children_milliseconds) )+' </td> <td class="profiler-duration profiler-duration-with-children" title="duration of this step and its children"> '+( MiniProfiler.formatDuration(it.timing.duration_milliseconds) )+' </td> <td class="profiler-duration time-from-start" title="time elapsed since profiling started"> <span class="profiler-unit">+</span>'+( MiniProfiler.formatDuration(it.timing.start_milliseconds) )+' </td> ';if(it.timing.has_sql_timings){out+=' <td class="profiler-duration ';if(it.timing.has_duplicate_sql_timings){out+='profiler-warning';}out+='" title="';if(it.timing.has_duplicate_sql_timings){out+='duplicate queries detected - ';}if(it.timing.executed_readers > 0 || it.timing.executed_scalars > 0 || it.timing.executed_non_queries > 0){out+=''+( it.timing.executed_readers )+' reader, '+( it.timing.executed_scalars )+' scalar, '+( it.timing.executed_non_queries )+' non-query statements executed';}out+='"> <a class="profiler-queries-show"> ';if(it.timing.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' '+( it.timing.sql_timings.length )+' <span class="profiler-unit">sql</span> </a> </td> <td class="profiler-duration" title="aggregate duration of all queries in this step (excludes children)"> '+( MiniProfiler.formatDuration(it.timing.sql_timings_duration_milliseconds) )+' </td> ';}else{out+=' <td colspan="2"></td> ';}out+=' ';var arr1=it.page.custom_timing_names;if(arr1){var value,i1=-1,l1=arr1.length-1;while(i1<l1){value=arr1[i1+=1];out+=' ';if(it.timing.custom_timings && it.timing.custom_timings[value]){out+=' <td class="profiler-duration" title="aggregate number of all '+( value.toLowerCase() )+' invocations in this step (excludes children)"> '+( it.timing.custom_timings[value].length )+' '+( value.toLowerCase() )+' </td> <td class="profiler-duration" title="aggregate duration of all '+( value.toLowerCase() )+' invocations in this step (excludes children)"> '+( MiniProfiler.formatDuration(it.timing.custom_timing_stats[value].duration) )+' </td> ';}else{out+=' <td colspan="2"></td> ';}out+=' ';} } out+=' </tr> ';if(it.timing.has_children){out+=' ';var arr2=it.timing.children;if(arr2){var value,i2=-1,l2=arr2.length-1;while(i2<l2){value=arr2[i2+=1];out+=' '+( MiniProfiler.templates.timingTemplate({timing: value, page: it}) )+' ';} } out+=' ';}return out;
|
|
18
|
+
var out=' <tr class="';if(it.timing.is_trivial){out+='profiler-trivial';}out+='" data-timing-id="'+( it.timing.id )+'"> <td class="profiler-label" title="';if(it.timing.name && it.timing.name.length > 45){out+=''+( it.timing.name );}out+='"> <span class="profiler-indent">'+( MiniProfiler.renderIndent(it.timing.depth) )+'</span> '+( it.timing.name.slice(0,45) );if(it.timing.name && it.timing.name.length > 45){out+='...';}out+=' </td> <td class="profiler-duration" title="duration of this step without any children\'s durations"> '+( MiniProfiler.formatDuration(it.timing.duration_without_children_milliseconds) )+' </td> <td class="profiler-duration profiler-duration-with-children" title="duration of this step and its children"> '+( MiniProfiler.formatDuration(it.timing.duration_milliseconds) )+' </td> <td class="profiler-duration time-from-start" title="time elapsed since profiling started"> <span class="profiler-unit">+</span>'+( MiniProfiler.formatDuration(it.timing.start_milliseconds) )+' </td> ';if(it.timing.has_sql_timings){out+=' <td class="profiler-duration ';if(it.timing.has_duplicate_sql_timings){out+='profiler-warning';}out+='" title="';if(it.timing.has_duplicate_sql_timings){out+='duplicate queries detected - ';}if(it.timing.executed_readers > 0 || it.timing.executed_scalars > 0 || it.timing.executed_non_queries > 0){out+=''+( it.timing.executed_readers )+' reader, '+( it.timing.executed_scalars )+' scalar, '+( it.timing.executed_non_queries )+' non-query statements executed';}out+='"> <a class="profiler-queries-show"> ';if(it.timing.has_duplicate_sql_timings){out+='<span class="profiler-nuclear">!</span>';}out+=' '+( it.timing.sql_timings.length )+' <span class="profiler-unit">sql</span> </a> </td> <td class="profiler-duration" title="aggregate duration of all queries in this step (excludes children)"> '+( MiniProfiler.formatDuration(it.timing.sql_timings_duration_milliseconds) )+' </td> ';}else{out+=' <td colspan="2"></td> ';}out+=' ';var arr1=it.page.custom_timing_names;if(arr1){var value,i1=-1,l1=arr1.length-1;while(i1<l1){value=arr1[i1+=1];out+=' ';if(it.timing.custom_timings && it.timing.custom_timings[value]){out+=' <td class="profiler-duration" title="aggregate number of all '+( value.toLowerCase() )+' invocations in this step (excludes children)"> '+( it.timing.custom_timings[value].length )+' '+( value.toLowerCase() )+' </td> <td class="profiler-duration" title="aggregate duration of all '+( value.toLowerCase() )+' invocations in this step (excludes children)"> '+( MiniProfiler.formatDuration(it.timing.custom_timing_stats[value].duration) )+' </td> ';}else{out+=' <td colspan="2"></td> ';}out+=' ';} } out+=' </tr> ';if(it.timing.has_children){out+=' ';var arr2=it.timing.children;if(arr2){var value,i2=-1,l2=arr2.length-1;while(i2<l2){value=arr2[i2+=1];out+=' '+( MiniProfiler.templates.timingTemplate({timing: value, page: it.page}) )+' ';} } out+=' ';}return out;
|
|
19
19
|
}
|
|
20
20
|
MiniProfiler.templates["sqlTimingTemplate"] = function anonymous(it
|
|
21
21
|
) {
|
@@ -89,10 +89,17 @@ module Rack
|
|
89
89
|
def has_valid_cookie?
|
90
90
|
valid_cookie = !@cookie.nil?
|
91
91
|
|
92
|
-
if (MiniProfiler.config.authorization_mode == :whitelist)
|
93
|
-
|
92
|
+
if (MiniProfiler.config.authorization_mode == :whitelist) && valid_cookie
|
93
|
+
begin
|
94
|
+
@allowed_tokens ||= @store.allowed_tokens
|
95
|
+
rescue => e
|
96
|
+
if @config.storage_failure != nil
|
97
|
+
@config.storage_failure.call(e)
|
98
|
+
end
|
99
|
+
end
|
94
100
|
|
95
|
-
valid_cookie =
|
101
|
+
valid_cookie = @allowed_tokens &&
|
102
|
+
(Array === @orig_auth_tokens) &&
|
96
103
|
((@allowed_tokens & @orig_auth_tokens).length > 0)
|
97
104
|
end
|
98
105
|
|
@@ -5,6 +5,11 @@ module Rack
|
|
5
5
|
class << self
|
6
6
|
|
7
7
|
include Rack::MiniProfiler::ProfilingMethods
|
8
|
+
attr_accessor :subscribe_sql_active_record
|
9
|
+
|
10
|
+
def patch_rails?
|
11
|
+
!!defined?(Rack::MINI_PROFILER_ENABLE_RAILS_PATCHES)
|
12
|
+
end
|
8
13
|
|
9
14
|
def generate_id
|
10
15
|
rand(36**20).to_s(36)
|
@@ -67,6 +72,17 @@ module Rack
|
|
67
72
|
This feature is disabled by default, to enable set the enable_advanced_debugging_tools option to true in Mini Profiler config.
|
68
73
|
TEXT
|
69
74
|
end
|
75
|
+
|
76
|
+
def binds_to_params(binds)
|
77
|
+
return if binds.nil? || config.max_sql_param_length == 0
|
78
|
+
# map ActiveRecord::Relation::QueryAttribute to [name, value]
|
79
|
+
params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
|
80
|
+
if (skip = config.skip_sql_param_names)
|
81
|
+
params.map { |(n, v)| n =~ skip ? [n, nil] : [n, v] }
|
82
|
+
else
|
83
|
+
params
|
84
|
+
end
|
85
|
+
end
|
70
86
|
end
|
71
87
|
|
72
88
|
#
|
@@ -136,7 +152,7 @@ module Rack
|
|
136
152
|
resources_env = env.dup
|
137
153
|
resources_env['PATH_INFO'] = file_name
|
138
154
|
|
139
|
-
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' =>
|
155
|
+
rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age:#{cache_control_value}")
|
140
156
|
rack_file.call(resources_env)
|
141
157
|
end
|
142
158
|
|
@@ -657,5 +673,8 @@ Append the following to your query string:
|
|
657
673
|
current.inject_js = false
|
658
674
|
end
|
659
675
|
|
676
|
+
def cache_control_value
|
677
|
+
86400
|
678
|
+
end
|
660
679
|
end
|
661
680
|
end
|
@@ -11,7 +11,7 @@ module Rack
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
attr_accessor :children_duration
|
14
|
+
attr_accessor :children_duration, :start, :parent
|
15
15
|
|
16
16
|
def initialize(name, page, parent)
|
17
17
|
start_millis = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i - page[:started]
|
@@ -62,10 +62,6 @@ module Rack
|
|
62
62
|
self[:start_milliseconds]
|
63
63
|
end
|
64
64
|
|
65
|
-
def start
|
66
|
-
@start
|
67
|
-
end
|
68
|
-
|
69
65
|
def depth
|
70
66
|
self[:depth]
|
71
67
|
end
|
@@ -91,6 +87,20 @@ module Rack
|
|
91
87
|
end
|
92
88
|
end
|
93
89
|
|
90
|
+
def move_child(child, destination)
|
91
|
+
if index = self[:children].index(child)
|
92
|
+
self[:children].slice!(index)
|
93
|
+
self[:has_children] = self[:children].size > 0
|
94
|
+
|
95
|
+
destination[:children].push(child)
|
96
|
+
destination[:has_children] = true
|
97
|
+
|
98
|
+
child[:parent_timing_id] = destination[:id]
|
99
|
+
child.parent = destination
|
100
|
+
child.adjust_depth
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
94
104
|
def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false)
|
95
105
|
TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace).tap do |timer|
|
96
106
|
self[:sql_timings].push(timer)
|
@@ -102,6 +112,19 @@ module Rack
|
|
102
112
|
end
|
103
113
|
end
|
104
114
|
|
115
|
+
def move_sql(sql, destination)
|
116
|
+
if index = self[:sql_timings].index(sql)
|
117
|
+
self[:sql_timings].slice!(index)
|
118
|
+
self[:has_sql_timings] = self[:sql_timings].size > 0
|
119
|
+
self[:sql_timings_duration_milliseconds] -= sql[:duration_milliseconds]
|
120
|
+
destination[:sql_timings].push(sql)
|
121
|
+
destination[:has_sql_timings] = true
|
122
|
+
destination[:sql_timings_duration_milliseconds] += sql[:duration_milliseconds]
|
123
|
+
sql[:parent_timing_id] = destination[:id]
|
124
|
+
sql.parent = destination
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
105
128
|
def add_custom(type, elapsed_ms, page)
|
106
129
|
TimerStruct::Custom.new(type, elapsed_ms, page, self).tap do |timer|
|
107
130
|
timer[:parent_timing_id] = self[:id]
|
@@ -119,18 +142,37 @@ module Rack
|
|
119
142
|
end
|
120
143
|
end
|
121
144
|
|
145
|
+
def move_custom(type, custom, destination)
|
146
|
+
if index = self[:custom_timings][type]&.index(custom)
|
147
|
+
custom[:parent_timing_id] = destination[:id]
|
148
|
+
custom.parent = destination
|
149
|
+
self[:custom_timings][type].slice!(index)
|
150
|
+
if self[:custom_timings][type].size == 0
|
151
|
+
self[:custom_timings].delete(type)
|
152
|
+
self[:custom_timing_stats].delete(type)
|
153
|
+
else
|
154
|
+
self[:custom_timing_stats][type][:count] -= 1
|
155
|
+
self[:custom_timing_stats][type][:duration] -= custom[:duration_milliseconds]
|
156
|
+
end
|
157
|
+
destination[:custom_timings][type] ||= []
|
158
|
+
destination[:custom_timings][type].push(custom)
|
159
|
+
destination[:custom_timing_stats][type] ||= { count: 0, duration: 0.0 }
|
160
|
+
destination[:custom_timing_stats][type][:count] += 1
|
161
|
+
destination[:custom_timing_stats][type][:duration] += custom[:duration_milliseconds]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
122
165
|
def record_time(milliseconds = nil)
|
123
166
|
milliseconds ||= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) * 1000
|
124
167
|
self[:duration_milliseconds] = milliseconds
|
125
168
|
self[:is_trivial] = true if milliseconds < self[:trivial_duration_threshold_milliseconds]
|
126
|
-
self[:duration_without_children_milliseconds] = milliseconds -
|
127
|
-
|
128
|
-
if @parent
|
129
|
-
@parent.children_duration += milliseconds
|
130
|
-
end
|
131
|
-
|
169
|
+
self[:duration_without_children_milliseconds] = milliseconds - self[:children].sum(&:duration_ms)
|
132
170
|
end
|
133
171
|
|
172
|
+
def adjust_depth
|
173
|
+
self[:depth] = self.parent ? self.parent[:depth] + 1 : 0
|
174
|
+
self[:children].each(&:adjust_depth)
|
175
|
+
end
|
134
176
|
end
|
135
177
|
end
|
136
178
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
|
+
require_relative './railtie_methods'
|
4
5
|
|
5
6
|
module Rack::MiniProfilerRails
|
7
|
+
extend Rack::MiniProfilerRailsMethods
|
6
8
|
|
7
9
|
# call direct if needed to do a defer init
|
8
10
|
def self.initialize!(app)
|
@@ -55,17 +57,81 @@ module Rack::MiniProfilerRails
|
|
55
57
|
|
56
58
|
# Install the Middleware
|
57
59
|
app.middleware.insert(0, Rack::MiniProfiler)
|
60
|
+
c.enable_advanced_debugging_tools = Rails.env.development?
|
61
|
+
|
62
|
+
if ::Rack::MiniProfiler.patch_rails?
|
63
|
+
# Attach to various Rails methods
|
64
|
+
ActiveSupport.on_load(:action_controller) do
|
65
|
+
::Rack::MiniProfiler.profile_method(ActionController::Base, :process) { |action| "Executing action: #{action}" }
|
66
|
+
end
|
67
|
+
|
68
|
+
ActiveSupport.on_load(:action_view) do
|
69
|
+
::Rack::MiniProfiler.profile_method(ActionView::Template, :render) { |x, y| "Rendering: #{@virtual_path}" }
|
70
|
+
end
|
71
|
+
else
|
72
|
+
subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
|
73
|
+
next if !should_measure?
|
74
|
+
|
75
|
+
current = Rack::MiniProfiler.current
|
76
|
+
description = "Executing action: #{payload[:action]}"
|
77
|
+
Thread.current[get_key(payload)] = current.current_timer
|
78
|
+
Rack::MiniProfiler.current.current_timer = current.current_timer.add_child(description)
|
79
|
+
end
|
80
|
+
|
81
|
+
subscribe("process_action.action_controller") do |name, start, finish, id, payload|
|
82
|
+
next if !should_measure?
|
83
|
+
|
84
|
+
key = get_key(payload)
|
85
|
+
parent_timer = Thread.current[key]
|
86
|
+
next if !parent_timer
|
87
|
+
|
88
|
+
Thread.current[key] = nil
|
89
|
+
Rack::MiniProfiler.current.current_timer.record_time
|
90
|
+
Rack::MiniProfiler.current.current_timer = parent_timer
|
91
|
+
end
|
92
|
+
|
93
|
+
subscribe("render_partial.action_view") do |name, start, finish, id, payload|
|
94
|
+
render_notification_handler(shorten_identifier(payload[:identifier]), finish, start)
|
95
|
+
end
|
58
96
|
|
59
|
-
|
60
|
-
|
61
|
-
|
97
|
+
subscribe("render_template.action_view") do |name, start, finish, id, payload|
|
98
|
+
render_notification_handler(shorten_identifier(payload[:identifier]), finish, start)
|
99
|
+
end
|
100
|
+
|
101
|
+
if Rack::MiniProfiler.subscribe_sql_active_record
|
102
|
+
# we don't want to subscribe if we've already patched a DB driver
|
103
|
+
# otherwise we would end up with 2 records for every query
|
104
|
+
subscribe("sql.active_record") do |name, start, finish, id, payload|
|
105
|
+
next if !should_measure?
|
106
|
+
next if payload[:name] =~ /SCHEMA/ && Rack::MiniProfiler.config.skip_schema_queries
|
107
|
+
|
108
|
+
Rack::MiniProfiler.record_sql(
|
109
|
+
payload[:sql],
|
110
|
+
(finish - start) * 1000,
|
111
|
+
Rack::MiniProfiler.binds_to_params(payload[:binds])
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
62
115
|
end
|
63
|
-
|
64
|
-
|
116
|
+
@already_initialized = true
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.subscribe(event, &blk)
|
120
|
+
if ActiveSupport::Notifications.respond_to?(:monotonic_subscribe)
|
121
|
+
ActiveSupport::Notifications.monotonic_subscribe(event) { |*args| blk.call(*args) }
|
122
|
+
else
|
123
|
+
ActiveSupport::Notifications.subscribe(event) do |name, start, finish, id, payload|
|
124
|
+
blk.call(name, start.to_f, finish.to_f, id, payload)
|
125
|
+
end
|
65
126
|
end
|
127
|
+
end
|
66
128
|
|
67
|
-
|
68
|
-
|
129
|
+
def self.get_key(payload)
|
130
|
+
"mini_profiler_parent_timer_#{payload[:controller]}_#{payload[:action]}".to_sym
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.shorten_identifier(identifier)
|
134
|
+
identifier.split('/').last(2).join('/')
|
69
135
|
end
|
70
136
|
|
71
137
|
def self.serves_static_assets?(app)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack::MiniProfilerRailsMethods
|
4
|
+
def render_notification_handler(name, finish, start, name_as_description: false)
|
5
|
+
return if !should_measure?
|
6
|
+
|
7
|
+
description = name_as_description ? name : "Rendering: #{name}"
|
8
|
+
current = Rack::MiniProfiler.current.current_timer
|
9
|
+
node = current.add_child(description)
|
10
|
+
duration = finish - start
|
11
|
+
duration_ms = duration * 1000
|
12
|
+
node.start -= duration
|
13
|
+
node[:start_milliseconds] -= duration_ms
|
14
|
+
node.record_time(duration_ms)
|
15
|
+
|
16
|
+
children_duration = 0
|
17
|
+
to_be_moved = { requests: [], sql: [], custom: {} }
|
18
|
+
current.children.each do |child|
|
19
|
+
next if child == node
|
20
|
+
if should_move?(child, node)
|
21
|
+
to_be_moved[:requests] << child
|
22
|
+
children_duration += child[:duration_milliseconds]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
node[:duration_without_children_milliseconds] = duration_ms - children_duration
|
26
|
+
to_be_moved[:requests].each { |req| current.move_child(req, node) }
|
27
|
+
|
28
|
+
current.sql_timings.each do |sql|
|
29
|
+
to_be_moved[:sql] << sql if should_move?(sql, node)
|
30
|
+
end
|
31
|
+
to_be_moved[:sql].each { |sql| current.move_sql(sql, node) }
|
32
|
+
|
33
|
+
current.custom_timings.each do |type, timings|
|
34
|
+
to_be_moved[:custom] = []
|
35
|
+
timings.each do |custom|
|
36
|
+
to_be_moved[:custom] << custom if should_move?(custom, node)
|
37
|
+
end
|
38
|
+
to_be_moved[:custom].each { |custom| current.move_custom(type, custom, node) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def should_measure?
|
43
|
+
current = Rack::MiniProfiler.current
|
44
|
+
current && current.measure
|
45
|
+
end
|
46
|
+
|
47
|
+
def should_move?(child, node)
|
48
|
+
start = :start_milliseconds
|
49
|
+
duration = :duration_milliseconds
|
50
|
+
child[start] >= node[start] &&
|
51
|
+
child[start] + child[duration] <= node[start] + node[duration]
|
52
|
+
end
|
53
|
+
|
54
|
+
extend self
|
55
|
+
end
|
@@ -15,17 +15,6 @@ module Rack
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def binds_to_params(binds)
|
19
|
-
return if binds.nil? || Rack::MiniProfiler.config.max_sql_param_length == 0
|
20
|
-
# map ActiveRecord::Relation::QueryAttribute to [name, value]
|
21
|
-
params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
|
22
|
-
if (skip = Rack::MiniProfiler.config.skip_sql_param_names)
|
23
|
-
params.map { |(n, v)| n =~ skip ? [n, nil] : [n, v] }
|
24
|
-
else
|
25
|
-
params
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
18
|
def log_with_miniprofiler(*args, &block)
|
30
19
|
return log_without_miniprofiler(*args, &block) unless SqlPatches.should_measure?
|
31
20
|
|
@@ -37,7 +26,7 @@ module Rack
|
|
37
26
|
return rval if Rack::MiniProfiler.config.skip_schema_queries && name =~ (/SCHEMA/)
|
38
27
|
|
39
28
|
elapsed_time = SqlPatches.elapsed_time(start)
|
40
|
-
Rack::MiniProfiler.record_sql(sql, elapsed_time, binds_to_params(binds))
|
29
|
+
Rack::MiniProfiler.record_sql(sql, elapsed_time, Rack::MiniProfiler.binds_to_params(binds))
|
41
30
|
rval
|
42
31
|
end
|
43
32
|
end
|
data/lib/patches/sql_patches.rb
CHANGED
@@ -23,18 +23,26 @@ class SqlPatches
|
|
23
23
|
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time).to_f * 1000).round(1)
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.patch_rails?
|
27
|
+
::Rack::MiniProfiler.patch_rails?
|
28
|
+
end
|
29
|
+
|
26
30
|
def self.sql_patches
|
27
31
|
patches = []
|
28
32
|
|
29
33
|
patches << 'mysql2' if defined?(Mysql2::Client) && Mysql2::Client.class == Class
|
30
34
|
patches << 'pg' if defined?(PG::Result) && PG::Result.class == Class
|
31
35
|
patches << 'oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) && ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class == Class &&
|
32
|
-
SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
|
36
|
+
SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) &&
|
37
|
+
patch_rails?
|
33
38
|
# if the adapters were directly patched, don't patch again
|
34
|
-
|
39
|
+
if !patches.empty?
|
40
|
+
Rack::MiniProfiler.subscribe_sql_active_record = false
|
41
|
+
return patches
|
42
|
+
end
|
35
43
|
patches << 'sequel' if defined?(Sequel::Database) && Sequel::Database.class == Class
|
36
|
-
patches << 'activerecord' if defined?(ActiveRecord) && ActiveRecord.class == Module
|
37
|
-
|
44
|
+
patches << 'activerecord' if defined?(ActiveRecord) && ActiveRecord.class == Module && patch_rails?
|
45
|
+
Rack::MiniProfiler.subscribe_sql_active_record = patches.empty? && !patch_rails?
|
38
46
|
patches
|
39
47
|
end
|
40
48
|
|
data/lib/rack-mini-profiler.rb
CHANGED
data/rack-mini-profiler.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.authors = ["Sam Saffron", "Robin Ward", "Aleks Totic"]
|
12
12
|
s.description = "Profiling toolkit for Rack applications with Rails integration. Client Side profiling, DB profiling and Server profiling."
|
13
13
|
s.email = "sam.saffron@gmail.com"
|
14
|
-
s.homepage = "
|
14
|
+
s.homepage = "https://miniprofiler.com"
|
15
15
|
s.license = "MIT"
|
16
16
|
s.files = [
|
17
17
|
'rack-mini-profiler.gemspec',
|
@@ -39,6 +39,8 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.add_development_dependency 'rubocop'
|
40
40
|
s.add_development_dependency 'mini_racer'
|
41
41
|
s.add_development_dependency 'nokogiri'
|
42
|
+
s.add_development_dependency 'rubocop-discourse'
|
43
|
+
s.add_development_dependency 'listen'
|
42
44
|
|
43
45
|
s.require_paths = ["lib"]
|
44
46
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mini-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-05-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -180,6 +180,34 @@ dependencies:
|
|
180
180
|
- - ">="
|
181
181
|
- !ruby/object:Gem::Version
|
182
182
|
version: '0'
|
183
|
+
- !ruby/object:Gem::Dependency
|
184
|
+
name: rubocop-discourse
|
185
|
+
requirement: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
type: :development
|
191
|
+
prerelease: false
|
192
|
+
version_requirements: !ruby/object:Gem::Requirement
|
193
|
+
requirements:
|
194
|
+
- - ">="
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
- !ruby/object:Gem::Dependency
|
198
|
+
name: listen
|
199
|
+
requirement: !ruby/object:Gem::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: '0'
|
204
|
+
type: :development
|
205
|
+
prerelease: false
|
206
|
+
version_requirements: !ruby/object:Gem::Requirement
|
207
|
+
requirements:
|
208
|
+
- - ">="
|
209
|
+
- !ruby/object:Gem::Version
|
210
|
+
version: '0'
|
183
211
|
description: Profiling toolkit for Rack applications with Rails integration. Client
|
184
212
|
Side profiling, DB profiling and Server profiling.
|
185
213
|
email: sam.saffron@gmail.com
|
@@ -191,6 +219,7 @@ extra_rdoc_files:
|
|
191
219
|
files:
|
192
220
|
- CHANGELOG.md
|
193
221
|
- README.md
|
222
|
+
- lib/enable_rails_patches.rb
|
194
223
|
- lib/generators/rack_profiler/USAGE
|
195
224
|
- lib/generators/rack_profiler/install_generator.rb
|
196
225
|
- lib/generators/rack_profiler/templates/rack_profiler.rb
|
@@ -223,6 +252,7 @@ files:
|
|
223
252
|
- lib/mini_profiler/timer_struct/sql.rb
|
224
253
|
- lib/mini_profiler/version.rb
|
225
254
|
- lib/mini_profiler_rails/railtie.rb
|
255
|
+
- lib/mini_profiler_rails/railtie_methods.rb
|
226
256
|
- lib/patches/db/activerecord.rb
|
227
257
|
- lib/patches/db/mongo.rb
|
228
258
|
- lib/patches/db/moped.rb
|
@@ -239,7 +269,7 @@ files:
|
|
239
269
|
- lib/patches/sql_patches.rb
|
240
270
|
- lib/rack-mini-profiler.rb
|
241
271
|
- rack-mini-profiler.gemspec
|
242
|
-
homepage:
|
272
|
+
homepage: https://miniprofiler.com
|
243
273
|
licenses:
|
244
274
|
- MIT
|
245
275
|
metadata:
|