event: merge the handlers but not the methods

Change-Id: I7ff8c97f7d9b9111c2333fc8241d37105416b4b3
Reviewed-on: https://go-review.googlesource.com/c/exp/+/324650
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/adapter/eventtest/eventtest.go b/event/adapter/eventtest/eventtest.go
index 915160a..4ed96fc 100644
--- a/event/adapter/eventtest/eventtest.go
+++ b/event/adapter/eventtest/eventtest.go
@@ -31,30 +31,30 @@
 }
 
 func (h *testHandler) Log(ctx context.Context, ev *event.Event) {
-	h.event(ctx, "log", ev)
+	h.event(ctx, ev)
 }
 
 func (h *testHandler) Metric(ctx context.Context, ev *event.Event) {
-	h.event(ctx, "metric", ev)
+	h.event(ctx, ev)
 }
 
 func (h *testHandler) Annotate(ctx context.Context, ev *event.Event) {
-	h.event(ctx, "annotate", ev)
+	h.event(ctx, ev)
 }
 
 func (h *testHandler) Start(ctx context.Context, ev *event.Event) context.Context {
-	h.event(ctx, "start", ev)
+	h.event(ctx, ev)
 	return ctx
 }
 
 func (h *testHandler) End(ctx context.Context, ev *event.Event) {
-	h.event(ctx, "end", ev)
+	h.event(ctx, ev)
 }
 
-func (h *testHandler) event(ctx context.Context, kind string, ev *event.Event) {
+func (h *testHandler) event(ctx context.Context, ev *event.Event) {
 	//TODO: choose between stdout and stderr based on the event
 	//TODO: decide if we should be calling h.tb.Fail()
-	h.printer.Event(os.Stdout, kind, ev)
+	h.printer.Event(os.Stdout, ev)
 }
 
 // FixedNow updates the exporter in the context to use a time function returned
diff --git a/event/adapter/logfmt/logfmt.go b/event/adapter/logfmt/logfmt.go
index a663c40..71aaa49 100644
--- a/event/adapter/logfmt/logfmt.go
+++ b/event/adapter/logfmt/logfmt.go
@@ -33,44 +33,37 @@
 }
 
 func (h *Handler) Log(ctx context.Context, ev *event.Event) {
-	h.printer.Event(h.to, "log", ev)
+	h.printer.Event(h.to, ev)
 }
 
 func (h *Handler) Metric(ctx context.Context, ev *event.Event) {
-	h.printer.Event(h.to, "metric", ev)
+	h.printer.Event(h.to, ev)
 }
 
 func (h *Handler) Annotate(ctx context.Context, ev *event.Event) {
-	h.printer.Event(h.to, "annotate", ev)
+	h.printer.Event(h.to, ev)
 }
 
 func (h *Handler) Start(ctx context.Context, ev *event.Event) context.Context {
-	h.printer.Event(h.to, "start", ev)
+	h.printer.Event(h.to, ev)
 	return ctx
 }
 
 func (h *Handler) End(ctx context.Context, ev *event.Event) {
-	h.printer.Event(h.to, "end", ev)
+	h.printer.Event(h.to, ev)
 }
 
-func (p *Printer) Event(w io.Writer, kind string, ev *event.Event) {
+func (p *Printer) Event(w io.Writer, ev *event.Event) {
 	const timeFormat = "2006-01-02T15:04:05"
 	p.needSep = false
 	if !ev.At.IsZero() {
 		p.label(w, "time", event.BytesOf(ev.At.AppendFormat(p.buf[:0], timeFormat)))
 	}
 
-	if ev.ID != 0 {
-		p.label(w, "trace", event.BytesOf(strconv.AppendUint(p.buf[:0], ev.ID, 10)))
-	}
 	if ev.Parent != 0 {
 		p.label(w, "parent", event.BytesOf(strconv.AppendUint(p.buf[:0], ev.Parent, 10)))
 	}
 
-	if kind != "log" && kind != "start" {
-		p.label(w, "kind", event.StringOf(kind))
-	}
-
 	for _, l := range ev.Labels {
 		if l.Name == "" {
 			continue
diff --git a/event/bench/event_test.go b/event/bench/event_test.go
index 9b733d7..4f34b07 100644
--- a/event/bench/event_test.go
+++ b/event/bench/event_test.go
@@ -126,7 +126,7 @@
 
 type noopHandler struct{}
 
-func (noopHandler) Log_(ctx context.Context, ev *event.Event)     {}
+func (noopHandler) Log(ctx context.Context, ev *event.Event)      {}
 func (noopHandler) Metric(ctx context.Context, ev *event.Event)   {}
 func (noopHandler) Annotate(ctx context.Context, ev *event.Event) {}
 func (noopHandler) End(ctx context.Context, ev *event.Event)      {}
diff --git a/event/builder.go b/event/builder.go
index d5037e3..cdceb06 100644
--- a/event/builder.go
+++ b/event/builder.go
@@ -101,7 +101,7 @@
 	if b.data == nil {
 		return
 	}
-	if b.data.exporter.log != nil {
+	if b.data.exporter.handler != nil {
 		b.log(message)
 	}
 	b.done()
@@ -113,7 +113,7 @@
 	if b.data == nil {
 		return
 	}
-	if b.data.exporter.log != nil {
+	if b.data.exporter.loggingEnabled() {
 		b.log(fmt.Sprintf(template, args...))
 	}
 	b.done()
@@ -124,7 +124,7 @@
 	defer b.data.exporter.mu.Unlock()
 	b.data.Event.Labels = append(b.data.Event.Labels, Message.Of(message))
 	b.data.exporter.prepare(&b.data.Event)
-	b.data.exporter.log.Log(b.ctx, &b.data.Event)
+	b.data.exporter.handler.Log(b.ctx, &b.data.Event)
 }
 
 // Metric is a helper that calls Deliver with MetricKind.
@@ -132,11 +132,12 @@
 	if b.data == nil {
 		return
 	}
-	if b.data.exporter.metric != nil {
+	if b.data.exporter.metricsEnabled() {
 		b.data.exporter.mu.Lock()
 		defer b.data.exporter.mu.Unlock()
+		b.data.Event.Labels = append(b.data.Event.Labels, Metric.Value())
 		b.data.exporter.prepare(&b.data.Event)
-		b.data.exporter.metric.Metric(b.ctx, &b.data.Event)
+		b.data.exporter.handler.Metric(b.ctx, &b.data.Event)
 	}
 	b.done()
 }
@@ -146,11 +147,11 @@
 	if b.data == nil {
 		return
 	}
-	if b.data.exporter.annotate != nil {
+	if b.data.exporter.annotationsEnabled() {
 		b.data.exporter.mu.Lock()
 		defer b.data.exporter.mu.Unlock()
 		b.data.exporter.prepare(&b.data.Event)
-		b.data.exporter.annotate.Annotate(b.ctx, &b.data.Event)
+		b.data.exporter.handler.Annotate(b.ctx, &b.data.Event)
 	}
 	b.done()
 }
@@ -160,11 +161,12 @@
 	if b.data == nil {
 		return
 	}
-	if b.data.exporter.trace != nil {
+	if b.data.exporter.tracingEnabled() {
 		b.data.exporter.mu.Lock()
 		defer b.data.exporter.mu.Unlock()
+		b.data.Event.Labels = append(b.data.Event.Labels, End.Value())
 		b.data.exporter.prepare(&b.data.Event)
-		b.data.exporter.trace.End(b.ctx, &b.data.Event)
+		b.data.exporter.handler.End(b.ctx, &b.data.Event)
 	}
 	b.done()
 }
@@ -195,21 +197,22 @@
 	}
 	ctx := b.ctx
 	end := func() {}
-	if b.data.exporter.trace != nil {
+	if b.data.exporter.tracingEnabled() {
 		b.data.exporter.mu.Lock()
 		defer b.data.exporter.mu.Unlock()
 		b.data.exporter.lastEvent++
-		b.data.Event.ID = b.data.exporter.lastEvent
+		traceID := b.data.exporter.lastEvent
+		b.data.Event.Labels = append(b.data.Event.Labels, Trace.Of(traceID))
 		b.data.exporter.prepare(&b.data.Event)
 		// create the end builder
 		eb := Builder{}
 		eb.data = builderPool.Get().(*builder)
 		eb.data.exporter = b.data.exporter
-		eb.data.Event.Parent = b.data.Event.ID
+		eb.data.Event.Parent = traceID
 		// and now deliver the start event
-		b.data.Event.Labels = append(b.data.Event.Labels, Trace.Of(name))
-		ctx = newContext(ctx, b.data.exporter, b.data.Event.ID)
-		ctx = b.data.exporter.trace.Start(ctx, &b.data.Event)
+		b.data.Event.Labels = append(b.data.Event.Labels, Name.Of(name))
+		ctx = newContext(ctx, b.data.exporter, traceID)
+		ctx = b.data.exporter.handler.Start(ctx, &b.data.Event)
 		eb.ctx = ctx
 		end = eb.End
 	}
diff --git a/event/builder_test.go b/event/builder_test.go
index a9b23e4..65d429a 100644
--- a/event/builder_test.go
+++ b/event/builder_test.go
@@ -78,6 +78,10 @@
 	t *testing.T
 }
 
+func (*testTraceHandler) Log(ctx context.Context, _ *event.Event)      {}
+func (*testTraceHandler) Annotate(ctx context.Context, _ *event.Event) {}
+func (*testTraceHandler) Metric(ctx context.Context, _ *event.Event)   {}
+
 func (*testTraceHandler) Start(ctx context.Context, _ *event.Event) context.Context {
 	return context.WithValue(ctx, "x", 1)
 }
diff --git a/event/common.go b/event/common.go
index 08c95a7..e3fbe16 100644
--- a/event/common.go
+++ b/event/common.go
@@ -5,15 +5,25 @@
 package event
 
 const Message = stringKey("msg")
-const Trace = stringKey("name")
+const Name = stringKey("name")
+const Trace = traceKey("trace")
+const End = tagKey("end")
+const Metric = tagKey("metric")
 
 type stringKey string
+type traceKey string
+type tagKey string
 
 // Of creates a new message Label.
 func (k stringKey) Of(msg string) Label {
 	return Label{Name: string(k), Value: StringOf(msg)}
 }
 
+func (k stringKey) Matches(ev *Event) bool {
+	_, found := k.Find(ev)
+	return found
+}
+
 func (k stringKey) Find(ev *Event) (string, bool) {
 	for i := len(ev.Labels) - 1; i >= 0; i-- {
 		if ev.Labels[i].Name == string(k) {
@@ -22,3 +32,36 @@
 	}
 	return "", false
 }
+
+// Of creates a new start Label.
+func (k traceKey) Of(id uint64) Label {
+	return Label{Name: string(k), Value: Uint64Of(id)}
+}
+
+func (k traceKey) Matches(ev *Event) bool {
+	_, found := k.Find(ev)
+	return found
+}
+
+func (k traceKey) Find(ev *Event) (uint64, bool) {
+	for i := len(ev.Labels) - 1; i >= 0; i-- {
+		if ev.Labels[i].Name == string(k) {
+			return ev.Labels[i].Value.Uint64(), true
+		}
+	}
+	return 0, false
+}
+
+// Value creates a new tag Label.
+func (k tagKey) Value() Label {
+	return Label{Name: string(k)}
+}
+
+func (k tagKey) Matches(ev *Event) bool {
+	for i := len(ev.Labels) - 1; i >= 0; i-- {
+		if ev.Labels[i].Name == string(k) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/event/common_test.go b/event/common_test.go
index 892287b..b86f9aa 100644
--- a/event/common_test.go
+++ b/event/common_test.go
@@ -22,23 +22,23 @@
 
 	event.To(ctx).Log(simple)
 	checkFind(t, h, "Log", event.Message, true, simple)
-	checkFind(t, h, "Log", event.Trace, false, "")
+	checkFind(t, h, "Log", event.Name, false, "")
 
 	event.To(ctx).Metric()
 	checkFind(t, h, "Metric", event.Message, false, "")
-	checkFind(t, h, "Metric", event.Trace, false, "")
+	checkFind(t, h, "Metric", event.Name, false, "")
 
 	event.To(ctx).Annotate()
 	checkFind(t, h, "Annotate", event.Message, false, "")
-	checkFind(t, h, "Annotate", event.Trace, false, "")
+	checkFind(t, h, "Annotate", event.Name, false, "")
 
 	_, end := event.To(ctx).Start(trace)
 	checkFind(t, h, "Start", event.Message, false, "")
-	checkFind(t, h, "Start", event.Trace, true, trace)
+	checkFind(t, h, "Start", event.Name, true, trace)
 
 	end()
 	checkFind(t, h, "End", event.Message, false, "")
-	checkFind(t, h, "End", event.Trace, false, "")
+	checkFind(t, h, "End", event.Name, false, "")
 }
 
 type finder interface {
diff --git a/event/event.go b/event/event.go
index 5e29773..a055ef8 100644
--- a/event/event.go
+++ b/event/event.go
@@ -12,41 +12,31 @@
 // Event holds the information about an event that occurred.
 // It combines the event metadata with the user supplied labels.
 type Event struct {
-	ID     uint64    // only set for trace events, a unique id per exporter for the trace.
 	Parent uint64    // id of the parent event for this event
 	At     time.Time // time at which the event is delivered to the exporter.
 	Labels []Label
 }
 
-// LogHandler is a the type for something that handles log events as they occur.
-type LogHandler interface {
+// Handler is a the type for something that handles events as they occur.
+type Handler interface {
 	// Log indicates a logging event.
 	Log(context.Context, *Event)
-}
-
-// MetricHandler is a the type for something that handles metric events as they
-// occur.
-type MetricHandler interface {
 	// Metric indicates a metric record event.
 	Metric(context.Context, *Event)
-}
-
-// AnnotateHandler is a the type for something that handles annotate events as
-// they occur.
-type AnnotateHandler interface {
 	// Annotate reports label values at a point in time.
 	Annotate(context.Context, *Event)
-}
-
-// TraceHandler is a the type for something that handles start and end events as
-// they occur.
-type TraceHandler interface {
 	// Start indicates a trace start event.
 	Start(context.Context, *Event) context.Context
 	// End indicates a trace end event.
 	End(context.Context, *Event)
 }
 
+// Matcher is the interface to something that can check if an event matches
+// a condition.
+type Matcher interface {
+	Matches(ev *Event) bool
+}
+
 // WithExporter returns a context with the exporter attached.
 // The exporter is called synchronously from the event call site, so it should
 // return quickly so as not to hold up user code.
@@ -59,3 +49,12 @@
 func SetDefaultExporter(e *Exporter) {
 	setDefaultExporter(e)
 }
+
+// Is uses the matcher to check if the event is a match.
+// This is a simple helper to convert code like
+//   event.End.Matches(ev)
+// to the more readable
+//   ev.Is(event.End)
+func (ev *Event) Is(m Matcher) bool {
+	return m.Matches(ev)
+}
diff --git a/event/event_test.go b/event/event_test.go
index b62957c..3f64305 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -54,7 +54,7 @@
 		},
 		expect: `
 time=2020-03-05T14:27:48 trace=1 name=span
-time=2020-03-05T14:27:49 parent=1 kind=end
+time=2020-03-05T14:27:49 parent=1 end
 `}, {
 		name: "span nested",
 		events: func(ctx context.Context) {
@@ -66,26 +66,26 @@
 		},
 		expect: `
 time=2020-03-05T14:27:48 trace=1 name=parent
-time=2020-03-05T14:27:49 trace=2 parent=1 name=child
+time=2020-03-05T14:27:49 parent=1 trace=2 name=child
 time=2020-03-05T14:27:50 parent=2 msg=message
-time=2020-03-05T14:27:51 parent=2 kind=end
-time=2020-03-05T14:27:52 parent=1 kind=end
+time=2020-03-05T14:27:51 parent=2 end
+time=2020-03-05T14:27:52 parent=1 end
 `}, {
 		name:   "metric",
 		events: func(ctx context.Context) { event.To(ctx).With(l1).Metric() },
-		expect: `time=2020-03-05T14:27:48 kind=metric l1=1`,
+		expect: `time=2020-03-05T14:27:48 l1=1 metric`,
 	}, {
 		name:   "metric 2",
 		events: func(ctx context.Context) { event.To(ctx).With(l1).With(l2).Metric() },
-		expect: `time=2020-03-05T14:27:48 kind=metric l1=1 l2=2`,
+		expect: `time=2020-03-05T14:27:48 l1=1 l2=2 metric`,
 	}, {
 		name:   "annotate",
 		events: func(ctx context.Context) { event.To(ctx).With(l1).Annotate() },
-		expect: `time=2020-03-05T14:27:48 kind=annotate l1=1`,
+		expect: `time=2020-03-05T14:27:48 l1=1`,
 	}, {
 		name:   "annotate 2",
 		events: func(ctx context.Context) { event.To(ctx).With(l1).With(l2).Annotate() },
-		expect: `time=2020-03-05T14:27:48 kind=annotate l1=1 l2=2`,
+		expect: `time=2020-03-05T14:27:48 l1=1 l2=2`,
 	}, {
 		name: "multiple events",
 		events: func(ctx context.Context) {
diff --git a/event/export.go b/event/export.go
index f9ef7e2..84c6521 100644
--- a/event/export.go
+++ b/event/export.go
@@ -19,10 +19,7 @@
 	Now func() time.Time
 
 	mu        sync.Mutex
-	log       LogHandler
-	metric    MetricHandler
-	annotate  AnnotateHandler
-	trace     TraceHandler
+	handler   Handler
 	lastEvent uint64
 }
 
@@ -44,13 +41,8 @@
 
 // NewExporter creates an Exporter using the supplied handler.
 // Event delivery is serialized to enable safe atomic handling.
-func NewExporter(handler interface{}) *Exporter {
-	e := &Exporter{Now: time.Now}
-	e.log, _ = handler.(LogHandler)
-	e.metric, _ = handler.(MetricHandler)
-	e.annotate, _ = handler.(AnnotateHandler)
-	e.trace, _ = handler.(TraceHandler)
-	return e
+func NewExporter(handler Handler) *Exporter {
+	return &Exporter{Now: time.Now, handler: handler}
 }
 
 func setDefaultExporter(e *Exporter) {
@@ -83,3 +75,10 @@
 		ev.At = e.Now()
 	}
 }
+
+//TODO: decide how to control the enable/disable behaviour
+
+func (e *Exporter) loggingEnabled() bool     { return true }
+func (e *Exporter) annotationsEnabled() bool { return true }
+func (e *Exporter) tracingEnabled() bool     { return true }
+func (e *Exporter) metricsEnabled() bool     { return true }
diff --git a/event/logging/internal/internal.go b/event/logging/internal/internal.go
index e971215..c7b56aa 100644
--- a/event/logging/internal/internal.go
+++ b/event/logging/internal/internal.go
@@ -25,6 +25,11 @@
 	copy(h.Got.Labels, ev.Labels)
 }
 
+func (h *TestHandler) Annotate(_ context.Context, _ *event.Event)                {}
+func (h *TestHandler) Metric(_ context.Context, _ *event.Event)                  {}
+func (h *TestHandler) Start(ctx context.Context, _ *event.Event) context.Context { return ctx }
+func (h *TestHandler) End(_ context.Context, _ *event.Event)                     {}
+
 var TestAt = time.Now()
 
 func NewTestExporter() (*event.Exporter, *TestHandler) {
diff --git a/event/otel/trace.go b/event/otel/trace.go
index 2cc56a4..8942841 100644
--- a/event/otel/trace.go
+++ b/event/otel/trace.go
@@ -15,17 +15,18 @@
 	tracer trace.Tracer
 }
 
-var _ event.TraceHandler = (*TraceHandler)(nil)
-
 func NewTraceHandler(t trace.Tracer) *TraceHandler {
 	return &TraceHandler{tracer: t}
 }
 
 type spanKey struct{}
 
-func (t *TraceHandler) Start(ctx context.Context, e *event.Event) context.Context {
-	opts := labelsToSpanOptions(e.Labels)
-	name, _ := event.Trace.Find(e)
+func (t *TraceHandler) Log(ctx context.Context, ev *event.Event)      {}
+func (t *TraceHandler) Annotate(ctx context.Context, ev *event.Event) {}
+func (t *TraceHandler) Metric(ctx context.Context, ev *event.Event)   {}
+func (t *TraceHandler) Start(ctx context.Context, ev *event.Event) context.Context {
+	opts := labelsToSpanOptions(ev.Labels)
+	name, _ := event.Name.Find(ev)
 	octx, span := t.tracer.Start(ctx, name, opts...)
 	return context.WithValue(octx, spanKey{}, span)
 }