rails_mini_profiler 0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +302 -0
  4. data/app/assets/config/rails_mini_profiler_manifest.js +1 -0
  5. data/app/assets/javascripts/rails_mini_profiler.js +15 -0
  6. data/app/assets/stylesheets/rails_mini_profiler/application.css +16 -0
  7. data/app/controllers/rails_mini_profiler/application_controller.rb +33 -0
  8. data/app/controllers/rails_mini_profiler/flamegraphs_controller.rb +23 -0
  9. data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +68 -0
  10. data/app/helpers/rails_mini_profiler/application_helper.rb +23 -0
  11. data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +16 -0
  12. data/app/javascript/images/bookmark.svg +10 -0
  13. data/app/javascript/images/chart.svg +12 -0
  14. data/app/javascript/images/check.svg +3 -0
  15. data/app/javascript/images/chevron.svg +3 -0
  16. data/app/javascript/images/delete.svg +9 -0
  17. data/app/javascript/images/filter.svg +1 -0
  18. data/app/javascript/images/graph.svg +11 -0
  19. data/app/javascript/images/logo.svg +18 -0
  20. data/app/javascript/images/logo_variant.svg +32 -0
  21. data/app/javascript/images/search.svg +9 -0
  22. data/app/javascript/images/setting.svg +10 -0
  23. data/app/javascript/images/show.svg +11 -0
  24. data/app/javascript/js/checklist_controller.js +48 -0
  25. data/app/javascript/js/enable_controller.js +24 -0
  26. data/app/javascript/js/filter_controller.js +44 -0
  27. data/app/javascript/js/search_controller.js +18 -0
  28. data/app/javascript/js/select_controller.js +47 -0
  29. data/app/javascript/packs/rails-mini-profiler.js +88 -0
  30. data/app/javascript/stylesheets/components/page_header/page_header.scss +3 -0
  31. data/app/javascript/stylesheets/components/pagination.scss +55 -0
  32. data/app/javascript/stylesheets/components/profiled_request_table/placeholder.scss +33 -0
  33. data/app/javascript/stylesheets/components/profiled_request_table/profiled_request_table.scss +179 -0
  34. data/app/javascript/stylesheets/flamegraph.scss +10 -0
  35. data/app/javascript/stylesheets/flashes.scss +15 -0
  36. data/app/javascript/stylesheets/navbar.scss +44 -0
  37. data/app/javascript/stylesheets/profiled_requests.scss +89 -0
  38. data/app/javascript/stylesheets/rails-mini-profiler.scss +205 -0
  39. data/app/javascript/stylesheets/traces.scss +82 -0
  40. data/app/models/rails_mini_profiler/application_record.rb +17 -0
  41. data/app/models/rails_mini_profiler/controller_trace.rb +37 -0
  42. data/app/models/rails_mini_profiler/flamegraph.rb +37 -0
  43. data/app/models/rails_mini_profiler/instantiation_trace.rb +37 -0
  44. data/app/models/rails_mini_profiler/profiled_request.rb +65 -0
  45. data/app/models/rails_mini_profiler/render_partial_trace.rb +37 -0
  46. data/app/models/rails_mini_profiler/render_template_trace.rb +37 -0
  47. data/app/models/rails_mini_profiler/rmp_trace.rb +35 -0
  48. data/app/models/rails_mini_profiler/sequel_trace.rb +37 -0
  49. data/app/models/rails_mini_profiler/trace.rb +46 -0
  50. data/app/presenters/rails_mini_profiler/base_presenter.rb +25 -0
  51. data/app/presenters/rails_mini_profiler/controller_trace_presenter.rb +18 -0
  52. data/app/presenters/rails_mini_profiler/instantiation_trace_presenter.rb +14 -0
  53. data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +38 -0
  54. data/app/presenters/rails_mini_profiler/render_partial_trace_presenter.rb +11 -0
  55. data/app/presenters/rails_mini_profiler/render_template_trace_presenter.rb +15 -0
  56. data/app/presenters/rails_mini_profiler/rmp_trace_presenter.rb +9 -0
  57. data/app/presenters/rails_mini_profiler/sequel_trace_presenter.rb +69 -0
  58. data/app/presenters/rails_mini_profiler/trace_presenter.rb +61 -0
  59. data/app/search/rails_mini_profiler/base_search.rb +67 -0
  60. data/app/search/rails_mini_profiler/profiled_request_search.rb +34 -0
  61. data/app/views/layouts/rails_mini_profiler/application.html.erb +15 -0
  62. data/app/views/layouts/rails_mini_profiler/flamegraph.html.erb +11 -0
  63. data/app/views/models/_flamegraph.json.jb +3 -0
  64. data/app/views/models/_profiled_request.jb +3 -0
  65. data/app/views/models/_trace.jb +3 -0
  66. data/app/views/rails_mini_profiler/badge.html.erb +37 -0
  67. data/app/views/rails_mini_profiler/flamegraphs/show.html.erb +13 -0
  68. data/app/views/rails_mini_profiler/flamegraphs/show.json.jb +3 -0
  69. data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +9 -0
  70. data/app/views/rails_mini_profiler/profiled_requests/index.json.jb +3 -0
  71. data/app/views/rails_mini_profiler/profiled_requests/shared/_trace.html.erb +40 -0
  72. data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +20 -0
  73. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
  74. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
  75. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
  76. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
  77. data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +40 -0
  78. data/app/views/rails_mini_profiler/profiled_requests/show.json.jb +5 -0
  79. data/app/views/rails_mini_profiler/shared/_flashes.html.erb +8 -0
  80. data/app/views/rails_mini_profiler/shared/_head.erb +13 -0
  81. data/app/views/rails_mini_profiler/shared/_navbar.html.erb +15 -0
  82. data/config/routes.rb +11 -0
  83. data/db/migrate/20210621185018_create_rmp.rb +46 -0
  84. data/lib/generators/rails_mini_profiler/USAGE +2 -0
  85. data/lib/generators/rails_mini_profiler/install_generator.rb +40 -0
  86. data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.js.erb +13 -0
  87. data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +29 -0
  88. data/lib/rails_mini_profiler/badge.rb +84 -0
  89. data/lib/rails_mini_profiler/configuration/storage.rb +47 -0
  90. data/lib/rails_mini_profiler/configuration/user_interface.rb +48 -0
  91. data/lib/rails_mini_profiler/configuration.rb +65 -0
  92. data/lib/rails_mini_profiler/engine.rb +34 -0
  93. data/lib/rails_mini_profiler/flamegraph_guard.rb +47 -0
  94. data/lib/rails_mini_profiler/guard.rb +57 -0
  95. data/lib/rails_mini_profiler/logger.rb +25 -0
  96. data/lib/rails_mini_profiler/middleware.rb +74 -0
  97. data/lib/rails_mini_profiler/models/base_model.rb +23 -0
  98. data/lib/rails_mini_profiler/redirect.rb +33 -0
  99. data/lib/rails_mini_profiler/request_context.rb +86 -0
  100. data/lib/rails_mini_profiler/request_wrapper.rb +69 -0
  101. data/lib/rails_mini_profiler/response_wrapper.rb +32 -0
  102. data/lib/rails_mini_profiler/tracing/controller_tracer.rb +15 -0
  103. data/lib/rails_mini_profiler/tracing/null_trace.rb +7 -0
  104. data/lib/rails_mini_profiler/tracing/sequel_tracer.rb +37 -0
  105. data/lib/rails_mini_profiler/tracing/sequel_tracker.rb +37 -0
  106. data/lib/rails_mini_profiler/tracing/subscriptions.rb +34 -0
  107. data/lib/rails_mini_profiler/tracing/trace.rb +45 -0
  108. data/lib/rails_mini_profiler/tracing/trace_factory.rb +37 -0
  109. data/lib/rails_mini_profiler/tracing/tracer.rb +31 -0
  110. data/lib/rails_mini_profiler/tracing/view_tracer.rb +12 -0
  111. data/lib/rails_mini_profiler/tracing.rb +11 -0
  112. data/lib/rails_mini_profiler/user.rb +40 -0
  113. data/lib/rails_mini_profiler/version.rb +5 -0
  114. data/lib/rails_mini_profiler.rb +79 -0
  115. data/lib/tasks/rails_mini_profiler_tasks.rake +8 -0
  116. data/public/rails_mini_profiler/speedscope/LICENSE +21 -0
  117. data/public/rails_mini_profiler/speedscope/demangle-cpp.1768f4cc.js +4 -0
  118. data/public/rails_mini_profiler/speedscope/demangle-cpp.1768f4cc.js.map +1 -0
  119. data/public/rails_mini_profiler/speedscope/favicon-16x16.f74b3187.png +0 -0
  120. data/public/rails_mini_profiler/speedscope/favicon-32x32.bc503437.png +0 -0
  121. data/public/rails_mini_profiler/speedscope/file-format-schema.json +324 -0
  122. data/public/rails_mini_profiler/speedscope/import.e3a73ef4.js +117 -0
  123. data/public/rails_mini_profiler/speedscope/import.e3a73ef4.js.map +1 -0
  124. data/public/rails_mini_profiler/speedscope/index.html +2 -0
  125. data/public/rails_mini_profiler/speedscope/release.txt +3 -0
  126. data/public/rails_mini_profiler/speedscope/reset.8c46b7a1.css +2 -0
  127. data/public/rails_mini_profiler/speedscope/reset.8c46b7a1.css.map +1 -0
  128. data/public/rails_mini_profiler/speedscope/source-map.438fa06b.js +24 -0
  129. data/public/rails_mini_profiler/speedscope/source-map.438fa06b.js.map +1 -0
  130. data/public/rails_mini_profiler/speedscope/speedscope.026f36b0.js +200 -0
  131. data/public/rails_mini_profiler/speedscope/speedscope.026f36b0.js.map +1 -0
  132. data/vendor/assets/images/bookmark.svg +10 -0
  133. data/vendor/assets/images/chart.svg +12 -0
  134. data/vendor/assets/images/check.svg +3 -0
  135. data/vendor/assets/images/chevron.svg +3 -0
  136. data/vendor/assets/images/delete.svg +9 -0
  137. data/vendor/assets/images/filter.svg +1 -0
  138. data/vendor/assets/images/graph.svg +11 -0
  139. data/vendor/assets/images/logo.svg +18 -0
  140. data/vendor/assets/images/logo_variant.svg +32 -0
  141. data/vendor/assets/images/search.svg +9 -0
  142. data/vendor/assets/images/setting.svg +10 -0
  143. data/vendor/assets/images/show.svg +11 -0
  144. data/vendor/assets/javascripts/rails-mini-profiler.css +1 -0
  145. data/vendor/assets/javascripts/rails-mini-profiler.js +1 -0
  146. metadata +248 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 96a6d11caabcae6df487601bf8f2283ddfd4d72eecefcab62355f56ae4dbf552
4
+ data.tar.gz: ddc642e94f3e9832023d3de12e532490ad509c4a9cfc61b4dd010dab811dc750
5
+ SHA512:
6
+ metadata.gz: be8f67fe210563c0857dd665b7a58ce02648814f7b8848eded9919a8af667332d3718d8d5525a4ea5777f380f8c16dfcb41baaec5d74c3b9f53b9284745809de
7
+ data.tar.gz: 237ddd6a552449b30e13fb0f36d866c65b3be913e4cce8f4f69b9d48e8f800cb75f773ede153409f1dc0aa21314fa0cfd1f6f80551a7e2621aa43b5bb28768ea
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 ADDED
@@ -0,0 +1,302 @@
1
+ <div align="center">
2
+
3
+ # Rails Mini Profiler
4
+
5
+ <img alt="logo" src="docs/images/logo.png" width="300px" height="auto">
6
+
7
+ ### Performance profiling for Rails, made simple.
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/rails_mini_profiler.svg)](https://badge.fury.io/rb/rails_mini_profiler)
10
+ [![Main](https://github.com/hschne/rails-mini-profiler/actions/workflows/main.yml/badge.svg)](https://github.com/hschne/rails-mini-profiler/actions/workflows/main.yml)
11
+ [![License](https://img.shields.io/github/license/hschne/rails-mini-profiler)](https://img.shields.io/github/license/hschne/rails-mini-profiler)
12
+
13
+ [![Maintainability](https://api.codeclimate.com/v1/badges/1fcc2f4d01ab5bf7a260/maintainability)](https://codeclimate.com/github/hschne/rails-mini-profiler/maintainability)
14
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/1fcc2f4d01ab5bf7a260/test_coverage)](https://codeclimate.com/github/hschne/rails-mini-profiler/test_coverage)
15
+
16
+ </div>
17
+
18
+ ## What's this?
19
+
20
+ Rails Mini Profiler is an easy-to-use performance profiler for your Rails applications. It is heavily inspired by [Rack Mini Profiler](https://github.com/MiniProfiler/rack-mini-profiler) and other APM tools. To find out how it stacks up against those check out [Why Rails Mini Profiler](#why-rails-mini-profiler)?
21
+
22
+ To see it in action view the preview below:
23
+
24
+ <div align="center">
25
+
26
+ [![Rails Mini Profiler Preview](http://img.youtube.com/vi/fSR8fCcsO8Q/0.jpg)](https://www.youtube.com/watch?v=fSR8fCcsO8Q)
27
+
28
+ </div>
29
+
30
+ **Note**: This gem is in early development and I'm looking for contributors. Try it out and leave some feedback, it really goes a long way in helping me out with development. Any [feature request](https://github.com/hschne/rails-mini-profiler/issues/new?assignees=&labels=type%3ABug&template=FEATURE_REQUEST.md&title=) or [bug report](https://github.com/hschne/rails-mini-profiler/issues/new?assignees=&labels=type%3AEnhancement&template=BUG_REPORT.md&title=) is welcome. If you like this project, leave a star to show your support! ⭐
31
+
32
+ ## Getting Started
33
+
34
+ Add Rails Mini Profiler to your Gemfile:
35
+
36
+ ```ruby
37
+ gem 'rails_mini_profiler'
38
+ ```
39
+
40
+ Install the gem and run the installer:
41
+
42
+ ```bash
43
+ bundle install
44
+ rails rails_mini_profiler:install
45
+ ```
46
+
47
+ Inspect the generated migration in `db/migrate` and run it:
48
+
49
+ ```
50
+ rails db:migrate
51
+ ```
52
+
53
+ Start your Rails application and perform some requests. You can either click the little hedgehog 🦔 on the top
54
+ left or navigate to `/rails_mini_profiler` to view collected performance metrics.
55
+
56
+ ## Usage
57
+
58
+ Rails Mini Profiler provides detailed information about your requests to help you figure out why certain requests perform poorly.
59
+
60
+ Installing it will generate a new initializer `config/initializers/rails_mini_profiler.rb` and add a new
61
+ route:
62
+
63
+ ```ruby
64
+ # routes.rb
65
+ Rails.application.routes.draw do
66
+ ...
67
+
68
+ mount RailsMiniProfiler::Engine => '/rails_mini_profiler'
69
+ end
70
+ ```
71
+
72
+ Once you perform requests against your applications you can inspect them using that route, or by clicking the badge on the
73
+ top right that is injected into your pages.
74
+
75
+ ### Request Overview
76
+
77
+ ![overview](docs/images/overview.png)
78
+
79
+ Requests to your application will be profiled automatically. You can view all stored requests by navigating to `yourapp/rails_mini_profiler/profiled_requests`.
80
+
81
+ ### Request Details
82
+
83
+ <p align="center">
84
+ <img alt="Light" src="docs/images/trace.png" width="45%">
85
+ &nbsp; &nbsp; &nbsp; &nbsp;
86
+ <img alt="Dark" src="docs/images/sequel.png" width="45%">
87
+ </p>
88
+
89
+ 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
+ By clicking on individual traces you can find out detailed information.
91
+
92
+ ### Flamegraphs
93
+
94
+ Rails Mini Profiler automatically records Flamegraphs for profiled requests. To enable this feature, add [Stackprof](https://github.com/tmm1/stackprof)
95
+ to your Gemfile:
96
+
97
+ ```ruby
98
+ gem 'stackprof'
99
+ ```
100
+
101
+ For convenience, Flamegraphs are recorded for every request. This may incur a significant performance penalty. To change the default behavior see [Configuration](#Configuration).
102
+
103
+ Flamegraphs are rendered using [Speedscope](https://github.com/jlfwong/speedscope). See [Troubleshooting](#Troubleshooting) if Flamegraphs are not rendering correctly.
104
+
105
+ ## Configuration
106
+
107
+ 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
+
109
+ | Name | Default | Description |
110
+ |--------------------------|------------------------------|-------------------------------------------------------------------------------------------------|
111
+ | `enabled` | `true` (dev)/ `false` (prod) | Whether or not RMP is enabled |
112
+ | `flamegraph_enabled` | `true` | Should flamegraphs be recorded automatically? |
113
+ | `flamegraph_sample_rate` | `0.5` | The flamegraph sample rate. How many snapshots per millisecond are created. |
114
+ | `skip_paths` | `[]` | An array of request paths that should not be profiled. Regex allowed. |
115
+ | `storage` | `Storage.new` | Storage configuration. See [Storage](#Storage). |
116
+ | `ui` | `UserInterface.new` | UI configuration. See [UI](#UI). |
117
+ | `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Users](#Users) |
118
+
119
+ ### Request Configuration
120
+
121
+ You may override the configuration by sending request parameters. The following parameters are available:
122
+
123
+ | Name | Description |
124
+ | ---------------- | ------------------------------------------------------------------------------------------- |
125
+ | `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediatly. |
126
+
127
+ ### Storage
128
+
129
+ Rails Mini Profiler stores profiling information in your database per default. You can configure various details of how
130
+ traces and requests are stored.
131
+
132
+ | Name | Default | Description |
133
+ | ------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- |
134
+ | `database` | `nil` | Set a custom database to be used for storing profiler information. Uses `connect_to` for profiler records |
135
+ | `profiled_requests_table` | `rmp_profiled_requests` | The table to be used to store profiled requests. |
136
+ | `flamegraphs_table` | `rmp_flamegraphs` | The table to be used to store flamegraphs. |
137
+ | `traces_table` | `rmp_traces` | The table to be used to store traces. |
138
+
139
+ Rails Mini Profiler does not offer an automatic way to clean up old profiling information. It is recommended you add a sweeper job to clean up old profiled requests periodically (e.g. using [clockwork](https://github.com/adamwiggins/clockwork). For example, with ActiveJob:
140
+
141
+ ```ruby
142
+ # Clockwork
143
+ every(1.month, 'purge rails mini profiler' do
144
+ ProfiledRequestCleanupJob.perform_later
145
+ end
146
+
147
+ # ActiveJob
148
+ class ProfiledRequestCleanupJob < ApplicationJob
149
+ queue_as :default
150
+
151
+ def perform
152
+ RailsMiniProfiler::ProfiledRequest.where('created_at < ?', 1.month.ago).destroy_all
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### UI
158
+
159
+ Rails Mini Profiler allows you to configure various UI features.
160
+
161
+ | Name | Default | Description |
162
+ |------------------|--------------|-------------------------------------------------------------------------------------------------|
163
+ | `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
164
+ | `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
165
+ | `page_size` | `25` | The page size for lists shown in the UI. |
166
+
167
+ ### Users
168
+
169
+ Profiling information is segregated by user ID. That means users cannot see each other's profiled requests.
170
+
171
+ Per default, individual users are identified by their IP address. You may change this by setting a custom user provider:
172
+
173
+ ```ruby
174
+ config.user_provider = proc { |env| Rack::Request.new(env).ip }
175
+ ```
176
+
177
+ You may also explicitly set the user from the application itself:
178
+
179
+ ```ruby
180
+ class ApplicationController < ActionController::Base
181
+ before_action do
182
+ RailsMiniProfiler::User.authorize(current_user.id)
183
+ end
184
+ end
185
+ ```
186
+
187
+ Note that you **must** set the current user when running Rails Mini Profiler in production. No profiles will be saved otherwise.
188
+
189
+ ### Profiling in Production
190
+
191
+ Rails Mini Profiler is not intended for performance reporting. There are other tools for that ( [Skylight](https://www.skylight.io/),
192
+ [New Relic](https://newrelic.com/), [DataDog](https://www.datadoghq.com/)...).
193
+
194
+ However, you can still use it in production to profile specific requests. Since profiling impacts performance, it is recommended
195
+ that you limit which requests are being profiled:
196
+
197
+ ```ruby
198
+ RailsMiniProfiler.configure do |config|
199
+ config.enabled = proc { |env| env.headers['RMP_ENABLED'].present? }
200
+ end
201
+ ```
202
+
203
+ Only requests by explicitly set users will be stored. To configure how individual users are identified see [Users](#Users)
204
+
205
+ ## Why Rails Mini Profiler?
206
+
207
+ Improving the performance of any application is a 3-step process. You have to answer these questions:
208
+
209
+ 1. What is slow?
210
+ 2. Why is it slow?
211
+ 3. Did my solution fix the slowness?
212
+
213
+ I'm a huge fan of [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), and AMP tools such as [Skylight](https://www.skylight.io/) or [Scout APM](https://scoutapm.com), and each of these tools has its place.
214
+
215
+ 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, using Flamegraphs and detailed query information.
216
+
217
+ 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.
218
+
219
+ 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 supports API-only applications.
220
+
221
+ ## Troubleshooting
222
+
223
+ ### Upgrading
224
+
225
+ 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.
226
+
227
+ 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:
228
+
229
+ ```bash
230
+ rails rails_mini_profiler:install
231
+ ```
232
+
233
+ If only the DB schema is out of date, drop the offending tables and re-run migrations for the latest version:
234
+
235
+ ```
236
+ rails rails_mini_profiler:install:migrations
237
+ rails db:migrate
238
+ ```
239
+
240
+ ### Support for API-Only Apps
241
+
242
+ 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):
243
+
244
+ ```
245
+ require "sprockets/railtie"
246
+ ```
247
+
248
+ Then, modify `application.rb`:
249
+
250
+ ```
251
+ module ApiOnly
252
+ class Application < Rails::Application
253
+
254
+ config.api_only = true # Either set this to false
255
+ config.middleware.use ActionDispatch::Flash # Or add this
256
+ end
257
+ end
258
+ ```
259
+
260
+ **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.
261
+
262
+ ### No Flamegraphs are being recored?
263
+
264
+ Make sure you have added [StackProf](https://github.com/tmm1/stackprof) to your Gemfile.
265
+
266
+ ```ruby
267
+ gem 'stackprof'
268
+ ```
269
+
270
+ ### Flamegraphs are not rendering?
271
+
272
+ Flamegraphs are loaded into [Speedscope](https://github.com/jlfwong/speedscope) using an Iframe and URI Encoded blobs (see [source](https://github.com/hschne/rails-mini-profiler/blob/main/app/views/rails_mini_profiler/flamegraphs/show.html.erb))
273
+ If your browser gives you warnings about blocking content due to CSP you _must_ enable `blob` as default source:
274
+
275
+ ```ruby
276
+ Rails.application.config.content_security_policy do |policy|
277
+ policy.default_src :self, :blob
278
+ ...
279
+ end
280
+ ```
281
+
282
+ ### Some requests have no Flamegraphs attached?
283
+
284
+ [StackProf](https://github.com/tmm1/stackprof), which is used for recording Flamegraphs, does not work on concurrent requests.
285
+ Because of this, concurrent requests may skip recording a Flamegraph.
286
+
287
+ It is recommended that you resend _only_ the request you wish to build a Flamegraph for.
288
+
289
+ ## Credit
290
+
291
+ This project was heavily inspired by projects such as [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) and
292
+ [rack-profiler](https://github.com/dawanda/rack-profiler). [Skylight](https://www.skylight.io/) was also a huge influence.
293
+
294
+ [Lena Schnedlitz](https://github.com/LenaSchnedlitz) designed the Logo and provided great support. Without her supreme CSS skills this project would not have been possible 🙌
295
+
296
+ ## Contributing
297
+
298
+ See [Contributing](CONTRIBUTING.md)
299
+
300
+ ## License
301
+
302
+ This gem is available as open source under the terms of the [MIT License](LICENSE).
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/rails_mini_profiler .css
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require_tree .
15
+ //= require rails-mini-profiler
@@ -0,0 +1,16 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ *= require rails-mini-profiler
16
+ */
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsMiniProfiler
4
+ class ApplicationController < ActionController::Base
5
+ include Pagy::Backend
6
+
7
+ rescue_from ActiveRecord::RecordNotFound, with: ->(error) { handle(error, 404) }
8
+
9
+ before_action :check_current_user
10
+
11
+ protected
12
+
13
+ def present(model, presenter_class = nil, **kwargs)
14
+ klass = presenter_class || "RailsMiniProfiler::#{model.class.to_s.demodulize}Presenter".constantize
15
+ presenter = klass.new(model, view_context, **kwargs)
16
+ yield(presenter) if block_given?
17
+ presenter
18
+ end
19
+
20
+ private
21
+
22
+ def handle(error, status = 500)
23
+ respond_to do |format|
24
+ format.html { redirect_back(fallback_location: profiled_requests_path, alert: error.to_s) }
25
+ format.json { render status: status, json: { message: error.to_s } }
26
+ end
27
+ end
28
+
29
+ def check_current_user
30
+ redirect_back(fallback_location: root_path) unless User.get(request.env).present?
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'rails_mini_profiler/application_controller'
4
+
5
+ module RailsMiniProfiler
6
+ class FlamegraphsController < ApplicationController
7
+ layout 'rails_mini_profiler/flamegraph'
8
+
9
+ before_action :set_flamegraph, only: %i[show]
10
+
11
+ def show; end
12
+
13
+ private
14
+
15
+ def set_flamegraph
16
+ @flamegraph = Flamegraph.find_by!(rmp_profiled_request_id: params[:id]).json_data
17
+ end
18
+
19
+ def configuration
20
+ @configuration ||= RailsMiniProfiler.configuration
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency 'rails_mini_profiler/application_controller'
4
+
5
+ module RailsMiniProfiler
6
+ class ProfiledRequestsController < ApplicationController
7
+ before_action :set_profiled_request, only: %i[show destroy]
8
+
9
+ def index
10
+ @profiled_requests = ProfiledRequest.where(user_id: user_id).order(id: :desc)
11
+ search = ProfiledRequestSearch.new(index_params, scope: @profiled_requests)
12
+ @pagy, @profiled_requests = pagy(search.results, items: configuration.ui.page_size)
13
+ @profiled_requests = @profiled_requests.map { |request| present(request) }
14
+ end
15
+
16
+ def show
17
+ @traces = @profiled_request.traces
18
+ @traces = @traces.where("#{payload_column} LIKE ?", "%#{params[:search]}%") if params[:search]
19
+ @traces = @traces
20
+ .order(:start)
21
+ .map { |trace| present(trace, profiled_request: @profiled_request) }
22
+ @profiled_request = present(@profiled_request)
23
+ end
24
+
25
+ def destroy
26
+ ProfiledRequest.where(user_id: user_id).destroy(@profiled_request.id)
27
+ redirect_to profiled_requests_url, notice: 'Profiled request was successfully destroyed.'
28
+ end
29
+
30
+ def destroy_all
31
+ ProfiledRequest.transaction do
32
+ profiled_requests = ProfiledRequest.where(user_id: user_id)
33
+ profiled_requests = ProfiledRequestSearch.new(index_params, scope: profiled_requests).results
34
+ Flamegraph.joins(:profiled_request).merge(profiled_requests).delete_all
35
+ Trace.joins(:profiled_request).merge(profiled_requests).delete_all
36
+ profiled_requests.delete_all
37
+ end
38
+ redirect_to profiled_requests_url, notice: 'Profiled requests cleared', status: :see_other
39
+ end
40
+
41
+ private
42
+
43
+ def index_params
44
+ params.permit(:path, :duration, id: [], method: [], media_type: [], status: [])
45
+ end
46
+
47
+ def user_id
48
+ @user_id ||= User.get(request.env)
49
+ end
50
+
51
+ def set_profiled_request
52
+ @profiled_request = ProfiledRequest.where(user_id: user_id).find(params[:id])
53
+ end
54
+
55
+ def configuration
56
+ @configuration ||= RailsMiniProfiler.configuration
57
+ end
58
+
59
+ def payload_column
60
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
61
+ # Cast json field to text to have access to the LIKE operator
62
+ 'payload::text'
63
+ else
64
+ 'payload'
65
+ end
66
+ end
67
+ end
68
+ end