github.com/slackhq/nebula@v1.9.0/stats.go (about)

     1  package nebula
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"net"
     8  	"net/http"
     9  	"runtime"
    10  	"strconv"
    11  	"time"
    12  
    13  	graphite "github.com/cyberdelia/go-metrics-graphite"
    14  	mp "github.com/nbrownus/go-metrics-prometheus"
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/prometheus/client_golang/prometheus/promhttp"
    17  	"github.com/rcrowley/go-metrics"
    18  	"github.com/sirupsen/logrus"
    19  	"github.com/slackhq/nebula/config"
    20  )
    21  
    22  // startStats initializes stats from config. On success, if any further work
    23  // is needed to serve stats, it returns a func to handle that work. If no
    24  // work is needed, it'll return nil. On failure, it returns nil, error.
    25  func startStats(l *logrus.Logger, c *config.C, buildVersion string, configTest bool) (func(), error) {
    26  	mType := c.GetString("stats.type", "")
    27  	if mType == "" || mType == "none" {
    28  		return nil, nil
    29  	}
    30  
    31  	interval := c.GetDuration("stats.interval", 0)
    32  	if interval == 0 {
    33  		return nil, fmt.Errorf("stats.interval was an invalid duration: %s", c.GetString("stats.interval", ""))
    34  	}
    35  
    36  	var startFn func()
    37  	switch mType {
    38  	case "graphite":
    39  		err := startGraphiteStats(l, interval, c, configTest)
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  	case "prometheus":
    44  		var err error
    45  		startFn, err = startPrometheusStats(l, interval, c, buildVersion, configTest)
    46  		if err != nil {
    47  			return nil, err
    48  		}
    49  	default:
    50  		return nil, fmt.Errorf("stats.type was not understood: %s", mType)
    51  	}
    52  
    53  	metrics.RegisterDebugGCStats(metrics.DefaultRegistry)
    54  	metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
    55  
    56  	go metrics.CaptureDebugGCStats(metrics.DefaultRegistry, interval)
    57  	go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, interval)
    58  
    59  	return startFn, nil
    60  }
    61  
    62  func startGraphiteStats(l *logrus.Logger, i time.Duration, c *config.C, configTest bool) error {
    63  	proto := c.GetString("stats.protocol", "tcp")
    64  	host := c.GetString("stats.host", "")
    65  	if host == "" {
    66  		return errors.New("stats.host can not be empty")
    67  	}
    68  
    69  	prefix := c.GetString("stats.prefix", "nebula")
    70  	addr, err := net.ResolveTCPAddr(proto, host)
    71  	if err != nil {
    72  		return fmt.Errorf("error while setting up graphite sink: %s", err)
    73  	}
    74  
    75  	if !configTest {
    76  		l.Infof("Starting graphite. Interval: %s, prefix: %s, addr: %s", i, prefix, addr)
    77  		go graphite.Graphite(metrics.DefaultRegistry, i, prefix, addr)
    78  	}
    79  	return nil
    80  }
    81  
    82  func startPrometheusStats(l *logrus.Logger, i time.Duration, c *config.C, buildVersion string, configTest bool) (func(), error) {
    83  	namespace := c.GetString("stats.namespace", "")
    84  	subsystem := c.GetString("stats.subsystem", "")
    85  
    86  	listen := c.GetString("stats.listen", "")
    87  	if listen == "" {
    88  		return nil, fmt.Errorf("stats.listen should not be empty")
    89  	}
    90  
    91  	path := c.GetString("stats.path", "")
    92  	if path == "" {
    93  		return nil, fmt.Errorf("stats.path should not be empty")
    94  	}
    95  
    96  	pr := prometheus.NewRegistry()
    97  	pClient := mp.NewPrometheusProvider(metrics.DefaultRegistry, namespace, subsystem, pr, i)
    98  	if !configTest {
    99  		go pClient.UpdatePrometheusMetrics()
   100  	}
   101  
   102  	// Export our version information as labels on a static gauge
   103  	g := prometheus.NewGauge(prometheus.GaugeOpts{
   104  		Namespace: namespace,
   105  		Subsystem: subsystem,
   106  		Name:      "info",
   107  		Help:      "Version information for the Nebula binary",
   108  		ConstLabels: prometheus.Labels{
   109  			"version":      buildVersion,
   110  			"goversion":    runtime.Version(),
   111  			"boringcrypto": strconv.FormatBool(boringEnabled()),
   112  		},
   113  	})
   114  	pr.MustRegister(g)
   115  	g.Set(1)
   116  
   117  	var startFn func()
   118  	if !configTest {
   119  		startFn = func() {
   120  			l.Infof("Prometheus stats listening on %s at %s", listen, path)
   121  			http.Handle(path, promhttp.HandlerFor(pr, promhttp.HandlerOpts{ErrorLog: l}))
   122  			log.Fatal(http.ListenAndServe(listen, nil))
   123  		}
   124  	}
   125  
   126  	return startFn, nil
   127  }