railswatch 1.0.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 +485 -0
- data/Rakefile +37 -0
- data/app/assets/config/railswatch_manifest.js +0 -0
- data/app/assets/images/activity.svg +13 -0
- data/app/assets/images/bot.svg +1 -0
- data/app/assets/images/close.svg +13 -0
- data/app/assets/images/details.svg +3 -0
- data/app/assets/images/download.svg +3 -0
- data/app/assets/images/export.svg +13 -0
- data/app/assets/images/external.svg +1 -0
- data/app/assets/images/git.svg +1 -0
- data/app/assets/images/github.svg +1 -0
- data/app/assets/images/home.svg +16 -0
- data/app/assets/images/import.svg +13 -0
- data/app/assets/images/menu.svg +16 -0
- data/app/assets/images/moon.svg +3 -0
- data/app/assets/images/stat.svg +1 -0
- data/app/assets/images/sun.svg +4 -0
- data/app/assets/images/user.svg +1 -0
- data/app/controllers/railswatch/base_controller.rb +35 -0
- data/app/controllers/railswatch/concerns/csv_exportable.rb +31 -0
- data/app/controllers/railswatch/railswatch_controller.rb +183 -0
- data/app/engine_assets/javascripts/apex_ext.js +30 -0
- data/app/engine_assets/javascripts/application.js +9 -0
- data/app/engine_assets/javascripts/autoupdate.js +79 -0
- data/app/engine_assets/javascripts/charts.js +279 -0
- data/app/engine_assets/javascripts/navbar.js +11 -0
- data/app/engine_assets/javascripts/panel.js +43 -0
- data/app/engine_assets/javascripts/table.js +12 -0
- data/app/engine_assets/javascripts/theme.js +43 -0
- data/app/engine_assets/stylesheets/panel.css +111 -0
- data/app/engine_assets/stylesheets/responsive.css +102 -0
- data/app/engine_assets/stylesheets/style.css +960 -0
- data/app/helpers/railswatch/railswatch_helper.rb +338 -0
- data/app/views/railswatch/_panel.html.erb +15 -0
- data/app/views/railswatch/layouts/railswatch.html.erb +81 -0
- data/app/views/railswatch/railswatch/_card.html.erb +7 -0
- data/app/views/railswatch/railswatch/_chart.html.erb +13 -0
- data/app/views/railswatch/railswatch/_crashes_table_content.html.erb +62 -0
- data/app/views/railswatch/railswatch/_custom_events_table_content.html.erb +27 -0
- data/app/views/railswatch/railswatch/_delayed_job_table_content.html.erb +52 -0
- data/app/views/railswatch/railswatch/_export.html.erb +4 -0
- data/app/views/railswatch/railswatch/_grape_requests_table_content.html.erb +31 -0
- data/app/views/railswatch/railswatch/_overview.html.erb +124 -0
- data/app/views/railswatch/railswatch/_rake_tasks_table_content.html.erb +25 -0
- data/app/views/railswatch/railswatch/_recent_requests_table_content.html.erb +28 -0
- data/app/views/railswatch/railswatch/_recent_row.html.erb +41 -0
- data/app/views/railswatch/railswatch/_requests_table_content.html.erb +51 -0
- data/app/views/railswatch/railswatch/_sidekiq_jobs_table_content.html.erb +50 -0
- data/app/views/railswatch/railswatch/_summary.html.erb +50 -0
- data/app/views/railswatch/railswatch/_table.html.erb +30 -0
- data/app/views/railswatch/railswatch/_trace.html.erb +78 -0
- data/app/views/railswatch/railswatch/crashes.html.erb +2 -0
- data/app/views/railswatch/railswatch/custom.html.erb +6 -0
- data/app/views/railswatch/railswatch/delayed_job.html.erb +6 -0
- data/app/views/railswatch/railswatch/grape.html.erb +6 -0
- data/app/views/railswatch/railswatch/index.html.erb +9 -0
- data/app/views/railswatch/railswatch/rake.html.erb +6 -0
- data/app/views/railswatch/railswatch/recent.html.erb +2 -0
- data/app/views/railswatch/railswatch/requests.html.erb +2 -0
- data/app/views/railswatch/railswatch/resources.html.erb +28 -0
- data/app/views/railswatch/railswatch/sidekiq.html.erb +6 -0
- data/app/views/railswatch/railswatch/slow.html.erb +2 -0
- data/app/views/railswatch/railswatch/summary.js.erb +3 -0
- data/app/views/railswatch/railswatch/trace.js.erb +9 -0
- data/app/views/railswatch/shared/_header.html.erb +39 -0
- data/app/views/railswatch/shared/_page_header.html.erb +23 -0
- data/config/routes.rb +27 -0
- data/lib/generators/railswatch/install/USAGE +19 -0
- data/lib/generators/railswatch/install/install_generator.rb +46 -0
- data/lib/generators/railswatch/install/templates/create_railswatch_tables.rb +140 -0
- data/lib/generators/railswatch/install/templates/initializer.rb +87 -0
- data/lib/railswatch/data_source.rb +106 -0
- data/lib/railswatch/engine.rb +103 -0
- data/lib/railswatch/events/record.rb +63 -0
- data/lib/railswatch/extensions/trace.rb +14 -0
- data/lib/railswatch/extensions/trace_db.rb +21 -0
- data/lib/railswatch/gems/custom_ext.rb +38 -0
- data/lib/railswatch/gems/delayed_job_ext.rb +70 -0
- data/lib/railswatch/gems/grape_ext.rb +64 -0
- data/lib/railswatch/gems/rake_ext.rb +69 -0
- data/lib/railswatch/gems/sidekiq_ext.rb +55 -0
- data/lib/railswatch/instrument/metrics_collector.rb +70 -0
- data/lib/railswatch/interface.rb +9 -0
- data/lib/railswatch/models/application_record.rb +31 -0
- data/lib/railswatch/models/base_record.rb +59 -0
- data/lib/railswatch/models/collection.rb +37 -0
- data/lib/railswatch/models/custom_record.rb +32 -0
- data/lib/railswatch/models/delayed_job_record.rb +39 -0
- data/lib/railswatch/models/event_record.rb +11 -0
- data/lib/railswatch/models/grape_record.rb +61 -0
- data/lib/railswatch/models/rake_record.rb +33 -0
- data/lib/railswatch/models/request_record.rb +105 -0
- data/lib/railswatch/models/resource_record.rb +33 -0
- data/lib/railswatch/models/sidekiq_record.rb +41 -0
- data/lib/railswatch/models/trace_record.rb +21 -0
- data/lib/railswatch/pruner.rb +47 -0
- data/lib/railswatch/rails/middleware.rb +117 -0
- data/lib/railswatch/rails/query_builder.rb +20 -0
- data/lib/railswatch/reports/annotations_report.rb +13 -0
- data/lib/railswatch/reports/base_report.rb +48 -0
- data/lib/railswatch/reports/breakdown_report.rb +11 -0
- data/lib/railswatch/reports/crash_report.rb +11 -0
- data/lib/railswatch/reports/overview_report.rb +88 -0
- data/lib/railswatch/reports/percentile_report.rb +16 -0
- data/lib/railswatch/reports/recent_requests_report.rb +21 -0
- data/lib/railswatch/reports/requests_report.rb +75 -0
- data/lib/railswatch/reports/resources_report.rb +42 -0
- data/lib/railswatch/reports/response_time_report.rb +27 -0
- data/lib/railswatch/reports/slow_requests_report.rb +21 -0
- data/lib/railswatch/reports/throughput_report.rb +16 -0
- data/lib/railswatch/reports/trace_report.rb +17 -0
- data/lib/railswatch/system_monitor/resources_monitor.rb +88 -0
- data/lib/railswatch/thread/current_request.rb +37 -0
- data/lib/railswatch/utils.rb +58 -0
- data/lib/railswatch/version.rb +7 -0
- data/lib/railswatch/widgets/base.rb +17 -0
- data/lib/railswatch/widgets/card.rb +19 -0
- data/lib/railswatch/widgets/chart.rb +33 -0
- data/lib/railswatch/widgets/crashes_table.rb +27 -0
- data/lib/railswatch/widgets/custom_events_table.rb +48 -0
- data/lib/railswatch/widgets/delayed_job_table.rb +31 -0
- data/lib/railswatch/widgets/grape_requests_table.rb +31 -0
- data/lib/railswatch/widgets/percentile_card.rb +23 -0
- data/lib/railswatch/widgets/rake_tasks_table.rb +31 -0
- data/lib/railswatch/widgets/recent_requests_table.rb +35 -0
- data/lib/railswatch/widgets/requests_table.rb +27 -0
- data/lib/railswatch/widgets/resource_chart.rb +116 -0
- data/lib/railswatch/widgets/response_time_chart.rb +29 -0
- data/lib/railswatch/widgets/sidekiq_jobs_table.rb +31 -0
- data/lib/railswatch/widgets/slow_requests_table.rb +33 -0
- data/lib/railswatch/widgets/table.rb +43 -0
- data/lib/railswatch/widgets/throughput_chart.rb +29 -0
- data/lib/railswatch.rb +184 -0
- data/lib/tasks/railswatch.rake +9 -0
- metadata +445 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<section class="rm-overview-grid">
|
|
2
|
+
<div class="rm-surface rm-overview-panel rm-overview-panel--metrics">
|
|
3
|
+
<div class="rm-section-heading">
|
|
4
|
+
<div>
|
|
5
|
+
<p class="rm-section-heading__eyebrow">Snapshot</p>
|
|
6
|
+
<h2 class="rm-section-heading__title">Health at a glance</h2>
|
|
7
|
+
</div>
|
|
8
|
+
<p class="rm-section-heading__meta">
|
|
9
|
+
<% if @overview[:latest_request_at].present? %>
|
|
10
|
+
Last sample <%= format_rm_datetime(@overview[:latest_request_at]) %>
|
|
11
|
+
<% else %>
|
|
12
|
+
Waiting for incoming traffic
|
|
13
|
+
<% end %>
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="rm-kpi-grid">
|
|
18
|
+
<article class="rm-kpi-card" data-tone="neutral">
|
|
19
|
+
<span class="rm-kpi-card__label">Total requests</span>
|
|
20
|
+
<strong class="rm-kpi-card__value"><%= compact_number(@overview[:total_requests]) %></strong>
|
|
21
|
+
<span class="rm-kpi-card__meta">Requests captured in scope</span>
|
|
22
|
+
</article>
|
|
23
|
+
|
|
24
|
+
<article class="rm-kpi-card" data-tone="healthy">
|
|
25
|
+
<span class="rm-kpi-card__label">Average response</span>
|
|
26
|
+
<strong class="rm-kpi-card__value"><%= ms(@overview[:average_duration]) %></strong>
|
|
27
|
+
<span class="rm-kpi-card__meta">Mean request runtime</span>
|
|
28
|
+
</article>
|
|
29
|
+
|
|
30
|
+
<article class="rm-kpi-card" data-tone="<%= health_tone(@overview[:error_rate]) %>">
|
|
31
|
+
<span class="rm-kpi-card__label">Error rate</span>
|
|
32
|
+
<strong class="rm-kpi-card__value"><%= percentage(@overview[:error_rate]) %></strong>
|
|
33
|
+
<span class="rm-kpi-card__meta">5xx share of observed traffic</span>
|
|
34
|
+
</article>
|
|
35
|
+
|
|
36
|
+
<article class="rm-kpi-card" data-tone="<%= @overview[:slow_requests].positive? ? 'warning' : 'healthy' %>">
|
|
37
|
+
<span class="rm-kpi-card__label">Slow requests</span>
|
|
38
|
+
<strong class="rm-kpi-card__value"><%= compact_number(@overview[:slow_requests]) %></strong>
|
|
39
|
+
<span class="rm-kpi-card__meta">Above <%= Railswatch.slow_requests_threshold %> ms</span>
|
|
40
|
+
</article>
|
|
41
|
+
|
|
42
|
+
<article class="rm-kpi-card" data-tone="neutral">
|
|
43
|
+
<span class="rm-kpi-card__label">Unique endpoints</span>
|
|
44
|
+
<strong class="rm-kpi-card__value"><%= compact_number(@overview[:unique_endpoints]) %></strong>
|
|
45
|
+
<span class="rm-kpi-card__meta">Controllers and actions in window</span>
|
|
46
|
+
</article>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="rm-surface rm-overview-panel">
|
|
51
|
+
<div class="rm-section-heading">
|
|
52
|
+
<div>
|
|
53
|
+
<p class="rm-section-heading__eyebrow">Insights</p>
|
|
54
|
+
<h2 class="rm-section-heading__title">Primary signals</h2>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="rm-insight-list">
|
|
59
|
+
<article class="rm-insight-item">
|
|
60
|
+
<span class="rm-insight-item__label">Busiest endpoint</span>
|
|
61
|
+
<strong class="rm-insight-item__value"><%= @overview.dig(:busiest_endpoint, :name) || 'No data yet' %></strong>
|
|
62
|
+
<span class="rm-insight-item__meta"><%= compact_number(@overview.dig(:busiest_endpoint, :count)) %> requests</span>
|
|
63
|
+
</article>
|
|
64
|
+
|
|
65
|
+
<article class="rm-insight-item">
|
|
66
|
+
<span class="rm-insight-item__label">Slowest p95 endpoint</span>
|
|
67
|
+
<strong class="rm-insight-item__value"><%= @overview.dig(:slowest_endpoint, :name) || 'No data yet' %></strong>
|
|
68
|
+
<span class="rm-insight-item__meta"><%= ms(@overview.dig(:slowest_endpoint, :p95_duration)) %> at p95</span>
|
|
69
|
+
</article>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="rm-surface rm-overview-panel">
|
|
74
|
+
<div class="rm-section-heading">
|
|
75
|
+
<div>
|
|
76
|
+
<p class="rm-section-heading__eyebrow">Traffic leaders</p>
|
|
77
|
+
<h2 class="rm-section-heading__title">Most active endpoints</h2>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="rm-ranking-list">
|
|
82
|
+
<% if @traffic_leaders.empty? %>
|
|
83
|
+
<p class="rm-empty-state">No request volume yet.</p>
|
|
84
|
+
<% else %>
|
|
85
|
+
<% @traffic_leaders.each_with_index do |row, index| %>
|
|
86
|
+
<article class="rm-ranking-item">
|
|
87
|
+
<span class="rm-ranking-item__index"><%= index + 1 %></span>
|
|
88
|
+
<div class="rm-ranking-item__body">
|
|
89
|
+
<strong class="rm-ranking-item__title"><%= row[:group] %></strong>
|
|
90
|
+
<span class="rm-ranking-item__meta"><%= compact_number(row[:count]) %> requests</span>
|
|
91
|
+
</div>
|
|
92
|
+
<span class="rm-ranking-item__value"><%= ms(row[:p95_duration]) %></span>
|
|
93
|
+
</article>
|
|
94
|
+
<% end %>
|
|
95
|
+
<% end %>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="rm-surface rm-overview-panel">
|
|
100
|
+
<div class="rm-section-heading">
|
|
101
|
+
<div>
|
|
102
|
+
<p class="rm-section-heading__eyebrow">Latency leaders</p>
|
|
103
|
+
<h2 class="rm-section-heading__title">Highest p95 endpoints</h2>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="rm-ranking-list">
|
|
108
|
+
<% if @latency_leaders.empty? %>
|
|
109
|
+
<p class="rm-empty-state">No latency data yet.</p>
|
|
110
|
+
<% else %>
|
|
111
|
+
<% @latency_leaders.each_with_index do |row, index| %>
|
|
112
|
+
<article class="rm-ranking-item">
|
|
113
|
+
<span class="rm-ranking-item__index"><%= index + 1 %></span>
|
|
114
|
+
<div class="rm-ranking-item__body">
|
|
115
|
+
<strong class="rm-ranking-item__title"><%= row[:group] %></strong>
|
|
116
|
+
<span class="rm-ranking-item__meta"><%= compact_number(row[:count]) %> requests</span>
|
|
117
|
+
</div>
|
|
118
|
+
<span class="rm-ranking-item__value"><%= ms(row[:p95_duration]) %></span>
|
|
119
|
+
</article>
|
|
120
|
+
<% end %>
|
|
121
|
+
<% end %>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</section>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<table class="<%= table.table_classes %>">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th data-sort="string">Datetime</th>
|
|
5
|
+
<th data-sort="string">Details</th>
|
|
6
|
+
<th data-sort="float">Duration</th>
|
|
7
|
+
<th data-sort="string">Status</th>
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
<% if table.data.empty? %>
|
|
12
|
+
<tr>
|
|
13
|
+
<td colspan="4"><%= table.empty_message %></td>
|
|
14
|
+
</tr>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% table.data.each do |e| %>
|
|
17
|
+
<tr>
|
|
18
|
+
<td><%= format_rm_datetime e[:datetime] %></td>
|
|
19
|
+
<td>[<%= e[:task].join(" ") %>]</td>
|
|
20
|
+
<td class="nowrap"><%= ms e[:duration] %></td>
|
|
21
|
+
<td><%= status_tag e[:status] %></td>
|
|
22
|
+
</tr>
|
|
23
|
+
<% end %>
|
|
24
|
+
</tbody>
|
|
25
|
+
</table>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<table id="<%= table.table_id %>" class="<%= table.table_classes %>">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th data-sort="string"></th>
|
|
5
|
+
<th data-sort="string">Datetime</th>
|
|
6
|
+
<th data-sort="string">Controller#action</th>
|
|
7
|
+
<th data-sort="string">Method</th>
|
|
8
|
+
<th data-sort="string">Format</th>
|
|
9
|
+
<th></th>
|
|
10
|
+
<th data-sort="string">Path</th>
|
|
11
|
+
<th data-sort="string">Status</th>
|
|
12
|
+
<th data-sort="float">Duration</th>
|
|
13
|
+
<th data-sort="float">Views</th>
|
|
14
|
+
<th data-sort="float">DB</th>
|
|
15
|
+
<th></th>
|
|
16
|
+
</tr>
|
|
17
|
+
</thead>
|
|
18
|
+
<tbody>
|
|
19
|
+
<% if table.data.empty? %>
|
|
20
|
+
<tr>
|
|
21
|
+
<td colspan="12"><%= table.empty_message %></td>
|
|
22
|
+
</tr>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% table.data.each do |e| %>
|
|
25
|
+
<%= render 'recent_row', e: e %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</tbody>
|
|
28
|
+
</table>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<tr from_timei="<%= e[:datetimei] %>">
|
|
2
|
+
<td>
|
|
3
|
+
<% if e[:request_id].present? %>
|
|
4
|
+
<%= link_to railswatch.railswatch_trace_path(id: e[:request_id]), remote: true do %>
|
|
5
|
+
<span class="stats_icon_max"><%= icon 'activity' %></span>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% end %>
|
|
8
|
+
</td>
|
|
9
|
+
<td><%= format_rm_datetime e[:datetime] %></td>
|
|
10
|
+
<td>
|
|
11
|
+
<% controller_action_info = e[:controller] + '#' + e[:action]%>
|
|
12
|
+
<%= link_to railswatch.railswatch_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true, title: controller_action_info do %>
|
|
13
|
+
<span class="details_icon">
|
|
14
|
+
<%= icon 'details' %>
|
|
15
|
+
</span>
|
|
16
|
+
<%= truncate(controller_action_info, length: 40) %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</td>
|
|
19
|
+
<td><%= e[:method] %></td>
|
|
20
|
+
<td><%= e[:format] %></td>
|
|
21
|
+
<td><%= bot_icon e["user_agent"] %></td>
|
|
22
|
+
<td>
|
|
23
|
+
<span class="with_external_icon">
|
|
24
|
+
<%= link_to_path(e) %>
|
|
25
|
+
<span class="icon external_icon">
|
|
26
|
+
<%= icon('external') %>
|
|
27
|
+
</span>
|
|
28
|
+
</span>
|
|
29
|
+
</td>
|
|
30
|
+
<td><%= status_tag e[:status] %></td>
|
|
31
|
+
<td class="nowrap"><%= ms e[:duration] %></td>
|
|
32
|
+
<td class="nowrap"><%= ms e[:view_runtime] %></td>
|
|
33
|
+
<td class="nowrap"><%= ms e[:db_runtime] %></td>
|
|
34
|
+
<td>
|
|
35
|
+
<% if e[:request_id].present? %>
|
|
36
|
+
<%= link_to railswatch.railswatch_trace_path(id: e[:request_id]), remote: true do %>
|
|
37
|
+
<span class="stats_icon_max"><%= icon 'activity' %></span>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% end %>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<table class="<%= table.table_classes %>">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th colspan='3'>Name</th>
|
|
5
|
+
<th colspan='3' class="attention">Percentile</th>
|
|
6
|
+
<th colspan='3'>Average</th>
|
|
7
|
+
<th colspan='3'>Slowest</th>
|
|
8
|
+
</tr>
|
|
9
|
+
<tr>
|
|
10
|
+
<th data-sort="string">Controller#action</th>
|
|
11
|
+
<th data-sort="string">Format</th>
|
|
12
|
+
<th data-sort="int">Requests</th>
|
|
13
|
+
<th data-sort="float" class="attention">P50</th>
|
|
14
|
+
<th data-sort="float" class="attention">P95</th>
|
|
15
|
+
<th data-sort="float" class="attention">P99</th>
|
|
16
|
+
<th data-sort="float">Duration</th>
|
|
17
|
+
<th data-sort="float">Views</th>
|
|
18
|
+
<th data-sort="float">DB</th>
|
|
19
|
+
<th data-sort="float">Duration</th>
|
|
20
|
+
<th data-sort="float">Views</th>
|
|
21
|
+
<th data-sort="float">DB</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
<% table.data.each do |e| %>
|
|
26
|
+
<% groups = e[:group].split("|") %>
|
|
27
|
+
<% c, a = groups[0].split("#") %>
|
|
28
|
+
<tr>
|
|
29
|
+
<td>
|
|
30
|
+
<%= link_to railswatch.railswatch_summary_path({controller_eq: c, action_eq: a}), remote: true do %>
|
|
31
|
+
<span class="details_icon">
|
|
32
|
+
<%= icon 'details' %>
|
|
33
|
+
</span>
|
|
34
|
+
<%= groups[0] %>
|
|
35
|
+
<% end %>
|
|
36
|
+
</td>
|
|
37
|
+
<td><%= link_to groups[1].try(:upcase), railswatch.railswatch_summary_path({controller_eq: c, action_eq: a, format_eq: groups[1]}), remote: true %></td>
|
|
38
|
+
<td><%= e[:count] %></td>
|
|
39
|
+
<td class="nowrap attention"><%= ms e[:p50_duration] %></td>
|
|
40
|
+
<td class="nowrap attention"><%= ms e[:p95_duration] %></td>
|
|
41
|
+
<td class="nowrap attention"><%= ms e[:p99_duration] %></td>
|
|
42
|
+
<td class="nowrap"><%= ms e[:duration_average] %></td>
|
|
43
|
+
<td class="nowrap"><%= ms e[:view_runtime_average] %></td>
|
|
44
|
+
<td class="nowrap"><%= ms e[:db_runtime_average] %></td>
|
|
45
|
+
<td class="nowrap"><%= ms e[:duration_slowest] %></td>
|
|
46
|
+
<td class="nowrap"><%= ms e[:view_runtime_slowest] %></td>
|
|
47
|
+
<td class="nowrap"><%= ms e[:db_runtime_slowest] %></td>
|
|
48
|
+
</tr>
|
|
49
|
+
<% end %>
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<table class="<%= table.table_classes %>">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th data-sort="string">Datetime</th>
|
|
5
|
+
<th data-sort="string">Queue</th>
|
|
6
|
+
<th data-sort="string">Worker</th>
|
|
7
|
+
<th data-sort="string">Job ID</th>
|
|
8
|
+
<th data-sort="string">Status</th>
|
|
9
|
+
<th data-sort="float">Duration</th>
|
|
10
|
+
<th>Args</th>
|
|
11
|
+
<th>Message / Backtrace</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<% if table.data.empty? %>
|
|
16
|
+
<tr>
|
|
17
|
+
<td colspan="8"><%= table.empty_message %></td>
|
|
18
|
+
</tr>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% table.data.each do |e| %>
|
|
21
|
+
<tr>
|
|
22
|
+
<td><%= format_rm_datetime e[:datetime] %></td>
|
|
23
|
+
<td><%= e[:queue] %></td>
|
|
24
|
+
<td><%= e[:worker] %></td>
|
|
25
|
+
<td><%= e[:jid] %></td>
|
|
26
|
+
<td><%= status_tag e[:status] %></td>
|
|
27
|
+
<td class="nowrap"><%= ms e[:duration] %></td>
|
|
28
|
+
<td class="very-small-text">
|
|
29
|
+
<% args = e[:job_args] %>
|
|
30
|
+
<% if args.present? %>
|
|
31
|
+
<%= args.is_a?(Array) ? args.map(&:inspect).join(', ') : args.inspect %>
|
|
32
|
+
<% else %>
|
|
33
|
+
-
|
|
34
|
+
<% end %>
|
|
35
|
+
</td>
|
|
36
|
+
<td>
|
|
37
|
+
<% if e[:message].present? %>
|
|
38
|
+
<div><%= e[:message] %></div>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% if e[:error_backtrace].present? %>
|
|
41
|
+
<pre class="rm-backtrace rm-backtrace-inline"><%= e[:error_backtrace] %></pre>
|
|
42
|
+
<% end %>
|
|
43
|
+
<% if e[:message].blank? && e[:error_backtrace].blank? %>
|
|
44
|
+
-
|
|
45
|
+
<% end %>
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
<% end %>
|
|
49
|
+
</tbody>
|
|
50
|
+
</table>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<h2 class="rm-panel-section-title">Throughput</h2>
|
|
2
|
+
<railswatch-chart id="throughput_report_chart_mini" type="TIR" legend="RPM" units="rpm" class="chart_mini">
|
|
3
|
+
<%= raw @throughput_report_data.to_json %>
|
|
4
|
+
</railswatch-chart>
|
|
5
|
+
|
|
6
|
+
<h2 class="rm-panel-section-title">Average Response Time</h2>
|
|
7
|
+
<railswatch-chart id="response_time_report_chart_mini" type="RT" legend="Response Time" class="chart_mini">
|
|
8
|
+
<%= raw @response_time_report_data.to_json %>
|
|
9
|
+
</railswatch-chart>
|
|
10
|
+
|
|
11
|
+
<h2 class="rm-panel-section-title"><%= title %></h2>
|
|
12
|
+
<div class="rm-table-shell">
|
|
13
|
+
<table class="table is-fullwidth is-hoverable is-narrow is-size-8">
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th data-sort="string">Datetime</th>
|
|
17
|
+
<th data-sort="string">Method</th>
|
|
18
|
+
<th></th>
|
|
19
|
+
<th data-sort="string">Path</th>
|
|
20
|
+
<th data-sort="string">Format</th>
|
|
21
|
+
<th data-sort="int">Status</th>
|
|
22
|
+
<th data-sort="float">Duration</th>
|
|
23
|
+
<th data-sort="float">Views</th>
|
|
24
|
+
<th data-sort="float">DB</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
<% @data.each do |e| %>
|
|
29
|
+
<tr>
|
|
30
|
+
<td><%= format_rm_datetime e[:datetime] %></td>
|
|
31
|
+
<td><%= e[:method] %></td>
|
|
32
|
+
<td><%= bot_icon e["user_agent"] %></td>
|
|
33
|
+
<td>
|
|
34
|
+
<span class="with_external_icon">
|
|
35
|
+
<%= link_to_path(e) %>
|
|
36
|
+
<span class="icon external_icon">
|
|
37
|
+
<%= icon('external') %>
|
|
38
|
+
</span>
|
|
39
|
+
</span>
|
|
40
|
+
</td>
|
|
41
|
+
<td><%= e[:format] %></td>
|
|
42
|
+
<td><%= status_tag e[:status] %></td>
|
|
43
|
+
<td class="nowrap"><%= ms e[:duration] %></td>
|
|
44
|
+
<td class="nowrap"><%= ms e[:view_runtime] %></td>
|
|
45
|
+
<td class="nowrap"><%= ms e[:db_runtime] %></td>
|
|
46
|
+
</tr>
|
|
47
|
+
<% end %>
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
50
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<section class="rm-surface rm-widget-card rm-widget-card--table">
|
|
2
|
+
<header class="rm-widget-card__header">
|
|
3
|
+
<div>
|
|
4
|
+
<h2 class="rm-widget-card__title"><%= table.subtitle %></h2>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div class="rm-widget-card__actions">
|
|
8
|
+
<% if table.show_export? %>
|
|
9
|
+
<%= render "export" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
12
|
+
</header>
|
|
13
|
+
|
|
14
|
+
<div class="rm-widget-card__body">
|
|
15
|
+
<% if table.auto_update_interval %>
|
|
16
|
+
<div class="rm-widget-card__toolbar">
|
|
17
|
+
<railswatch-autoupdate interval="<%= table.auto_update_interval %>">
|
|
18
|
+
<label class="rm-toggle">
|
|
19
|
+
<input type="checkbox" />
|
|
20
|
+
<span>Auto-update</span>
|
|
21
|
+
</label>
|
|
22
|
+
</railswatch-autoupdate>
|
|
23
|
+
</div>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<div class="rm-table-shell">
|
|
27
|
+
<%= render table.content_partial_path, table: table %>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</section>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<% ctx = @record&.request_context.presence %>
|
|
2
|
+
<% bt = @record&.backtrace.presence %>
|
|
3
|
+
|
|
4
|
+
<% if ctx.present? %>
|
|
5
|
+
<h2 class="rm-panel-section-title">Request Context</h2>
|
|
6
|
+
<div class="rm-context-grid">
|
|
7
|
+
<% if ctx['ip'].present? %>
|
|
8
|
+
<div class="rm-context-row">
|
|
9
|
+
<span class="rm-context-label">IP</span>
|
|
10
|
+
<span class="rm-context-value"><%= ctx['ip'] %></span>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% if ctx['user_agent'].present? %>
|
|
14
|
+
<div class="rm-context-row">
|
|
15
|
+
<span class="rm-context-label">User Agent</span>
|
|
16
|
+
<span class="rm-context-value rm-context-ua"><%= ctx['user_agent'] %></span>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% if ctx['user'].present? %>
|
|
20
|
+
<div class="rm-context-row">
|
|
21
|
+
<span class="rm-context-label">User</span>
|
|
22
|
+
<span class="rm-context-value">
|
|
23
|
+
<% ctx['user'].each do |k, v| %>
|
|
24
|
+
<span class="rm-context-kv"><strong><%= k %>:</strong> <%= v %></span>
|
|
25
|
+
<% end %>
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
<% end %>
|
|
29
|
+
<% if ctx['params'].present? %>
|
|
30
|
+
<div class="rm-context-row">
|
|
31
|
+
<span class="rm-context-label">Params</span>
|
|
32
|
+
<span class="rm-context-value">
|
|
33
|
+
<% ctx['params'].each do |k, v| %>
|
|
34
|
+
<span class="rm-context-kv"><strong><%= k %>:</strong> <%= v %></span>
|
|
35
|
+
<% end %>
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
<% end %>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
41
|
+
|
|
42
|
+
<% if bt.present? %>
|
|
43
|
+
<h2 class="rm-panel-section-title">Exception Backtrace</h2>
|
|
44
|
+
<pre class="rm-backtrace"><%= bt.join("\n") %></pre>
|
|
45
|
+
<% end %>
|
|
46
|
+
|
|
47
|
+
<h2 class="rm-panel-section-title">Trace</h2>
|
|
48
|
+
<div class="rm-table-shell">
|
|
49
|
+
<table class="table is-fullwidth is-hoverable is-narrow is-size-7">
|
|
50
|
+
<tbody>
|
|
51
|
+
<% @data.each do |e| %>
|
|
52
|
+
<tr>
|
|
53
|
+
<% if e["group"] == 'db' %>
|
|
54
|
+
<td class="nowrap has-text-right <%= duration_alert_class(e['duration']) %>">
|
|
55
|
+
<%= e["duration"] %> ms
|
|
56
|
+
</td>
|
|
57
|
+
<td>
|
|
58
|
+
<%= e["sql"] %>
|
|
59
|
+
</td>
|
|
60
|
+
<% elsif e["group"] == 'view' %>
|
|
61
|
+
<td class="nowrap has-text-right <%= duration_alert_class(extract_duration(e['message'])) %>">
|
|
62
|
+
<%= extract_duration(e["message"]) %>
|
|
63
|
+
</td>
|
|
64
|
+
<td>
|
|
65
|
+
<%= e["message"] %>
|
|
66
|
+
</td>
|
|
67
|
+
<% end %>
|
|
68
|
+
</tr>
|
|
69
|
+
<% end %>
|
|
70
|
+
|
|
71
|
+
<% if @data.empty? %>
|
|
72
|
+
<tr>
|
|
73
|
+
<td>Nothing to show here. Perhaps details about the request already expired. Setting "slow_requests_time_window" equal to "duration" helps keep traces available.</td>
|
|
74
|
+
</tr>
|
|
75
|
+
<% end %>
|
|
76
|
+
</tbody>
|
|
77
|
+
</table>
|
|
78
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<%= render 'railswatch/shared/page_header', **page_meta(:resources), autoupdate_interval: '10s' %>
|
|
2
|
+
|
|
3
|
+
<% @resources_report.servers.each do |server| %>
|
|
4
|
+
<section class="rm-resource-group">
|
|
5
|
+
<div class="rm-resource-group__header">
|
|
6
|
+
<p class="rm-section-heading__eyebrow">Server</p>
|
|
7
|
+
<h2 class="rm-resource-group__title"><%= server.name %></h2>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="rm-resource-grid">
|
|
11
|
+
<% server.charts.each do |chart| %>
|
|
12
|
+
<section class="rm-surface rm-widget-card">
|
|
13
|
+
<header class="rm-widget-card__header">
|
|
14
|
+
<div>
|
|
15
|
+
<h3 class="rm-widget-card__title"><%= chart.subtitle %></h3>
|
|
16
|
+
</div>
|
|
17
|
+
</header>
|
|
18
|
+
<div class="rm-widget-card__body">
|
|
19
|
+
<railswatch-chart id="<%= chart.id %>" type="<%= chart.type %>" legend="<%= chart.legend %>" class="chart">
|
|
20
|
+
<%= raw chart.data.to_json %>
|
|
21
|
+
</railswatch-chart>
|
|
22
|
+
<p class="rm-widget-card__description"><%= chart.description %></p>
|
|
23
|
+
</div>
|
|
24
|
+
</section>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
</section>
|
|
28
|
+
<% end %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<% if @record %>
|
|
2
|
+
window.panel.header.html("<%= j report_name(@record.record_hash.with_indifferent_access.except(*Railswatch.ignore_trace_headers)) %>" + window.panel.close);
|
|
3
|
+
<% else %>
|
|
4
|
+
window.panel.header.html(window.panel.close);
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
window.panel.content.html("<%= j render '/railswatch/railswatch/trace' %>");
|
|
8
|
+
|
|
9
|
+
showPanel();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<header class="rm-topbar" data-skip-autoupdate>
|
|
2
|
+
<div class="rm-topbar__brand">
|
|
3
|
+
<%= link_to railswatch.railswatch_url, class: "rm-brand" do %>
|
|
4
|
+
<span class="rm-brand__mark">RM</span>
|
|
5
|
+
<span class="rm-brand__copy">
|
|
6
|
+
<strong>Railswatch</strong>
|
|
7
|
+
<small>Observe performance without leaving your app</small>
|
|
8
|
+
</span>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="rm-topbar__controls">
|
|
13
|
+
<button type="button" class="rm-icon-button rm-mobile-nav-toggle" data-nav-toggle aria-expanded="false" aria-controls="railswatch-nav">
|
|
14
|
+
<span class="rm-mobile-nav-toggle__line"></span>
|
|
15
|
+
<span class="rm-mobile-nav-toggle__line"></span>
|
|
16
|
+
<span class="rm-mobile-nav-toggle__line"></span>
|
|
17
|
+
<span class="is-sr-only">Toggle navigation</span>
|
|
18
|
+
</button>
|
|
19
|
+
|
|
20
|
+
<button type="button" class="rm-icon-button" data-theme-toggle aria-label="Toggle color theme">
|
|
21
|
+
<span class="rm-theme-toggle__icon rm-theme-toggle__icon--sun"><%= icon('sun') %></span>
|
|
22
|
+
<span class="rm-theme-toggle__icon rm-theme-toggle__icon--moon"><%= icon('moon') %></span>
|
|
23
|
+
<span class="rm-theme-toggle__label" data-theme-label>Theme</span>
|
|
24
|
+
</button>
|
|
25
|
+
|
|
26
|
+
<%= link_to Railswatch.home_link, class: "rm-icon-button", aria: { label: 'Application home' } do %>
|
|
27
|
+
<span class="home_icon"><%= icon('home') %></span>
|
|
28
|
+
<span>Home</span>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<nav id="railswatch-nav" class="rm-nav" aria-label="Primary">
|
|
33
|
+
<% navigation_items.each do |item| %>
|
|
34
|
+
<%= link_to item[:path], class: "rm-nav__link #{active?(item[:section])}" do %>
|
|
35
|
+
<span><%= item[:label] %></span>
|
|
36
|
+
<% end %>
|
|
37
|
+
<% end %>
|
|
38
|
+
</nav>
|
|
39
|
+
</header>
|