rails_live_dashboard 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +63 -0
- data/Rakefile +8 -0
- data/app/assets/builds/rails_live_dashboard/application.css +1 -0
- data/app/assets/builds/rails_live_dashboard/application.js +39 -0
- data/app/assets/builds/rails_live_dashboard/application.js.map +7 -0
- data/app/assets/javascripts/rails_live_dashboard/application.js +2 -0
- data/app/assets/javascripts/rails_live_dashboard/controllers/application.js +9 -0
- data/app/assets/javascripts/rails_live_dashboard/controllers/index.js +6 -0
- data/app/assets/javascripts/rails_live_dashboard/controllers/reveal_controller.js +28 -0
- data/app/assets/javascripts/rails_live_dashboard/controllers/tabs_controller.js +37 -0
- data/app/assets/stylesheets/rails_live_dashboard/application.css +5 -0
- data/app/components/rails_live_dashboard/query_duration_badge_component.html.erb +3 -0
- data/app/components/rails_live_dashboard/query_duration_badge_component.rb +27 -0
- data/app/components/rails_live_dashboard/request_duration_badge_component.html.erb +3 -0
- data/app/components/rails_live_dashboard/request_duration_badge_component.rb +27 -0
- data/app/components/rails_live_dashboard/request_method_badge_component.html.erb +6 -0
- data/app/components/rails_live_dashboard/request_method_badge_component.rb +24 -0
- data/app/components/rails_live_dashboard/request_status_badge_component.html.erb +3 -0
- data/app/components/rails_live_dashboard/request_status_badge_component.rb +22 -0
- data/app/controllers/rails_live_dashboard/application_controller.rb +4 -0
- data/app/controllers/rails_live_dashboard/clean_controller.rb +9 -0
- data/app/controllers/rails_live_dashboard/dashboard_controller.rb +8 -0
- data/app/controllers/rails_live_dashboard/exceptions_controller.rb +13 -0
- data/app/controllers/rails_live_dashboard/queries_controller.rb +11 -0
- data/app/controllers/rails_live_dashboard/requests_controller.rb +13 -0
- data/app/controllers/rails_live_dashboard/widgets/slowest_queries_controller.rb +11 -0
- data/app/controllers/rails_live_dashboard/widgets/slowest_requests_controller.rb +11 -0
- data/app/helpers/rails_live_dashboard/application_helper.rb +4 -0
- data/app/helpers/rails_live_dashboard/clean_helper.rb +4 -0
- data/app/helpers/rails_live_dashboard/dashboard_helper.rb +4 -0
- data/app/helpers/rails_live_dashboard/exceptions_helper.rb +4 -0
- data/app/helpers/rails_live_dashboard/requests_helper.rb +4 -0
- data/app/jobs/rails_live_dashboard/application_job.rb +4 -0
- data/app/mailers/rails_live_dashboard/application_mailer.rb +6 -0
- data/app/models/rails_live_dashboard/application_record.rb +5 -0
- data/app/models/rails_live_dashboard/entry.rb +5 -0
- data/app/models/rails_live_dashboard/exception.rb +31 -0
- data/app/models/rails_live_dashboard/query.rb +5 -0
- data/app/models/rails_live_dashboard/request.rb +45 -0
- data/app/models/rails_live_dashboard/types/content.rb +32 -0
- data/app/models/rails_live_dashboard/types/exception_content.rb +42 -0
- data/app/models/rails_live_dashboard/types/query_content.rb +42 -0
- data/app/models/rails_live_dashboard/types/request_content.rb +49 -0
- data/app/views/layouts/rails_live_dashboard/application.html.erb +18 -0
- data/app/views/rails_live_dashboard/dashboard/show.html.erb +27 -0
- data/app/views/rails_live_dashboard/exceptions/_tabs.html.erb +16 -0
- data/app/views/rails_live_dashboard/exceptions/index.html.erb +55 -0
- data/app/views/rails_live_dashboard/exceptions/show.html.erb +41 -0
- data/app/views/rails_live_dashboard/queries/_list.html.erb +43 -0
- data/app/views/rails_live_dashboard/queries/index.html.erb +11 -0
- data/app/views/rails_live_dashboard/queries/show.html.erb +33 -0
- data/app/views/rails_live_dashboard/requests/_contents.html.erb +26 -0
- data/app/views/rails_live_dashboard/requests/_exceptions.html.erb +43 -0
- data/app/views/rails_live_dashboard/requests/_queries.html.erb +11 -0
- data/app/views/rails_live_dashboard/requests/_relateds.html.erb +23 -0
- data/app/views/rails_live_dashboard/requests/index.html.erb +48 -0
- data/app/views/rails_live_dashboard/requests/show.html.erb +46 -0
- data/app/views/rails_live_dashboard/shared/_header.html.erb +43 -0
- data/app/views/rails_live_dashboard/widgets/slowest_queries/show.html.erb +42 -0
- data/app/views/rails_live_dashboard/widgets/slowest_requests/show.html.erb +42 -0
- data/config/routes.rb +14 -0
- data/db/migrate/20240213133517_create_rails_live_dashboard_entries.rb +14 -0
- data/lib/generators/rails_live_dashboard/install_generator.rb +11 -0
- data/lib/generators/templates/initializer.rb +3 -0
- data/lib/rails_live_dashboard/configuration.rb +13 -0
- data/lib/rails_live_dashboard/context.rb +19 -0
- data/lib/rails_live_dashboard/engine.rb +28 -0
- data/lib/rails_live_dashboard/recorders/exception_recorder.rb +42 -0
- data/lib/rails_live_dashboard/recorders/query_recorder.rb +30 -0
- data/lib/rails_live_dashboard/recorders/request_recorder.rb +30 -0
- data/lib/rails_live_dashboard/subscribers/action_controller_subscriber.rb +28 -0
- data/lib/rails_live_dashboard/subscribers/active_record_subscriber.rb +23 -0
- data/lib/rails_live_dashboard/version.rb +3 -0
- data/lib/rails_live_dashboard.rb +19 -0
- data/lib/tasks/rails_live_dashboard_tasks.rake +4 -0
- metadata +136 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
<div class="mt-10">
|
2
|
+
<h3 class="text-base font-semibold leading-6 text-gray-900">System information</h3>
|
3
|
+
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-4">
|
4
|
+
<div class="overflow-hidden rounded-lg bg-gradient-to-r from-orange-700 to-orange-500 px-4 py-5 shadow sm:p-6">
|
5
|
+
<dt class="truncate text-sm font-medium text-white">Ruby version</dt>
|
6
|
+
<dd class="mt-1 text-3xl font-semibold tracking-tight text-white"><%= @ruby_version %></dd>
|
7
|
+
</div>
|
8
|
+
<div class="overflow-hidden rounded-lg bg-gradient-to-r from-red-700 to-red-500 px-4 py-5 shadow sm:p-6">
|
9
|
+
<dt class="truncate text-sm font-medium text-white">Rails version</dt>
|
10
|
+
<dd class="mt-1 text-3xl font-semibold tracking-tight text-white"><%= @rails_version %></dd>
|
11
|
+
</div>
|
12
|
+
</dl>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 mt-10">
|
16
|
+
<turbo-frame id="slow_requests_widget" src="<%= widgets_slowest_requests_path %>">
|
17
|
+
<div class="flex justify-center items-center h-64 w-full rounded-lg bg-white shadow-sm">
|
18
|
+
<%= lucide_icon('loader-2', class: 'h-16 w-16 text-gray-800 animate-spin') %>
|
19
|
+
</div>
|
20
|
+
</turbo-frame>
|
21
|
+
|
22
|
+
<turbo-frame id="slowest_queries_widget" src="<%= widgets_slowest_queries_path %>">
|
23
|
+
<div class="flex justify-center items-center h-64 w-full rounded-lg bg-white shadow-sm">
|
24
|
+
<%= lucide_icon('loader-2', class: 'h-16 w-16 text-gray-800 animate-spin') %>
|
25
|
+
</div>
|
26
|
+
</turbo-frame>
|
27
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div
|
2
|
+
class="overflow-hidden bg-white shadow sm:rounded-lg"
|
3
|
+
data-controller="tabs"
|
4
|
+
data-tabs-active-class="border-indigo-500 text-indigo-600"
|
5
|
+
data-tabs-default-tab-value="tab_backtrace"
|
6
|
+
>
|
7
|
+
<nav class="isolate flex rounded-t-lg shadow" aria-label="Tabs">
|
8
|
+
<button id="tab_backtrace" class="w-full text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">Backtrace</button>
|
9
|
+
</nav>
|
10
|
+
|
11
|
+
<div class="p-5 bg-gray-800 text-white">
|
12
|
+
<div data-tabs-target="tab" id="tab_backtrace">
|
13
|
+
<%= @exception.backtrace.join("\n") || "[]" %>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<div class="mt-10">
|
2
|
+
<div class="mt-8 flow-root">
|
3
|
+
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
4
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
5
|
+
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
|
6
|
+
<table class="min-w-full divide-y divide-gray-300">
|
7
|
+
<thead class="bg-gray-50">
|
8
|
+
<tr>
|
9
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Type</th>
|
10
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Occurrences</th>
|
11
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Date</th>
|
12
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
13
|
+
<span class="sr-only">Show</span>
|
14
|
+
</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
<tbody class="divide-y divide-gray-200 bg-white">
|
18
|
+
<% if @exceptions.empty? %>
|
19
|
+
<tr>
|
20
|
+
<td colspan="4">
|
21
|
+
<p class="p-5 text-center">No exception registered</p>
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
|
26
|
+
<% @exceptions.each do |exception| %>
|
27
|
+
<tr>
|
28
|
+
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
|
29
|
+
<div class="min-w-0 flex-auto">
|
30
|
+
<p class="text-sm font-semibold leading-6 text-gray-900">
|
31
|
+
<%= exception.class_name %>
|
32
|
+
</p>
|
33
|
+
<p class="mt-1 truncate text-xs leading-5 text-gray-500">
|
34
|
+
<%= exception.message %>
|
35
|
+
</p>
|
36
|
+
</div>
|
37
|
+
</td>
|
38
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
39
|
+
<%= exception.occurrences %>
|
40
|
+
</td>
|
41
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
42
|
+
<%= exception.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
43
|
+
</td>
|
44
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
45
|
+
<%= link_to 'Show', exception_path(exception), class: 'text-indigo-600 hover:text-indigo-900' %>
|
46
|
+
</td>
|
47
|
+
</tr>
|
48
|
+
<% end %>
|
49
|
+
</tbody>
|
50
|
+
</table>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<div class="flex flex-col mt-10 gap-10">
|
2
|
+
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
|
3
|
+
<div class="px-4 py-6 sm:px-6">
|
4
|
+
<h3 class="text-lg font-semibold leading-7 text-gray-900">Exception Details</h3>
|
5
|
+
</div>
|
6
|
+
<div class="border-t border-gray-100">
|
7
|
+
<dl class="divide-y divide-gray-100">
|
8
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
9
|
+
<dt class="text-sm font-medium text-gray-900">Date</dt>
|
10
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @exception.created_at.strftime("%Y-%m-%d %H:%M:%S") %></dd>
|
11
|
+
</div>
|
12
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
13
|
+
<dt class="text-sm font-medium text-gray-900">Class</dt>
|
14
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
15
|
+
<%= @exception.class_name %>
|
16
|
+
</dd>
|
17
|
+
</div>
|
18
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
19
|
+
<dt class="text-sm font-medium text-gray-900">Message</dt>
|
20
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
21
|
+
<%= @exception.message %>
|
22
|
+
</dd>
|
23
|
+
</div>
|
24
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
25
|
+
<dt class="text-sm font-medium text-gray-900">File</dt>
|
26
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
27
|
+
<%= @exception.file %>:<%= @exception.line %>
|
28
|
+
</dd>
|
29
|
+
</div>
|
30
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
31
|
+
<dt class="text-sm font-medium text-gray-900">Occurrences</dt>
|
32
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
33
|
+
<%= @exception.occurrences %>
|
34
|
+
</dd>
|
35
|
+
</div>
|
36
|
+
</dl>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<%= render 'tabs' %>
|
41
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<table class="min-w-full divide-y divide-gray-300">
|
2
|
+
<thead class="bg-gray-50">
|
3
|
+
<tr>
|
4
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Name</th>
|
5
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">SQL</th>
|
6
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Duration</th>
|
7
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Date</th>
|
8
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
9
|
+
<span class="sr-only">Edit</span>
|
10
|
+
</th>
|
11
|
+
</tr>
|
12
|
+
</thead>
|
13
|
+
<tbody class="divide-y divide-gray-200 bg-white">
|
14
|
+
<% if @queries.empty? %>
|
15
|
+
<tr>
|
16
|
+
<td colspan="5">
|
17
|
+
<p class="p-5 text-center">No queries registered</p>
|
18
|
+
</td>
|
19
|
+
</tr>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<% @queries.each do |query| %>
|
23
|
+
<tr>
|
24
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-800">
|
25
|
+
<%= query.content.name %>
|
26
|
+
</td>
|
27
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
28
|
+
<%= query.content.sql[0..80] %>
|
29
|
+
<%= "..." if query.content.sql.size > 80 %>
|
30
|
+
</td>
|
31
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
32
|
+
<%= render RailsLiveDashboard::QueryDurationBadgeComponent.new(query.content.duration) %>
|
33
|
+
</td>
|
34
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
35
|
+
<%= query.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
36
|
+
</td>
|
37
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
38
|
+
<%= link_to 'Show', query_path(query), class: 'text-indigo-600 hover:text-indigo-900' %>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<% end %>
|
42
|
+
</tbody>
|
43
|
+
</table>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="mt-10">
|
2
|
+
<div class="mt-8 flow-root">
|
3
|
+
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
4
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
5
|
+
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
|
6
|
+
<%= render 'list' %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div class="flex flex-col mt-10 gap-10">
|
2
|
+
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
|
3
|
+
<div class="px-4 py-6 sm:px-6">
|
4
|
+
<h3 class="text-lg font-semibold leading-7 text-gray-900">Query Details</h3>
|
5
|
+
</div>
|
6
|
+
<div class="border-t border-gray-100">
|
7
|
+
<dl class="divide-y divide-gray-100">
|
8
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
9
|
+
<dt class="text-sm font-medium text-gray-900">Date</dt>
|
10
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @query.created_at.strftime("%Y-%m-%d %H:%M:%S") %></dd>
|
11
|
+
</div>
|
12
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
13
|
+
<dt class="text-sm font-medium text-gray-900">SQL</dt>
|
14
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @query.content.sql %></dd>
|
15
|
+
</div>
|
16
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
17
|
+
<dt class="text-sm font-medium text-gray-900">Parameters</dt>
|
18
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @query.content.parameters %></dd>
|
19
|
+
</div>
|
20
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
21
|
+
<dt class="text-sm font-medium text-gray-900">Duration</dt>
|
22
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
23
|
+
<%= render RailsLiveDashboard::QueryDurationBadgeComponent.new(@query.content.duration) %>
|
24
|
+
</dd>
|
25
|
+
</div>
|
26
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
27
|
+
<dt class="text-sm font-medium text-gray-900">Cached</dt>
|
28
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @query.content.cached %></dd>
|
29
|
+
</div>
|
30
|
+
</dl>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</div>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<div
|
2
|
+
class="overflow-hidden bg-white shadow sm:rounded-lg"
|
3
|
+
data-controller="tabs"
|
4
|
+
data-tabs-active-class="border-indigo-500 text-indigo-600"
|
5
|
+
data-tabs-default-tab-value="tab_payload"
|
6
|
+
>
|
7
|
+
<nav class="isolate flex rounded-t-lg shadow" aria-label="Tabs">
|
8
|
+
<button id="tab_payload" class="w-1/3 text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">Payload</button>
|
9
|
+
<button id="tab_headers" class="w-1/3 text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">Headers</button>
|
10
|
+
<button id="tab_body" class="w-1/3 text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">Body</button>
|
11
|
+
</nav>
|
12
|
+
|
13
|
+
<div class="p-5 bg-gray-800 text-white">
|
14
|
+
<div data-tabs-target="tab" id="tab_payload">
|
15
|
+
<pre><%= JSON.pretty_generate(@request.payload || {}) %></pre>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div data-tabs-target="tab" id="tab_headers">
|
19
|
+
<pre><%= JSON.pretty_generate(@request.headers || {}) %></pre>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div data-tabs-target="tab" id="tab_body">
|
23
|
+
<pre><%= @request.body || "No body" %></pre>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<% if @exceptions.empty? %>
|
2
|
+
<p class="py-10 text-center">No exceptions for this request</p>
|
3
|
+
<% else %>
|
4
|
+
<div class="flow-root">
|
5
|
+
<div class="overflow-x-auto">
|
6
|
+
<div class="inline-block min-w-full align-middle">
|
7
|
+
<table class="min-w-full divide-y divide-gray-300">
|
8
|
+
<thead class="bg-gray-50">
|
9
|
+
<tr>
|
10
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Type</th>
|
11
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Date</th>
|
12
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
13
|
+
<span class="sr-only">Show</span>
|
14
|
+
</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
<tbody class="divide-y divide-gray-200 bg-white">
|
18
|
+
<% @exceptions.each do |exception| %>
|
19
|
+
<tr>
|
20
|
+
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
|
21
|
+
<div class="min-w-0 flex-auto">
|
22
|
+
<p class="text-sm font-semibold leading-6 text-gray-900">
|
23
|
+
<%= exception.class_name %>
|
24
|
+
</p>
|
25
|
+
<p class="mt-1 truncate text-xs leading-5 text-gray-500">
|
26
|
+
<%= exception.message %>
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
</td>
|
30
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
31
|
+
<%= exception.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
32
|
+
</td>
|
33
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
34
|
+
<%= link_to 'Show', exception_path(exception), class: 'text-indigo-600 hover:text-indigo-900' %>
|
35
|
+
</td>
|
36
|
+
</tr>
|
37
|
+
<% end %>
|
38
|
+
</tbody>
|
39
|
+
</table>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% if @queries.empty? %>
|
2
|
+
<p class="py-10 text-center">No queries for this request</p>
|
3
|
+
<% else %>
|
4
|
+
<div class="flow-root">
|
5
|
+
<div class="overflow-x-auto">
|
6
|
+
<div class="inline-block min-w-full align-middle">
|
7
|
+
<%= render 'rails_live_dashboard/queries/list' %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div
|
2
|
+
class="overflow-hidden bg-white shadow sm:rounded-lg"
|
3
|
+
data-controller="tabs"
|
4
|
+
data-tabs-active-class="border-indigo-500 text-indigo-600"
|
5
|
+
data-tabs-default-tab-value="tab_queries"
|
6
|
+
>
|
7
|
+
<nav class="isolate flex rounded-t-lg shadow" aria-label="Tabs">
|
8
|
+
<button id="tab_queries" class="w-1/2 text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">
|
9
|
+
Queries (<%= @queries.size %>)
|
10
|
+
</button>
|
11
|
+
<button id="tab_exceptions" class="w-1/2 text-gray-500 hover:text-gray-700 border-b-2 py-4 px-1 text-center text-sm font-medium" data-tabs-target="button" data-action="click->tabs#select">
|
12
|
+
Exceptions (<%= @exceptions.size %>)
|
13
|
+
</button>
|
14
|
+
</nav>
|
15
|
+
|
16
|
+
<div data-tabs-target="tab" id="tab_queries">
|
17
|
+
<%= render 'queries' %>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<div data-tabs-target="tab" id="tab_exceptions">
|
21
|
+
<%= render 'exceptions' %>
|
22
|
+
</div>
|
23
|
+
</div>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<div class="mt-10">
|
2
|
+
<div class="mt-8 flow-root">
|
3
|
+
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
4
|
+
<div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
|
5
|
+
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
|
6
|
+
<table class="min-w-full divide-y divide-gray-300">
|
7
|
+
<thead class="bg-gray-50">
|
8
|
+
<tr>
|
9
|
+
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Method</th>
|
10
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">URL</th>
|
11
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Status</th>
|
12
|
+
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Date</th>
|
13
|
+
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
14
|
+
<span class="sr-only">Edit</span>
|
15
|
+
</th>
|
16
|
+
</tr>
|
17
|
+
</thead>
|
18
|
+
<tbody class="divide-y divide-gray-200 bg-white">
|
19
|
+
<% if @requests.empty? %>
|
20
|
+
<tr>
|
21
|
+
<td colspan="5">
|
22
|
+
<p class="p-5 text-center">No request registered</p>
|
23
|
+
</td>
|
24
|
+
</tr>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% @requests.each do |req| %>
|
28
|
+
<tr>
|
29
|
+
<td class="whitespace-nowrap py-4 pl-4 pr-3 sm:pl-6">
|
30
|
+
<%= render RailsLiveDashboard::RequestMethodBadgeComponent.new(req.method) %>
|
31
|
+
</td>
|
32
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"><%= req.path %></td>
|
33
|
+
<td class="whitespace-nowrap px-3 py-4">
|
34
|
+
<%= render RailsLiveDashboard::RequestStatusBadgeComponent.new(req.status_code) %>
|
35
|
+
</td>
|
36
|
+
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"><%= req.created_at.strftime("%Y-%m-%d %H:%M:%S") %></td>
|
37
|
+
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
38
|
+
<%= link_to 'Show', request_path(req), class: 'text-indigo-600 hover:text-indigo-900' %>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<% end %>
|
42
|
+
</tbody>
|
43
|
+
</table>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
</div>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<div class="flex flex-col mt-10 gap-10">
|
2
|
+
<div class="overflow-hidden bg-white shadow sm:rounded-lg">
|
3
|
+
<div class="px-4 py-6 sm:px-6">
|
4
|
+
<h3 class="text-lg font-semibold leading-7 text-gray-900">Request Details</h3>
|
5
|
+
</div>
|
6
|
+
<div class="border-t border-gray-100">
|
7
|
+
<dl class="divide-y divide-gray-100">
|
8
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
9
|
+
<dt class="text-sm font-medium text-gray-900">Date</dt>
|
10
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @request.created_at.strftime("%Y-%m-%d %H:%M:%S") %></dd>
|
11
|
+
</div>
|
12
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
13
|
+
<dt class="text-sm font-medium text-gray-900">Method</dt>
|
14
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
15
|
+
<%= render RailsLiveDashboard::RequestMethodBadgeComponent.new(@request.method, turbo_stream: @request.turbo_stream?) %>
|
16
|
+
</dd>
|
17
|
+
</div>
|
18
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
19
|
+
<dt class="text-sm font-medium text-gray-900">Controller</dt>
|
20
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @request.controller %></dd>
|
21
|
+
</div>
|
22
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
23
|
+
<dt class="text-sm font-medium text-gray-900">Action</dt>
|
24
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @request.controller_action %></dd>
|
25
|
+
</div>
|
26
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
27
|
+
<dt class="text-sm font-medium text-gray-900">Path</dt>
|
28
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @request.path %></dd>
|
29
|
+
</div>
|
30
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
31
|
+
<dt class="text-sm font-medium text-gray-900">Status</dt>
|
32
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
33
|
+
<%= render RailsLiveDashboard::RequestStatusBadgeComponent.new(@request.status_code) %>
|
34
|
+
</dd>
|
35
|
+
</div>
|
36
|
+
<div class="p-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
37
|
+
<dt class="text-sm font-medium text-gray-900">Duration</dt>
|
38
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @request.duration %> ms</dd>
|
39
|
+
</div>
|
40
|
+
</dl>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<%= render 'contents' %>
|
45
|
+
<%= render 'relateds' %>
|
46
|
+
</div>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<header class="bg-gray-800" data-controller="reveal">
|
2
|
+
<div class="mx-auto max-w-7xl px-2 sm:px-4 lg:divide-y lg:divide-gray-700 lg:px-8">
|
3
|
+
<div class="relative flex h-16 justify-between">
|
4
|
+
<div class="relative z-10 flex px-2 lg:px-0">
|
5
|
+
<div class="flex flex-shrink-0 items-center">
|
6
|
+
<h1 class="text-xl text-white">Live Dashboard</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<div class="relative z-10 flex items-center lg:hidden">
|
10
|
+
<button type="button" class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false" data-action="click->reveal#toggle">
|
11
|
+
<%= lucide_icon('menu', class: 'block h-6 w-6', 'data-reveal-target': 'item') %>
|
12
|
+
<%= lucide_icon('x', class: 'hidden h-6 w-6', 'data-reveal-target': 'item') %>
|
13
|
+
</button>
|
14
|
+
</div>
|
15
|
+
<div class="hidden lg:relative lg:z-10 lg:ml-4 lg:flex lg:items-center">
|
16
|
+
<%= link_to clean_path, title: 'Clear all data', data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white' do %>
|
17
|
+
<span class="sr-only">Clear records</span>
|
18
|
+
<%= lucide_icon('trash', class: 'h-6 w-6') %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<nav class="hidden lg:flex lg:space-x-8 lg:py-2" aria-label="Global">
|
24
|
+
<%= link_to 'Dashboard', dashboard_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(dashboard_path)) %>
|
25
|
+
<%= link_to 'Requests', requests_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(requests_path)) %>
|
26
|
+
<%= link_to 'Exceptions', exceptions_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(exceptions_path)) %>
|
27
|
+
<%= link_to 'Queries', queries_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white inline-flex items-center rounded-md py-2 px-3 text-sm font-medium', 'bg-gray-900 text-white': current_page?(queries_path)) %>
|
28
|
+
</nav>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<nav class="hidden" aria-label="Global" id="mobile-menu" data-reveal-target="item">
|
32
|
+
<div class="space-y-1 px-2 pb-3 pt-2">
|
33
|
+
<%= link_to 'Clear all data', clean_path, title: 'Clear all data', data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, class: 'text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium' %>
|
34
|
+
</div>
|
35
|
+
<hr class='my-2 border-gray-500' />
|
36
|
+
<div class="space-y-1 px-2 pb-3 pt-2">
|
37
|
+
<%= link_to 'Dashboard', dashboard_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(dashboard_path)) %>
|
38
|
+
<%= link_to 'Requests', requests_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(requests_path)) %>
|
39
|
+
<%= link_to 'Exceptions', exceptions_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(exceptions_path)) %>
|
40
|
+
<%= link_to 'Queries', queries_path, class: class_names('text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md py-2 px-3 text-base font-medium', 'bg-gray-900 text-white': current_page?(queries_path)) %>
|
41
|
+
</div>
|
42
|
+
</nav>
|
43
|
+
</header>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<turbo-frame id="slowest_queries_widget">
|
2
|
+
<h3 class="text-base font-semibold leading-6 text-gray-900">Slowest Queries</h3>
|
3
|
+
<dl class="mt-3 rounded-lg">
|
4
|
+
<ul role="list" class="divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 rounded-lg">
|
5
|
+
<% if @queries.empty? %>
|
6
|
+
<li>
|
7
|
+
<p class="p-5 text-center">No queries registered</p>
|
8
|
+
</li>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% @queries.each do |query| %>
|
12
|
+
<li>
|
13
|
+
<a href="<%= query_path(query) %>" target="_top" class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6">
|
14
|
+
<div class="flex min-w-0 gap-x-4">
|
15
|
+
<div class="min-w-0 flex-auto">
|
16
|
+
<p class="text-sm leading-6 font-semibold text-gray-900">
|
17
|
+
<%= query.content.name %>
|
18
|
+
</p>
|
19
|
+
<p class="mt-2 flex text-sm leading-6 text-gray-900">
|
20
|
+
<%= query.content.sql[0..40] %>...
|
21
|
+
</p>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<div class="flex shrink-0 items-center gap-x-4">
|
25
|
+
<div class="hidden sm:flex sm:flex-col sm:items-end">
|
26
|
+
<p class="text-sm leading-6 text-gray-900">
|
27
|
+
<%= render RailsLiveDashboard::QueryDurationBadgeComponent.new(query.content.duration) %>
|
28
|
+
</p>
|
29
|
+
<p class="mt-1 text-xs leading-5 text-gray-500">
|
30
|
+
<%= query.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
31
|
+
</p>
|
32
|
+
</div>
|
33
|
+
<svg class="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
34
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
35
|
+
</svg>
|
36
|
+
</div>
|
37
|
+
</a>
|
38
|
+
</li>
|
39
|
+
<%end %>
|
40
|
+
</ul>
|
41
|
+
</dl>
|
42
|
+
</turbo-frame>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<turbo-frame id="slow_requests_widget">
|
2
|
+
<h3 class="text-base font-semibold leading-6 text-gray-900">Slowest Requests</h3>
|
3
|
+
<dl class="mt-3 rounded-lg">
|
4
|
+
<ul role="list" class="divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 rounded-lg">
|
5
|
+
<% if @requests.empty? %>
|
6
|
+
<li>
|
7
|
+
<p class="p-5 text-center">No requests registered</p>
|
8
|
+
</li>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% @requests.each do |req| %>
|
12
|
+
<li>
|
13
|
+
<a href="<%= request_path(req) %>" target="_top" class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6">
|
14
|
+
<div class="flex min-w-0 gap-x-4">
|
15
|
+
<div class="min-w-0 flex-auto">
|
16
|
+
<p class="text-sm leading-6 text-gray-900">
|
17
|
+
<%= render RailsLiveDashboard::RequestMethodBadgeComponent.new(req.method) %>
|
18
|
+
</p>
|
19
|
+
<p class="mt-2 flex text-sm leading-6 text-gray-900">
|
20
|
+
<%= req.content.path %>
|
21
|
+
</p>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<div class="flex shrink-0 items-center gap-x-4">
|
25
|
+
<div class="hidden sm:flex sm:flex-col sm:items-end">
|
26
|
+
<p class="text-sm leading-6 text-gray-900">
|
27
|
+
<%= render RailsLiveDashboard::RequestDurationBadgeComponent.new(req.content.duration) %>
|
28
|
+
</p>
|
29
|
+
<p class="mt-1 text-xs leading-5 text-gray-500">
|
30
|
+
<%= req.created_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
31
|
+
</p>
|
32
|
+
</div>
|
33
|
+
<svg class="h-5 w-5 flex-none text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
34
|
+
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
35
|
+
</svg>
|
36
|
+
</div>
|
37
|
+
</a>
|
38
|
+
</li>
|
39
|
+
<%end %>
|
40
|
+
</ul>
|
41
|
+
</dl>
|
42
|
+
</turbo-frame>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
RailsLiveDashboard::Engine.routes.draw do
|
2
|
+
root to: 'dashboard#show'
|
3
|
+
|
4
|
+
resource :dashboard, only: [:show], controller: :dashboard
|
5
|
+
resource :clean, only: [:destroy], controller: :clean
|
6
|
+
resources :requests, only: %i[index show]
|
7
|
+
resources :exceptions, only: %i[index show]
|
8
|
+
resources :queries, only: %i[index show]
|
9
|
+
|
10
|
+
namespace :widgets do
|
11
|
+
get 'slowest-requests', action: :show, controller: :slowest_requests
|
12
|
+
get 'slowest-queries', action: :show, controller: :slowest_queries
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateRailsLiveDashboardEntries < ActiveRecord::Migration[7.1]
|
2
|
+
def change
|
3
|
+
create_table :rails_live_dashboard_entries do |t|
|
4
|
+
t.string :type, null: false
|
5
|
+
t.string :batch_id, limit: 36, null: false
|
6
|
+
t.json :content, null: false
|
7
|
+
t.boolean :should_show, default: true
|
8
|
+
t.datetime :created_at, null: false
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :rails_live_dashboard_entries, :batch_id
|
12
|
+
add_index :rails_live_dashboard_entries, :created_at
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module RailsLiveDashboard
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
desc 'Create configuration file to RailsLiveDashboard'
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __dir__)
|
6
|
+
|
7
|
+
def copy_initializer_file
|
8
|
+
copy_file 'initializer.rb', Rails.root.join('config/initializers/rails_live_dashboard.rb')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|