rails_mini_profiler 0.3.0 → 0.7.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/LICENSE +20 -0
- data/README.md +65 -36
- data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
- data/app/controllers/rails_mini_profiler/application_controller.rb +15 -7
- data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +28 -13
- data/app/helpers/rails_mini_profiler/application_helper.rb +3 -1
- data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
- data/app/javascript/images/check.svg +3 -0
- data/app/javascript/images/chevron.svg +3 -0
- data/app/javascript/images/copy.svg +6 -0
- data/app/javascript/images/filter.svg +1 -0
- data/app/javascript/images/logo_variant.svg +2 -2
- data/app/javascript/images/search.svg +4 -5
- data/app/javascript/js/checklist_controller.js +48 -0
- data/app/javascript/js/clipboard_controller.js +53 -0
- data/app/javascript/js/enable_controller.js +23 -0
- data/app/javascript/js/filter_controller.js +48 -0
- data/app/javascript/js/search_controller.js +18 -0
- data/app/javascript/js/select_controller.js +52 -0
- data/app/javascript/packs/rails-mini-profiler.js +69 -53
- data/app/javascript/stylesheets/components/buttons.scss +59 -0
- data/app/javascript/stylesheets/components/dropdown.scss +103 -0
- data/app/javascript/stylesheets/components/input.scss +10 -0
- data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +7 -13
- data/app/javascript/stylesheets/components/page_header.scss +7 -0
- data/app/javascript/stylesheets/components/pagination.scss +55 -0
- data/app/javascript/stylesheets/components/placeholder.scss +36 -0
- data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
- data/app/javascript/stylesheets/components/trace.scss +93 -0
- data/app/javascript/stylesheets/flamegraph.scss +3 -2
- data/app/javascript/stylesheets/flashes.scss +3 -5
- data/app/javascript/stylesheets/profiled_requests.scss +12 -161
- data/app/javascript/stylesheets/rails-mini-profiler.scss +82 -66
- data/app/javascript/stylesheets/traces.scss +52 -56
- data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +10 -17
- data/app/presenters/rails_mini_profiler/trace_presenter.rb +9 -7
- data/app/search/rails_mini_profiler/base_search.rb +67 -0
- data/app/search/rails_mini_profiler/profiled_request_search.rb +33 -0
- data/app/search/rails_mini_profiler/trace_search.rb +27 -0
- data/app/views/models/_flamegraph.json.jb +3 -0
- data/app/views/models/_profiled_request.jb +3 -0
- data/app/views/models/_trace.jb +3 -0
- data/app/views/rails_mini_profiler/badge.html.erb +2 -2
- data/app/views/rails_mini_profiler/flamegraphs/show.json.jb +3 -0
- data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
- data/app/views/rails_mini_profiler/profiled_requests/index.json.jb +3 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +19 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
- data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +23 -6
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_header.erb +87 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +5 -18
- data/app/views/rails_mini_profiler/profiled_requests/show.json.jb +5 -0
- data/app/views/rails_mini_profiler/shared/_head.erb +1 -1
- data/lib/generators/rails_mini_profiler/install_generator.rb +7 -1
- data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +11 -4
- data/lib/rails_mini_profiler/badge.rb +4 -2
- data/lib/rails_mini_profiler/{storage.rb → configuration/storage.rb} +0 -0
- data/lib/rails_mini_profiler/configuration/user_interface.rb +70 -0
- data/lib/rails_mini_profiler/configuration.rb +7 -7
- data/lib/rails_mini_profiler/engine.rb +12 -8
- data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
- data/lib/rails_mini_profiler/guard.rb +19 -1
- data/lib/rails_mini_profiler/middleware.rb +6 -4
- data/lib/rails_mini_profiler/redirect.rb +1 -1
- data/lib/rails_mini_profiler/request_context.rb +6 -1
- data/lib/rails_mini_profiler/request_wrapper.rb +44 -8
- data/lib/rails_mini_profiler/tracing/controller_tracer.rb +15 -0
- data/lib/rails_mini_profiler/tracing/null_trace.rb +7 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracer.rb +37 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracker.rb +37 -0
- data/lib/rails_mini_profiler/tracing/subscriptions.rb +34 -0
- data/lib/rails_mini_profiler/{models → tracing}/trace.rb +10 -2
- data/lib/rails_mini_profiler/tracing/trace_factory.rb +37 -0
- data/lib/rails_mini_profiler/tracing/tracer.rb +31 -0
- data/lib/rails_mini_profiler/tracing/view_tracer.rb +12 -0
- data/lib/rails_mini_profiler/tracing.rb +11 -0
- data/lib/rails_mini_profiler/user.rb +1 -1
- data/lib/rails_mini_profiler/version.rb +1 -4
- data/lib/rails_mini_profiler.rb +5 -7
- data/vendor/assets/images/check.svg +3 -0
- data/vendor/assets/images/chevron.svg +3 -0
- data/vendor/assets/images/copy.svg +6 -0
- data/vendor/assets/images/filter.svg +1 -0
- data/vendor/assets/images/logo_variant.svg +2 -2
- data/vendor/assets/images/search.svg +4 -5
- data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
- metadata +95 -11
- data/Rakefile +0 -18
- data/lib/rails_mini_profiler/tracers.rb +0 -85
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb1212a4d90c46143193b2173e9e136aa6757a74a6591137cba33169235d175d
|
|
4
|
+
data.tar.gz: f799849a5d978c2c22d39b3fa1f4be6c02541f75b7a2312d98a510a1d69a195b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae143e874778e4af9560e5b6521003c5d4933ec86ffe5900a56c57b93791a3d0faa3ac18294cecccac12048dc35c38a70aa7a53ffa91b12d2025a89c0acc0384
|
|
7
|
+
data.tar.gz: f2a8ca075571593f1cce9d1b59910ea5aac8f95a06c277298be9e0e93be5dcd1b013fbaefbef22bf173f6e2db40fed29068cfda44d422aa372cdde30818ec9cf
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2021 hschne
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -63,8 +63,6 @@ route:
|
|
|
63
63
|
```ruby
|
|
64
64
|
# routes.rb
|
|
65
65
|
Rails.application.routes.draw do
|
|
66
|
-
...
|
|
67
|
-
|
|
68
66
|
mount RailsMiniProfiler::Engine => '/rails_mini_profiler'
|
|
69
67
|
end
|
|
70
68
|
```
|
|
@@ -76,18 +74,18 @@ top right that is injected into your pages.
|
|
|
76
74
|
|
|
77
75
|

|
|
78
76
|
|
|
79
|
-
Requests to your application will be profiled automatically. You can view all stored requests by navigating to `yourapp/rails_mini_profiler/profiled_requests`.
|
|
77
|
+
Requests to your application will be profiled automatically. You can view and search all stored requests by navigating to `yourapp/rails_mini_profiler/profiled_requests`.
|
|
80
78
|
|
|
81
79
|
### Request Details
|
|
82
80
|
|
|
83
81
|
<p align="center">
|
|
84
82
|
<img alt="Light" src="docs/images/trace.png" width="45%">
|
|
85
83
|
|
|
86
|
-
<img alt="Dark" src="docs/images/
|
|
84
|
+
<img alt="Dark" src="docs/images/trace-details.png" width="45%">
|
|
87
85
|
</p>
|
|
88
86
|
|
|
89
87
|
This view shows you how your requests spend their time. How much of it is spent in the DB, how much in rendering views?
|
|
90
|
-
|
|
88
|
+
May can filter and clicking on individual traces to gain deeper insights and see detailed information.
|
|
91
89
|
|
|
92
90
|
### Flamegraphs
|
|
93
91
|
|
|
@@ -106,31 +104,23 @@ Flamegraphs are rendered using [Speedscope](https://github.com/jlfwong/speedscop
|
|
|
106
104
|
|
|
107
105
|
Rails Mini Profiler provides a wide array of configuration options. You can find details below. For an example configuration check `initializers/rails_mini_profiler.rb` (or [the template file](https://github.com/hschne/rails-mini-profiler/blob/main/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb)).
|
|
108
106
|
|
|
109
|
-
|
|
|
110
|
-
|
|
107
|
+
| Name | Default | Description |
|
|
108
|
+
|--------------------------|------------------------------|-------------------------------------------------------------------------------------------------|
|
|
111
109
|
| `enabled` | `true` (dev)/ `false` (prod) | Whether or not RMP is enabled |
|
|
112
|
-
| `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
|
|
113
|
-
| `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
|
|
114
110
|
| `flamegraph_enabled` | `true` | Should flamegraphs be recorded automatically? |
|
|
115
111
|
| `flamegraph_sample_rate` | `0.5` | The flamegraph sample rate. How many snapshots per millisecond are created. |
|
|
116
112
|
| `skip_paths` | `[]` | An array of request paths that should not be profiled. Regex allowed. |
|
|
117
|
-
| `storage` | `Storage`
|
|
118
|
-
| `
|
|
119
|
-
|
|
120
|
-
### Request Configuration
|
|
121
|
-
|
|
122
|
-
You may override the configuration by sending request parameters. The following parameters are available:
|
|
113
|
+
| `storage` | `Storage.new` | Storage configuration. See [Storage](#Storage). |
|
|
114
|
+
| `ui` | `UserInterface.new` | UI configuration. See [UI](#UI). |
|
|
115
|
+
| `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Authorization](#Authorization) |
|
|
123
116
|
|
|
124
|
-
| Option | Description |
|
|
125
|
-
| ---------------- | ------------------------------------------------------------------------------------------- |
|
|
126
|
-
| `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediatly. |
|
|
127
117
|
|
|
128
118
|
### Storage
|
|
129
119
|
|
|
130
120
|
Rails Mini Profiler stores profiling information in your database per default. You can configure various details of how
|
|
131
121
|
traces and requests are stored.
|
|
132
122
|
|
|
133
|
-
|
|
|
123
|
+
| Name | Default | Description |
|
|
134
124
|
| ------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
135
125
|
| `database` | `nil` | Set a custom database to be used for storing profiler information. Uses `connect_to` for profiler records |
|
|
136
126
|
| `profiled_requests_table` | `rmp_profiled_requests` | The table to be used to store profiled requests. |
|
|
@@ -141,7 +131,7 @@ Rails Mini Profiler does not offer an automatic way to clean up old profiling in
|
|
|
141
131
|
|
|
142
132
|
```ruby
|
|
143
133
|
# Clockwork
|
|
144
|
-
every(1.month, 'purge rails mini profiler' do
|
|
134
|
+
every(1.month, 'purge rails mini profiler') do
|
|
145
135
|
ProfiledRequestCleanupJob.perform_later
|
|
146
136
|
end
|
|
147
137
|
|
|
@@ -155,17 +145,37 @@ class ProfiledRequestCleanupJob < ApplicationJob
|
|
|
155
145
|
end
|
|
156
146
|
```
|
|
157
147
|
|
|
158
|
-
###
|
|
148
|
+
### UI
|
|
149
|
+
|
|
150
|
+
Rails Mini Profiler allows you to configure various UI features.
|
|
151
|
+
|
|
152
|
+
| Name | Default | Description |
|
|
153
|
+
|---------------------|---------------------------|-------------------------------------------------------------------------------------------------|
|
|
154
|
+
| `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
|
|
155
|
+
| `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
|
|
156
|
+
| `base_controller` | `ApplicationController` | Which controller UI controllers should inherit from. |
|
|
157
|
+
| `page_size` | `25` | The page size for lists shown in the UI. |
|
|
158
|
+
| `webpacker_enabled` | `true` | Use Webpacker if available? Disable to fall back to the asset pipeline. |
|
|
159
|
+
|
|
160
|
+
### Request Configuration
|
|
161
|
+
|
|
162
|
+
You may override static configuration on a per-request by attaching request parameters. For example, `https://myapp.com/api/mydata?rmp_flamegraph=false`
|
|
163
|
+
|
|
164
|
+
| Name | Description |
|
|
165
|
+
| ---------------- | ------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediately. |
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
### Authorization
|
|
161
169
|
|
|
162
|
-
|
|
170
|
+
Profiling information is segregated by user ID. That means users cannot see each other's profiled requests. Per default, individual users are identified by their IP adress.
|
|
171
|
+
|
|
172
|
+
You may change this by setting a custom user provider:
|
|
163
173
|
|
|
164
174
|
```ruby
|
|
165
175
|
config.user_provider = proc { |env| Rack::Request.new(env).ip }
|
|
166
176
|
```
|
|
167
177
|
|
|
168
|
-
You may also explicitly set the user
|
|
178
|
+
You may also explicitly set the user by modifying your application controller (or the controller you have configured as UI base controller):
|
|
169
179
|
|
|
170
180
|
```ruby
|
|
171
181
|
class ApplicationController < ActionController::Base
|
|
@@ -175,15 +185,18 @@ class ApplicationController < ActionController::Base
|
|
|
175
185
|
end
|
|
176
186
|
```
|
|
177
187
|
|
|
178
|
-
|
|
188
|
+
`ApplicationController` is used as the default base class for UI controllers. To change it, you may use `configuration.ui.base_controller`.
|
|
179
189
|
|
|
180
190
|
### Profiling in Production
|
|
181
191
|
|
|
182
192
|
Rails Mini Profiler is not intended for performance reporting. There are other tools for that ( [Skylight](https://www.skylight.io/),
|
|
183
|
-
[New Relic](https://newrelic.com/), [DataDog](https://www.datadoghq.com/)...).
|
|
193
|
+
[New Relic](https://newrelic.com/), [DataDog](https://www.datadoghq.com/)...). But you can still use RMP in production to profile specific requests.
|
|
194
|
+
|
|
195
|
+
Per default, *no requests will be profiled* in production, and the Rails Mini Profiler UI will be inaccessible.
|
|
184
196
|
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
#### Enabling Profiling
|
|
198
|
+
|
|
199
|
+
Since profiling impacts performance, it is recommended that you limit which requests are being profiled:
|
|
187
200
|
|
|
188
201
|
```ruby
|
|
189
202
|
RailsMiniProfiler.configure do |config|
|
|
@@ -191,7 +204,17 @@ RailsMiniProfiler.configure do |config|
|
|
|
191
204
|
end
|
|
192
205
|
```
|
|
193
206
|
|
|
194
|
-
|
|
207
|
+
#### Authorizing Users
|
|
208
|
+
|
|
209
|
+
You must explicitly authorize profiling for users, as well as authenticate them to the UI:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class ApplicationController < ActionController::Base
|
|
213
|
+
before_action do
|
|
214
|
+
RailsMiniProfiler::User.authorize(current_user.id) # Requests by this user will now be profiled, and they get access to the UI
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
```
|
|
195
218
|
|
|
196
219
|
## Why Rails Mini Profiler?
|
|
197
220
|
|
|
@@ -201,13 +224,13 @@ Improving the performance of any application is a 3-step process. You have to an
|
|
|
201
224
|
2. Why is it slow?
|
|
202
225
|
3. Did my solution fix the slowness?
|
|
203
226
|
|
|
204
|
-
I'm a huge fan of [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), and
|
|
227
|
+
I'm a huge fan of [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), and APM tools such as [Skylight](https://www.skylight.io/) or [Scout APM](https://scoutapm.com). Each of these tools has its own areas where it succeeds.
|
|
205
228
|
|
|
206
|
-
APM tools are excellent for profiling your app in production - aka. they show you what is slow - and offer _some_ hints as to what causes the slowdown. `rack-mini-profiler` can do some sampling in production, but excels at providing detailed insight into _why_ something is slow
|
|
229
|
+
APM tools are excellent for profiling your app in production - aka. they show you what is slow - and offer _some_ hints as to what causes the slowdown. `rack-mini-profiler` can do some sampling in production, but excels at providing detailed insight into _why_ something is slow by providing Flamegraphs and detailed query information.
|
|
207
230
|
|
|
208
231
|
Rails Mini Profiler improves upon `rack-mini-profiler` in the latter regard. It is a developer tool, rather than a monitoring tool, and sets a big focus on developer experience. Simply put, it aims to be the best tool available to help you figure out _why_ specific requests are slow.
|
|
209
232
|
|
|
210
|
-
As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e.g. Sinatra) or production sampling, but provides a much better user experience and better
|
|
233
|
+
As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e.g. Sinatra) or production sampling, but provides a much better user experience and better support for API-only applications.
|
|
211
234
|
|
|
212
235
|
## Troubleshooting
|
|
213
236
|
|
|
@@ -215,7 +238,13 @@ As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e
|
|
|
215
238
|
|
|
216
239
|
Rails Mini Profiler is in early development. As such, breaking changes may still be introduced on a regular basis. While Rails Mini Profiler is in pre-release we do not offer upgrade migrations.
|
|
217
240
|
|
|
218
|
-
If an upgrade to Rails Mini Profiler breaks your application, we recommend that you
|
|
241
|
+
If an upgrade to Rails Mini Profiler breaks your application, we recommend that you clean house and start over. Re-run the initializer and overwrite existing files:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
rails rails_mini_profiler:install
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
If only the DB schema is out of date, drop the offending tables and re-run migrations for the latest version:
|
|
219
248
|
|
|
220
249
|
```
|
|
221
250
|
rails rails_mini_profiler:install:migrations
|
|
@@ -226,13 +255,13 @@ rails db:migrate
|
|
|
226
255
|
|
|
227
256
|
Rails Mini Profiler supports API-only apps, but you have to make some small adjustments to use it. At the top of `application.rb` add [Sprockets](https://github.com/rails/sprockets-rails):
|
|
228
257
|
|
|
229
|
-
```
|
|
258
|
+
```ruby
|
|
230
259
|
require "sprockets/railtie"
|
|
231
260
|
```
|
|
232
261
|
|
|
233
262
|
Then, modify `application.rb`:
|
|
234
263
|
|
|
235
|
-
```
|
|
264
|
+
```ruby
|
|
236
265
|
module ApiOnly
|
|
237
266
|
class Application < Rails::Application
|
|
238
267
|
|
|
@@ -242,7 +271,7 @@ module ApiOnly
|
|
|
242
271
|
end
|
|
243
272
|
```
|
|
244
273
|
|
|
245
|
-
**Note
|
|
274
|
+
**Note**: Sprockets and flash are currently required for some of Rails Mini Profiler's UI features. These modifications may no longer be needed in the future.
|
|
246
275
|
|
|
247
276
|
### No Flamegraphs are being recored?
|
|
248
277
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsMiniProfiler
|
|
4
|
+
class DatabaseAdapter
|
|
5
|
+
class << self
|
|
6
|
+
def cast_to_text(column)
|
|
7
|
+
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
|
8
|
+
# Cast json field to text to have access to the LIKE operator
|
|
9
|
+
"#{column}::text"
|
|
10
|
+
else
|
|
11
|
+
column
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module RailsMiniProfiler
|
|
4
|
-
class ApplicationController <
|
|
5
|
-
rescue_from ActiveRecord::RecordNotFound, with:
|
|
4
|
+
class ApplicationController < RailsMiniProfiler.configuration.ui.base_controller
|
|
5
|
+
rescue_from ActiveRecord::RecordNotFound, with: ->(error) { handle(error, 404) }
|
|
6
6
|
|
|
7
|
-
before_action :
|
|
7
|
+
before_action :check_rmp_user
|
|
8
8
|
|
|
9
9
|
protected
|
|
10
10
|
|
|
@@ -17,12 +17,20 @@ module RailsMiniProfiler
|
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
|
-
def
|
|
21
|
-
|
|
20
|
+
def handle(error, status = 500)
|
|
21
|
+
respond_to do |format|
|
|
22
|
+
format.html { redirect_back(fallback_location: profiled_requests_path, alert: error.to_s) }
|
|
23
|
+
format.json { render status: status, json: { message: error.to_s } }
|
|
24
|
+
end
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
def
|
|
25
|
-
|
|
27
|
+
def check_rmp_user
|
|
28
|
+
user = User.get(request.env).present?
|
|
29
|
+
redirect_back(fallback_location: fallback_location) unless user
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fallback_location
|
|
33
|
+
defined?(main_app.root_path) ? main_app.root_path : '/'
|
|
26
34
|
end
|
|
27
35
|
end
|
|
28
36
|
end
|
|
@@ -4,22 +4,28 @@ require_dependency 'rails_mini_profiler/application_controller'
|
|
|
4
4
|
|
|
5
5
|
module RailsMiniProfiler
|
|
6
6
|
class ProfiledRequestsController < ApplicationController
|
|
7
|
+
include Pagy::Backend
|
|
8
|
+
|
|
7
9
|
before_action :set_profiled_request, only: %i[show destroy]
|
|
8
10
|
|
|
9
11
|
def index
|
|
10
|
-
@profiled_requests = ProfiledRequest
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@profiled_requests = @profiled_requests.where('request_path LIKE ?', "%#{params[:path]}%") if params[:path]
|
|
12
|
+
@profiled_requests = ProfiledRequest.where(user_id: user_id).order(id: :desc)
|
|
13
|
+
search = ProfiledRequestSearch.new(index_params, scope: @profiled_requests)
|
|
14
|
+
@pagy, @profiled_requests = pagy(search.results, items: configuration.ui.page_size)
|
|
14
15
|
@profiled_requests = @profiled_requests.map { |request| present(request) }
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def show
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
search = TraceSearch.new(show_params, scope: @profiled_request.traces)
|
|
20
|
+
context = {
|
|
21
|
+
start: @profiled_request.start,
|
|
22
|
+
finish: @profiled_request.finish,
|
|
23
|
+
total_duration: @profiled_request.duration,
|
|
24
|
+
total_allocations: @profiled_request.allocations
|
|
25
|
+
}
|
|
26
|
+
@traces = search.results
|
|
21
27
|
.order(:start)
|
|
22
|
-
.map { |trace| present(trace,
|
|
28
|
+
.map { |trace| present(trace, context: context) }
|
|
23
29
|
@profiled_request = present(@profiled_request)
|
|
24
30
|
end
|
|
25
31
|
|
|
@@ -30,16 +36,25 @@ module RailsMiniProfiler
|
|
|
30
36
|
|
|
31
37
|
def destroy_all
|
|
32
38
|
ProfiledRequest.transaction do
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
profiled_requests = ProfiledRequest.where(user_id: user_id)
|
|
40
|
+
profiled_requests = ProfiledRequestSearch.new(index_params, scope: profiled_requests).results
|
|
41
|
+
Flamegraph.joins(:profiled_request).merge(profiled_requests).delete_all
|
|
42
|
+
Trace.joins(:profiled_request).merge(profiled_requests).delete_all
|
|
43
|
+
profiled_requests.delete_all
|
|
37
44
|
end
|
|
38
|
-
redirect_to profiled_requests_url, notice: 'Profiled
|
|
45
|
+
redirect_to profiled_requests_url, notice: 'Profiled requests cleared', status: :see_other
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
private
|
|
42
49
|
|
|
50
|
+
def show_params
|
|
51
|
+
params.permit(:id, :payload, :duration, :allocations, name: [])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def index_params
|
|
55
|
+
params.permit(:path, :duration, id: [], method: [], media_type: [], status: [])
|
|
56
|
+
end
|
|
57
|
+
|
|
43
58
|
def user_id
|
|
44
59
|
@user_id ||= User.get(request.env)
|
|
45
60
|
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module RailsMiniProfiler
|
|
4
4
|
module ApplicationHelper
|
|
5
|
+
include Pagy::Frontend
|
|
6
|
+
|
|
5
7
|
def present(model, presenter_class = nil, **kwargs)
|
|
6
8
|
klass = presenter_class || "#{model.class}Presenter".constantize
|
|
7
9
|
presenter = klass.new(model, self, **kwargs)
|
|
@@ -10,7 +12,7 @@ module RailsMiniProfiler
|
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def inline_svg(path, options = {})
|
|
13
|
-
if defined?(Webpacker::Engine)
|
|
15
|
+
if defined?(Webpacker::Engine) && RailsMiniProfiler.configuration.ui.webpacker_enabled
|
|
14
16
|
path = "media/images/#{path}"
|
|
15
17
|
inline_svg_pack_tag(path, options)
|
|
16
18
|
else
|
|
@@ -12,5 +12,15 @@ module RailsMiniProfiler
|
|
|
12
12
|
def formatted_allocations(allocations)
|
|
13
13
|
number_to_human(allocations, units: { unit: '', thousand: 'k', million: 'M', billion: 'B', trillion: 'T' })
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
def trace_display_name(name)
|
|
17
|
+
{
|
|
18
|
+
'sql.active_record': 'ActiveRecord Query',
|
|
19
|
+
'instantiation.active_record': 'ActiveRecord Instantiation',
|
|
20
|
+
'render_template.action_view': 'Render View',
|
|
21
|
+
'render_partial.action_view': 'Render Partial',
|
|
22
|
+
'process_action.action_controller': 'Controller'
|
|
23
|
+
}[name.to_sym]
|
|
24
|
+
end
|
|
15
25
|
end
|
|
16
26
|
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
2
|
+
viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3" xml:space="preserve">
|
|
3
|
+
<path fill="currentColor"
|
|
4
|
+
d="M314.25 85.4h-227c-21.3 0-38.6 17.3-38.6 38.6v325.7c0 21.3 17.3 38.6 38.6 38.6h227c21.3 0 38.6-17.3 38.6-38.6V124c-.1-21.3-17.4-38.6-38.6-38.6zm11.5 364.2c0 6.4-5.2 11.6-11.6 11.6h-227c-6.4 0-11.6-5.2-11.6-11.6V124c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.6z"/>
|
|
5
|
+
<path fill="currentColor"
|
|
6
|
+
d="M401.05 0h-227c-21.3 0-38.6 17.3-38.6 38.6 0 7.5 6 13.5 13.5 13.5s13.5-6 13.5-13.5c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.7c0 6.4-5.2 11.6-11.6 11.6-7.5 0-13.5 6-13.5 13.5s6 13.5 13.5 13.5c21.3 0 38.6-17.3 38.6-38.6V38.6c0-21.3-17.3-38.6-38.6-38.6z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
|
2
|
-
<style
|
|
1
|
+
<svg id="logo-variant" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
|
2
|
+
<style>#logo-variant path{fill:none;stroke-width:64;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4}</style>
|
|
3
3
|
<defs>
|
|
4
4
|
<linearGradient id="lightShadow">
|
|
5
5
|
<stop offset="0" style="stop-color:#c22121;stop-opacity:1"/>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
<g id="Search" transform="translate(2.000000, 2.000000)" fill="#000000" fill-rule="nonzero">
|
|
3
|
+
<g id="Iconly/Bulk/Search" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
|
|
4
|
+
<g id="Search" transform="translate(2.000000, 2.000000)" fill="currentColor" fill-rule="nonzero">
|
|
6
5
|
<ellipse id="Ellipse_746" cx="8.59921927" cy="8.65324385" rx="8.59921927" ry="8.65324385"></ellipse>
|
|
7
|
-
<path d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
|
6
|
+
<path fill="currentColor" d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
|
8
7
|
</g>
|
|
9
8
|
</g>
|
|
10
|
-
</svg>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["count"];
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.setCount();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
checkAll() {
|
|
11
|
+
this.setAllCheckboxes(true);
|
|
12
|
+
this.setCount();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
checkNone() {
|
|
16
|
+
this.setAllCheckboxes(false);
|
|
17
|
+
this.setCount();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
onChecked() {
|
|
21
|
+
this.setCount();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setAllCheckboxes(checked) {
|
|
25
|
+
this.checkboxes.forEach((el) => {
|
|
26
|
+
const checkbox = el;
|
|
27
|
+
|
|
28
|
+
if (!checkbox.disabled) {
|
|
29
|
+
checkbox.checked = checked;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setCount() {
|
|
35
|
+
if (this.hasCountTarget) {
|
|
36
|
+
const count = this.selectedCheckboxes.length;
|
|
37
|
+
this.countTarget.innerHTML = `${count} selected`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get selectedCheckboxes() {
|
|
42
|
+
return this.checkboxes.filter((c) => c.checked);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get checkboxes() {
|
|
46
|
+
return new Array(...this.element.querySelectorAll("input[type=checkbox]"));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["button", "source"];
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
if (!this.hasButtonTarget) return;
|
|
8
|
+
|
|
9
|
+
this.originalText = this.buttonTarget.innerText;
|
|
10
|
+
this.successDuration = 2000;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
copy(event) {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
|
|
16
|
+
let text = this.sourceTarget.innerText;
|
|
17
|
+
const filter = this.data.get("filter");
|
|
18
|
+
if (filter) {
|
|
19
|
+
text = new RegExp(filter).exec(text)[0];
|
|
20
|
+
}
|
|
21
|
+
const temporaryInput = document.createElement("textarea");
|
|
22
|
+
temporaryInput.value = text;
|
|
23
|
+
document.body.appendChild(temporaryInput);
|
|
24
|
+
temporaryInput.select();
|
|
25
|
+
document.execCommand("copy");
|
|
26
|
+
document.body.removeChild(temporaryInput);
|
|
27
|
+
|
|
28
|
+
this.copied();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
copied() {
|
|
32
|
+
if (!this.hasButtonTarget) return;
|
|
33
|
+
|
|
34
|
+
if (this.timeout) {
|
|
35
|
+
clearTimeout(this.timeout);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const copiedClass = this.data.get("copiedClass");
|
|
39
|
+
if (copiedClass) {
|
|
40
|
+
this.buttonTarget.classList.add(copiedClass);
|
|
41
|
+
}
|
|
42
|
+
const copiedMessage = this.data.get("copiedMessage");
|
|
43
|
+
const content = this.buttonTarget.innerHTML;
|
|
44
|
+
if (copiedMessage) {
|
|
45
|
+
this.buttonTarget.innerHTML = copiedMessage;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.timeout = setTimeout(() => {
|
|
49
|
+
this.buttonTarget.classList.remove(copiedClass);
|
|
50
|
+
this.buttonTarget.innerHTML = content;
|
|
51
|
+
}, this.successDuration);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["enable"];
|
|
5
|
+
|
|
6
|
+
enable() {
|
|
7
|
+
this.enableTarget.disabled = false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
disable() {
|
|
11
|
+
this.enableTarget.disabled = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
change(event) {
|
|
15
|
+
if (event.type.match(/rmp:select:.*/)) {
|
|
16
|
+
if (event.detail.count > 0) {
|
|
17
|
+
this.enable();
|
|
18
|
+
} else {
|
|
19
|
+
this.disable();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["filter"];
|
|
5
|
+
|
|
6
|
+
apply() {
|
|
7
|
+
location.href = `${window.location.pathname}?${this.params}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
reset() {
|
|
11
|
+
location.href = `${window.location.pathname}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
post() {
|
|
15
|
+
const token = document.head.querySelector(
|
|
16
|
+
'meta[name="csrf-token"]'
|
|
17
|
+
).content;
|
|
18
|
+
const path = `${window.location.pathname}/destroy_all?${this.params}`;
|
|
19
|
+
fetch(path, {
|
|
20
|
+
method: "DELETE",
|
|
21
|
+
redirect: "follow",
|
|
22
|
+
headers: {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
credentials: "same-origin",
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({ authenticity_token: token }),
|
|
27
|
+
}).then((response) => {
|
|
28
|
+
if (response.redirected) {
|
|
29
|
+
window.location.href = response.url;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get params() {
|
|
35
|
+
return this.activeFilterTargets()
|
|
36
|
+
.map((t) => `${t.name}=${t.value}`)
|
|
37
|
+
.join("&");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
activeFilterTargets() {
|
|
41
|
+
return this.filterTargets.filter(function (target) {
|
|
42
|
+
if (target.type === "checkbox" || target.type === "radio")
|
|
43
|
+
return target.checked;
|
|
44
|
+
|
|
45
|
+
return target.value.length > 0;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|