Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enables prometheus exemplars support #1768

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/go-github/v43 v43.0.0
github.com/google/uuid v1.5.0
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0
github.com/hashicorp/go-memdb v1.3.4
github.com/hashicorp/go-multierror v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,10 @@ github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoIS
github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 h1:f4tggROQKKcnh4eItay6z/HbHLqghBxS8g7pyMhmDio=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0/go.mod h1:hKAkSgNkL0FII46ZkJcpVEAai4KV+swlIWCKfekd1pA=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
Expand Down
3 changes: 1 addition & 2 deletions pkg/cmd/devtools.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/go-logr/zerologr"
grpclog "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/jzelinskie/cobrautil/v2"
"github.com/jzelinskie/cobrautil/v2/cobragrpc"
"github.com/jzelinskie/cobrautil/v2/cobrahttp"
Expand Down Expand Up @@ -62,7 +61,7 @@ func runfunc(cmd *cobra.Command, _ []string) error {
grpc.ChainUnaryInterceptor(
grpclog.UnaryServerInterceptor(server.InterceptorLogger(log.Logger)),
otelgrpc.UnaryServerInterceptor(), // nolint: staticcheck
grpcprom.UnaryServerInterceptor,
server.GRPCMetricsUnaryInterceptor,
))
if err != nil {
log.Ctx(cmd.Context()).Fatal().Err(err).Msg("failed to create gRPC server")
Expand Down
46 changes: 40 additions & 6 deletions pkg/cmd/server/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import (

"github.com/fatih/color"
"github.com/go-logr/zerologr"
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
grpclog "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/jzelinskie/cobrautil/v2"
"github.com/jzelinskie/cobrautil/v2/cobraotel"
"github.com/jzelinskie/cobrautil/v2/cobrazerolog"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"

Expand Down Expand Up @@ -78,7 +79,10 @@ func DefaultPreRunE(programName string) cobrautil.CobraRunFunc {
func MetricsHandler(telemetryRegistry *prometheus.Registry, c *Config) http.Handler {
mux := http.NewServeMux()

mux.Handle("/metrics", promhttp.Handler())
mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{
// Opt into OpenMetrics e.g. to support exemplars.
EnableOpenMetrics: true,
}))
if telemetryRegistry != nil {
mux.Handle("/telemetry", promhttp.HandlerFor(telemetryRegistry, promhttp.HandlerOpts{}))
}
Expand Down Expand Up @@ -148,6 +152,16 @@ type MiddlewareOption struct {
enableResponseLog bool
}

// GRPCMetricsUnaryInterceptor creates the default prometheus metrics interceptor for unary gRPCs
var GRPCMetricsUnaryInterceptor grpc.UnaryServerInterceptor

// GRPCMetricsStreamingInterceptor creates the default prometheus metrics interceptor for streaming gRPCs
var GRPCMetricsStreamingInterceptor grpc.StreamServerInterceptor

func init() {
GRPCMetricsUnaryInterceptor, GRPCMetricsStreamingInterceptor = createServerMetrics()
}

// DefaultUnaryMiddleware generates the default middleware chain used for the public SpiceDB Unary gRPC methods
func DefaultUnaryMiddleware(opts MiddlewareOption) (*MiddlewareChain[grpc.UnaryServerInterceptor], error) {
chain, err := NewMiddlewareChain([]ReferenceableMiddleware[grpc.UnaryServerInterceptor]{
Expand All @@ -173,7 +187,7 @@ func DefaultUnaryMiddleware(opts MiddlewareOption) (*MiddlewareChain[grpc.UnaryS

NewUnaryMiddleware().
WithName(DefaultMiddlewareGRPCProm).
WithInterceptor(grpcprom.UnaryServerInterceptor).
WithInterceptor(GRPCMetricsUnaryInterceptor).
Done(),

NewUnaryMiddleware().
Expand Down Expand Up @@ -239,7 +253,7 @@ func DefaultStreamingMiddleware(opts MiddlewareOption) (*MiddlewareChain[grpc.St

NewStreamMiddleware().
WithName(DefaultMiddlewareGRPCProm).
WithInterceptor(grpcprom.StreamServerInterceptor).
WithInterceptor(GRPCMetricsStreamingInterceptor).
Done(),

NewStreamMiddleware().
Expand Down Expand Up @@ -303,7 +317,7 @@ func DefaultDispatchMiddleware(logger zerolog.Logger, authFunc grpcauth.AuthFunc
logmw.UnaryServerInterceptor(logmw.ExtractMetadataField("x-request-id", "requestID")),
grpclog.UnaryServerInterceptor(InterceptorLogger(logger), defaultGRPCLogOptions...),
otelgrpc.UnaryServerInterceptor(), // nolint: staticcheck
grpcprom.UnaryServerInterceptor,
GRPCMetricsUnaryInterceptor,
grpcauth.UnaryServerInterceptor(authFunc),
datastoremw.UnaryServerInterceptor(ds),
servicespecific.UnaryServerInterceptor,
Expand All @@ -312,7 +326,7 @@ func DefaultDispatchMiddleware(logger zerolog.Logger, authFunc grpcauth.AuthFunc
logmw.StreamServerInterceptor(logmw.ExtractMetadataField("x-request-id", "requestID")),
grpclog.StreamServerInterceptor(InterceptorLogger(logger), defaultGRPCLogOptions...),
otelgrpc.StreamServerInterceptor(), // nolint: staticcheck
grpcprom.StreamServerInterceptor,
GRPCMetricsStreamingInterceptor,
grpcauth.StreamServerInterceptor(authFunc),
datastoremw.StreamServerInterceptor(ds),
servicespecific.StreamServerInterceptor,
Expand All @@ -338,3 +352,23 @@ func InterceptorLogger(l zerolog.Logger) grpclog.Logger {
}
})
}

// initializes prometheus grpc interceptors with exemplar support enabled
func createServerMetrics() (grpc.UnaryServerInterceptor, grpc.StreamServerInterceptor) {
srvMetrics := grpcprom.NewServerMetrics(
grpcprom.WithServerHandlingTimeHistogram(
grpcprom.WithHistogramBuckets([]float64{.001, .003, .006, .010, .018, .024, .032, .042, .056, .075, .100, .178, .316, .562, 1, 5}),
),
)

prometheus.DefaultRegisterer.MustRegister(srvMetrics)
exemplarFromContext := func(ctx context.Context) prometheus.Labels {
if span := trace.SpanContextFromContext(ctx); span.IsSampled() {
return prometheus.Labels{"traceID": span.TraceID().String()}
}
return nil
}

exemplarContext := grpcprom.WithExemplarFromContext(exemplarFromContext)
return srvMetrics.UnaryServerInterceptor(exemplarContext), srvMetrics.StreamServerInterceptor(exemplarContext)
}
18 changes: 0 additions & 18 deletions pkg/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (
"net"
"net/http"
"strconv"
"sync"
"time"

"github.com/authzed/consistent"
"github.com/authzed/grpcutil"
"github.com/cespare/xxhash/v2"
"github.com/ecordell/optgen/helpers"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/hashicorp/go-multierror"
"github.com/prometheus/client_golang/prometheus"
"github.com/rs/cors"
Expand Down Expand Up @@ -237,8 +235,6 @@ func (c *Config) Complete(ctx context.Context) (RunnableServer, error) {
ds = schemacaching.NewCachingDatastoreProxy(ds, nscc, c.DatastoreConfig.GCWindow, cachingMode, c.SchemaWatchHeartbeat)
closeables.AddWithError(ds.Close)

enableGRPCHistogram()

specificConcurrencyLimits := c.DispatchConcurrencyLimits
concurrencyLimits := specificConcurrencyLimits.WithOverallDefaultLimit(c.GlobalDispatchConcurrencyLimit)

Expand Down Expand Up @@ -631,17 +627,3 @@ func (c *completedServerConfig) Run(ctx context.Context) error {

return nil
}

var promOnce sync.Once

// enableGRPCHistogram enables the standard time history for gRPC requests,
// ensuring that it is only enabled once
func enableGRPCHistogram() {
// EnableHandlingTimeHistogram is not thread safe and only needs to happen
// once
promOnce.Do(func() {
grpcprom.EnableHandlingTimeHistogram(grpcprom.WithHistogramBuckets(
[]float64{.006, .010, .018, .024, .032, .042, .056, .075, .100, .178, .316, .562, 1.000},
))
})
}
Loading