Files
photoprism/internal/api/metrics.go
2025-09-13 12:58:28 +02:00

112 lines
3.5 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"
dto "github.com/prometheus/client_model/go"
"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/service/http/header"
)
// GetMetrics provides a Prometheus-compatible metrics stream for monitoring.
//
// @Summary a prometheus-compatible metrics endpoint for monitoring this instance
// @Id GetMetrics
// @Tags Metrics
// @Produce event-stream
// @Success 200 {object} []dto.MetricFamily
// @Failure 401,403 {object} i18n.Response
// @Router /api/v1/metrics [get]
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())
var metrics []*dto.MetricFamily
var err error
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)
}