grpc/rpclog/grpc.go
func UnaryClientInterceptor(log ttnlog.Interface) grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {
		log := getLog(log).WithField("method", method)
		log = log.WithFields(FieldsFromOutgoingContext(ctx))
		start := time.Now()
		err = invoker(ctx, method, req, reply, cc, opts...)
		log = log.WithField("duration", time.Since(start))
		if err != nil {
			log.WithError(err).Debug("rpc-client: call failed")
			return
		}
		log.Debug("rpc-client: call done")
		return
	}
}
func UnaryClientInterceptor(log ttnlog.Interface) grpc.UnaryClientInterceptor {
	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {
		log := getLog(log).WithField("method", method)
		log = log.WithFields(FieldsFromOutgoingContext(ctx))
		start := time.Now()
		err = invoker(ctx, method, req, reply, cc, opts...)
		log = log.WithField("duration", time.Since(start))
		if err != nil {
			log.WithError(err).Debug("rpc-client: call failed")
			return
		}
		log.Debug("rpc-client: call done")
		return
	}
}
// UnaryClientInterceptor logs unary RPCs on the client side