Files
danbooru/app/logical/server_status.rb
evazion f8d52e6758 /status: add more information to /status page.
Add the following:

* Container name, machine name, worker id.
* Container uptime, puma uptime, worker uptime.
* Number of requests processed by current worker.
* ExifTool version.

Also change /status page to show information in tables instead of lists.
2021-09-26 23:11:08 -05:00

194 lines
4.7 KiB
Ruby

# Returns status information about the running server, including software
# versions, basic load info, and Redis and Postgres info.
#
# @see StatusController
# @see https://danbooru.donmai.us/status
class ServerStatus
extend Memoist
include ActiveModel::Serializers::JSON
include ActiveModel::Serializers::Xml
attr_reader :request
def initialize(request)
@request = request
end
def serializable_hash(options = {})
{
ip: request.remote_ip,
headers: http_headers,
instance: {
container_name: container_name,
instance_name: instance_name,
worker_name: worker_name,
container_uptime: container_uptime,
instance_uptime: instance_uptime,
worker_uptime: worker_uptime,
requests_processed: requests_processed,
danbooru_version: danbooru_version,
ruby_version: RUBY_VERSION,
distro_version: distro_version,
libvips_version: libvips_version,
ffmpeg_version: ffmpeg_version,
mkvmerge_version: mkvmerge_version,
exiftool_version: exiftool_version,
redis_version: redis_version,
postgres_version: postgres_version,
},
server: {
node_name: node_name,
node_uptime: node_uptime,
loadavg: loadavg,
kernel_version: kernel_version,
},
postgres: {
connection_stats: postgres_connection_stats,
},
redis: {
info: redis_info,
}
}
end
concerning :InfoMethods do
def http_headers
headers = request.headers.env.select { |key| key.starts_with?("HTTP_") }
headers = headers.transform_keys { |key| key.delete_prefix("HTTP_").titleize.tr(" ", "-") }
headers = headers.except("Cookie")
headers = headers.reject { |k, v| v.blank? }
headers
end
def hostname
Socket.gethostname
end
def instance_name
if container_name.present?
"#{container_name}/#{node_name}"
else
node_name
end
end
def container_name
ENV["K8S_POD_NAME"]
end
def node_name
ENV["K8S_NODE_NAME"] || hostname
end
def worker_name
Thread.current.object_id
end
def node_uptime
uptime = File.read("/proc/uptime").split[0].to_f.seconds
Danbooru::Helpers.distance_of_time_in_words(uptime)
end
def container_uptime
started_at = File.stat("/proc/1").mtime
uptime = Time.zone.now - started_at
Danbooru::Helpers.distance_of_time_in_words(uptime)
end
def instance_uptime
started_at = File.stat("/proc/#{Process.ppid}").mtime
uptime = Time.zone.now - started_at
Danbooru::Helpers.distance_of_time_in_words(uptime)
end
def worker_uptime
started_at = File.stat("/proc/#{Process.pid}").mtime
uptime = Time.zone.now - started_at
Danbooru::Helpers.distance_of_time_in_words(uptime)
end
def requests_processed
if Puma::Server.current.present?
Puma::Server.current.requests_count
end
end
def loadavg
File.read("/proc/loadavg").chomp
end
def danbooru_version
Rails.application.config.x.git_hash
end
def kernel_version
File.read("/proc/version").chomp
end
def distro_version
`. /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
def exiftool_version
`exiftool -ver`.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