go-grpc-prometheus icon indicating copy to clipboard operation
go-grpc-prometheus copied to clipboard

Support Namespace and Subsytem for ServerMetrics

Open saurcery opened this issue 6 years ago • 3 comments

After poking around with ServerMetrics type I was unable to modify the behavior ofNewServerMetrics() function under server_metrics.go. Can someone help me understand how can one add Namespace/Subsystem CounterOpts to e.g. ServerMetrics.serverStartedCounter?

saurcery avatar Dec 15 '17 06:12 saurcery

Looks like this issue is a bit old, but it took me a few minutes of digging to figure out how to do this properly so figured I would share:

package grpcutil

import (
	"context"

	"acme/pkg/metrics"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
	grpctag "github.com/grpc-ecosystem/go-grpc-middleware/tags"
	grpcot "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
	grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
	prom "github.com/prometheus/client_golang/prometheus"
	grpc "google.golang.org/grpc"

	"go.uber.org/zap"
)

var sbprom = grpcprom.NewServerMetrics(
	grpcprom.CounterOption(func(o *prom.CounterOpts) {
		o.Namespace = metrics.Namespace
	}),
)

func init() {
	prom.MustRegister(sbprom) // sbprom is a collector
}

// Dial adds the default dial options to opts before calling
// grpc.DialContext.
func Dial(ctx context.Context, target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) {
	return grpc.DialContext(ctx, target, append(DialOptions(), opts...)...)
}

// DialOptions returns the default grpc dial options.
func DialOptions() []grpc.DialOption {
	return []grpc.DialOption{
		grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor),
		grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor),
		grpc.WithUnaryInterceptor(grpcot.UnaryClientInterceptor()),
		grpc.WithStreamInterceptor(grpcot.StreamClientInterceptor()),
	}
}

// NewServer adds the default server options to opts before calling
// grpc.NewServer.
func NewServer(l *zap.Logger, opts ...grpc.ServerOption) *grpc.Server {
	srv := grpc.NewServer(append(ServerOptions(l), opts...)...)

	sbprom.InitializeMetrics(srv)
	return srv
}

// ServerOptions returns the default grpc server options.
func ServerOptions(l *zap.Logger) []grpc.ServerOption {
	return []grpc.ServerOption{
		grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
			grpctag.StreamServerInterceptor(),
			grpcot.StreamServerInterceptor(),
			sbprom.StreamServerInterceptor(),
			grpczap.StreamServerInterceptor(l),
		)),
		grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
			grpctag.UnaryServerInterceptor(),
			grpcot.UnaryServerInterceptor(),
			sbprom.UnaryServerInterceptor(),
			grpczap.UnaryServerInterceptor(l),
		)),
	}
}

cstockton avatar Mar 29 '19 16:03 cstockton

@cstockton if i want to use 'sbprom' counter metrics in one of my grpc demo service like this one 'pb.RegisterDemoServiceServer(grpcServer, demoServer)' and i want to increment counter based on certain condition in demo service is it possible ? can you provide small example for this ?

yspanchal avatar May 03 '19 14:05 yspanchal

@yspanchal You would want to wrap the grpc.StreamServerInterceptor - it's just an ordinary function so you can wrap the call to the sbprom interceptors.

func certainConditionWrapper(
	maybe grpc.StreamServerInterceptor,
) grpc.StreamServerInterceptor {
	return grpc.StreamServerInterceptor(func(
		srv interface{},
		ss grpc.ServerStream,
		info *grpc.StreamServerInfo,
		handler grpc.StreamHandler,
	) error {
		if true {
			return maybe(srv, ss, info, handler)
		}
		return handler(srv, ss)
	})
}

// ServerOptions returns the default grpc server options.
func ServerOptions(l *zap.Logger) []grpc.ServerOption {
	return []grpc.ServerOption{
		grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
			grpctag.StreamServerInterceptor(),
			grpcot.StreamServerInterceptor(),
			certainConditionWrapper(sbSrvProm.StreamServerInterceptor()),
			grpczap.StreamServerInterceptor(l),
		)),
		grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
			grpctag.UnaryServerInterceptor(),
			grpcot.UnaryServerInterceptor(),
			sbSrvProm.UnaryServerInterceptor(),
			grpczap.UnaryServerInterceptor(l),
		)),
	}
}

cstockton avatar May 03 '19 16:05 cstockton