event: separate metric types with Record methods

This is an alternative to having metrics as builders. Instead we use
the same builder for everything, but BuilderMetric takes a type that
is a pair of a Metric and Value. These pairs are created by the Record
metric of each Metric type.

This has the main advantage of separate builders -- typed metric
values -- without the downsides of repeated code, both in the
implementation and the client.

Change-Id: I8d39880b364975416e6fe94f6545fd3fe201aa61
Reviewed-on: https://go-review.googlesource.com/c/exp/+/326390
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 637c621..07d45a8 100644
--- a/event/bench/event_test.go
+++ b/event/bench/event_test.go
@@ -71,14 +71,14 @@
 	gauge       = event.NewFloatGauge("gauge")
 	eventMetric = Hooks{
 		AStart: func(ctx context.Context, a int) context.Context {
-			gauge.To(ctx).With(aStat.Of(a)).Record(1)
-			gauge.To(ctx).With(aCount.Of(1)).Record(1)
+			event.To(ctx).With(aStat.Of(a)).Metric(gauge.Record(1))
+			event.To(ctx).With(aCount.Of(1)).Metric(gauge.Record(1))
 			return ctx
 		},
 		AEnd: func(ctx context.Context) {},
 		BStart: func(ctx context.Context, b string) context.Context {
-			gauge.To(ctx).With(bLength.Of(len(b))).Record(1)
-			gauge.To(ctx).With(bCount.Of(1)).Record(1)
+			event.To(ctx).With(bLength.Of(len(b))).Metric(gauge.Record(1))
+			event.To(ctx).With(bCount.Of(1)).Metric(gauge.Record(1))
 			return ctx
 		},
 		BEnd: func(ctx context.Context) {},
diff --git a/event/builder.go b/event/builder.go
index 5b14267..6966078 100644
--- a/event/builder.go
+++ b/event/builder.go
@@ -185,35 +185,23 @@
 	b.done()
 }
 
-// Metric is a helper that calls Deliver with MetricKind.
-// func (b Builder) Metric(m *Metric, value Value) {
-// 	if b.data == nil {
-// 		return
-// 	}
-// 	checkValid(b.data, b.builderID)
-// 	if b.data.exporter.metricsEnabled() {
-// 		b.data.exporter.mu.Lock()
-// 		defer b.data.exporter.mu.Unlock()
-// 		if b.data.Event.Namespace == "" {
-// 			b.data.Event.Namespace = m.Namespace()
-// 		}
-// 		b.data.Event.Labels = append(b.data.Event.Labels, MetricValue.Of(value), MetricKey.Of(ValueOf(m)))
-// 		b.data.exporter.prepare(&b.data.Event)
-// 		b.data.exporter.handler.Metric(b.ctx, &b.data.Event)
-// 	}
-// 	b.done()
-// }
-
-// func (b Builder) Count(m *Metric) {
-// 	if m.Kind() != Counter {
-// 		panic("Builder.Count called on non-counter")
-// 	}
-// 	b.Metric(m, Int64Of(1))
-// }
-
-// func (b Builder) Since(m *Metric, start time.Time) {
-// 	b.Metric(m, DurationOf(time.Since(start)))
-// }
+func (b Builder) Metric(mv MetricValue) {
+	if b.data == nil {
+		return
+	}
+	checkValid(b.data, b.builderID)
+	if b.data.exporter.metricsEnabled() {
+		b.data.exporter.mu.Lock()
+		defer b.data.exporter.mu.Unlock()
+		if b.data.Event.Namespace == "" {
+			b.data.Event.Namespace = mv.m.Descriptor().Namespace()
+		}
+		b.data.Event.Labels = append(b.data.Event.Labels, MetricVal.Of(mv.v), MetricKey.Of(ValueOf(mv.m)))
+		b.data.exporter.prepare(&b.data.Event)
+		b.data.exporter.handler.Metric(b.ctx, &b.data.Event)
+	}
+	b.done()
+}
 
 // Annotate is a helper that calls Deliver with AnnotateKind.
 func (b Builder) Annotate() {
diff --git a/event/common.go b/event/common.go
index f7b426e..2d58a94 100644
--- a/event/common.go
+++ b/event/common.go
@@ -9,7 +9,7 @@
 const Trace = traceKey("trace")
 const End = tagKey("end")
 const MetricKey = valueKey("metric")
-const MetricValue = valueKey("metricValue")
+const MetricVal = valueKey("metricValue")
 
 type stringKey string
 type traceKey string
diff --git a/event/common_test.go b/event/common_test.go
index 6470f1f..27764df 100644
--- a/event/common_test.go
+++ b/event/common_test.go
@@ -25,7 +25,7 @@
 	checkFind(t, h, "Log", event.Message, true, simple)
 	checkFind(t, h, "Log", event.Name, false, "")
 
-	m.To(ctx).Record(3)
+	event.To(ctx).Metric(m.Record(3))
 	checkFind(t, h, "Metric", event.Message, false, "")
 	checkFind(t, h, "Metric", event.Name, false, "")
 
diff --git a/event/event_test.go b/event/event_test.go
index 210f29c..1656e56 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -76,16 +76,16 @@
 time=2020-03-05T14:27:52 parent=1 end
 `}, {
 		name:   "counter",
-		events: func(ctx context.Context) { counter.To(ctx).With(l1).Record(2) },
+		events: func(ctx context.Context) { event.To(ctx).With(l1).Metric(counter.Record(2)) },
 		expect: `time=2020-03-05T14:27:48 l1=1 metricValue=2 metric=Metric("golang.org/x/exp/event_test/hits")`,
 	}, {
 		name:   "gauge",
-		events: func(ctx context.Context) { gauge.To(ctx).With(l1).Record(98.6) },
+		events: func(ctx context.Context) { event.To(ctx).With(l1).Metric(gauge.Record(98.6)) },
 		expect: `time=2020-03-05T14:27:48 l1=1 metricValue=98.6 metric=Metric("golang.org/x/exp/event_test/temperature")`,
 	}, {
 		name: "duration",
 		events: func(ctx context.Context) {
-			latency.To(ctx).With(l1).With(l2).Record(3 * time.Second)
+			event.To(ctx).With(l1).With(l2).Metric(latency.Record(3 * time.Second))
 		},
 		expect: `time=2020-03-05T14:27:48 l1=1 l2=2 metricValue=3s metric=Metric("golang.org/x/exp/event_test/latency")`,
 	}, {
diff --git a/event/metric.go b/event/metric.go
index 15861b3..9ca35c8 100644
--- a/event/metric.go
+++ b/event/metric.go
@@ -5,7 +5,6 @@
 package event
 
 import (
-	"context"
 	"fmt"
 	"time"
 )
@@ -54,8 +53,8 @@
 	return m
 }
 
-func (m *MetricDescriptor) Name() string      { return m.name }
-func (m *MetricDescriptor) Namespace() string { return m.namespace }
+func (m MetricDescriptor) Name() string      { return m.name }
+func (m MetricDescriptor) Namespace() string { return m.namespace }
 
 // A counter is a metric that counts something cumulatively.
 type Counter struct {
@@ -70,47 +69,13 @@
 	return c.MetricDescriptor
 }
 
-func (c *Counter) To(ctx context.Context) CounterBuilder {
-	b := CounterBuilder{builderCommon: builderCommon{ctx: ctx}, c: c}
-	b.data = newBuilder(ctx)
-	if b.data != nil {
-		b.builderID = b.data.id
-	}
-	return b
+type MetricValue struct {
+	m Metric
+	v Value
 }
 
-type CounterBuilder struct {
-	builderCommon
-	c *Counter
-}
-
-func (b CounterBuilder) With(label Label) CounterBuilder {
-	b.addLabel(label)
-	return b
-}
-
-func (b CounterBuilder) WithAll(labels ...Label) CounterBuilder {
-	b.addLabels(labels)
-	return b
-}
-
-func (b CounterBuilder) Record(v uint64) {
-	record(b.builderCommon, b.c, Uint64Of(v))
-}
-
-func record(b builderCommon, m Metric, v Value) {
-	if b.data == nil {
-		return
-	}
-	checkValid(b.data, b.builderID)
-	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, MetricValue.Of(v), MetricKey.Of(ValueOf(m)))
-		b.data.exporter.prepare(&b.data.Event)
-		b.data.exporter.handler.Metric(b.ctx, &b.data.Event)
-	}
-	b.done()
+func (c *Counter) Record(v uint64) MetricValue {
+	return MetricValue{c, Uint64Of(v)}
 }
 
 // A FloatGauge records a single floating-point value that may go up or down.
@@ -127,32 +92,8 @@
 	return g.MetricDescriptor
 }
 
-func (g *FloatGauge) To(ctx context.Context) FloatGaugeBuilder {
-	b := FloatGaugeBuilder{builderCommon: builderCommon{ctx: ctx}, g: g}
-	b.data = newBuilder(ctx)
-	if b.data != nil {
-		b.builderID = b.data.id
-	}
-	return b
-}
-
-type FloatGaugeBuilder struct {
-	builderCommon
-	g *FloatGauge
-}
-
-func (b FloatGaugeBuilder) With(label Label) FloatGaugeBuilder {
-	b.addLabel(label)
-	return b
-}
-
-func (b FloatGaugeBuilder) WithAll(labels ...Label) FloatGaugeBuilder {
-	b.addLabels(labels)
-	return b
-}
-
-func (b FloatGaugeBuilder) Record(v float64) {
-	record(b.builderCommon, b.g, Float64Of(v))
+func (g *FloatGauge) Record(v float64) MetricValue {
+	return MetricValue{g, Float64Of(v)}
 }
 
 // A Duration records a distribution of durations.
@@ -169,30 +110,6 @@
 	return d.MetricDescriptor
 }
 
-func (d *Duration) To(ctx context.Context) DurationBuilder {
-	b := DurationBuilder{builderCommon: builderCommon{ctx: ctx}, d: d}
-	b.data = newBuilder(ctx)
-	if b.data != nil {
-		b.builderID = b.data.id
-	}
-	return b
-}
-
-type DurationBuilder struct {
-	builderCommon
-	d *Duration
-}
-
-func (b DurationBuilder) With(label Label) DurationBuilder {
-	b.addLabel(label)
-	return b
-}
-
-func (b DurationBuilder) WithAll(labels ...Label) DurationBuilder {
-	b.addLabels(labels)
-	return b
-}
-
-func (b DurationBuilder) Record(v time.Duration) {
-	record(b.builderCommon, b.d, DurationOf(v))
+func (d *Duration) Record(v time.Duration) MetricValue {
+	return MetricValue{d, DurationOf(v)}
 }