2022-04-27 01:00:20 -05:00
|
|
|
//go:build !metrics
|
|
|
|
// +build !metrics
|
2021-10-15 10:05:00 -05:00
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
// nolint: varnamelen
|
2021-10-15 10:05:00 -05:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"net/http"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
2021-12-03 22:50:58 -05:00
|
|
|
"zotregistry.io/zot/pkg/extensions/monitoring"
|
|
|
|
"zotregistry.io/zot/pkg/log"
|
2021-10-15 10:05:00 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type Collector struct {
|
|
|
|
Client *monitoring.MetricsClient
|
|
|
|
MetricsDesc map[string]*prometheus.Desc // all known metrics descriptions
|
|
|
|
invalidChars *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements prometheus.Collector interface.
|
|
|
|
func (zc Collector) Describe(ch chan<- *prometheus.Desc) {
|
|
|
|
for _, metricDescription := range zc.MetricsDesc {
|
|
|
|
ch <- metricDescription
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements prometheus.Collector interface.
|
|
|
|
func (zc Collector) Collect(ch chan<- prometheus.Metric) {
|
|
|
|
metrics, err := zc.Client.GetMetrics()
|
|
|
|
if err != nil {
|
2021-12-13 14:23:31 -05:00
|
|
|
fmt.Printf("error getting metrics: %v\n", err)
|
2021-10-15 10:05:00 -05:00
|
|
|
ch <- prometheus.MustNewConstMetric(zc.MetricsDesc["zot_up"], prometheus.GaugeValue, 0)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(zc.MetricsDesc["zot_up"], prometheus.GaugeValue, 1)
|
|
|
|
|
|
|
|
for _, g := range metrics.Gauges {
|
|
|
|
name := zc.invalidChars.ReplaceAllLiteralString(g.Name, "_")
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
zc.MetricsDesc[name], prometheus.GaugeValue, g.Value, g.LabelValues...)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range metrics.Counters {
|
|
|
|
name := zc.invalidChars.ReplaceAllLiteralString(c.Name, "_")
|
|
|
|
name += "_total"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, float64(c.Count), c.LabelValues...)
|
|
|
|
}
|
|
|
|
|
2021-12-13 14:23:31 -05:00
|
|
|
for _, summary := range metrics.Summaries {
|
|
|
|
mname := zc.invalidChars.ReplaceAllLiteralString(summary.Name, "_")
|
2021-10-15 10:05:00 -05:00
|
|
|
name := mname + "_count"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-12-13 14:23:31 -05:00
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, float64(summary.Count), summary.LabelValues...)
|
2021-10-15 10:05:00 -05:00
|
|
|
|
|
|
|
name = mname + "_sum"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-12-13 14:23:31 -05:00
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, summary.Sum, summary.LabelValues...)
|
2021-10-15 10:05:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, h := range metrics.Histograms {
|
|
|
|
mname := zc.invalidChars.ReplaceAllLiteralString(h.Name, "_")
|
|
|
|
name := mname + "_count"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, float64(h.Count), h.LabelValues...)
|
|
|
|
|
|
|
|
name = mname + "_sum"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, h.Sum, h.LabelValues...)
|
|
|
|
|
|
|
|
if h.Buckets != nil {
|
2021-12-21 08:19:40 -05:00
|
|
|
for _, fvalue := range monitoring.GetBuckets(h.Name) {
|
2021-10-15 10:05:00 -05:00
|
|
|
var svalue string
|
|
|
|
if fvalue == math.MaxFloat64 {
|
|
|
|
svalue = "+Inf"
|
|
|
|
} else {
|
|
|
|
svalue = strconv.FormatFloat(fvalue, 'f', -1, 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
name = mname + "_bucket"
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
zc.MetricsDesc[name], prometheus.CounterValue, float64(h.Buckets[svalue]), append(h.LabelValues, svalue)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func panicOnDuplicateMetricName(m map[string]*prometheus.Desc, name string, log log.Logger) {
|
|
|
|
if _, present := m[name]; present {
|
|
|
|
log.Fatal().Msg("Duplicate keys: metric " + name + " already present")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetCollector(c *Controller) *Collector {
|
2021-12-13 14:23:31 -05:00
|
|
|
// compute all metrics description map
|
2021-10-15 10:05:00 -05:00
|
|
|
MetricsDesc := map[string]*prometheus.Desc{
|
|
|
|
"zot_up": prometheus.NewDesc(
|
|
|
|
"zot_up",
|
|
|
|
"Connection to zot server was successfully established.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
invalidChars := regexp.MustCompile("[^a-zA-Z0-9:_]")
|
|
|
|
|
|
|
|
for metricName, metricLabelNames := range monitoring.GetCounters() {
|
|
|
|
name := invalidChars.ReplaceAllLiteralString(metricName, "_")
|
|
|
|
name += "_total"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
for metricName, metricLabelNames := range monitoring.GetGauges() {
|
|
|
|
name := invalidChars.ReplaceAllLiteralString(metricName, "_")
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
for metricName, metricLabelNames := range monitoring.GetSummaries() {
|
|
|
|
mname := invalidChars.ReplaceAllLiteralString(metricName, "_")
|
|
|
|
|
|
|
|
name := mname + "_count"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
|
|
|
|
name = mname + "_sum"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
for metricName, metricLabelNames := range monitoring.GetHistograms() {
|
|
|
|
mname := invalidChars.ReplaceAllLiteralString(metricName, "_")
|
|
|
|
|
|
|
|
name := mname + "_count"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
|
|
|
|
name = mname + "_sum"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil)
|
|
|
|
|
|
|
|
name = mname + "_bucket"
|
|
|
|
panicOnDuplicateMetricName(MetricsDesc, name, c.Log)
|
|
|
|
// Append a new label to hitogram bucket - le - 'lower or equal'
|
|
|
|
MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, append(metricLabelNames, "le"), nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// parameters to connect to the zot server
|
|
|
|
serverAddr := fmt.Sprintf("%s://%s:%s", c.Config.Server.Protocol,
|
|
|
|
c.Config.Server.Host, c.Config.Server.Port)
|
|
|
|
cfg := &monitoring.MetricsConfig{Address: serverAddr}
|
|
|
|
|
|
|
|
return &Collector{
|
|
|
|
Client: monitoring.NewMetricsClient(cfg, c.Log),
|
|
|
|
MetricsDesc: MetricsDesc,
|
|
|
|
invalidChars: invalidChars,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func runExporter(c *Controller) {
|
|
|
|
err := prometheus.Register(GetCollector(c))
|
|
|
|
if err != nil {
|
|
|
|
c.Log.Error().Err(err).Msg("Expected error in testing")
|
|
|
|
}
|
|
|
|
|
|
|
|
http.Handle(c.Config.Exporter.Metrics.Path, promhttp.Handler())
|
|
|
|
exporterAddr := fmt.Sprintf(":%s", c.Config.Exporter.Port)
|
|
|
|
c.Log.Info().Msgf("Exporter is listening on %s & exposes metrics on %s path",
|
|
|
|
exporterAddr, c.Config.Exporter.Metrics.Path)
|
|
|
|
|
|
|
|
serverAddr := fmt.Sprintf("%s://%s:%s", c.Config.Server.Protocol,
|
|
|
|
c.Config.Server.Host, c.Config.Server.Port)
|
|
|
|
c.Log.Info().Msgf("Scraping metrics from %s", serverAddr)
|
|
|
|
c.Log.Fatal().Err(http.ListenAndServe(exporterAddr, nil)).Msg("Exporter stopped")
|
|
|
|
}
|