event: clean up exporter construction

This makes exporter construction in tests more like normal usage, and changes
the way we fixup time to modify the exporter stored in the context instead.

Change-Id: I02033fb2a6e7fb5ec6f73a2a52d2116582b11804
Reviewed-on: https://go-review.googlesource.com/c/exp/+/324152
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 80bc0e3..71ffb80 100644
--- a/event/adapter/eventtest/eventtest.go
+++ b/event/adapter/eventtest/eventtest.go
@@ -11,7 +11,7 @@
 
 import (
 	"context"
-	"strings"
+	"os"
 	"testing"
 	"time"
 
@@ -22,53 +22,47 @@
 // NewContext returns a context you should use for the active test.
 func NewContext(ctx context.Context, tb testing.TB) context.Context {
 	h := &testHandler{tb: tb}
-	h.p = logfmt.NewHandler(&h.buf)
 	return event.WithExporter(ctx, event.NewExporter(h))
 }
 
 type testHandler struct {
-	tb  testing.TB
-	buf strings.Builder
-	p   *logfmt.Handler
+	tb      testing.TB
+	printer logfmt.Printer
 }
 
 func (h *testHandler) Log(ctx context.Context, ev *event.Event) {
-	h.p.Log(ctx, ev)
-	h.deliver()
+	h.event(ctx, "log", ev)
 }
 
 func (h *testHandler) Metric(ctx context.Context, ev *event.Event) {
-	h.p.Metric(ctx, ev)
-	h.deliver()
+	h.event(ctx, "metric", ev)
 }
 
 func (h *testHandler) Annotate(ctx context.Context, ev *event.Event) {
-	h.p.Annotate(ctx, ev)
-	h.deliver()
+	h.event(ctx, "annotate", ev)
 }
 
 func (h *testHandler) Start(ctx context.Context, ev *event.Event) context.Context {
-	ctx = h.p.Start(ctx, ev)
-	h.deliver()
+	h.event(ctx, "start", ev)
 	return ctx
 }
 
 func (h *testHandler) End(ctx context.Context, ev *event.Event) {
-	h.p.End(ctx, ev)
-	h.deliver()
+	h.event(ctx, "end", ev)
 }
 
-func (h *testHandler) deliver() {
-	if h.buf.Len() == 0 {
-		return
-	}
-	h.tb.Log(h.buf.String())
-	h.buf.Reset()
+func (h *testHandler) event(ctx context.Context, kind string, 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)
 }
 
-func TestNow() func() time.Time {
+// FixedNow updates the exporter in the context to use a time function returned
+// by TestNow to make tests reproducible.
+func FixedNow(ctx context.Context) {
 	nextTime, _ := time.Parse(time.RFC3339Nano, "2020-03-05T14:27:48Z")
-	return func() time.Time {
+	e, _ := event.FromContext(ctx)
+	e.Now = func() time.Time {
 		thisTime := nextTime
 		nextTime = nextTime.Add(time.Second)
 		return thisTime
diff --git a/event/bench/bench_test.go b/event/bench/bench_test.go
index 774bddb..69b48b5 100644
--- a/event/bench/bench_test.go
+++ b/event/bench/bench_test.go
@@ -9,6 +9,7 @@
 	"io"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/google/go-cmp/cmp"
 )
@@ -99,3 +100,12 @@
 		t.Errorf("Got %d allocs, expect %d", got, expect)
 	}
 }
+
+func newTimer() func() time.Time {
+	nextTime, _ := time.Parse(time.RFC3339Nano, "2020-03-05T14:27:48Z")
+	return func() time.Time {
+		thisTime := nextTime
+		nextTime = nextTime.Add(time.Second)
+		return thisTime
+	}
+}
diff --git a/event/bench/event_test.go b/event/bench/event_test.go
index 729f13a..9b733d7 100644
--- a/event/bench/event_test.go
+++ b/event/bench/event_test.go
@@ -89,15 +89,15 @@
 }
 
 func eventNoop() context.Context {
-	e := event.NewExporter(noopHandler{})
-	e.Now = eventtest.TestNow()
-	return event.WithExporter(context.Background(), e)
+	ctx := event.WithExporter(context.Background(), event.NewExporter(noopHandler{}))
+	eventtest.FixedNow(ctx)
+	return ctx
 }
 
 func eventPrint(w io.Writer) context.Context {
-	e := event.NewExporter(logfmt.NewHandler(w))
-	e.Now = eventtest.TestNow()
-	return event.WithExporter(context.Background(), e)
+	ctx := event.WithExporter(context.Background(), event.NewExporter(logfmt.NewHandler(w)))
+	eventtest.FixedNow(ctx)
+	return ctx
 }
 
 func BenchmarkLogEventNoExporter(b *testing.B) {
diff --git a/event/bench/logrus_test.go b/event/bench/logrus_test.go
index 3eefa40..1755570 100644
--- a/event/bench/logrus_test.go
+++ b/event/bench/logrus_test.go
@@ -11,7 +11,6 @@
 	"time"
 
 	"github.com/sirupsen/logrus"
-	"golang.org/x/exp/event/adapter/eventtest"
 )
 
 var (
@@ -62,7 +61,7 @@
 		Out:   w,
 		Level: logrus.InfoLevel,
 		Formatter: &logrusTimeFormatter{
-			now: eventtest.TestNow(),
+			now: newTimer(),
 			wrapped: &logrus.TextFormatter{
 				FullTimestamp:   true,
 				TimestampFormat: timeFormat,
diff --git a/event/bench/stdlib_test.go b/event/bench/stdlib_test.go
index 3542da9..1ab434b 100644
--- a/event/bench/stdlib_test.go
+++ b/event/bench/stdlib_test.go
@@ -11,8 +11,6 @@
 	"io/ioutil"
 	"log"
 	"testing"
-
-	"golang.org/x/exp/event/adapter/eventtest"
 )
 
 var (
@@ -83,7 +81,7 @@
 }
 
 func stdlibWriter(w io.Writer) context.Context {
-	now := eventtest.TestNow()
+	now := newTimer()
 	return context.WithValue(context.Background(), writerKey{},
 		func(msg string, args ...interface{}) {
 			fmt.Fprint(w, now().Format(timeFormat), " ")
diff --git a/event/bench/zap_test.go b/event/bench/zap_test.go
index 18f41a6..7cf6ab8 100644
--- a/event/bench/zap_test.go
+++ b/event/bench/zap_test.go
@@ -12,7 +12,6 @@
 
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
-	"golang.org/x/exp/event/adapter/eventtest"
 )
 
 var (
@@ -49,7 +48,7 @@
 }
 
 func zapPrint(w io.Writer) context.Context {
-	now := eventtest.TestNow()
+	now := newTimer()
 	ec := zap.NewProductionEncoderConfig()
 	ec.EncodeDuration = zapcore.NanosDurationEncoder
 	timeEncoder := zapcore.TimeEncoderOfLayout(timeFormat)
diff --git a/event/bench/zerolog_test.go b/event/bench/zerolog_test.go
index 65eb66d..8d1ed46 100644
--- a/event/bench/zerolog_test.go
+++ b/event/bench/zerolog_test.go
@@ -10,7 +10,6 @@
 	"testing"
 
 	"github.com/rs/zerolog"
-	"golang.org/x/exp/event/adapter/eventtest"
 )
 
 var (
@@ -43,7 +42,7 @@
 
 func zerologPrint(w io.Writer) context.Context {
 	zerolog.TimeFieldFormat = timeFormat
-	zerolog.TimestampFunc = eventtest.TestNow()
+	zerolog.TimestampFunc = newTimer()
 	logger := zerolog.New(zerolog.SyncWriter(w)).With().Timestamp().Logger()
 	return logger.WithContext(context.Background())
 }
diff --git a/event/builder.go b/event/builder.go
index f83604b..d5037e3 100644
--- a/event/builder.go
+++ b/event/builder.go
@@ -40,7 +40,7 @@
 }
 
 func newBuilder(ctx context.Context) *builder {
-	exporter, parent := fromContext(ctx)
+	exporter, parent := FromContext(ctx)
 	if exporter == nil {
 		return nil
 	}
diff --git a/event/event_test.go b/event/event_test.go
index 23a7c4c..b62957c 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -98,9 +98,8 @@
 time=2020-03-05T14:27:49 myString="some string value" msg="string event"
 `}} {
 		buf := &strings.Builder{}
-		e := event.NewExporter(logfmt.NewHandler(buf))
-		e.Now = eventtest.TestNow()
-		ctx := event.WithExporter(ctx, e)
+		ctx := event.WithExporter(ctx, event.NewExporter(logfmt.NewHandler(buf)))
+		eventtest.FixedNow(ctx)
 		test.events(ctx)
 		got := strings.TrimSpace(buf.String())
 		expect := strings.TrimSpace(test.expect)
@@ -111,9 +110,8 @@
 }
 
 func ExampleLog() {
-	e := event.NewExporter(logfmt.NewHandler(os.Stdout))
-	e.Now = eventtest.TestNow()
-	ctx := event.WithExporter(context.Background(), e)
+	ctx := event.WithExporter(context.Background(), event.NewExporter(logfmt.NewHandler(os.Stdout)))
+	eventtest.FixedNow(ctx)
 	event.To(ctx).With(keys.Int("myInt").Of(6)).Log("my event")
 	event.To(ctx).With(keys.String("myString").Of("some string value")).Log("error event")
 	// Output:
diff --git a/event/export.go b/event/export.go
index d8a5d26..f9ef7e2 100644
--- a/event/export.go
+++ b/event/export.go
@@ -65,7 +65,8 @@
 	return context.WithValue(ctx, contextKey, contextValue{exporter: exporter, parent: parent})
 }
 
-func fromContext(ctx context.Context) (*Exporter, uint64) {
+// FromContext returns the exporter and current trace for the supplied context.
+func FromContext(ctx context.Context) (*Exporter, uint64) {
 	if v, ok := ctx.Value(contextKey).(contextValue); ok {
 		return v.exporter, v.parent
 	}
diff --git a/event/severity/severity_test.go b/event/severity/severity_test.go
index d8ae10c..f31f0cf 100644
--- a/event/severity/severity_test.go
+++ b/event/severity/severity_test.go
@@ -34,9 +34,8 @@
 		expect: `time=2020-03-05T14:27:48 level=info msg="a message"`},
 	} {
 		buf := &strings.Builder{}
-		e := event.NewExporter(logfmt.NewHandler(buf))
-		e.Now = eventtest.TestNow()
-		ctx := event.WithExporter(ctx, e)
+		ctx := event.WithExporter(ctx, event.NewExporter(logfmt.NewHandler(buf)))
+		eventtest.FixedNow(ctx)
 		test.events(ctx)
 		got := strings.TrimSpace(buf.String())
 		expect := strings.TrimSpace(test.expect)