VOL-4788 : Lib in in voltha-libs-go for support of prometheus counters in voltha
Change-Id: I1a4a81f775595b89dbc2a5e4411e84034e30e1af
diff --git a/pkg/stats/promserver.go b/pkg/stats/promserver.go
new file mode 100644
index 0000000..1fbfd38
--- /dev/null
+++ b/pkg/stats/promserver.go
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stats
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v7/pkg/log"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+const (
+ cPrefix = "voltha"
+)
+
+type PromStatsServer struct {
+ // To hold the counters which are specific to devices
+ devCounters *prometheus.CounterVec
+ // To hold the counters which are NOT tied to specific devices
+ otherCounters *prometheus.CounterVec
+ // To hold the durations which are specific to devices
+ devDurations *prometheus.HistogramVec
+ // To hold the durations which are NOT tied to specific to devices
+ otherDurations *prometheus.HistogramVec
+}
+
+var StatsServer = PromStatsServer{}
+
+// Start starts the statistics manager with name and makes the collected stats available
+// at port p. All the statistics collected by this collector will be appended with "voltha_(name)_"
+// when they appear in Prometheus. The function starts a prometheus HTTP listener in the background and does not return
+// any errors, the listener is stopped on context cancellation
+func (ps *PromStatsServer) Start(ctx context.Context, p int, name CollectorName) {
+ //log.SetLogger(logging.New())
+ ps.initializeCollectors(ctx, name)
+
+ logger.Infow(ctx, "Starting Statistics HTTP Server", log.Fields{"listeningPort": p})
+
+ http.Handle("/metrics", promhttp.Handler())
+ server := &http.Server{Addr: fmt.Sprintf(":%d", p), Handler: nil}
+
+ go func() {
+ <-ctx.Done()
+ logger.Infof(ctx, "Shutting down the Statistics HTTP server")
+ err := server.Shutdown(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Statistics server shutting down failure", log.Fields{"error": err})
+ }
+ }()
+
+ go func() {
+ err := server.ListenAndServe()
+ if err != nil && err != http.ErrServerClosed {
+ logger.Errorw(ctx, "Starting Statistics HTTP server error", log.Fields{"error": err})
+ }
+ }()
+
+ logger.Infow(ctx, "Started Prometheus listener for statistics on port", log.Fields{"port": p})
+}
+
+func (ps *PromStatsServer) initializeCollectors(ctx context.Context, name CollectorName) {
+ logger.Infof(ctx, "Initializing statistics collector")
+
+ collectorName := cPrefix + "_" + name.String()
+
+ var (
+ // in milliseconds
+ defBuckets = []float64{2, 5, 10, 25, 50, 100, 300, 1000, 5000}
+ )
+
+ ps.devCounters = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: collectorName,
+ Name: "device_counters",
+ Help: "Device specific counters",
+ },
+ []string{"device_id", "serial_no", "counter"},
+ )
+
+ ps.otherCounters = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: collectorName,
+ Name: "counters",
+ Help: "Non device counters",
+ },
+ []string{"counter"},
+ )
+
+ ps.devDurations = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: collectorName,
+ Name: "device_durations",
+ Help: "Time taken in ms to complete a specific task for a specific device",
+ Buckets: defBuckets,
+ },
+ []string{"device_id", "serial_no", "duration"},
+ )
+
+ ps.otherDurations = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: collectorName,
+ Name: "durations",
+ Help: "Time taken in ms to complete a specific task not tied to a device",
+ Buckets: defBuckets,
+ },
+ []string{"duration"},
+ )
+
+ prometheus.MustRegister(ps.devCounters)
+ prometheus.MustRegister(ps.otherCounters)
+ prometheus.MustRegister(ps.devDurations)
+ prometheus.MustRegister(ps.otherDurations)
+}
+
+//CountForDevice counts the number of times the counterName happens for device devId with serial number sn. Each call to Count increments it by one.
+func (ps *PromStatsServer) CountForDevice(devId, sn string, counterName DeviceCounter) {
+ if ps.devCounters != nil {
+ ps.devCounters.WithLabelValues(devId, sn, counterName.String()).Inc()
+ }
+}
+
+//AddForDevice adds val to counter.
+func (ps *PromStatsServer) AddForDevice(devId, sn string, counter DeviceCounter, val float64) {
+ if ps.devCounters != nil {
+ ps.devCounters.WithLabelValues(devId, sn, counter.String()).Add(val)
+ }
+}
+
+//CollectDurationForDevice calculates the duration from startTime to time.Now() for device devID with serial number sn.
+func (ps *PromStatsServer) CollectDurationForDevice(devID, sn string, dName DeviceDuration, startTime time.Time) {
+ if ps.otherDurations != nil {
+ timeSpent := time.Since(startTime)
+ ps.devDurations.WithLabelValues(devID, sn, dName.String()).Observe(float64(timeSpent.Milliseconds()))
+ }
+}
+
+//Count counts the number of times the counterName happens. Each call to Count increments it by one.
+func (ps *PromStatsServer) Count(counter NonDeviceCounter) {
+ if ps.otherCounters != nil {
+ ps.otherCounters.WithLabelValues(counter.String()).Inc()
+ }
+}
+
+//Add adds val to counter.
+func (ps *PromStatsServer) Add(counter NonDeviceCounter, val float64) {
+ if ps.otherCounters != nil {
+ ps.otherCounters.WithLabelValues(counter.String()).Add(val)
+ }
+}
+
+//CollectDuration calculates the duration from startTime to time.Now().
+func (ps *PromStatsServer) CollectDuration(dName NonDeviceDuration, startTime time.Time) {
+ if ps.otherDurations != nil {
+ timeSpent := time.Since(startTime)
+ ps.otherDurations.WithLabelValues(dName.String()).Observe(float64(timeSpent.Milliseconds()))
+ }
+}