rails_mini_profiler 0.6.0 → 0.7.2
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/README.md +47 -35
- data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
- data/app/controllers/rails_mini_profiler/application_controller.rb +9 -15
- data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +31 -4
- data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
- data/app/javascript/js/clipboard_controller.js +9 -2
- data/app/javascript/js/filter_controller.js +4 -0
- data/app/javascript/stylesheets/components/buttons.scss +59 -0
- data/app/javascript/stylesheets/components/{profiled_request_table/profiled_request_table.scss → dropdown.scss} +0 -76
- data/app/javascript/stylesheets/components/input.scss +10 -0
- data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +0 -0
- data/app/javascript/stylesheets/components/page_header.scss +7 -0
- data/app/javascript/stylesheets/components/{profiled_request_table/placeholder.scss → placeholder.scss} +4 -1
- data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
- data/app/javascript/stylesheets/components/trace.scss +93 -0
- data/app/javascript/stylesheets/profiled_requests.scss +3 -67
- data/app/javascript/stylesheets/rails-mini-profiler.scss +16 -30
- data/app/javascript/stylesheets/traces.scss +44 -76
- data/app/models/rails_mini_profiler/flamegraph.rb +1 -1
- data/app/models/rails_mini_profiler/profiled_request.rb +1 -1
- data/app/models/rails_mini_profiler/trace.rb +4 -19
- data/app/presenters/rails_mini_profiler/controller_trace_presenter.rb +10 -2
- data/app/presenters/rails_mini_profiler/instantiation_trace_presenter.rb +15 -3
- data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +2 -2
- data/app/presenters/rails_mini_profiler/render_partial_trace_presenter.rb +6 -2
- data/app/presenters/rails_mini_profiler/render_template_trace_presenter.rb +6 -2
- data/app/presenters/rails_mini_profiler/sequel_trace_presenter.rb +16 -4
- data/app/presenters/rails_mini_profiler/trace_presenter.rb +10 -8
- data/app/search/rails_mini_profiler/profiled_request_search.rb +0 -1
- data/app/search/rails_mini_profiler/trace_search.rb +27 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +1 -2
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +7 -7
- data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +24 -22
- 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 +4 -17
- data/db/migrate/20210621185018_create_rmp.rb +3 -3
- data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +1 -0
- data/lib/rails_mini_profiler/badge.rb +20 -11
- data/lib/rails_mini_profiler/configuration/user_interface.rb +26 -0
- data/lib/rails_mini_profiler/configuration.rb +4 -0
- data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
- data/lib/rails_mini_profiler/middleware.rb +12 -10
- data/lib/rails_mini_profiler/request_context.rb +22 -18
- data/lib/rails_mini_profiler/request_wrapper.rb +12 -55
- data/lib/rails_mini_profiler/response_wrapper.rb +21 -17
- data/lib/rails_mini_profiler/{tracing → tracers}/controller_tracer.rb +15 -1
- data/lib/rails_mini_profiler/tracers/instantiation_tracer.rb +17 -0
- data/lib/rails_mini_profiler/{tracing → tracers}/null_trace.rb +1 -1
- data/lib/rails_mini_profiler/tracers/registry.rb +76 -0
- data/lib/rails_mini_profiler/tracers/rmp_tracer.rb +17 -0
- data/lib/rails_mini_profiler/{tracing → tracers}/sequel_tracer.rb +15 -1
- data/lib/rails_mini_profiler/{tracing → tracers}/sequel_tracker.rb +4 -1
- data/lib/rails_mini_profiler/{tracing → tracers}/subscriptions.rb +6 -12
- data/lib/rails_mini_profiler/{tracing → tracers}/trace.rb +2 -2
- data/lib/rails_mini_profiler/tracers/trace_factory.rb +27 -0
- data/lib/rails_mini_profiler/tracers/tracer.rb +52 -0
- data/lib/rails_mini_profiler/tracers/view_tracer.rb +29 -0
- data/lib/rails_mini_profiler/tracers.rb +14 -0
- data/lib/rails_mini_profiler/user.rb +1 -1
- data/lib/rails_mini_profiler/version.rb +1 -1
- data/lib/rails_mini_profiler.rb +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
- metadata +39 -26
- data/app/javascript/stylesheets/components/page_header/page_header.scss +0 -3
- data/app/models/rails_mini_profiler/controller_trace.rb +0 -37
- data/app/models/rails_mini_profiler/instantiation_trace.rb +0 -37
- data/app/models/rails_mini_profiler/render_partial_trace.rb +0 -37
- data/app/models/rails_mini_profiler/render_template_trace.rb +0 -37
- data/app/models/rails_mini_profiler/rmp_trace.rb +0 -35
- data/app/models/rails_mini_profiler/sequel_trace.rb +0 -37
- data/lib/rails_mini_profiler/tracing/trace_factory.rb +0 -37
- data/lib/rails_mini_profiler/tracing/tracer.rb +0 -31
- data/lib/rails_mini_profiler/tracing/view_tracer.rb +0 -12
- data/lib/rails_mini_profiler/tracing.rb +0 -11
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
class TraceSearch < BaseSearch
|
5
|
+
option(:name) do |scope, value|
|
6
|
+
scope.where(name: value)
|
7
|
+
end
|
8
|
+
|
9
|
+
option(:duration) do |scope, value|
|
10
|
+
scope.where('duration > :duration', duration: value)
|
11
|
+
end
|
12
|
+
|
13
|
+
option(:allocations) do |scope, value|
|
14
|
+
scope.where('allocations > :allocations', allocations: value)
|
15
|
+
end
|
16
|
+
|
17
|
+
option(:payload) do |scope, value|
|
18
|
+
payload_column = DatabaseAdapter.cast_to_text(:payload)
|
19
|
+
scope.where("#{payload_column} LIKE ?", "%#{value}%")
|
20
|
+
end
|
21
|
+
|
22
|
+
option(:backtrace) do |scope, value|
|
23
|
+
backtrace_column = Adapters::DatabaseAdapter.cast_to_text(:backtrace)
|
24
|
+
scope.where("#{backtrace_column} LIKE ?", "%#{value}%")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -12,9 +12,8 @@
|
|
12
12
|
}) -%>
|
13
13
|
<%= link_to(destroy_all_profiled_requests_url,
|
14
14
|
method: :delete,
|
15
|
-
class: 'clear-action',
|
16
15
|
data: { confirm: "This will delete all requests. Are you sure?" }) do %>
|
17
|
-
<%= button_tag('Delete All') %>
|
16
|
+
<%= button_tag('Delete All', class: 'btn-red') %>
|
18
17
|
<% end %>
|
19
18
|
</div>
|
20
19
|
</div>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
placeholder: 'Search Path',
|
24
24
|
class: 'dropdown-search-field',
|
25
25
|
data: { 'filters-target': 'filter' } %>
|
26
|
-
<%= form.submit 'Search', class: '
|
26
|
+
<%= form.submit 'Search', class: 'btn-red', data: { action: 'click->search#submit' } -%>
|
27
27
|
<% end %>
|
28
28
|
</div>
|
29
29
|
</div>
|
@@ -74,7 +74,7 @@
|
|
74
74
|
</button>
|
75
75
|
</div>
|
76
76
|
</th>
|
77
|
-
<th style="width: 15
|
77
|
+
<th style="width: 15%;" class="" data-controller="dropdown">
|
78
78
|
<button data-action="click->dropdown#toggle click@window->dropdown#hide" data-dropdown-target="button" class="dropdown-toggle none">
|
79
79
|
Media Type <%= inline_svg('filter.svg', class: 'table-filter-icon') %> </button>
|
80
80
|
<div data-controller="checklist" data-dropdown-target="menu" class="dropdown-container hidden">
|
@@ -97,7 +97,7 @@
|
|
97
97
|
</button>
|
98
98
|
</div>
|
99
99
|
</th>
|
100
|
-
<th style="width: 10
|
100
|
+
<th style="width: 10%;" class="text-left" data-controller="dropdown">
|
101
101
|
<button data-action="click->dropdown#toggle click@window->dropdown#hide" data-dropdown-target="button" class="dropdown-toggle none">
|
102
102
|
Duration <%= inline_svg('filter.svg', class: 'table-filter-icon') %> </button>
|
103
103
|
<div data-controller="checklist" data-dropdown-target="menu" class="dropdown-container hidden">
|
@@ -107,12 +107,12 @@
|
|
107
107
|
Clear filter
|
108
108
|
</button>
|
109
109
|
</div>
|
110
|
-
<%
|
110
|
+
<% [100_00, 250_00, 500_00].each do |duration| %>
|
111
111
|
<%= label_tag nil, class: "dropdown-entry" do %>
|
112
|
-
<%= radio_button_tag 'duration', duration, params.fetch(:duration, []).include?(duration),
|
112
|
+
<%= radio_button_tag 'duration', duration.to_s , params.fetch(:duration, []).include?(duration.to_s),
|
113
113
|
class: "",
|
114
114
|
data: { target: "filters.filter" } %>
|
115
|
-
<%= duration %>
|
115
|
+
<%= "> #{duration / 100}ms" %>
|
116
116
|
<% end %>
|
117
117
|
<% end %>
|
118
118
|
<button class="dropdown-footer" data-action="filters#apply">
|
@@ -120,6 +120,6 @@
|
|
120
120
|
</button>
|
121
121
|
</div>
|
122
122
|
</th>
|
123
|
-
<th style="width: 15
|
123
|
+
<th style="width: 15%;" class="text-left">Date</th>
|
124
124
|
</tr>
|
125
125
|
</thead>
|
@@ -7,8 +7,29 @@
|
|
7
7
|
<button class="popover-close">x</button>
|
8
8
|
</section>
|
9
9
|
<section class="popover-body">
|
10
|
-
<%= trace.
|
11
|
-
|
10
|
+
<%= trace.content %>
|
11
|
+
|
12
|
+
<% if trace.backtrace %>
|
13
|
+
<section class="popover-footer">
|
14
|
+
<div
|
15
|
+
class="backtrace"
|
16
|
+
data-controller="clipboard"
|
17
|
+
data-clipboard-filter=".+?(?=:in)"
|
18
|
+
data-clipboard-copied-message="Copied" >
|
19
|
+
<pre data-clipboard-target="source"><%= trace.backtrace %></pre>
|
20
|
+
<button
|
21
|
+
title="Copy to clipboard"
|
22
|
+
type="button"
|
23
|
+
data-action="clipboard#copy"
|
24
|
+
data-clipboard-target="button"
|
25
|
+
>
|
26
|
+
<%= inline_svg('copy.svg') %>
|
27
|
+
</button>
|
28
|
+
</div>
|
29
|
+
</section>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<table class="trace-details-table">
|
12
33
|
<thead>
|
13
34
|
<tr>
|
14
35
|
<th class="text-left"></th>
|
@@ -30,26 +51,7 @@
|
|
30
51
|
</table>
|
31
52
|
</section>
|
32
53
|
|
33
|
-
|
34
|
-
<section class="popover-footer">
|
35
|
-
<div
|
36
|
-
class="backtrace"
|
37
|
-
data-controller="clipboard"
|
38
|
-
data-clipboard-filter=".+?(?=:in)"
|
39
|
-
data-clipboard-copied-class="copied"
|
40
|
-
>
|
41
|
-
<pre data-clipboard-target="source"><%= trace.backtrace %></pre>
|
42
|
-
<button
|
43
|
-
title="Copy to clipboard"
|
44
|
-
type="button"
|
45
|
-
data-action="clipboard#copy"
|
46
|
-
data-clipboard-target="button"
|
47
|
-
>
|
48
|
-
<%= inline_svg('copy.svg') %>
|
49
|
-
</button>
|
50
|
-
</div>
|
51
|
-
</section>
|
52
|
-
<% end %>
|
54
|
+
|
53
55
|
</div>
|
54
56
|
</div>
|
55
57
|
</li>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<section class="trace-list">
|
2
|
+
<%= render "rails_mini_profiler/profiled_requests/show/trace_list_header" %>
|
3
|
+
<% if @traces.empty? %>
|
4
|
+
<%= render "rails_mini_profiler/profiled_requests/show/trace_list_placeholder" %>
|
5
|
+
<% else %>
|
6
|
+
<ol>
|
7
|
+
<% @traces.each do |trace| %>
|
8
|
+
<%= render "rails_mini_profiler/profiled_requests/show/trace", trace: trace %>
|
9
|
+
<% end %>
|
10
|
+
</ol>
|
11
|
+
<% end %>
|
12
|
+
</section>
|
@@ -0,0 +1,87 @@
|
|
1
|
+
<div class="trace-list-header" data-controller="filters">
|
2
|
+
<div class="trace-list-filters">
|
3
|
+
<%= form_with url: profiled_request_url(@profiled_request.id), method: :get do |form| %>
|
4
|
+
<%= form.search_field :payload,
|
5
|
+
value: params[:payload],
|
6
|
+
placeholder: 'Search Traces...',
|
7
|
+
class: 'search-field',
|
8
|
+
data: { 'filters-target': 'filter' } %>
|
9
|
+
<% end %>
|
10
|
+
<div data-controller="dropdown">
|
11
|
+
<button data-action="click->dropdown#toggle click@window->dropdown#hide" data-dropdown-target="button" class="dropdown-toggle none" >
|
12
|
+
Type <%= inline_svg('filter.svg', class: 'table-filter-icon') %> </button>
|
13
|
+
<div data-controller="checklist" data-dropdown-target="menu" class="dropdown-container hidden">
|
14
|
+
<div class="dropdown-header">
|
15
|
+
Select Type...
|
16
|
+
<button class="clear-filters" data-action="checklist#checkNone">
|
17
|
+
Clear filter
|
18
|
+
</button>
|
19
|
+
</div>
|
20
|
+
<% trace_names = %w[process_action.action_controller sql.active_record instantiation.active_record render_template.action_view render_partial.action_view] %>
|
21
|
+
<% trace_names.each do |name| %>
|
22
|
+
<%= label_tag nil, class: "dropdown-entry" do %>
|
23
|
+
<%= check_box_tag 'name[]', name, params.fetch(:name, []).include?(name),
|
24
|
+
class: "",
|
25
|
+
data: { 'filters-target': "filter" } %>
|
26
|
+
<%= trace_display_name(name) %>
|
27
|
+
<% end %>
|
28
|
+
<% end %>
|
29
|
+
<button class="dropdown-footer" data-action="filters#apply">
|
30
|
+
Apply
|
31
|
+
</button>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div data-controller="dropdown">
|
36
|
+
<button data-action="click->dropdown#toggle click@window->dropdown#hide" data-dropdown-target="button" class="dropdown-toggle none">
|
37
|
+
Duration <%= inline_svg('filter.svg', class: 'table-filter-icon') %> </button>
|
38
|
+
<div data-controller="checklist" data-dropdown-target="menu" class="dropdown-container hidden">
|
39
|
+
<div class="dropdown-header">
|
40
|
+
Select Duration...
|
41
|
+
<button class="clear-filters" data-action="checklist#checkNone filters#apply">
|
42
|
+
Clear filter
|
43
|
+
</button>
|
44
|
+
</div>
|
45
|
+
<% [100_00, 250_00, 500_00].each do |duration| %>
|
46
|
+
<%= label_tag nil, class: "dropdown-entry" do %>
|
47
|
+
<%= radio_button_tag 'duration', duration.to_s, params.fetch(:duration, []).include?(duration.to_s),
|
48
|
+
class: "",
|
49
|
+
data: { target: "filters.filter" } %>
|
50
|
+
<%= "> #{formatted_duration(duration)}ms" %>
|
51
|
+
<% end %>
|
52
|
+
<% end %>
|
53
|
+
<button class="dropdown-footer" data-action="filters#apply">
|
54
|
+
Apply
|
55
|
+
</button>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div data-controller="dropdown">
|
60
|
+
<button data-action="click->dropdown#toggle click@window->dropdown#hide" data-dropdown-target="button" class="dropdown-toggle none">
|
61
|
+
Allocations <%= inline_svg('filter.svg', class: 'table-filter-icon') %> </button>
|
62
|
+
<div data-controller="checklist" data-dropdown-target="menu" class="dropdown-container hidden">
|
63
|
+
<div class="dropdown-header">
|
64
|
+
Select Allocation...
|
65
|
+
<button class="clear-filters" data-action="filters#apply">
|
66
|
+
Clear filter
|
67
|
+
</button>
|
68
|
+
</div>
|
69
|
+
<!-- https://twitter.com/nateberkopec/status/1442648442149367809-->
|
70
|
+
<% [10_000, 100_000, 1_000_000, 10_000_000].each do |allocations| %>
|
71
|
+
<%= label_tag nil, class: "dropdown-entry" do %>
|
72
|
+
<%= radio_button_tag 'allocations', allocations.to_s, params.fetch(:allocations, []).include?(allocations.to_s),
|
73
|
+
class: "",
|
74
|
+
data: { target: "filters.filter" } %>
|
75
|
+
<%= "> #{formatted_allocations(allocations)}" %>
|
76
|
+
<% end %>
|
77
|
+
<% end %>
|
78
|
+
<button class="dropdown-footer" data-action="filters#apply">
|
79
|
+
Apply
|
80
|
+
</button>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
<%= link_to(profiled_request_url(@profiled_request.id)) do %>
|
85
|
+
<%= button_tag('Clear All', class: 'btn-red') %>
|
86
|
+
<% end %>
|
87
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<tr class="no-row">
|
2
|
+
<td colspan="100%">
|
3
|
+
<div class="placeholder">
|
4
|
+
<%= inline_svg('logo_variant.svg', class: 'placeholder-image') %>
|
5
|
+
<div class="placeholder-text">
|
6
|
+
<h2>No Traces found!</h2>
|
7
|
+
<p>Modify filters or reload this page</p>
|
8
|
+
</div>
|
9
|
+
<%= link_to('Clear Filters', profiled_request_url(@profiled_request.id), class: 'placeholder-link') %>
|
10
|
+
</div>
|
11
|
+
</td>
|
12
|
+
</tr>
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<section class="page-header">
|
2
2
|
<h1> <%= @profiled_request.request_path %> </h1>
|
3
|
+
</section>
|
3
4
|
|
5
|
+
<div class="profiled-request-details">
|
4
6
|
<ul class="request-details-data">
|
5
7
|
<li class="data-item">
|
6
8
|
<small>Method</small>
|
@@ -18,23 +20,8 @@
|
|
18
20
|
<small>Allocations</small>
|
19
21
|
<span><%= @profiled_request.allocations %></span>
|
20
22
|
</li>
|
21
|
-
|
22
23
|
</ul>
|
23
|
-
|
24
|
-
</section>
|
25
|
-
|
26
|
-
<section class="request-details-actions">
|
27
|
-
<%= form_with id: 'trace-form', url: profiled_request_url(@profiled_request.id), method: :get do |form| %>
|
28
|
-
<%= form.search_field :search, id: 'trace-search', placeholder: 'Search Traces...', class: 'search-field' %>
|
29
|
-
<% end %>
|
30
24
|
<%= @profiled_request.flamegraph_button %>
|
31
|
-
</
|
25
|
+
</div>
|
32
26
|
|
33
|
-
|
34
|
-
<h2>Traces</h2>
|
35
|
-
<ol class="trace-list">
|
36
|
-
<% @traces.each do |trace| %>
|
37
|
-
<%= render "rails_mini_profiler/profiled_requests/shared/trace", trace: trace %>
|
38
|
-
<% end %>
|
39
|
-
</ol>
|
40
|
-
</section>
|
27
|
+
<%= render "rails_mini_profiler/profiled_requests/show/trace_list" %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class CreateRmp < ActiveRecord::Migration[6.0]
|
4
4
|
def change
|
5
|
-
create_table :rmp_profiled_requests do |t|
|
5
|
+
create_table :rmp_profiled_requests, charset: 'utf8' do |t|
|
6
6
|
t.string :user_id
|
7
7
|
t.bigint :start
|
8
8
|
t.bigint :finish
|
@@ -23,7 +23,7 @@ class CreateRmp < ActiveRecord::Migration[6.0]
|
|
23
23
|
t.index :created_at
|
24
24
|
end
|
25
25
|
|
26
|
-
create_table :rmp_traces do |t|
|
26
|
+
create_table :rmp_traces, charset: 'utf8' do |t|
|
27
27
|
t.belongs_to :rmp_profiled_request, null: false, foreign_key: true
|
28
28
|
t.string :name
|
29
29
|
t.bigint :start
|
@@ -36,7 +36,7 @@ class CreateRmp < ActiveRecord::Migration[6.0]
|
|
36
36
|
t.timestamps
|
37
37
|
end
|
38
38
|
|
39
|
-
create_table :rmp_flamegraphs do |t|
|
39
|
+
create_table :rmp_flamegraphs, charset: 'utf8' do |t|
|
40
40
|
t.belongs_to :rmp_profiled_request, null: false, foreign_key: true
|
41
41
|
t.binary :data
|
42
42
|
|
@@ -22,6 +22,7 @@ RailsMiniProfiler.configure do |config|
|
|
22
22
|
# Configure the Rails Mini Profiler User Interface
|
23
23
|
# config.ui.badge_enabled = true
|
24
24
|
# config.ui.badge_position = 'top-left'
|
25
|
+
# config.ui.base_controller = ApplicationController
|
25
26
|
# config.ui.page_size = 25
|
26
27
|
# config.ui.webpacker_enabled = true
|
27
28
|
|
@@ -23,30 +23,39 @@ module RailsMiniProfiler
|
|
23
23
|
#
|
24
24
|
# @return [ResponseWrapper] The modified response
|
25
25
|
def render
|
26
|
-
|
27
|
-
return @original_response unless content_type =~ %r{text/html}
|
28
|
-
|
29
|
-
return @original_response unless @configuration.ui.badge_enabled
|
26
|
+
return @original_response unless render_badge?
|
30
27
|
|
31
|
-
modified_response =
|
28
|
+
modified_response = ResponseWrapper.new([], @original_response.status, @original_response.headers)
|
32
29
|
modified_response.write(modified_body)
|
33
30
|
modified_response.finish
|
34
31
|
|
35
|
-
|
36
|
-
response.close if response.respond_to?(:close)
|
32
|
+
@original_response.close if @original_response.respond_to?(:close)
|
37
33
|
|
38
|
-
|
39
|
-
@original_response.headers,
|
40
|
-
modified_response)
|
34
|
+
modified_response
|
41
35
|
end
|
42
36
|
|
43
37
|
private
|
44
38
|
|
39
|
+
def render_badge?
|
40
|
+
content_type = @original_response.headers['Content-Type']
|
41
|
+
unless content_type =~ %r{text/html}
|
42
|
+
RailsMiniProfiler.logger.debug("badge not rendered, response has content type #{content_type}")
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
unless @configuration.ui.badge_enabled
|
47
|
+
RailsMiniProfiler.logger.debug('badge not rendered, disabled in configuration')
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
45
54
|
# Modify the body of the original response
|
46
55
|
#
|
47
56
|
# @return String The modified body
|
48
57
|
def modified_body
|
49
|
-
body = @original_response.
|
58
|
+
body = @original_response.body
|
50
59
|
index = body.rindex(%r{</body>}i) || body.rindex(%r{</html>}i)
|
51
60
|
if index
|
52
61
|
body.dup.insert(index, badge_content)
|
@@ -9,6 +9,8 @@ module RailsMiniProfiler
|
|
9
9
|
# @!attribute badge_position
|
10
10
|
# @see Badge
|
11
11
|
# @return [String] the position of the interactive HTML badge
|
12
|
+
# @!attribute base_controller
|
13
|
+
# @return [class] the controller the UI controllers should inherit from
|
12
14
|
# @!attribute page_size
|
13
15
|
# @return [Integer] how many items to render per page in list views
|
14
16
|
# @!attribute webpacker_enabled
|
@@ -36,6 +38,8 @@ module RailsMiniProfiler
|
|
36
38
|
:page_size,
|
37
39
|
:webpacker_enabled
|
38
40
|
|
41
|
+
attr_writer :base_controller
|
42
|
+
|
39
43
|
def initialize(**kwargs)
|
40
44
|
defaults!
|
41
45
|
kwargs.each { |key, value| instance_variable_set("@#{key}", value) }
|
@@ -45,8 +49,30 @@ module RailsMiniProfiler
|
|
45
49
|
def defaults!
|
46
50
|
@badge_enabled = true
|
47
51
|
@badge_position = 'top-left'
|
52
|
+
# We must not set base controller during when the app loads, aka during autoload time, as we are loading
|
53
|
+
# constants. Rather, we only load the base controller constants when any engine controllers are first initialized
|
54
|
+
# and call #base_controller
|
55
|
+
@base_controller = nil
|
48
56
|
@page_size = 25
|
49
57
|
@webpacker_enabled = true
|
50
58
|
end
|
59
|
+
|
60
|
+
def base_controller
|
61
|
+
@base_controller ||= default_base_controller
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_base_controller
|
65
|
+
app_controller_exists = class_exists?('::ApplicationController')
|
66
|
+
return ::ApplicationController if app_controller_exists && ::ApplicationController < ActionController::Base
|
67
|
+
|
68
|
+
ActionController::Base
|
69
|
+
end
|
70
|
+
|
71
|
+
def class_exists?(class_name)
|
72
|
+
klass = Module.const_get(class_name)
|
73
|
+
klass.is_a?(Class)
|
74
|
+
rescue NameError
|
75
|
+
false
|
76
|
+
end
|
51
77
|
end
|
52
78
|
end
|
@@ -18,6 +18,8 @@ module RailsMiniProfiler
|
|
18
18
|
# @return [Array<String>] a list of regex patterns for paths to skip
|
19
19
|
# @!attribute storage
|
20
20
|
# @return [Storage] the storage configuration
|
21
|
+
# @!attribute tracers
|
22
|
+
# @return [Array<Symbol>] the list of enabled tracers
|
21
23
|
# @!attribute ui
|
22
24
|
# @return [UserInterface] the ui configuration
|
23
25
|
# @!attribute user_provider
|
@@ -30,6 +32,7 @@ module RailsMiniProfiler
|
|
30
32
|
:flamegraph_sample_rate,
|
31
33
|
:skip_paths,
|
32
34
|
:storage,
|
35
|
+
:tracers,
|
33
36
|
:ui,
|
34
37
|
:user_provider
|
35
38
|
|
@@ -46,6 +49,7 @@ module RailsMiniProfiler
|
|
46
49
|
@logger = RailsMiniProfiler::Logger.new(Rails.logger)
|
47
50
|
@skip_paths = []
|
48
51
|
@storage = Storage.new
|
52
|
+
@tracers = %i[controller instantiation sequel view rmp]
|
49
53
|
@ui = UserInterface.new
|
50
54
|
@user_provider = proc { |env| Rack::Request.new(env).ip }
|
51
55
|
end
|
@@ -11,17 +11,12 @@ module RailsMiniProfiler
|
|
11
11
|
def record(&block)
|
12
12
|
return block.call unless enabled?
|
13
13
|
|
14
|
-
sample_rate = @configuration.flamegraph_sample_rate
|
15
14
|
if StackProf.running?
|
16
15
|
RailsMiniProfiler.logger.error('Stackprof is already running, cannot record Flamegraph')
|
17
16
|
return block.call
|
18
17
|
end
|
19
18
|
|
20
|
-
result =
|
21
|
-
flamegraph = StackProf.run(mode: :wall, raw: true, aggregate: false, interval: (sample_rate * 1000).to_i) do
|
22
|
-
result = block.call
|
23
|
-
end
|
24
|
-
|
19
|
+
flamegraph, result = record_flamegraph(block)
|
25
20
|
unless flamegraph
|
26
21
|
RailsMiniProfiler.logger.error('Failed to record Flamegraph, possibly due to concurrent requests')
|
27
22
|
return result
|
@@ -33,6 +28,15 @@ module RailsMiniProfiler
|
|
33
28
|
|
34
29
|
private
|
35
30
|
|
31
|
+
def record_flamegraph(block)
|
32
|
+
sample_rate = @configuration.flamegraph_sample_rate
|
33
|
+
result = nil
|
34
|
+
flamegraph = StackProf.run(mode: :wall, raw: true, aggregate: false, interval: (sample_rate * 1000).to_i) do
|
35
|
+
result = block.call
|
36
|
+
end
|
37
|
+
[flamegraph, result]
|
38
|
+
end
|
39
|
+
|
36
40
|
def enabled?
|
37
41
|
defined?(StackProf) && StackProf.respond_to?(:run) && config_enabled?
|
38
42
|
end
|
@@ -5,21 +5,25 @@ module RailsMiniProfiler
|
|
5
5
|
def initialize(app)
|
6
6
|
@app = app
|
7
7
|
@config = RailsMiniProfiler.configuration
|
8
|
-
|
8
|
+
@registry = Tracers::Registry.setup!(@config)
|
9
|
+
Tracers::Subscriptions.setup!(@registry.tracers.keys) { |trace| track_trace(trace) }
|
10
|
+
@trace_factory = Tracers::TraceFactory.new(@registry)
|
9
11
|
end
|
10
12
|
|
11
13
|
def call(env)
|
12
|
-
request = RequestWrapper.new(env
|
14
|
+
request = RequestWrapper.new(env)
|
13
15
|
request_context = RequestContext.new(request)
|
14
16
|
return @app.call(env) unless Guard.new(request_context).profile?
|
15
17
|
|
16
|
-
request_context.profiled_request = ProfiledRequest.new
|
17
18
|
result = with_tracing(request_context) { profile(request_context) }
|
18
19
|
return result unless request_context.authorized?
|
19
20
|
|
20
|
-
|
21
|
+
status, headers, body = result
|
22
|
+
request_context.response = ResponseWrapper.new(body, status, headers)
|
21
23
|
complete!(request_context)
|
22
|
-
request_context.saved? ? render_response(request_context) : result
|
24
|
+
request_context.saved? ? render_response(request_context).to_a : result
|
25
|
+
ensure
|
26
|
+
User.current_user = nil
|
23
27
|
end
|
24
28
|
|
25
29
|
def traces
|
@@ -33,14 +37,13 @@ module RailsMiniProfiler
|
|
33
37
|
def track_trace(event)
|
34
38
|
return if traces.nil?
|
35
39
|
|
36
|
-
trace =
|
37
|
-
traces.append(trace) unless trace.is_a?(RailsMiniProfiler::
|
40
|
+
trace = @trace_factory.create(event)
|
41
|
+
traces.append(trace) unless trace.is_a?(RailsMiniProfiler::Tracers::NullTrace)
|
38
42
|
end
|
39
43
|
|
40
44
|
private
|
41
45
|
|
42
46
|
def complete!(request_context)
|
43
|
-
request_context.complete_profiling!
|
44
47
|
request_context.save_results!
|
45
48
|
true
|
46
49
|
rescue ActiveRecord::ActiveRecordError => e
|
@@ -52,8 +55,7 @@ module RailsMiniProfiler
|
|
52
55
|
redirect = Redirect.new(request_context).render
|
53
56
|
return redirect if redirect
|
54
57
|
|
55
|
-
|
56
|
-
[modified_response.status, modified_response.headers, modified_response.response]
|
58
|
+
Badge.new(request_context).render
|
57
59
|
end
|
58
60
|
|
59
61
|
def profile(request_context)
|
@@ -10,7 +10,7 @@ module RailsMiniProfiler
|
|
10
10
|
# @return [RequestWrapper] the request as sent to the application
|
11
11
|
# @!attribute response
|
12
12
|
# @return [ResponseWrapper] the response as rendered by the application
|
13
|
-
# @!attribute profiled_request
|
13
|
+
# # @!attribute profiled_request
|
14
14
|
# @return [ProfiledRequest] the profiling data as gathered during profiling
|
15
15
|
# @!attribute traces
|
16
16
|
# @return [Array<Models::Trace>] trace wrappers gathered during profiling
|
@@ -19,8 +19,10 @@ module RailsMiniProfiler
|
|
19
19
|
class RequestContext
|
20
20
|
attr_reader :request
|
21
21
|
|
22
|
-
attr_accessor :response, :
|
22
|
+
attr_accessor :response, :traces, :flamegraph, :profiled_request
|
23
23
|
|
24
|
+
# Create a new request context
|
25
|
+
#
|
24
26
|
# @param request [RequestWrapper] the request as sent to the application
|
25
27
|
def initialize(request)
|
26
28
|
@request = request
|
@@ -36,39 +38,41 @@ module RailsMiniProfiler
|
|
36
38
|
@authorized ||= User.get(@env).present?
|
37
39
|
end
|
38
40
|
|
39
|
-
# Completes profiling, setting all data and preparing for saving it.
|
40
|
-
def complete_profiling!
|
41
|
-
profiled_request.user_id = User.current_user
|
42
|
-
profiled_request.request = @request
|
43
|
-
profiled_request.response = @response
|
44
|
-
total_time = traces.find { |trace| trace.name == 'rails_mini_profiler.total_time' }
|
45
|
-
profiled_request.total_time = total_time
|
46
|
-
@complete = true
|
47
|
-
end
|
48
|
-
|
49
41
|
# Save profiling data in the database.
|
50
42
|
#
|
51
43
|
# This will store the profiled request, as well as any attached traces and Flamgraph.
|
52
44
|
def save_results!
|
53
45
|
ActiveRecord::Base.transaction do
|
46
|
+
profiled_request = build_profiled_request
|
54
47
|
profiled_request.flamegraph = RailsMiniProfiler::Flamegraph.new(data: flamegraph) if flamegraph.present?
|
55
48
|
profiled_request.save
|
56
|
-
insert_traces unless traces.empty?
|
49
|
+
insert_traces(profiled_request) unless traces.empty?
|
50
|
+
@profiled_request = profiled_request
|
57
51
|
end
|
58
52
|
@saved = true
|
59
53
|
end
|
60
54
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
# Check if profiling results have been saved
|
56
|
+
#
|
57
|
+
# @return [Boolean] true if profiling results have been saved
|
65
58
|
def saved?
|
66
59
|
@saved
|
67
60
|
end
|
68
61
|
|
69
62
|
private
|
70
63
|
|
71
|
-
def
|
64
|
+
def build_profiled_request
|
65
|
+
new_profiled_request = ProfiledRequest.new
|
66
|
+
new_profiled_request.user_id = User.current_user
|
67
|
+
new_profiled_request.request = @request
|
68
|
+
new_profiled_request.response = @response
|
69
|
+
total_time = traces.find { |trace| trace.name == 'rails_mini_profiler.total_time' }
|
70
|
+
new_profiled_request.total_time = total_time
|
71
|
+
new_profiled_request
|
72
|
+
end
|
73
|
+
|
74
|
+
# We insert multiple at once for performance reasons.
|
75
|
+
def insert_traces(profiled_request)
|
72
76
|
return if traces.empty?
|
73
77
|
|
74
78
|
timestamp = Time.zone.now
|