func createFilters(fullMethod string) []string {
	service := strings.SplitN(fullMethod[1:], "/", 2)[0]
	return []string{"/" + service, fullMethod}
}
createFilters splits the package.service part from the full method, i.e.,
/package.service/method and returns the service and method filter.
package hooks
import (
	"context"
	"strings"
	"sync"
	"google.golang.org/grpc"
)
type UnaryHandlerMiddleware func(grpc.UnaryHandler) grpc.UnaryHandler
type StreamHandlerMiddleware func(grpc.StreamHandler) grpc.StreamHandler
var unaryHooks sync.Map
var streamHooks sync.Map
type hooks struct {
	mu   sync.Mutex
	list []struct {
		name string
		f    interface{}
	}
}
func registerHook(hooksMap *sync.Map, filter, name string, f interface{}) {
	val, _ := hooksMap.LoadOrStore(filter, &hooks{})
	hooks := val.(*hooks)
	hooks.mu.Lock()
	defer hooks.mu.Unlock()
	for i := 0; i < len(hooks.list); i++ {
		if hooks.list[i].name == name {
			hooks.list[i].f = f
			return
		}
	}
	hooks.list = append(hooks.list, struct {
		name string
		f    interface{}
	}{name, f})
}
func unregisterHook(hooksMap *sync.Map, filter, name string) bool {
	val, ok := hooksMap.Load(filter)
	if !ok {
		return false
	}
	hooks := val.(*hooks)
	hooks.mu.Lock()
	defer hooks.mu.Unlock()
	for i := 0; i < len(hooks.list); i++ {
		if hooks.list[i].name == name {
			hooks.list = append(hooks.list[:i], hooks.list[i+1:]...)
			return true
		}
	}
	return false
}
func RegisterUnaryHook(filter, name string, f UnaryHandlerMiddleware) {
	registerHook(&unaryHooks, filter, name, f)
}
func UnregisterUnaryHook(filter, name string) bool {
	return unregisterHook(&unaryHooks, filter, name)
}
func RegisterStreamHook(filter, name string, f StreamHandlerMiddleware) {
	registerHook(&streamHooks, filter, name, f)
}
func UnregisterStreamHook(filter, name string) bool {
	return unregisterHook(&streamHooks, filter, name)
}