func UnaryClientInterceptor() grpc.UnaryClientInterceptor {
	return UnaryClientInterceptorWithConfig(config.GRPCOutgoingConfig{})
}
UnaryClientInterceptor is identical to UnaryClientInterceptorWithConfig called
with an empty config.GRPCOutgoingConfig.
package hnygrpc
import (
	"context"
	"reflect"
	"runtime"
	"github.com/honeycombio/beeline-go/propagation"
	"github.com/honeycombio/beeline-go/timer"
	"github.com/honeycombio/beeline-go/trace"
	"github.com/honeycombio/beeline-go/wrappers/config"
	"github.com/honeycombio/libhoney-go"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)
func getMetadataStringValue(md metadata.MD, key string) string {
	if val, ok := md[key]; ok {
		if len(val) > 0 {
			return val[0]
		}
		return ""
	}
	return ""
}
func startSpanOrTraceFromUnaryGRPC(
	ctx context.Context,
	info *grpc.UnaryServerInfo,
	parserHook config.GRPCTraceParserHook,
) (context.Context, *trace.Span) {
	span := trace.GetSpanFromContext(ctx)
	if span == nil {
		var tr *trace.Trace
		md, ok := metadata.FromIncomingContext(ctx)
		if ok {
			if parserHook == nil {
				beelineHeader := getMetadataStringValue(md, propagation.TracePropagationGRPCHeader)
				prop, _ := propagation.UnmarshalHoneycombTraceContext(beelineHeader)
				ctx, tr = trace.NewTrace(ctx, prop)
			} else {
				prop := parserHook(ctx)
				ctx, tr = trace.NewTraceFromPropagationContext(ctx, prop)
			}
		} else {
			ctx, tr = trace.NewTrace(ctx, nil)
		}
		span = tr.GetRootSpan()
	} else {
		ctx, span = span.CreateChild(ctx)
	}
	return ctx, span
}
func addFields(ctx context.Context, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, span *trace.Span) {
	handlerName := runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name()
	span.AddField("name", handlerName)
	span.AddField("meta.type", "grpc_request")
	span.AddField("handler.name", handlerName)
	span.AddField("handler.method", info.FullMethod)
	md, ok := metadata.FromIncomingContext(ctx)
	if ok {
		if val, ok := md["content-type"]; ok {
			span.AddField("request.content_type", val[0])
		}
		if val, ok := md[":authority"]; ok {
			span.AddField("request.header.authority", val[0])
		}
		if val, ok := md["user-agent"]; ok {
			span.AddField("request.header.user_agent", val[0])
		}
	}
}
func UnaryServerInterceptorWithConfig(cfg config.GRPCIncomingConfig) grpc.UnaryServerInterceptor {
	return func(
		ctx context.Context,
		req interface{},
		info *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (interface{}, error) {
		ctx, span := startSpanOrTraceFromUnaryGRPC(ctx, info, cfg.GRPCParserHook)
		defer span.Send()
		addFields(ctx, info, handler, span)
		resp, err := handler(ctx, req)
		if err != nil {
			span.AddTraceField("handler_error", err.Error())
		}
		span.AddField("response.grpc_status_code", status.Code(err))
		return resp, err
	}
}
func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return UnaryServerInterceptorWithConfig(config.GRPCIncomingConfig{})
}
func UnaryClientInterceptorWithConfig(cfg config.GRPCOutgoingConfig) grpc.UnaryClientInterceptor {
	return func(
		ctx context.Context,
		method string,
		req interface{},
		reply interface{},
		cc *grpc.ClientConn,
		invoker grpc.UnaryInvoker,
		opts ...grpc.CallOption,
	) error {
		span := trace.GetSpanFromContext(ctx)
		if span == nil {
			tm := timer.Start()
			ev := libhoney.NewEvent()
			defer ev.Send()
			ev.AddField("name", method)
			ev.AddField("meta.type", "grpc_client")
			ev.AddField("request.target", cc.Target())
			err := invoker(ctx, method, req, reply, cc, opts...)
			if err != nil {
				ev.AddField("error", err.Error())
			}
			dur := tm.Finish()
			ev.AddField("duration_ms", dur)
			return err
		}
		ctx, span = span.CreateChild(ctx)
		defer span.Send()
		span.AddField("name", method)
		span.AddField("meta.type", "grpc_client")
		span.AddField("request.target", cc.Target())
		md, ok := metadata.FromOutgoingContext(ctx)
		if !ok {
			md = metadata.New(nil)
		} else {
			md = md.Copy()
		}
		if cfg.GRPCPropagationHook == nil {
			md.Set(propagation.TracePropagationGRPCHeader, span.SerializeHeaders())
		} else {
			md = metadata.Join(md, cfg.GRPCPropagationHook(span.PropagationContext()))
		}
		ctx = metadata.NewOutgoingContext(ctx, md)
		err := invoker(ctx, method, req, reply, cc, opts...)
		if err != nil {
			span.AddField("error", err.Error())
		}
		return err
	}
}