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,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Reports
|
|
5
|
+
class ResourcesReport < BaseReport
|
|
6
|
+
Server = Struct.new(:report, :key) do
|
|
7
|
+
def name
|
|
8
|
+
key.split('///').join(', ')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def charts
|
|
12
|
+
Railswatch.system_monitors.map do |class_name|
|
|
13
|
+
Widgets.const_get(class_name).new(self)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def servers
|
|
19
|
+
data.keys.map { |key| Server.new(self, key) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def extract_signal(&block)
|
|
23
|
+
data.transform_values do |records|
|
|
24
|
+
prepare_report(records.to_h do |entry|
|
|
25
|
+
[entry[:datetimei] * 1000, block.call(entry)]
|
|
26
|
+
end)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def data
|
|
33
|
+
@data ||= db.order(:occurred_at).map(&:record_hash)
|
|
34
|
+
.group_by { |entry| "#{entry[:server]}///#{entry[:context]}///#{entry[:role]}" }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def prepare_report(input)
|
|
38
|
+
nullify_data(input, Railswatch.system_monitor_duration)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Reports
|
|
5
|
+
class ResponseTimeReport < BaseReport
|
|
6
|
+
def data
|
|
7
|
+
averages = grouped_durations.transform_values { |values| values.sum.to_f / values.count }
|
|
8
|
+
nullify_data(averages).map { |x, y| [x, y&.round(2) || 0] }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def grouped_durations
|
|
14
|
+
db.pluck(:occurred_at, :duration_ms).each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |row, buckets|
|
|
15
|
+
occurred_at, duration_ms = row
|
|
16
|
+
next if duration_ms.nil?
|
|
17
|
+
|
|
18
|
+
buckets[bucket_key(occurred_at)] << duration_ms
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def bucket_key(occurred_at)
|
|
23
|
+
occurred_at.change(sec: 0, usec: 0).to_i * 1000
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Reports
|
|
5
|
+
class SlowRequestsReport < BaseReport
|
|
6
|
+
def data
|
|
7
|
+
db.where('occurred_at > ?', Railswatch.slow_requests_time_window.ago)
|
|
8
|
+
.where('duration_ms > ?', Railswatch.slow_requests_threshold.to_f)
|
|
9
|
+
.order(occurred_at: :desc)
|
|
10
|
+
.limit(limit)
|
|
11
|
+
.map(&:record_hash)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def limit
|
|
17
|
+
Railswatch.slow_requests_limit ? Railswatch.slow_requests_limit.to_i : 100_000
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Reports
|
|
5
|
+
class ThroughputReport < BaseReport
|
|
6
|
+
def data
|
|
7
|
+
series = db.pluck(:occurred_at).each_with_object(Hash.new(0)) do |occurred_at, buckets|
|
|
8
|
+
bucket = occurred_at.change(sec: 0, usec: 0).to_i * 1000
|
|
9
|
+
buckets[bucket] += 1
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
nullify_data(series.transform_values(&:to_f)).map { |x, y| [x, (y || 0).round(2)] }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Reports
|
|
5
|
+
class TraceReport
|
|
6
|
+
attr_reader :request_id
|
|
7
|
+
|
|
8
|
+
def initialize(request_id:)
|
|
9
|
+
@request_id = request_id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def data
|
|
13
|
+
Railswatch::Models::TraceRecord.find_by(request_id: request_id)&.value || []
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module SystemMonitor
|
|
5
|
+
class ResourcesMonitor
|
|
6
|
+
attr_reader :context, :role
|
|
7
|
+
|
|
8
|
+
def initialize(context, role)
|
|
9
|
+
@context = context
|
|
10
|
+
@role = role
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@thread = nil
|
|
13
|
+
|
|
14
|
+
return unless Railswatch._resource_monitor_enabled
|
|
15
|
+
|
|
16
|
+
start_monitoring
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def start_monitoring
|
|
20
|
+
@mutex.synchronize do
|
|
21
|
+
return if @thread
|
|
22
|
+
|
|
23
|
+
@thread = Thread.new { monitor_loop }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def stop_monitoring
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
return unless @thread
|
|
30
|
+
|
|
31
|
+
@thread.kill
|
|
32
|
+
@thread = nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def payload
|
|
37
|
+
monitors.reduce({}) do |data, monitor|
|
|
38
|
+
data.merge(monitor.key => monitor.measure)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def monitors
|
|
43
|
+
@monitors ||= Railswatch.system_monitors.map do |class_name|
|
|
44
|
+
Railswatch::Widgets.const_get(class_name).new(nil)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def run
|
|
49
|
+
store_data(payload)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def store_data(data) # rubocop:disable Metrics/MethodLength
|
|
53
|
+
now = Railswatch::Utils.kind_of_now
|
|
54
|
+
now = now.change(sec: 0, usec: 0)
|
|
55
|
+
|
|
56
|
+
Railswatch.log(resource_log_message(data))
|
|
57
|
+
Railswatch::Models::ResourceRecord.new(
|
|
58
|
+
server: server_id,
|
|
59
|
+
context: context,
|
|
60
|
+
role: role,
|
|
61
|
+
datetime: now.strftime(Railswatch::FORMAT),
|
|
62
|
+
datetimei: now.to_i,
|
|
63
|
+
json: data
|
|
64
|
+
).save
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def server_id
|
|
68
|
+
@server_id ||= ENV['RAILSWATCH_SERVER_ID'] || `hostname`.strip
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def monitor_loop
|
|
74
|
+
loop do
|
|
75
|
+
run
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
::Rails.logger.error "Monitor error: #{e.message}"
|
|
78
|
+
ensure
|
|
79
|
+
sleep 60
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resource_log_message(data)
|
|
84
|
+
"Server: #{server_id}, Context: #{context}, Role: #{role}, data: #{data}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
class CurrentRequest
|
|
5
|
+
attr_reader :request_id, :tracings, :ignore
|
|
6
|
+
attr_accessor :data, :record
|
|
7
|
+
|
|
8
|
+
def self.init
|
|
9
|
+
Thread.current[:rp_current_request] ||= CurrentRequest.new(SecureRandom.hex(16))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.current
|
|
13
|
+
CurrentRequest.init
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.cleanup
|
|
17
|
+
Railswatch.log(
|
|
18
|
+
'----------------------------------------------------> ' \
|
|
19
|
+
"CurrentRequest.cleanup !!!!!!!!!!!! -------------------------\n\n"
|
|
20
|
+
)
|
|
21
|
+
Railswatch.skip = false
|
|
22
|
+
Thread.current[:rp_current_request] = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize(request_id)
|
|
26
|
+
@request_id = request_id
|
|
27
|
+
@tracings = []
|
|
28
|
+
@ignore = Set.new
|
|
29
|
+
@data = nil
|
|
30
|
+
@record = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def trace(options = {})
|
|
34
|
+
@tracings << options.merge(time: Railswatch::Utils.time.to_i)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
class Utils
|
|
5
|
+
DEFAULT_TIME_OFFSET = 1.minute
|
|
6
|
+
|
|
7
|
+
def self.time
|
|
8
|
+
Time.now.utc
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.kind_of_now
|
|
12
|
+
time + DEFAULT_TIME_OFFSET
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.from_datetimei(datetimei)
|
|
16
|
+
Time.at(datetimei, in: '+00:00')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.days(duration = Railswatch.duration)
|
|
20
|
+
(duration / 1.day) + 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.median(array)
|
|
24
|
+
sorted = array.sort
|
|
25
|
+
size = sorted.size
|
|
26
|
+
center = size / 2
|
|
27
|
+
|
|
28
|
+
if size.zero?
|
|
29
|
+
nil
|
|
30
|
+
elsif size.even?
|
|
31
|
+
(sorted[center - 1] + sorted[center]) / 2.0
|
|
32
|
+
else
|
|
33
|
+
sorted[center]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.percentile(values, percentile)
|
|
38
|
+
return nil if values.empty?
|
|
39
|
+
|
|
40
|
+
sorted = values.sort
|
|
41
|
+
rank = (percentile.to_f / 100) * (sorted.size - 1)
|
|
42
|
+
|
|
43
|
+
lower = sorted[rank.floor]
|
|
44
|
+
upper = sorted[rank.ceil]
|
|
45
|
+
lower + ((upper - lower) * (rank - rank.floor))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.filter_params(params)
|
|
49
|
+
return {} if params.blank?
|
|
50
|
+
return {} unless defined?(::Rails) && ::Rails.application
|
|
51
|
+
|
|
52
|
+
filter = ActiveSupport::ParameterFilter.new(::Rails.application.config.filter_parameters)
|
|
53
|
+
filter.filter(params.to_h)
|
|
54
|
+
rescue StandardError
|
|
55
|
+
{}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :datasource
|
|
7
|
+
|
|
8
|
+
def initialize(datasource)
|
|
9
|
+
@datasource = datasource
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_partial_path
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class Card < Base
|
|
6
|
+
def label
|
|
7
|
+
raise NotImplementedError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def value
|
|
11
|
+
raise NotImplementedError
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_partial_path
|
|
15
|
+
'railswatch/railswatch/card'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class Chart < Base
|
|
6
|
+
attr_accessor :subtitle, :description, :legend, :units
|
|
7
|
+
|
|
8
|
+
def initialize(datasource, subtitle: nil, description: nil, legend: nil, units: nil)
|
|
9
|
+
super(datasource)
|
|
10
|
+
@subtitle = subtitle
|
|
11
|
+
@description = description
|
|
12
|
+
@legend = legend
|
|
13
|
+
@units = units
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def id
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def type
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def data
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_partial_path
|
|
29
|
+
'railswatch/railswatch/chart'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class CrashesTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
'Crash Report'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::CrashReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
'We are glad that this list is empty ;)'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def table_classes
|
|
19
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def content_partial_path
|
|
23
|
+
'railswatch/railswatch/crashes_table_content'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class CustomEventsTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
"Recent Events (last #{Railswatch.recent_requests_time_window / 60} minutes)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RecentRequestsReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
example_message.html_safe
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def example_message
|
|
19
|
+
<<~HTML
|
|
20
|
+
Nothing to show here. Try to make a few requests in the main app.
|
|
21
|
+
|
|
22
|
+
<pre>
|
|
23
|
+
<code>
|
|
24
|
+
# in controller for example
|
|
25
|
+
def index
|
|
26
|
+
Railswatch.measure("stats calculation", "reports#index") do
|
|
27
|
+
stats = User.calculate_stats
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
</code>
|
|
31
|
+
</pre>
|
|
32
|
+
HTML
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def show_export?
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def table_classes
|
|
40
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def content_partial_path
|
|
44
|
+
'railswatch/railswatch/custom_events_table_content'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class DelayedJobTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
"Recent Jobs (last #{Railswatch.recent_requests_time_window / 60} minutes)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RecentRequestsReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
'Nothing to show here. Try to make a few requests in the main app.'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show_export?
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def table_classes
|
|
23
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def content_partial_path
|
|
27
|
+
'railswatch/railswatch/delayed_job_table_content'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class GrapeRequestsTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
"Recent Requests (last #{Railswatch.recent_requests_time_window / 60} minutes)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RecentRequestsReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
'Nothing to show here. Try to make a few requests in the main app.'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show_export?
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def table_classes
|
|
23
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def content_partial_path
|
|
27
|
+
'railswatch/railswatch/grape_requests_table_content'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class PercentileCard < Card
|
|
6
|
+
def value
|
|
7
|
+
Reports::PercentileReport.new(datasource.db).data[label.to_sym]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class P50Card < PercentileCard
|
|
12
|
+
def label = 'p50'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class P95Card < PercentileCard
|
|
16
|
+
def label = 'p95'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class P99Card < PercentileCard
|
|
20
|
+
def label = 'p99'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class RakeTasksTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
"Recent Rake tasks (last #{Railswatch.recent_requests_time_window / 60} minutes)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RecentRequestsReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
'Nothing to show here. Try to make a few requests in the main app.'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show_export?
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def table_classes
|
|
23
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def content_partial_path
|
|
27
|
+
'railswatch/railswatch/rake_tasks_table_content'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class RecentRequestsTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
"Recent Requests (last #{Railswatch.recent_requests_time_window / 60} minutes)"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RecentRequestsReport.new(datasource.db).data
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def empty_message
|
|
15
|
+
'Nothing to show here. Try to make a few requests in the main app.'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def auto_update_interval
|
|
19
|
+
'3s'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def table_id
|
|
23
|
+
'recent'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def table_classes
|
|
27
|
+
'table is-fullwidth is-hoverable is-narrow'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def content_partial_path
|
|
31
|
+
'railswatch/railswatch/recent_requests_table_content'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railswatch
|
|
4
|
+
module Widgets
|
|
5
|
+
class RequestsTable < Table
|
|
6
|
+
def subtitle
|
|
7
|
+
'Requests Analysis'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def data
|
|
11
|
+
@data ||= Railswatch::Reports::RequestsReport.new(
|
|
12
|
+
datasource.db,
|
|
13
|
+
group: :controller_action_format,
|
|
14
|
+
sort: :count
|
|
15
|
+
).data
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def empty_message
|
|
19
|
+
'No requests recorded yet.'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def content_partial_path
|
|
23
|
+
'railswatch/railswatch/requests_table_content'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|