zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/exporter/api/exporter.go (about) 1 //go:build !metrics 2 // +build !metrics 3 4 //nolint:varnamelen 5 package api 6 7 import ( 8 "fmt" 9 "math" 10 "net/http" 11 "regexp" 12 "strconv" 13 "time" 14 15 "github.com/prometheus/client_golang/prometheus" 16 "github.com/prometheus/client_golang/prometheus/promhttp" 17 18 "zotregistry.io/zot/pkg/extensions/monitoring" 19 "zotregistry.io/zot/pkg/log" 20 ) 21 22 const ( 23 idleTimeout = 120 * time.Second 24 readHeaderTimeout = 5 * time.Second 25 ) 26 27 type Collector struct { 28 Client *monitoring.MetricsClient 29 MetricsDesc map[string]*prometheus.Desc // all known metrics descriptions 30 invalidChars *regexp.Regexp 31 } 32 33 // Implements prometheus.Collector interface. 34 func (zc Collector) Describe(ch chan<- *prometheus.Desc) { 35 for _, metricDescription := range zc.MetricsDesc { 36 ch <- metricDescription 37 } 38 } 39 40 // Implements prometheus.Collector interface. 41 func (zc Collector) Collect(ch chan<- prometheus.Metric) { 42 metrics, err := zc.Client.GetMetrics() 43 if err != nil { 44 fmt.Printf("error getting metrics: %v\n", err) 45 ch <- prometheus.MustNewConstMetric(zc.MetricsDesc["zot_up"], prometheus.GaugeValue, 0) 46 47 return 48 } 49 ch <- prometheus.MustNewConstMetric(zc.MetricsDesc["zot_up"], prometheus.GaugeValue, 1) 50 51 for _, g := range metrics.Gauges { 52 name := zc.invalidChars.ReplaceAllLiteralString(g.Name, "_") 53 ch <- prometheus.MustNewConstMetric( 54 zc.MetricsDesc[name], prometheus.GaugeValue, g.Value, g.LabelValues...) 55 } 56 57 for _, c := range metrics.Counters { 58 name := zc.invalidChars.ReplaceAllLiteralString(c.Name, "_") 59 name += "_total" 60 ch <- prometheus.MustNewConstMetric( 61 zc.MetricsDesc[name], prometheus.CounterValue, float64(c.Count), c.LabelValues...) 62 } 63 64 for _, summary := range metrics.Summaries { 65 mname := zc.invalidChars.ReplaceAllLiteralString(summary.Name, "_") 66 name := mname + "_count" 67 ch <- prometheus.MustNewConstMetric( 68 zc.MetricsDesc[name], prometheus.CounterValue, float64(summary.Count), summary.LabelValues...) 69 70 name = mname + "_sum" 71 ch <- prometheus.MustNewConstMetric( 72 zc.MetricsDesc[name], prometheus.CounterValue, summary.Sum, summary.LabelValues...) 73 } 74 75 for _, h := range metrics.Histograms { 76 mname := zc.invalidChars.ReplaceAllLiteralString(h.Name, "_") 77 name := mname + "_count" 78 ch <- prometheus.MustNewConstMetric( 79 zc.MetricsDesc[name], prometheus.CounterValue, float64(h.Count), h.LabelValues...) 80 81 name = mname + "_sum" 82 ch <- prometheus.MustNewConstMetric( 83 zc.MetricsDesc[name], prometheus.CounterValue, h.Sum, h.LabelValues...) 84 85 if h.Buckets != nil { 86 for _, fvalue := range monitoring.GetBuckets(h.Name) { 87 var svalue string 88 if fvalue == math.MaxFloat64 { 89 svalue = "+Inf" 90 } else { 91 svalue = strconv.FormatFloat(fvalue, 'f', -1, 64) 92 } 93 94 name = mname + "_bucket" 95 ch <- prometheus.MustNewConstMetric( 96 zc.MetricsDesc[name], prometheus.CounterValue, float64(h.Buckets[svalue]), append(h.LabelValues, svalue)...) 97 } 98 } 99 } 100 } 101 102 func panicOnDuplicateMetricName(m map[string]*prometheus.Desc, name string, log log.Logger) { 103 if _, present := m[name]; present { 104 log.Fatal().Msg("Duplicate keys: metric " + name + " already present") 105 } 106 } 107 108 func GetCollector(c *Controller) *Collector { 109 // compute all metrics description map 110 MetricsDesc := map[string]*prometheus.Desc{ 111 "zot_up": prometheus.NewDesc( 112 "zot_up", 113 "Connection to zot server was successfully established.", 114 nil, nil, 115 ), 116 } 117 invalidChars := regexp.MustCompile("[^a-zA-Z0-9:_]") 118 119 for metricName, metricLabelNames := range monitoring.GetCounters() { 120 name := invalidChars.ReplaceAllLiteralString(metricName, "_") 121 name += "_total" 122 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 123 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 124 } 125 126 for metricName, metricLabelNames := range monitoring.GetGauges() { 127 name := invalidChars.ReplaceAllLiteralString(metricName, "_") 128 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 129 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 130 } 131 132 for metricName, metricLabelNames := range monitoring.GetSummaries() { 133 mname := invalidChars.ReplaceAllLiteralString(metricName, "_") 134 135 name := mname + "_count" 136 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 137 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 138 139 name = mname + "_sum" 140 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 141 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 142 } 143 144 for metricName, metricLabelNames := range monitoring.GetHistograms() { 145 mname := invalidChars.ReplaceAllLiteralString(metricName, "_") 146 147 name := mname + "_count" 148 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 149 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 150 151 name = mname + "_sum" 152 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 153 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, metricLabelNames, nil) 154 155 name = mname + "_bucket" 156 panicOnDuplicateMetricName(MetricsDesc, name, c.Log) 157 // Append a new label to hitogram bucket - le - 'lower or equal' 158 MetricsDesc[name] = prometheus.NewDesc(name, "Metric "+name, append(metricLabelNames, "le"), nil) 159 } 160 161 // parameters to connect to the zot server 162 serverAddr := fmt.Sprintf("%s://%s:%s", c.Config.Server.Protocol, 163 c.Config.Server.Host, c.Config.Server.Port) 164 cfg := &monitoring.MetricsConfig{Address: serverAddr} 165 166 return &Collector{ 167 Client: monitoring.NewMetricsClient(cfg, c.Log), 168 MetricsDesc: MetricsDesc, 169 invalidChars: invalidChars, 170 } 171 } 172 173 func runExporter(c *Controller) { 174 exporterAddr := fmt.Sprintf(":%s", c.Config.Exporter.Port) 175 server := &http.Server{ 176 Addr: exporterAddr, 177 IdleTimeout: idleTimeout, 178 ReadHeaderTimeout: readHeaderTimeout, 179 } 180 181 err := prometheus.Register(GetCollector(c)) 182 if err != nil { 183 c.Log.Error().Err(err).Msg("Expected error in testing") 184 } 185 186 http.Handle(c.Config.Exporter.Metrics.Path, promhttp.Handler()) 187 c.Log.Info().Str("exporter addr", exporterAddr). 188 Str("exporter metrics path", c.Config.Exporter.Metrics.Path). 189 Msg("Exporter is listening on exporter addr & exposes metrics on exporter metrics path") 190 191 serverAddr := fmt.Sprintf("%s://%s:%s", c.Config.Server.Protocol, 192 c.Config.Server.Host, c.Config.Server.Port) 193 c.Log.Info().Str("serverAddr", serverAddr).Msg("Scraping metrics") 194 c.Log.Fatal().Err(server.ListenAndServe()).Msg("Exporter stopped") 195 }