event: suggestion: event.Start returns end

The majority of calls to start a trace span will be followed by a deferred call to end one:

    ctx = event.To(ctx).Start(name)
    defer event.To(ctx).End()

The main problem with this is that it is easy to forget that end must
be called. It's also more verbose than necessary.

Instead, have event.Start return both a context and a function that
can be deferred:

    ctx, end := event.To(ctx).Start(name)
    defer end()

The second return value will make it hard to forget the defer, and it
follows a common Go pattern.

You can also ignore it and call event.To(ctx).End() elsewhere if that
suits you.

Lastly, remove Builder.Start. It invites misuse by end users: they
should always do tracing with event.Start to properly construct the
span tree. (If you really want to do it, you can still call Deliver.)

Change-Id: Ibe7780ce5277e9d6dac5406e879a35d727a89eff
Reviewed-on: https://go-review.googlesource.com/c/exp/+/313409
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/event/bench/event_test.go b/event/bench/event_test.go
index 582adc8..a646de4 100644
--- a/event/bench/event_test.go
+++ b/event/bench/event_test.go
@@ -50,7 +50,7 @@
 
 	eventTrace = Hooks{
 		AStart: func(ctx context.Context, a int) context.Context {
-			ctx = event.Start(ctx, aMsg)
+			ctx, _ = event.Start(ctx, aMsg)
 			event.To(ctx).With(aValue.Of(a)).Annotate()
 			return ctx
 		},
@@ -58,7 +58,7 @@
 			event.To(ctx).End()
 		},
 		BStart: func(ctx context.Context, b string) context.Context {
-			ctx = event.Start(ctx, bMsg)
+			ctx, _ = event.Start(ctx, bMsg)
 			event.To(ctx).With(bValue.Of(b)).Annotate()
 			return ctx
 		},
diff --git a/event/builder.go b/event/builder.go
index 1b528f9..32f80d1 100644
--- a/event/builder.go
+++ b/event/builder.go
@@ -90,11 +90,6 @@
 	b.Deliver(LogKind, fmt.Sprintf(template, args...))
 }
 
-// Start is a helper that calls Deliver with StartKind.
-func (b *Builder) Start(name string) uint64 {
-	return b.Deliver(StartKind, name)
-}
-
 // End is a helper that calls Deliver with EndKind.
 func (b *Builder) End() {
 	b.Deliver(EndKind, "")
diff --git a/event/event_test.go b/event/event_test.go
index 1742163..665f31a 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -65,8 +65,8 @@
 `}, {
 		name: "span",
 		events: func(ctx context.Context) {
-			ctx = event.Start(ctx, "span")
-			event.To(ctx).End()
+			ctx, end := event.Start(ctx, "span")
+			end()
 		},
 		expect: `
 2020/03/05 14:27:48 [start:1] span
@@ -74,10 +74,10 @@
 `}, {
 		name: "span nested",
 		events: func(ctx context.Context) {
-			ctx = event.Start(ctx, "parent")
-			defer func() { event.To(ctx).End() }()
-			child := event.Start(ctx, "child")
-			defer func() { event.To(child).End() }()
+			ctx, end := event.Start(ctx, "parent")
+			defer end()
+			child, end2 := event.Start(ctx, "child")
+			defer end2()
 			event.To(child).Log("message")
 		},
 		expect: `
diff --git a/event/export.go b/event/export.go
index 8f2ff09..8bb3f38 100644
--- a/event/export.go
+++ b/event/export.go
@@ -87,14 +87,22 @@
 }
 
 // Start delivers a start event and also updates the context with the event id.
-func Start(ctx context.Context, name string) context.Context {
+// Its second argument is a function that should be called to deliver the
+// matching end event. In lieue of calling the end function, you can invoke
+//    event.To(ctx).End()
+// on the returned context.
+func Start(ctx context.Context, name string) (_ context.Context, end func()) {
 	b := To(ctx)
-	if b.exporter == nil {
-		return ctx
+	if b == nil || b.exporter == nil {
+		return ctx, func() {}
 	}
 	v := contextValue{exporter: b.exporter}
-	v.parent = b.Start(name)
-	return context.WithValue(ctx, contextKey{}, v)
+	v.parent = b.Deliver(StartKind, name)
+	return context.WithValue(ctx, contextKey{}, v), func() {
+		eb := v.exporter.Builder()
+		eb.Event.Parent = v.parent
+		eb.Deliver(EndKind, "")
+	}
 }
 
 // Deliver events to the underlying handler.