Merge branch 'add-prometheus-metrics-endpoint'
From https://github.com/iv-org/invidious/pull/3576
This commit is contained in:
commit
e5c0f15398
6 changed files with 99 additions and 0 deletions
|
@ -190,6 +190,7 @@ end
|
||||||
|
|
||||||
if CONFIG.statistics_enabled
|
if CONFIG.statistics_enabled
|
||||||
Invidious::Jobs.register Invidious::Jobs::StatisticsRefreshJob.new(PG_DB, SOFTWARE)
|
Invidious::Jobs.register Invidious::Jobs::StatisticsRefreshJob.new(PG_DB, SOFTWARE)
|
||||||
|
add_handler Metrics::RouteMetricsCollector.new
|
||||||
end
|
end
|
||||||
|
|
||||||
if (CONFIG.use_pubsub_feeds.is_a?(Bool) && CONFIG.use_pubsub_feeds.as(Bool)) || (CONFIG.use_pubsub_feeds.is_a?(Int32) && CONFIG.use_pubsub_feeds.as(Int32) > 0)
|
if (CONFIG.use_pubsub_feeds.is_a?(Bool) && CONFIG.use_pubsub_feeds.as(Bool)) || (CONFIG.use_pubsub_feeds.is_a?(Int32) && CONFIG.use_pubsub_feeds.as(Int32) > 0)
|
||||||
|
|
|
@ -198,3 +198,11 @@ def get_playback_statistic
|
||||||
|
|
||||||
return tracker.as(Hash(String, Int64 | Float64))
|
return tracker.as(Hash(String, Int64 | Float64))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_prometheus_metrics(metrics_struct : Hash(String, Number)) : String
|
||||||
|
return String.build do |str|
|
||||||
|
metrics_struct.each.each do |key, value|
|
||||||
|
str << key << " " << value << "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -27,6 +27,11 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
||||||
"playback" => {} of String => Int64 | Float64,
|
"playback" => {} of String => Int64 | Float64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATISTICS_PROMETHEUS = {
|
||||||
|
"invidious_updated_at" => Time.utc.to_unix,
|
||||||
|
"invidious_last_channel_refreshed_at" => 0_i64,
|
||||||
|
}
|
||||||
|
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
|
|
||||||
def initialize(@db, @software_config : Hash(String, String))
|
def initialize(@db, @software_config : Hash(String, String))
|
||||||
|
@ -59,6 +64,9 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
||||||
users["activeHalfyear"] = Invidious::Database::Statistics.count_users_active_6m
|
users["activeHalfyear"] = Invidious::Database::Statistics.count_users_active_6m
|
||||||
users["activeMonth"] = Invidious::Database::Statistics.count_users_active_1m
|
users["activeMonth"] = Invidious::Database::Statistics.count_users_active_1m
|
||||||
|
|
||||||
|
STATISTICS_PROMETHEUS["invidious_updated_at"] = Time.utc.to_unix
|
||||||
|
STATISTICS_PROMETHEUS["invidious_last_channel_refreshed_at"] = Invidious::Database::Statistics.channel_last_update.try &.to_unix || 0_i64
|
||||||
|
|
||||||
STATISTICS["metadata"] = {
|
STATISTICS["metadata"] = {
|
||||||
"updatedAt" => Time.utc.to_unix,
|
"updatedAt" => Time.utc.to_unix,
|
||||||
"lastChannelRefreshedAt" => Invidious::Database::Statistics.channel_last_update.try &.to_unix || 0_i64,
|
"lastChannelRefreshedAt" => Invidious::Database::Statistics.channel_last_update.try &.to_unix || 0_i64,
|
||||||
|
|
46
src/invidious/metrics.cr
Normal file
46
src/invidious/metrics.cr
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Module containing an optional Kemal handler, which can be used
|
||||||
|
# to collect metrics about the usage of various Invidious routes.
|
||||||
|
module Metrics
|
||||||
|
record MetricLabels, request_method : String, request_route : String, response_code : Int32
|
||||||
|
|
||||||
|
class RouteMetricsCollector < Kemal::Handler
|
||||||
|
# Counts how many times a given route was used
|
||||||
|
@@num_of_request_counters = Hash(MetricLabels, Int64).new
|
||||||
|
# Counts how much time was used to handle requests to each route
|
||||||
|
@@request_duration_seconds_sums = Hash(MetricLabels, Float32).new
|
||||||
|
|
||||||
|
def self.num_of_request_counters
|
||||||
|
return @@num_of_request_counters
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.request_duration_seconds_sums
|
||||||
|
return @@request_duration_seconds_sums
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(context : HTTP::Server::Context)
|
||||||
|
request_handling_started = Time.utc
|
||||||
|
begin
|
||||||
|
call_next(context)
|
||||||
|
ensure
|
||||||
|
request_handling_finished = Time.utc
|
||||||
|
request_path = context.route.path
|
||||||
|
request_method = context.request.method
|
||||||
|
seconds_spent_handling = (request_handling_finished - request_handling_started).to_f
|
||||||
|
response_status = context.response.status_code.to_i
|
||||||
|
|
||||||
|
LOGGER.trace("Collecting metrics: handling #{request_method} #{request_path} took #{seconds_spent_handling}s and finished with status #{response_status}")
|
||||||
|
metric_key = MetricLabels.new request_path, request_method, response_status
|
||||||
|
|
||||||
|
unless @@num_of_request_counters.has_key?(metric_key)
|
||||||
|
@@num_of_request_counters[metric_key] = 0
|
||||||
|
end
|
||||||
|
@@num_of_request_counters[metric_key] += 1
|
||||||
|
|
||||||
|
unless @@request_duration_seconds_sums.has_key?(metric_key)
|
||||||
|
@@request_duration_seconds_sums[metric_key] = 0.0
|
||||||
|
end
|
||||||
|
@@request_duration_seconds_sums[metric_key] += seconds_spent_handling
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,5 @@
|
||||||
|
require "../../../metrics.cr"
|
||||||
|
|
||||||
module Invidious::Routes::API::V1::Misc
|
module Invidious::Routes::API::V1::Misc
|
||||||
# Stats API endpoint for Invidious
|
# Stats API endpoint for Invidious
|
||||||
def self.stats(env)
|
def self.stats(env)
|
||||||
|
@ -26,6 +28,39 @@ module Invidious::Routes::API::V1::Misc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.metrics(env)
|
||||||
|
if !CONFIG.statistics_enabled
|
||||||
|
env.response.status_code = 204
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
env.response.content_type = "text/plain"
|
||||||
|
|
||||||
|
return String.build do |str|
|
||||||
|
Metrics::RouteMetricsCollector.num_of_request_counters.each do |metric_labels, value|
|
||||||
|
str << "http_requests_total{"
|
||||||
|
str << "method=\"" << metric_labels.request_method << "\" "
|
||||||
|
str << "route=\"" << metric_labels.request_route << "\" "
|
||||||
|
str << "response_code=\"" << metric_labels.response_code << "\""
|
||||||
|
str << "} "
|
||||||
|
str << value << "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
Metrics::RouteMetricsCollector.request_duration_seconds_sums.each do |metric_labels, value|
|
||||||
|
str << "http_request_duration_seconds_sum{"
|
||||||
|
str << "method=\"" << metric_labels.request_method << "\" "
|
||||||
|
str << "route=\"" << metric_labels.request_route << "\" "
|
||||||
|
str << "response_code=\"" << metric_labels.response_code << "\""
|
||||||
|
str << "} "
|
||||||
|
str << value << "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
Invidious::Jobs::StatisticsRefreshJob::STATISTICS_PROMETHEUS.each.each do |key, value|
|
||||||
|
str << key << " " << value << "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# APIv1 currently uses the same logic for both
|
# APIv1 currently uses the same logic for both
|
||||||
# user playlists and Invidious playlists. This means that we can't
|
# user playlists and Invidious playlists. This means that we can't
|
||||||
# reasonably split them yet. This should be addressed in APIv2
|
# reasonably split them yet. This should be addressed in APIv2
|
||||||
|
|
|
@ -310,6 +310,7 @@ module Invidious::Routing
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
get "/api/v1/stats", {{namespace}}::Misc, :stats
|
get "/api/v1/stats", {{namespace}}::Misc, :stats
|
||||||
|
get "/api/v1/metrics", {{namespace}}::Misc, :metrics
|
||||||
get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||||
get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||||
get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes
|
get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes
|
||||||
|
|
Loading…
Add table
Reference in a new issue