package monitoring
import (
"context"
"fmt"
"time"
"github.com/google/trillian/util/clock"
"google.golang.org/grpc"
)
const traceSpanRoot = "/trillian/mon/"
type RPCStatsInterceptor struct {
prefix string
timeSource clock.TimeSource
ReqCount Counter
ReqSuccessCount Counter
ReqSuccessLatency Histogram
ReqErrorCount Counter
ReqErrorLatency Histogram
}
func NewRPCStatsInterceptor(timeSource clock.TimeSource, prefix string, mf MetricFactory) *RPCStatsInterceptor {
if mf == nil {
mf = InertMetricFactory{}
}
interceptor := RPCStatsInterceptor{
prefix: prefix,
timeSource: timeSource,
ReqCount: mf.NewCounter(prefixedName(prefix, "rpc_requests"), "Number of requests", "method"),
ReqSuccessCount: mf.NewCounter(prefixedName(prefix, "rpc_success"), "Number of successful requests", "method"),
ReqSuccessLatency: mf.NewHistogram(prefixedName(prefix, "rpc_success_latency"), "Latency of successful requests in seconds", "method"),
ReqErrorCount: mf.NewCounter(prefixedName(prefix, "rpc_errors"), "Number of errored requests", "method"),
ReqErrorLatency: mf.NewHistogram(prefixedName(prefix, "rpc_error_latency"), "Latency of errored requests in seconds", "method"),
}
return &interceptor
}
func prefixedName(prefix, name string) string {
return fmt.Sprintf("%s_%s", prefix, name)
}
func (r *RPCStatsInterceptor) recordFailureLatency(labels []string, startTime time.Time) {
latency := clock.SecondsSince(r.timeSource, startTime)
r.ReqErrorCount.Inc(labels...)
r.ReqErrorLatency.Observe(latency, labels...)
}
func (r *RPCStatsInterceptor) Interceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
labels := []string{info.FullMethod}
ctx, spanEnd := StartSpan(ctx, traceSpanRoot)
defer spanEnd()
r.ReqCount.Inc(labels...)
startTime := r.timeSource.Now()
defer func() {
if rec := recover(); rec != nil {
r.recordFailureLatency(labels, startTime)
panic(rec)
}
}()
rsp, err := handler(ctx, req)
if err != nil {
r.recordFailureLatency(labels, startTime)
} else {
latency := clock.SecondsSince(r.timeSource, startTime)
r.ReqSuccessCount.Inc(labels...)
r.ReqSuccessLatency.Observe(latency, labels...)
}
return rsp, err
}
}