event: add support for custom event kinds

Change-Id: Ifb79607cac9378a1de8416f9fe958f34f4585d4c
Reviewed-on: https://go-review.googlesource.com/c/exp/+/333072
Trust: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/event/common.go b/event/common.go
index 4d6dbf8..2bd82a4 100644
--- a/event/common.go
+++ b/event/common.go
@@ -7,6 +7,7 @@
 import (
 	"context"
 	"fmt"
+	"sync"
 )
 
 const (
@@ -24,6 +25,8 @@
 	MetricKind
 	StartKind
 	EndKind
+
+	dynamicKindStart
 )
 
 type (
@@ -31,6 +34,49 @@
 	interfaceKey string
 )
 
+var (
+	dynamicKindMu    sync.Mutex
+	nextDynamicKind  = dynamicKindStart
+	dynamicKindNames map[Kind]string
+)
+
+func NewKind(name string) Kind {
+	dynamicKindMu.Lock()
+	defer dynamicKindMu.Unlock()
+	for _, n := range dynamicKindNames {
+		if n == name {
+			panic(fmt.Errorf("kind %s is already registered", name))
+		}
+	}
+	k := nextDynamicKind
+	nextDynamicKind++
+	dynamicKindNames[k] = name
+	return k
+}
+
+func (k Kind) String() string {
+	switch k {
+	case unknownKind:
+		return "unknown"
+	case LogKind:
+		return "log"
+	case MetricKind:
+		return "metric"
+	case StartKind:
+		return "start"
+	case EndKind:
+		return "end"
+	default:
+		dynamicKindMu.Lock()
+		defer dynamicKindMu.Unlock()
+		name, ok := dynamicKindNames[k]
+		if !ok {
+			return fmt.Sprintf("?unknownKind:%d?", k)
+		}
+		return name
+	}
+}
+
 func Log(ctx context.Context, msg string, labels ...Label) {
 	ev := New(ctx, LogKind)
 	if ev != nil {