From 552989a05b0456fe76f851773a20d4e217350163 Mon Sep 17 00:00:00 2001
From: Aleksey Lobanov <alekseylobanov1@gmail.com>
Date: Mon, 3 Oct 2022 02:59:53 +0300
Subject: [PATCH] Add basic Prometheus metrics handler (#1830)

* feat: Add Prometheus configuration options

* feat: Add Prometheus metrics handler

* build: prometheus became direct dependency

* docs: change description for prometheus metrics path
---
 cmd/root.go           | 12 ++++++++++++
 conf/configuration.go |  9 +++++++++
 go.mod                |  2 +-
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/cmd/root.go b/cmd/root.go
index fb9e3a716..46bc80218 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -13,6 +13,8 @@ import (
 	"github.com/navidrome/navidrome/resources"
 	"github.com/navidrome/navidrome/scheduler"
 	"github.com/oklog/run"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 )
@@ -84,6 +86,9 @@ func startServer() (func() error, func(err error)) {
 			if conf.Server.ListenBrainz.Enabled {
 				a.MountRouter("ListenBrainz Auth", consts.URLPathNativeAPI+"/listenbrainz", CreateListenBrainzRouter())
 			}
+			if conf.Server.Prometheus.Enabled {
+				a.MountRouter("Prometheus metrics", conf.Server.Prometheus.MetricsPath, promhttp.Handler())
+			}
 			return a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port))
 		}, func(err error) {
 			if err != nil {
@@ -191,12 +196,19 @@ func init() {
 	rootCmd.Flags().String("imagecachesize", viper.GetString("imagecachesize"), "size of image (art work) cache. set to 0 to disable cache")
 	rootCmd.Flags().Bool("autoimportplaylists", viper.GetBool("autoimportplaylists"), "enable/disable .m3u playlist auto-import`")
 
+	rootCmd.Flags().Bool("prometheus.enabled", viper.GetBool("prometheus.enabled"), "enable/disable prometheus metrics endpoint`")
+	rootCmd.Flags().String("prometheus.metricspath", viper.GetString("prometheus.metricspath"), "http endpoint for prometheus metrics")
+
 	_ = viper.BindPFlag("address", rootCmd.Flags().Lookup("address"))
 	_ = viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
 	_ = viper.BindPFlag("sessiontimeout", rootCmd.Flags().Lookup("sessiontimeout"))
 	_ = viper.BindPFlag("scaninterval", rootCmd.Flags().Lookup("scaninterval"))
 	_ = viper.BindPFlag("baseurl", rootCmd.Flags().Lookup("baseurl"))
 	_ = viper.BindPFlag("uiloginbackgroundurl", rootCmd.Flags().Lookup("uiloginbackgroundurl"))
+
+	_ = viper.BindPFlag("prometheus.enabled", rootCmd.Flags().Lookup("prometheus.enabled"))
+	_ = viper.BindPFlag("prometheus.metricspath", rootCmd.Flags().Lookup("prometheus.metricspath"))
+
 	_ = viper.BindPFlag("enabletranscodingconfig", rootCmd.Flags().Lookup("enabletranscodingconfig"))
 	_ = viper.BindPFlag("transcodingcachesize", rootCmd.Flags().Lookup("transcodingcachesize"))
 	_ = viper.BindPFlag("imagecachesize", rootCmd.Flags().Lookup("imagecachesize"))
diff --git a/conf/configuration.go b/conf/configuration.go
index 672d046c2..945e21394 100644
--- a/conf/configuration.go
+++ b/conf/configuration.go
@@ -57,6 +57,7 @@ type configOptions struct {
 	PasswordEncryptionKey  string
 	ReverseProxyUserHeader string
 	ReverseProxyWhitelist  string
+	Prometheus             prometheusOptions
 
 	Scanner scannerOptions
 
@@ -101,6 +102,11 @@ type listenBrainzOptions struct {
 	BaseURL string
 }
 
+type prometheusOptions struct {
+	Enabled     bool
+	MetricsPath string
+}
+
 var (
 	Server = &configOptions{}
 	hooks  []func()
@@ -244,6 +250,9 @@ func init() {
 	viper.SetDefault("reverseproxyuserheader", "Remote-User")
 	viper.SetDefault("reverseproxywhitelist", "")
 
+	viper.SetDefault("prometheus.enabled", false)
+	viper.SetDefault("prometheus.metricspath", "/metrics")
+
 	viper.SetDefault("scanner.extractor", consts.DefaultScannerExtractor)
 	viper.SetDefault("scanner.genreseparators", ";/,")
 
diff --git a/go.mod b/go.mod
index 08afd51ab..754e139eb 100644
--- a/go.mod
+++ b/go.mod
@@ -36,6 +36,7 @@ require (
 	github.com/onsi/ginkgo/v2 v2.2.0
 	github.com/onsi/gomega v1.20.2
 	github.com/pressly/goose v2.7.0+incompatible
+	github.com/prometheus/client_golang v1.12.1
 	github.com/robfig/cron/v3 v3.0.1
 	github.com/sirupsen/logrus v1.9.0
 	github.com/spf13/cobra v1.5.0
@@ -177,7 +178,6 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/polyfloyd/go-errorlint v1.0.2 // indirect
-	github.com/prometheus/client_golang v1.12.1 // indirect
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/common v0.32.1 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect