mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-24 23:59:25 +01:00
182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/gorilla/mux"
|
|
prometheus2 "github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
log "github.com/sirupsen/logrus"
|
|
"go.opentelemetry.io/otel/exporters/prometheus"
|
|
metric2 "go.opentelemetry.io/otel/metric"
|
|
"go.opentelemetry.io/otel/sdk/metric"
|
|
"net"
|
|
"net/http"
|
|
"reflect"
|
|
)
|
|
|
|
const defaultEndpoint = "/metrics"
|
|
|
|
// MockAppMetrics mocks the AppMetrics interface
|
|
type MockAppMetrics struct {
|
|
GetMeterFunc func() metric2.Meter
|
|
CloseFunc func() error
|
|
ExposeFunc func(port int, endpoint string) error
|
|
IDPMetricsFunc func() *IDPMetrics
|
|
HTTPMiddlewareFunc func() *HTTPMiddleware
|
|
GRPCMetricsFunc func() *GRPCMetrics
|
|
}
|
|
|
|
// GetMeter mocks the GetMeter function of the AppMetrics interface
|
|
func (mock *MockAppMetrics) GetMeter() metric2.Meter {
|
|
if mock.GetMeterFunc != nil {
|
|
return mock.GetMeterFunc()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close mocks the Close function of the AppMetrics interface
|
|
func (mock *MockAppMetrics) Close() error {
|
|
if mock.CloseFunc != nil {
|
|
return mock.CloseFunc()
|
|
}
|
|
return fmt.Errorf("unimplemented")
|
|
}
|
|
|
|
// Expose mocks the Expose function of the AppMetrics interface
|
|
func (mock *MockAppMetrics) Expose(port int, endpoint string) error {
|
|
if mock.ExposeFunc != nil {
|
|
return mock.ExposeFunc(port, endpoint)
|
|
}
|
|
return fmt.Errorf("unimplemented")
|
|
}
|
|
|
|
// IDPMetrics mocks the IDPMetrics function of the IDPMetrics interface
|
|
func (mock *MockAppMetrics) IDPMetrics() *IDPMetrics {
|
|
if mock.IDPMetricsFunc != nil {
|
|
return mock.IDPMetricsFunc()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HTTPMiddleware mocks the HTTPMiddleware function of the IDPMetrics interface
|
|
func (mock *MockAppMetrics) HTTPMiddleware() *HTTPMiddleware {
|
|
if mock.HTTPMiddlewareFunc != nil {
|
|
return mock.HTTPMiddlewareFunc()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GRPCMetrics mocks the GRPCMetrics function of the IDPMetrics interface
|
|
func (mock *MockAppMetrics) GRPCMetrics() *GRPCMetrics {
|
|
if mock.GRPCMetricsFunc != nil {
|
|
return mock.GRPCMetricsFunc()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AppMetrics is metrics interface
|
|
type AppMetrics interface {
|
|
GetMeter() metric2.Meter
|
|
Close() error
|
|
Expose(port int, endpoint string) error
|
|
IDPMetrics() *IDPMetrics
|
|
HTTPMiddleware() *HTTPMiddleware
|
|
GRPCMetrics() *GRPCMetrics
|
|
}
|
|
|
|
// defaultAppMetrics are core application metrics based on OpenTelemetry https://opentelemetry.io/
|
|
type defaultAppMetrics struct {
|
|
// Meter can be used by different application parts to create counters and measure things
|
|
Meter metric2.Meter
|
|
listener net.Listener
|
|
ctx context.Context
|
|
idpMetrics *IDPMetrics
|
|
httpMiddleware *HTTPMiddleware
|
|
grpcMetrics *GRPCMetrics
|
|
}
|
|
|
|
// IDPMetrics returns metrics for the idp package
|
|
func (appMetrics *defaultAppMetrics) IDPMetrics() *IDPMetrics {
|
|
return appMetrics.idpMetrics
|
|
}
|
|
|
|
// HTTPMiddleware returns metrics for the http api package
|
|
func (appMetrics *defaultAppMetrics) HTTPMiddleware() *HTTPMiddleware {
|
|
return appMetrics.httpMiddleware
|
|
}
|
|
|
|
// GRPCMetrics returns metrics for the gRPC api
|
|
func (appMetrics *defaultAppMetrics) GRPCMetrics() *GRPCMetrics {
|
|
return appMetrics.grpcMetrics
|
|
}
|
|
|
|
// Close stop application metrics HTTP handler and closes listener.
|
|
func (appMetrics *defaultAppMetrics) Close() error {
|
|
if appMetrics.listener == nil {
|
|
return nil
|
|
}
|
|
return appMetrics.listener.Close()
|
|
}
|
|
|
|
// Expose metrics on a given port and endpoint. If endpoint is empty a defaultEndpoint one will be used.
|
|
// Exposes metrics in the Prometheus format https://prometheus.io/
|
|
func (appMetrics *defaultAppMetrics) Expose(port int, endpoint string) error {
|
|
if endpoint == "" {
|
|
endpoint = defaultEndpoint
|
|
}
|
|
rootRouter := mux.NewRouter()
|
|
rootRouter.Handle(endpoint, promhttp.HandlerFor(
|
|
prometheus2.DefaultGatherer,
|
|
promhttp.HandlerOpts{EnableOpenMetrics: true}))
|
|
listener, err := net.Listen("tcp4", fmt.Sprintf(":%d", port))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
appMetrics.listener = listener
|
|
go func() {
|
|
err := http.Serve(listener, rootRouter)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}()
|
|
|
|
log.Infof("enabled application metrics and exposing on http://%s", listener.Addr().String())
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetMeter returns metrics meter that can be used to add various counters
|
|
func (appMetrics *defaultAppMetrics) GetMeter() metric2.Meter {
|
|
return appMetrics.Meter
|
|
}
|
|
|
|
// NewDefaultAppMetrics and expose them via defaultEndpoint on a given HTTP port
|
|
func NewDefaultAppMetrics(ctx context.Context) (AppMetrics, error) {
|
|
exporter, err := prometheus.New()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
provider := metric.NewMeterProvider(metric.WithReader(exporter))
|
|
pkg := reflect.TypeOf(defaultEndpoint).PkgPath()
|
|
meter := provider.Meter(pkg)
|
|
|
|
idpMetrics, err := NewIDPMetrics(ctx, meter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
middleware, err := NewMetricsMiddleware(ctx, meter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
grpcMetrics, err := NewGRPCMetrics(ctx, meter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &defaultAppMetrics{Meter: meter, ctx: ctx, idpMetrics: idpMetrics, httpMiddleware: middleware,
|
|
grpcMetrics: grpcMetrics}, nil
|
|
}
|