github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/monitoring/rpc_stats_interceptor.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package monitoring provides monitoring functionality. 16 package monitoring 17 18 import ( 19 "context" 20 "fmt" 21 "time" 22 23 "github.com/google/trillian/util" 24 "go.opencensus.io/trace" 25 "google.golang.org/grpc" 26 ) 27 28 const traceSpanRoot = "github.com/google/trillian/monitoring.RPCStatsInterceptor" 29 30 // RPCStatsInterceptor provides a gRPC interceptor that records statistics about the RPCs passing through it. 31 type RPCStatsInterceptor struct { 32 prefix string 33 timeSource util.TimeSource 34 ReqCount Counter 35 ReqSuccessCount Counter 36 ReqSuccessLatency Histogram 37 ReqErrorCount Counter 38 ReqErrorLatency Histogram 39 } 40 41 // NewRPCStatsInterceptor creates a new RPCStatsInterceptor for the given application/component, with 42 // a specified time source. 43 func NewRPCStatsInterceptor(timeSource util.TimeSource, prefix string, mf MetricFactory) *RPCStatsInterceptor { 44 interceptor := RPCStatsInterceptor{ 45 prefix: prefix, 46 timeSource: timeSource, 47 ReqCount: mf.NewCounter(prefixedName(prefix, "rpc_requests"), "Number of requests", "method"), 48 ReqSuccessCount: mf.NewCounter(prefixedName(prefix, "rpc_success"), "Number of successful requests", "method"), 49 ReqSuccessLatency: mf.NewHistogram(prefixedName(prefix, "rpc_success_latency"), "Latency of successful requests in seconds", "method"), 50 ReqErrorCount: mf.NewCounter(prefixedName(prefix, "rpc_errors"), "Number of errored requests", "method"), 51 ReqErrorLatency: mf.NewHistogram(prefixedName(prefix, "rpc_error_latency"), "Latency of errored requests in seconds", "method"), 52 } 53 return &interceptor 54 } 55 56 func prefixedName(prefix, name string) string { 57 return fmt.Sprintf("%s_%s", prefix, name) 58 } 59 60 func (r *RPCStatsInterceptor) recordFailureLatency(labels []string, startTime time.Time) { 61 latency := util.SecondsSince(r.timeSource, startTime) 62 r.ReqErrorCount.Inc(labels...) 63 r.ReqErrorLatency.Observe(latency, labels...) 64 } 65 66 // Interceptor returns a UnaryServerInterceptor that can be registered with an RPC server and 67 // will record request counts / errors and latencies for that servers handlers 68 func (r *RPCStatsInterceptor) Interceptor() grpc.UnaryServerInterceptor { 69 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 70 labels := []string{info.FullMethod} 71 72 // This interceptor wraps the request handler so we should track the 73 // additional latency it imposes. 74 ctx, span := trace.StartSpan(ctx, traceSpanRoot) 75 defer span.End() 76 77 // Increase the request count for the method and start the clock 78 r.ReqCount.Inc(labels...) 79 startTime := r.timeSource.Now() 80 81 defer func() { 82 if rec := recover(); rec != nil { 83 // If we reach here then the handler exited via panic, count it as a server failure 84 r.recordFailureLatency(labels, startTime) 85 panic(rec) 86 } 87 }() 88 89 // Invoke the actual operation 90 rsp, err := handler(ctx, req) 91 92 // Record success / failure and latency 93 if err != nil { 94 r.recordFailureLatency(labels, startTime) 95 } else { 96 latency := util.SecondsSince(r.timeSource, startTime) 97 r.ReqSuccessCount.Inc(labels...) 98 r.ReqSuccessLatency.Observe(latency, labels...) 99 } 100 101 // Pass the result of the handler invocation back 102 return rsp, err 103 } 104 }