mirror of
https://github.com/photoprism/photoprism.git
synced 2025-10-27 10:51:09 +08:00
The prometheus text format requires metrics endpoints respond with the content-type 'text/plain; version=0.0.4'. Without this, newer versions of prometheus fail to scrape the metrics endpoint and report an error. It's possible to work around this by setting the 'fallback_scrape_protocol' setting in the prometheus scrape target configuration, but this revision sets the content type appropriately to avoid this in the first place.
101 lines
3.1 KiB
Go
101 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"io"
|
|
"runtime"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
"github.com/prometheus/common/expfmt"
|
|
|
|
"github.com/photoprism/photoprism/internal/auth/acl"
|
|
"github.com/photoprism/photoprism/internal/config"
|
|
"github.com/photoprism/photoprism/internal/photoprism/get"
|
|
"github.com/photoprism/photoprism/pkg/media/http/header"
|
|
)
|
|
|
|
// GetMetrics provides a prometheus-compatible metrics endpoint for monitoring.
|
|
//
|
|
// GET /api/v1/metrics
|
|
func GetMetrics(router *gin.RouterGroup) {
|
|
router.GET("/metrics", func(c *gin.Context) {
|
|
s := Auth(c, acl.ResourceMetrics, acl.AccessAll)
|
|
|
|
// Abort if permission is not granted.
|
|
if s.Abort(c) {
|
|
return
|
|
}
|
|
|
|
conf := get.Config()
|
|
counts := conf.ClientUser(false).Count
|
|
|
|
header.SetContentType(c.Request, header.ContentTypePrometheus)
|
|
|
|
c.Stream(func(w io.Writer) bool {
|
|
reg := prometheus.NewRegistry()
|
|
reg.MustRegister(collectors.NewGoCollector())
|
|
|
|
factory := promauto.With(reg)
|
|
|
|
registerCountMetrics(factory, counts)
|
|
registerBuildInfoMetric(factory, conf.ClientPublic())
|
|
|
|
metrics, err := reg.Gather()
|
|
if err != nil {
|
|
logErr("metrics", err)
|
|
return false
|
|
}
|
|
|
|
for _, metric := range metrics {
|
|
if _, err := expfmt.MetricFamilyToText(w, metric); err != nil {
|
|
logErr("metrics", err)
|
|
return false
|
|
}
|
|
}
|
|
|
|
return false
|
|
})
|
|
})
|
|
}
|
|
|
|
// registerCountMetrics registers metrics that can be monitored with the /api/v1/metrics endpoint.=
|
|
func registerCountMetrics(factory promauto.Factory, counts config.ClientCounts) {
|
|
metric := factory.NewGaugeVec(
|
|
prometheus.GaugeOpts{
|
|
Namespace: "photoprism",
|
|
Subsystem: "statistics",
|
|
Name: "media_count",
|
|
Help: "media statistics for this photoprism instance",
|
|
}, []string{"stat"},
|
|
)
|
|
|
|
metric.With(prometheus.Labels{"stat": "all"}).Set(float64(counts.All))
|
|
metric.With(prometheus.Labels{"stat": "photos"}).Set(float64(counts.Photos))
|
|
metric.With(prometheus.Labels{"stat": "media"}).Set(float64(counts.Media))
|
|
metric.With(prometheus.Labels{"stat": "live"}).Set(float64(counts.Live))
|
|
metric.With(prometheus.Labels{"stat": "videos"}).Set(float64(counts.Videos))
|
|
metric.With(prometheus.Labels{"stat": "audio"}).Set(float64(counts.Audio))
|
|
metric.With(prometheus.Labels{"stat": "documents"}).Set(float64(counts.Documents))
|
|
metric.With(prometheus.Labels{"stat": "albums"}).Set(float64(counts.Albums))
|
|
metric.With(prometheus.Labels{"stat": "folders"}).Set(float64(counts.Folders))
|
|
metric.With(prometheus.Labels{"stat": "files"}).Set(float64(counts.Files))
|
|
}
|
|
|
|
// registerBuildInfoMetric registers a metric that provides build information.
|
|
func registerBuildInfoMetric(factory promauto.Factory, conf *config.ClientConfig) {
|
|
factory.NewGaugeVec(
|
|
prometheus.GaugeOpts{
|
|
Namespace: "photoprism",
|
|
Name: "build_info",
|
|
Help: "information about the photoprism instance",
|
|
}, []string{"edition", "goversion", "version"},
|
|
).With(prometheus.Labels{
|
|
"edition": conf.Edition,
|
|
"goversion": runtime.Version(),
|
|
"version": conf.Version,
|
|
}).Set(1.0)
|
|
}
|