Add basic server status page at /status.
Lists versions of various dependencies plus some Postgres and Redis metrics.
This commit is contained in:
8
app/controllers/status_controller.rb
Normal file
8
app/controllers/status_controller.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class StatusController < ApplicationController
|
||||
respond_to :html, :json, :xml
|
||||
|
||||
def show
|
||||
@status = ServerStatus.new
|
||||
respond_with(@status)
|
||||
end
|
||||
end
|
||||
109
app/logical/server_status.rb
Normal file
109
app/logical/server_status.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
class ServerStatus
|
||||
extend Memoist
|
||||
include ActiveModel::Serializers::JSON
|
||||
include ActiveModel::Serializers::Xml
|
||||
|
||||
def serializable_hash(*options)
|
||||
{
|
||||
status: {
|
||||
hostname: hostname,
|
||||
uptime: uptime,
|
||||
loadavg: loadavg,
|
||||
ruby_version: RUBY_VERSION,
|
||||
distro_version: distro_version,
|
||||
kernel_version: kernel_version,
|
||||
libvips_version: libvips_version,
|
||||
ffmpeg_version: ffmpeg_version,
|
||||
mkvmerge_version: mkvmerge_version,
|
||||
redis_version: redis_version,
|
||||
postgres_version: postgres_version,
|
||||
},
|
||||
postgres: {
|
||||
connection_stats: postgres_connection_stats,
|
||||
},
|
||||
redis: {
|
||||
info: redis_info,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
concerning :InfoMethods do
|
||||
def hostname
|
||||
Socket.gethostname
|
||||
end
|
||||
|
||||
def uptime
|
||||
seconds = File.read("/proc/uptime").split[0].to_f
|
||||
"#{seconds.seconds.in_days.round} days"
|
||||
end
|
||||
|
||||
def loadavg
|
||||
File.read("/proc/loadavg").chomp
|
||||
end
|
||||
|
||||
def kernel_version
|
||||
File.read("/proc/version").chomp
|
||||
end
|
||||
|
||||
def distro_version
|
||||
`source /etc/os-release; echo "$NAME $VERSION"`.chomp
|
||||
end
|
||||
|
||||
def libvips_version
|
||||
Vips::LIBRARY_VERSION
|
||||
end
|
||||
|
||||
def ffmpeg_version
|
||||
version = `ffmpeg -version`
|
||||
version[/ffmpeg version ([0-9.]+)/, 1]
|
||||
end
|
||||
|
||||
def mkvmerge_version
|
||||
`mkvmerge --version`.chomp
|
||||
end
|
||||
end
|
||||
|
||||
concerning :RedisMethods do
|
||||
def redis_info
|
||||
return {} if Rails.cache.try(:redis).nil?
|
||||
Rails.cache.redis.info
|
||||
end
|
||||
|
||||
def redis_used_memory
|
||||
redis_info["used_memory_rss_human"]
|
||||
end
|
||||
|
||||
def redis_version
|
||||
redis_info["redis_version"]
|
||||
end
|
||||
end
|
||||
|
||||
concerning :PostgresMethods do
|
||||
def postgres_version
|
||||
ApplicationRecord.connection.select_value("SELECT version()")
|
||||
end
|
||||
|
||||
def postgres_active_connections
|
||||
ApplicationRecord.connection.select_value("SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'")
|
||||
end
|
||||
|
||||
def postgres_connection_stats
|
||||
run_query("SELECT pid, state, query_start, state_change, xact_start, backend_start, backend_type FROM pg_stat_activity ORDER BY state, query_start DESC, backend_type")
|
||||
end
|
||||
|
||||
def run_query(query)
|
||||
result = ApplicationRecord.connection.select_all(query)
|
||||
serialize_result(result)
|
||||
end
|
||||
|
||||
def serialize_result(result)
|
||||
result.rows.map do |row|
|
||||
row.each_with_index.map do |col, i|
|
||||
[result.columns[i], col]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
memoize :redis_info
|
||||
end
|
||||
32
app/views/status/show.html.erb
Normal file
32
app/views/status/show.html.erb
Normal file
@@ -0,0 +1,32 @@
|
||||
<div id="c-status">
|
||||
<div id="a-show" class="fixed-width-container">
|
||||
<h1>Status</h1>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Server: <%= @status.hostname %>
|
||||
</summary>
|
||||
<%= render "list", hash: @status.serializable_hash[:status] %>
|
||||
</details>
|
||||
|
||||
<h1>Postgres</h1>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<%= pluralize @status.postgres_active_connections, "active connection" %>.
|
||||
</summary>
|
||||
|
||||
<%= render "table", rows: @status.serializable_hash[:postgres][:connection_stats] %>
|
||||
</details>
|
||||
|
||||
<h1>Redis</h1>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<%= @status.redis_used_memory %> memory used.
|
||||
</summary>
|
||||
|
||||
<%= render "list", hash: @status.serializable_hash[:redis][:info] %>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
@@ -227,6 +227,7 @@ Rails.application.routes.draw do
|
||||
get :sign_out, on: :collection
|
||||
end
|
||||
resource :source, :only => [:show]
|
||||
resource :status, only: [:show], controller: "status"
|
||||
resources :tags do
|
||||
collection do
|
||||
get :autocomplete
|
||||
|
||||
20
test/functional/status_controller_test.rb
Normal file
20
test/functional/status_controller_test.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
require 'test_helper'
|
||||
|
||||
class StatusControllerTest < ActionDispatch::IntegrationTest
|
||||
context "The status controller" do
|
||||
should "work for a html response" do
|
||||
get status_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "work for a json response" do
|
||||
get status_path(format: :json)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "work for an xml response" do
|
||||
get status_path(format: :json)
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user