event/otel: metrics
Add an event handler for OpenTelemetry metrics.
The first time it sees an event.Metric, the handler creates a matching
otel instrument and caches it. On each call, it uses the instrument to
record the metric value.
Change-Id: I07d6f40601c7d2a801ed9fbe3cf7c24d5698f3f1
Reviewed-on: https://go-review.googlesource.com/c/exp/+/320350
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/event/common.go b/event/common.go
index 2bd82a4..f12b84a 100644
--- a/event/common.go
+++ b/event/common.go
@@ -11,7 +11,7 @@
)
const (
- MetricKey = "metric"
+ MetricKey = interfaceKey("metric")
MetricVal = "metricValue"
DurationMetric = interfaceKey("durationMetric")
)
diff --git a/event/event_test.go b/event/event_test.go
index d918bcd..1757a97 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !disable_events
// +build !disable_events
package event_test
@@ -24,9 +25,9 @@
l1 = event.Int64("l1", 1)
l2 = event.Int64("l2", 2)
l3 = event.Int64("l3", 3)
- counter = event.NewCounter("hits", "cache hits")
- gauge = event.NewFloatGauge("temperature", "CPU board temperature in Celsius")
- latency = event.NewDuration("latency", "how long it took")
+ counter = event.NewCounter("hits", nil)
+ gauge = event.NewFloatGauge("temperature", nil)
+ latency = event.NewDuration("latency", nil)
err = errors.New("an error")
)
@@ -275,7 +276,7 @@
func TestTraceDuration(t *testing.T) {
// Verify that a trace can can emit a latency metric.
- dur := event.NewDuration("test", "")
+ dur := event.NewDuration("test", nil)
want := time.Second
check := func(t *testing.T, h *testTraceDurationHandler) {
@@ -313,7 +314,7 @@
func (t *testTraceDurationHandler) Event(ctx context.Context, ev *event.Event) context.Context {
for _, l := range ev.Labels {
- if l.Name == event.MetricVal {
+ if l.Name == string(event.MetricVal) {
t.got = l
}
}
@@ -322,7 +323,7 @@
func BenchmarkBuildContext(b *testing.B) {
// How long does it take to deliver an event from a nested context?
- c := event.NewCounter("c", "")
+ c := event.NewCounter("c", nil)
for _, depth := range []int{1, 5, 7, 10} {
b.Run(fmt.Sprintf("depth %d", depth), func(b *testing.B) {
ctx := event.WithExporter(context.Background(), event.NewExporter(nopHandler{}, eventtest.ExporterOptions()))
diff --git a/event/metric.go b/event/metric.go
index a603a10..e74689e 100644
--- a/event/metric.go
+++ b/event/metric.go
@@ -6,81 +6,71 @@
import (
"context"
- "fmt"
"time"
)
+// A Unit is a unit of measurement for a metric.
+type Unit string
+
+const (
+ UnitDimensionless Unit = "1"
+ UnitBytes Unit = "By"
+ UnitMilliseconds Unit = "ms"
+)
+
// A Metric represents a kind of recorded measurement.
type Metric interface {
- Descriptor() *MetricDescriptor
+ Name() string
+ Options() MetricOptions
}
-// A MetricDescriptor describes a metric.
-type MetricDescriptor struct {
- namespace string
- name string
- description string
- // TODO: deal with units. Follow otel, or define Go types for common units.
- // We don't need a time unit because we'll use time.Duration, and the only
- // other unit otel currently defines (besides dimensionless) is bytes.
-}
+type MetricOptions struct {
+ // A string that should be common for all metrics of an application or
+ // service. Defaults to the import path of the package calling
+ // the metric construction function (NewCounter, etc.).
+ Namespace string
-// NewMetricDescriptor creates a MetricDescriptor with the given name.
-// The namespace defaults to the import path of the caller of NewMetricDescriptor.
-// Use SetNamespace to provide a different one.
-// Neither the name nor the namespace can be empty.
-func NewMetricDescriptor(name, description string) *MetricDescriptor {
- return newMetricDescriptor(name, description)
-}
+ // Optional description of the metric.
+ Description string
-func newMetricDescriptor(name, description string) *MetricDescriptor {
- if name == "" {
- panic("name cannot be empty")
- }
- return &MetricDescriptor{
- name: name,
- namespace: scanStack().Space,
- description: description,
- }
+ // Optional unit for the metric. Defaults to UnitDimensionless.
+ Unit Unit
}
-// SetNamespace sets the namespace of m to a non-empty string.
-func (m *MetricDescriptor) SetNamespace(ns string) {
- if ns == "" {
- panic("namespace cannot be empty")
- }
- m.namespace = ns
-}
-
-func (m *MetricDescriptor) String() string {
- return fmt.Sprintf("Metric(\"%s/%s\")", m.namespace, m.name)
-}
-
-func (m *MetricDescriptor) Name() string { return m.name }
-func (m *MetricDescriptor) Namespace() string { return m.namespace }
-func (m *MetricDescriptor) Description() string { return m.description }
-
// A Counter is a metric that counts something cumulatively.
type Counter struct {
- *MetricDescriptor
+ name string
+ opts MetricOptions
+}
+
+func initOpts(popts *MetricOptions) MetricOptions {
+ var opts MetricOptions
+ if popts != nil {
+ opts = *popts
+ }
+ if opts.Namespace == "" {
+ opts.Namespace = scanStack().Space
+ }
+ if opts.Unit == "" {
+ opts.Unit = UnitDimensionless
+ }
+ return opts
}
// NewCounter creates a counter with the given name.
-func NewCounter(name, description string) *Counter {
- return &Counter{newMetricDescriptor(name, description)}
+func NewCounter(name string, opts *MetricOptions) *Counter {
+ return &Counter{name, initOpts(opts)}
}
-// Descriptor returns the receiver's MetricDescriptor.
-func (c *Counter) Descriptor() *MetricDescriptor {
- return c.MetricDescriptor
-}
+func (c *Counter) Name() string { return c.name }
+func (c *Counter) Options() MetricOptions { return c.opts }
// Record delivers a metric event with the given metric, value and labels to the
// exporter in the context.
func (c *Counter) Record(ctx context.Context, v int64, labels ...Label) {
ev := New(ctx, MetricKind)
if ev != nil {
- record(ev, c, Int64(MetricVal, v))
+ record(ev, c, Int64(string(MetricVal), v))
ev.Labels = append(ev.Labels, labels...)
ev.Deliver()
}
@@ -89,26 +79,24 @@
// A FloatGauge records a single floating-point value that may go up or down.
// TODO(generics): Gauge[T]
type FloatGauge struct {
- *MetricDescriptor
+ name string
+ opts MetricOptions
}
// NewFloatGauge creates a new FloatGauge with the given name.
-func NewFloatGauge(name, description string) *FloatGauge {
- return &FloatGauge{newMetricDescriptor(name, description)}
+func NewFloatGauge(name string, opts *MetricOptions) *FloatGauge {
+ return &FloatGauge{name, initOpts(opts)}
}
-// Descriptor returns the receiver's MetricDescriptor.
-func (g *FloatGauge) Descriptor() *MetricDescriptor {
- return g.MetricDescriptor
-}
+func (g *FloatGauge) Name() string { return g.name }
+func (g *FloatGauge) Options() MetricOptions { return g.opts }
// Record converts its argument into a Value and returns a MetricValue with the
-// receiver and the value. It is intended to be used as an argument to
-// Builder.Metric.
+// receiver and the value.
func (g *FloatGauge) Record(ctx context.Context, v float64, labels ...Label) {
ev := New(ctx, MetricKind)
if ev != nil {
- record(ev, g, Float64(MetricVal, v))
+ record(ev, g, Float64(string(MetricVal), v))
ev.Labels = append(ev.Labels, labels...)
ev.Deliver()
}
@@ -117,26 +105,24 @@
// A DurationDistribution records a distribution of durations.
// TODO(generics): Distribution[T]
type DurationDistribution struct {
- *MetricDescriptor
+ name string
+ opts MetricOptions
}
// NewDuration creates a new Duration with the given name.
-func NewDuration(name, description string) *DurationDistribution {
- return &DurationDistribution{newMetricDescriptor(name, description)}
+func NewDuration(name string, opts *MetricOptions) *DurationDistribution {
+ return &DurationDistribution{name, initOpts(opts)}
}
-// Descriptor returns the receiver's MetricDescriptor.
-func (d *DurationDistribution) Descriptor() *MetricDescriptor {
- return d.MetricDescriptor
-}
+func (d *DurationDistribution) Name() string { return d.name }
+func (d *DurationDistribution) Options() MetricOptions { return d.opts }
// Record converts its argument into a Value and returns a MetricValue with the
-// receiver and the value. It is intended to be used as an argument to
-// Builder.Metric.
+// receiver and the value.
func (d *DurationDistribution) Record(ctx context.Context, v time.Duration, labels ...Label) {
ev := New(ctx, MetricKind)
if ev != nil {
- record(ev, d, Duration(MetricVal, v))
+ record(ev, d, Duration(string(MetricVal), v))
ev.Labels = append(ev.Labels, labels...)
ev.Deliver()
}
@@ -144,31 +130,29 @@
// An IntDistribution records a distribution of int64s.
type IntDistribution struct {
- *MetricDescriptor
+ name string
+ opts MetricOptions
}
+func (d *IntDistribution) Name() string { return d.name }
+func (d *IntDistribution) Options() MetricOptions { return d.opts }
+
// NewIntDistribution creates a new IntDistribution with the given name.
-func NewIntDistribution(name, description string) *IntDistribution {
- return &IntDistribution{newMetricDescriptor(name, description)}
-}
-
-// Descriptor returns the receiver's MetricDescriptor.
-func (d *IntDistribution) Descriptor() *MetricDescriptor {
- return d.MetricDescriptor
+func NewIntDistribution(name string, opts *MetricOptions) *IntDistribution {
+ return &IntDistribution{name, initOpts(opts)}
}
// Record converts its argument into a Value and returns a MetricValue with the
-// receiver and the value. It is intended to be used as an argument to
-// Builder.Metric.
+// receiver and the value.
func (d *IntDistribution) Record(ctx context.Context, v int64, labels ...Label) {
ev := New(ctx, MetricKind)
if ev != nil {
- record(ev, d, Int64(MetricVal, v))
+ record(ev, d, Int64(string(MetricVal), v))
ev.Labels = append(ev.Labels, labels...)
ev.Deliver()
}
}
func record(ev *Event, m Metric, l Label) {
- ev.Labels = append(ev.Labels, l, Value(MetricKey, m))
+ ev.Labels = append(ev.Labels, l, MetricKey.Of(m))
}
diff --git a/event/otel/metric.go b/event/otel/metric.go
new file mode 100644
index 0000000..910cc94
--- /dev/null
+++ b/event/otel/metric.go
@@ -0,0 +1,131 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package otel
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/metric"
+ otelunit "go.opentelemetry.io/otel/metric/unit"
+ "golang.org/x/exp/event"
+)
+
+// MetricHandler is an event.Handler for OpenTelemetry metrics.
+// Its Event method handles Metric events and ignores all others.
+type MetricHandler struct {
+ meter metric.MeterMust
+ mu sync.Mutex
+ // A map from event.Metrics to, effectively, otel Meters.
+ // But since the only thing we need from the Meter is recording a value, we
+ // use a function for that that closes over the Meter itself.
+ recordFuncs map[event.Metric]recordFunc
+}
+
+type recordFunc func(context.Context, event.Label, []attribute.KeyValue)
+
+var _ event.Handler = (*MetricHandler)(nil)
+
+// NewMetricHandler creates a new MetricHandler.
+func NewMetricHandler(m metric.Meter) *MetricHandler {
+ return &MetricHandler{
+ meter: metric.Must(m),
+ recordFuncs: map[event.Metric]recordFunc{},
+ }
+}
+
+func (m *MetricHandler) Event(ctx context.Context, e *event.Event) context.Context {
+ if e.Kind != event.MetricKind {
+ return ctx
+ }
+ // Get the otel instrument corresponding to the event's MetricDescriptor,
+ // or create a new one.
+ mi, ok := event.MetricKey.Find(e)
+ if !ok {
+ panic(errors.New("no metric key for metric event"))
+ }
+ em := mi.(event.Metric)
+ lval := e.Find(event.MetricVal)
+ if !lval.HasValue() {
+ panic(errors.New("no metric value for metric event"))
+ }
+ rf := m.getRecordFunc(em)
+ if rf == nil {
+ panic(fmt.Errorf("unable to record for metric %v", em))
+ }
+ rf(ctx, lval, labelsToAttributes(e.Labels))
+ return ctx
+}
+
+func (m *MetricHandler) getRecordFunc(em event.Metric) recordFunc {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if f, ok := m.recordFuncs[em]; ok {
+ return f
+ }
+ f := m.newRecordFunc(em)
+ m.recordFuncs[em] = f
+ return f
+}
+
+func (m *MetricHandler) newRecordFunc(em event.Metric) recordFunc {
+ opts := em.Options()
+ name := opts.Namespace + "/" + em.Name()
+ otelOpts := []metric.InstrumentOption{
+ metric.WithDescription(opts.Description),
+ metric.WithUnit(otelunit.Unit(opts.Unit)), // cast OK: same strings
+ }
+ switch em.(type) {
+ case *event.Counter:
+ c := m.meter.NewInt64Counter(name, otelOpts...)
+ return func(ctx context.Context, l event.Label, attrs []attribute.KeyValue) {
+ c.Add(ctx, l.Int64(), attrs...)
+ }
+
+ case *event.FloatGauge:
+ g := m.meter.NewFloat64UpDownCounter(name, otelOpts...)
+ return func(ctx context.Context, l event.Label, attrs []attribute.KeyValue) {
+ g.Add(ctx, l.Float64(), attrs...)
+ }
+
+ case *event.DurationDistribution:
+ r := m.meter.NewInt64Histogram(name, otelOpts...)
+ return func(ctx context.Context, l event.Label, attrs []attribute.KeyValue) {
+ r.Record(ctx, l.Duration().Nanoseconds(), attrs...)
+ }
+
+ default:
+ return nil
+ }
+}
+
+func labelsToAttributes(ls []event.Label) []attribute.KeyValue {
+ var attrs []attribute.KeyValue
+ for _, l := range ls {
+ if l.Name == string(event.MetricKey) || l.Name == string(event.MetricVal) {
+ continue
+ }
+ attrs = append(attrs, labelToAttribute(l))
+ }
+ return attrs
+}
+
+func labelToAttribute(l event.Label) attribute.KeyValue {
+ switch {
+ case l.IsString():
+ return attribute.String(l.Name, l.String())
+ case l.IsInt64():
+ return attribute.Int64(l.Name, l.Int64())
+ case l.IsFloat64():
+ return attribute.Float64(l.Name, l.Float64())
+ case l.IsBool():
+ return attribute.Bool(l.Name, l.Bool())
+ default: // including uint64
+ panic(fmt.Errorf("cannot convert label value of type %T to attribute.KeyValue", l.Interface()))
+ }
+}
diff --git a/event/otel/metric_test.go b/event/otel/metric_test.go
new file mode 100644
index 0000000..300f4a7
--- /dev/null
+++ b/event/otel/metric_test.go
@@ -0,0 +1,75 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package otel_test
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/metric/metrictest"
+ "go.opentelemetry.io/otel/metric/number"
+ "golang.org/x/exp/event"
+ "golang.org/x/exp/event/otel"
+)
+
+func TestMeter(t *testing.T) {
+ ctx := context.Background()
+ mp := metrictest.NewMeterProvider()
+ mh := otel.NewMetricHandler(mp.Meter("test"))
+ ctx = event.WithExporter(ctx, event.NewExporter(mh, nil))
+ recordMetrics(ctx)
+
+ lib := metrictest.Library{InstrumentationName: "test"}
+ emptyLabels := map[attribute.Key]attribute.Value{}
+ got := metrictest.AsStructs(mp.MeasurementBatches)
+ want := []metrictest.Measured{
+ {
+ Name: "golang.org/x/exp/event/otel_test/hits",
+ Number: number.NewInt64Number(8),
+ Labels: emptyLabels,
+ Library: lib,
+ },
+ {
+ Name: "golang.org/x/exp/event/otel_test/temp",
+ Number: number.NewFloat64Number(-100),
+ Labels: map[attribute.Key]attribute.Value{"location": attribute.StringValue("Mare Imbrium")},
+ Library: lib,
+ },
+ {
+ Name: "golang.org/x/exp/event/otel_test/latency",
+ Number: number.NewInt64Number(int64(1248 * time.Millisecond)),
+ Labels: emptyLabels,
+ Library: lib,
+ },
+ {
+ Name: "golang.org/x/exp/event/otel_test/latency",
+ Number: number.NewInt64Number(int64(1255 * time.Millisecond)),
+ Labels: emptyLabels,
+ Library: lib,
+ },
+ }
+
+ if diff := cmp.Diff(want, got, cmp.Comparer(valuesEqual)); diff != "" {
+ t.Errorf("mismatch (-want, got):\n%s", diff)
+ }
+}
+
+func valuesEqual(v1, v2 attribute.Value) bool {
+ return v1.AsInterface() == v2.AsInterface()
+}
+
+func recordMetrics(ctx context.Context) {
+ c := event.NewCounter("hits", &event.MetricOptions{Description: "Earth meteorite hits"})
+ g := event.NewFloatGauge("temp", &event.MetricOptions{Description: "moon surface temperature in Kelvin"})
+ d := event.NewDuration("latency", &event.MetricOptions{Description: "Earth-moon comms lag, milliseconds"})
+
+ c.Record(ctx, 8)
+ g.Record(ctx, -100, event.String("location", "Mare Imbrium"))
+ d.Record(ctx, 1248*time.Millisecond)
+ d.Record(ctx, 1255*time.Millisecond)
+}
diff --git a/go.mod b/go.mod
index 7cd7c67..cc530ad 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,8 @@
github.com/google/go-cmp v0.5.6
github.com/rs/zerolog v1.21.0
github.com/sirupsen/logrus v1.8.1
+ go.opentelemetry.io/otel v1.3.0
+ go.opentelemetry.io/otel/metric v0.26.0
go.opentelemetry.io/otel/sdk v1.3.0
go.opentelemetry.io/otel/trace v1.3.0
go.uber.org/zap v1.16.0
@@ -25,10 +27,8 @@
require (
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-logr/stdr v1.2.0 // indirect
- go.opentelemetry.io/otel v1.3.0 // indirect
- go.opentelemetry.io/otel/metric v0.20.0 // indirect
+ go.opentelemetry.io/otel/internal/metric v0.26.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
honnef.co/go/tools v0.1.3 // indirect
)
-
diff --git a/go.sum b/go.sum
index d7654bd..1445e46 100644
--- a/go.sum
+++ b/go.sum
@@ -69,8 +69,6 @@
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
-github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
@@ -97,8 +95,6 @@
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -259,26 +255,19 @@
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
-go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
-go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
-go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
-go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw=
-go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
-go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
-go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
+go.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=
+go.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=
+go.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=
+go.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
-go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
-go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -303,7 +292,6 @@
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -323,7 +311,6 @@
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -343,8 +330,6 @@
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -354,7 +339,6 @@
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -372,19 +356,13 @@
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/jsonrpc2/defs.go b/jsonrpc2/defs.go
index 80b25c1..e3677de 100644
--- a/jsonrpc2/defs.go
+++ b/jsonrpc2/defs.go
@@ -14,11 +14,20 @@
func StatusCode(v string) event.Label { return event.String("status.code", v) }
var (
- Started = event.NewCounter("started", "Count of started RPCs.")
- Finished = event.NewCounter("finished", "Count of finished RPCs (includes error).")
- ReceivedBytes = event.NewIntDistribution("received_bytes", "Bytes received.") //, unit.Bytes)
- SentBytes = event.NewIntDistribution("sent_bytes", "Bytes sent.") //, unit.Bytes)
- Latency = event.NewDuration("latency", "Elapsed time of an RPC.") //, unit.Milliseconds)
+ Started = event.NewCounter("started", &event.MetricOptions{Description: "Count of started RPCs."})
+ Finished = event.NewCounter("finished", &event.MetricOptions{Description: "Count of finished RPCs (includes error)."})
+ ReceivedBytes = event.NewIntDistribution("received_bytes", &event.MetricOptions{
+ Description: "Bytes received.",
+ Unit: event.UnitBytes,
+ })
+ SentBytes = event.NewIntDistribution("sent_bytes", &event.MetricOptions{
+ Description: "Bytes sent.",
+ Unit: event.UnitBytes,
+ })
+ Latency = event.NewDuration("latency", &event.MetricOptions{
+ Description: "Elapsed time of an RPC.",
+ Unit: event.UnitMilliseconds,
+ })
)
const (