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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +485 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/railswatch_manifest.js +0 -0
  6. data/app/assets/images/activity.svg +13 -0
  7. data/app/assets/images/bot.svg +1 -0
  8. data/app/assets/images/close.svg +13 -0
  9. data/app/assets/images/details.svg +3 -0
  10. data/app/assets/images/download.svg +3 -0
  11. data/app/assets/images/export.svg +13 -0
  12. data/app/assets/images/external.svg +1 -0
  13. data/app/assets/images/git.svg +1 -0
  14. data/app/assets/images/github.svg +1 -0
  15. data/app/assets/images/home.svg +16 -0
  16. data/app/assets/images/import.svg +13 -0
  17. data/app/assets/images/menu.svg +16 -0
  18. data/app/assets/images/moon.svg +3 -0
  19. data/app/assets/images/stat.svg +1 -0
  20. data/app/assets/images/sun.svg +4 -0
  21. data/app/assets/images/user.svg +1 -0
  22. data/app/controllers/railswatch/base_controller.rb +35 -0
  23. data/app/controllers/railswatch/concerns/csv_exportable.rb +31 -0
  24. data/app/controllers/railswatch/railswatch_controller.rb +183 -0
  25. data/app/engine_assets/javascripts/apex_ext.js +30 -0
  26. data/app/engine_assets/javascripts/application.js +9 -0
  27. data/app/engine_assets/javascripts/autoupdate.js +79 -0
  28. data/app/engine_assets/javascripts/charts.js +279 -0
  29. data/app/engine_assets/javascripts/navbar.js +11 -0
  30. data/app/engine_assets/javascripts/panel.js +43 -0
  31. data/app/engine_assets/javascripts/table.js +12 -0
  32. data/app/engine_assets/javascripts/theme.js +43 -0
  33. data/app/engine_assets/stylesheets/panel.css +111 -0
  34. data/app/engine_assets/stylesheets/responsive.css +102 -0
  35. data/app/engine_assets/stylesheets/style.css +960 -0
  36. data/app/helpers/railswatch/railswatch_helper.rb +338 -0
  37. data/app/views/railswatch/_panel.html.erb +15 -0
  38. data/app/views/railswatch/layouts/railswatch.html.erb +81 -0
  39. data/app/views/railswatch/railswatch/_card.html.erb +7 -0
  40. data/app/views/railswatch/railswatch/_chart.html.erb +13 -0
  41. data/app/views/railswatch/railswatch/_crashes_table_content.html.erb +62 -0
  42. data/app/views/railswatch/railswatch/_custom_events_table_content.html.erb +27 -0
  43. data/app/views/railswatch/railswatch/_delayed_job_table_content.html.erb +52 -0
  44. data/app/views/railswatch/railswatch/_export.html.erb +4 -0
  45. data/app/views/railswatch/railswatch/_grape_requests_table_content.html.erb +31 -0
  46. data/app/views/railswatch/railswatch/_overview.html.erb +124 -0
  47. data/app/views/railswatch/railswatch/_rake_tasks_table_content.html.erb +25 -0
  48. data/app/views/railswatch/railswatch/_recent_requests_table_content.html.erb +28 -0
  49. data/app/views/railswatch/railswatch/_recent_row.html.erb +41 -0
  50. data/app/views/railswatch/railswatch/_requests_table_content.html.erb +51 -0
  51. data/app/views/railswatch/railswatch/_sidekiq_jobs_table_content.html.erb +50 -0
  52. data/app/views/railswatch/railswatch/_summary.html.erb +50 -0
  53. data/app/views/railswatch/railswatch/_table.html.erb +30 -0
  54. data/app/views/railswatch/railswatch/_trace.html.erb +78 -0
  55. data/app/views/railswatch/railswatch/crashes.html.erb +2 -0
  56. data/app/views/railswatch/railswatch/custom.html.erb +6 -0
  57. data/app/views/railswatch/railswatch/delayed_job.html.erb +6 -0
  58. data/app/views/railswatch/railswatch/grape.html.erb +6 -0
  59. data/app/views/railswatch/railswatch/index.html.erb +9 -0
  60. data/app/views/railswatch/railswatch/rake.html.erb +6 -0
  61. data/app/views/railswatch/railswatch/recent.html.erb +2 -0
  62. data/app/views/railswatch/railswatch/requests.html.erb +2 -0
  63. data/app/views/railswatch/railswatch/resources.html.erb +28 -0
  64. data/app/views/railswatch/railswatch/sidekiq.html.erb +6 -0
  65. data/app/views/railswatch/railswatch/slow.html.erb +2 -0
  66. data/app/views/railswatch/railswatch/summary.js.erb +3 -0
  67. data/app/views/railswatch/railswatch/trace.js.erb +9 -0
  68. data/app/views/railswatch/shared/_header.html.erb +39 -0
  69. data/app/views/railswatch/shared/_page_header.html.erb +23 -0
  70. data/config/routes.rb +27 -0
  71. data/lib/generators/railswatch/install/USAGE +19 -0
  72. data/lib/generators/railswatch/install/install_generator.rb +46 -0
  73. data/lib/generators/railswatch/install/templates/create_railswatch_tables.rb +140 -0
  74. data/lib/generators/railswatch/install/templates/initializer.rb +87 -0
  75. data/lib/railswatch/data_source.rb +106 -0
  76. data/lib/railswatch/engine.rb +103 -0
  77. data/lib/railswatch/events/record.rb +63 -0
  78. data/lib/railswatch/extensions/trace.rb +14 -0
  79. data/lib/railswatch/extensions/trace_db.rb +21 -0
  80. data/lib/railswatch/gems/custom_ext.rb +38 -0
  81. data/lib/railswatch/gems/delayed_job_ext.rb +70 -0
  82. data/lib/railswatch/gems/grape_ext.rb +64 -0
  83. data/lib/railswatch/gems/rake_ext.rb +69 -0
  84. data/lib/railswatch/gems/sidekiq_ext.rb +55 -0
  85. data/lib/railswatch/instrument/metrics_collector.rb +70 -0
  86. data/lib/railswatch/interface.rb +9 -0
  87. data/lib/railswatch/models/application_record.rb +31 -0
  88. data/lib/railswatch/models/base_record.rb +59 -0
  89. data/lib/railswatch/models/collection.rb +37 -0
  90. data/lib/railswatch/models/custom_record.rb +32 -0
  91. data/lib/railswatch/models/delayed_job_record.rb +39 -0
  92. data/lib/railswatch/models/event_record.rb +11 -0
  93. data/lib/railswatch/models/grape_record.rb +61 -0
  94. data/lib/railswatch/models/rake_record.rb +33 -0
  95. data/lib/railswatch/models/request_record.rb +105 -0
  96. data/lib/railswatch/models/resource_record.rb +33 -0
  97. data/lib/railswatch/models/sidekiq_record.rb +41 -0
  98. data/lib/railswatch/models/trace_record.rb +21 -0
  99. data/lib/railswatch/pruner.rb +47 -0
  100. data/lib/railswatch/rails/middleware.rb +117 -0
  101. data/lib/railswatch/rails/query_builder.rb +20 -0
  102. data/lib/railswatch/reports/annotations_report.rb +13 -0
  103. data/lib/railswatch/reports/base_report.rb +48 -0
  104. data/lib/railswatch/reports/breakdown_report.rb +11 -0
  105. data/lib/railswatch/reports/crash_report.rb +11 -0
  106. data/lib/railswatch/reports/overview_report.rb +88 -0
  107. data/lib/railswatch/reports/percentile_report.rb +16 -0
  108. data/lib/railswatch/reports/recent_requests_report.rb +21 -0
  109. data/lib/railswatch/reports/requests_report.rb +75 -0
  110. data/lib/railswatch/reports/resources_report.rb +42 -0
  111. data/lib/railswatch/reports/response_time_report.rb +27 -0
  112. data/lib/railswatch/reports/slow_requests_report.rb +21 -0
  113. data/lib/railswatch/reports/throughput_report.rb +16 -0
  114. data/lib/railswatch/reports/trace_report.rb +17 -0
  115. data/lib/railswatch/system_monitor/resources_monitor.rb +88 -0
  116. data/lib/railswatch/thread/current_request.rb +37 -0
  117. data/lib/railswatch/utils.rb +58 -0
  118. data/lib/railswatch/version.rb +7 -0
  119. data/lib/railswatch/widgets/base.rb +17 -0
  120. data/lib/railswatch/widgets/card.rb +19 -0
  121. data/lib/railswatch/widgets/chart.rb +33 -0
  122. data/lib/railswatch/widgets/crashes_table.rb +27 -0
  123. data/lib/railswatch/widgets/custom_events_table.rb +48 -0
  124. data/lib/railswatch/widgets/delayed_job_table.rb +31 -0
  125. data/lib/railswatch/widgets/grape_requests_table.rb +31 -0
  126. data/lib/railswatch/widgets/percentile_card.rb +23 -0
  127. data/lib/railswatch/widgets/rake_tasks_table.rb +31 -0
  128. data/lib/railswatch/widgets/recent_requests_table.rb +35 -0
  129. data/lib/railswatch/widgets/requests_table.rb +27 -0
  130. data/lib/railswatch/widgets/resource_chart.rb +116 -0
  131. data/lib/railswatch/widgets/response_time_chart.rb +29 -0
  132. data/lib/railswatch/widgets/sidekiq_jobs_table.rb +31 -0
  133. data/lib/railswatch/widgets/slow_requests_table.rb +33 -0
  134. data/lib/railswatch/widgets/table.rb +43 -0
  135. data/lib/railswatch/widgets/throughput_chart.rb +29 -0
  136. data/lib/railswatch.rb +184 -0
  137. data/lib/tasks/railswatch.rake +9 -0
  138. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railswatch
4
+ VERSION = '1.0.0'
5
+ SCHEMA = '1.0.0'
6
+ EVENTS_SCHEMA = '1.0.0'
7
+ 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