You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-09-16 09:26:25 +02:00
Some metrics followup (#177)
* prevent passing observer descriptors to NewHandle * add some test for metrics * reword some docs
This commit is contained in:
@@ -66,6 +66,23 @@ type ObservationCallback func(LabelSet, MeasurementValue)
|
||||
// the registered observers.
|
||||
type ObserverCallback func(Meter, Observer, ObservationCallback)
|
||||
|
||||
// WithDescriptor is an interface that all metric implement.
|
||||
type WithDescriptor interface {
|
||||
// Descriptor returns a descriptor of this metric.
|
||||
Descriptor() *Descriptor
|
||||
}
|
||||
|
||||
type hiddenType struct{}
|
||||
|
||||
// ExplicitReportingMetric is an interface that is implemented only by
|
||||
// metrics that support getting a Handle.
|
||||
type ExplicitReportingMetric interface {
|
||||
WithDescriptor
|
||||
// SupportHandle is a dummy function that can be only
|
||||
// implemented in this package.
|
||||
SupportHandle() hiddenType
|
||||
}
|
||||
|
||||
// Meter is an interface to the metrics portion of the OpenTelemetry SDK.
|
||||
type Meter interface {
|
||||
// DefineLabels returns a reference to a set of labels that
|
||||
@@ -78,7 +95,7 @@ type Meter interface {
|
||||
// NewHandle creates a Handle that contains the passed
|
||||
// key-value pairs. This should not be used directly - prefer
|
||||
// using GetHandle function of a metric.
|
||||
NewHandle(*Descriptor, LabelSet) Handle
|
||||
NewHandle(ExplicitReportingMetric, LabelSet) Handle
|
||||
// DeleteHandle destroys the Handle and does a cleanup of the
|
||||
// underlying resources.
|
||||
DeleteHandle(Handle)
|
||||
|
718
api/metric/api_test.go
Normal file
718
api/metric/api_test.go
Normal file
@@ -0,0 +1,718 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metric
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/unit"
|
||||
)
|
||||
|
||||
func TestCounterOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
opts []CounterOptionApplier
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{
|
||||
name: "no opts",
|
||||
opts: nil,
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "keys keys keys",
|
||||
opts: []CounterOptionApplier{
|
||||
WithKeys(key("foo"), key("foo2")),
|
||||
WithKeys(key("bar"), key("bar2")),
|
||||
WithKeys(key("baz"), key("baz2")),
|
||||
},
|
||||
keys: []core.Key{
|
||||
key("foo"), key("foo2"),
|
||||
key("bar"), key("bar2"),
|
||||
key("baz"), key("baz2"),
|
||||
},
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
opts: []CounterOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "stuff",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description override",
|
||||
opts: []CounterOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
WithDescription("things"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "things",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit",
|
||||
opts: []CounterOptionApplier{
|
||||
WithUnit("s"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "s",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit override",
|
||||
opts: []CounterOptionApplier{
|
||||
WithUnit("s"),
|
||||
WithUnit("h"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "h",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "nonmonotonic",
|
||||
opts: []CounterOptionApplier{
|
||||
WithNonMonotonic(true),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
name: "nonmonotonic, but not really",
|
||||
opts: []CounterOptionApplier{
|
||||
WithNonMonotonic(true),
|
||||
WithNonMonotonic(false),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
}
|
||||
checkCounterDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) {
|
||||
e := descriptor{
|
||||
name: tt.name,
|
||||
keys: tt.keys,
|
||||
desc: tt.desc,
|
||||
unit: tt.unit,
|
||||
alt: tt.alt,
|
||||
kind: CounterKind,
|
||||
vk: vk,
|
||||
}
|
||||
checkDescriptor(t, e, d)
|
||||
}
|
||||
for idx, tt := range testcases {
|
||||
t.Logf("Testing counter case %s (%d)", tt.name, idx)
|
||||
f := NewFloat64Counter(tt.name, tt.opts...)
|
||||
checkCounterDescriptor(tt, Float64ValueKind, f.Descriptor())
|
||||
i := NewInt64Counter(tt.name, tt.opts...)
|
||||
checkCounterDescriptor(tt, Int64ValueKind, i.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaugeOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
opts []GaugeOptionApplier
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{
|
||||
name: "no opts",
|
||||
opts: nil,
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "keys keys keys",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithKeys(key("foo"), key("foo2")),
|
||||
WithKeys(key("bar"), key("bar2")),
|
||||
WithKeys(key("baz"), key("baz2")),
|
||||
},
|
||||
keys: []core.Key{
|
||||
key("foo"), key("foo2"),
|
||||
key("bar"), key("bar2"),
|
||||
key("baz"), key("baz2"),
|
||||
},
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "stuff",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description override",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
WithDescription("things"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "things",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithUnit("s"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "s",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit override",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithUnit("s"),
|
||||
WithUnit("h"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "h",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "monotonic",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithMonotonic(true),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
name: "monotonic, but not really",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithMonotonic(true),
|
||||
WithMonotonic(false),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
}
|
||||
checkGaugeDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) {
|
||||
e := descriptor{
|
||||
name: tt.name,
|
||||
keys: tt.keys,
|
||||
desc: tt.desc,
|
||||
unit: tt.unit,
|
||||
alt: tt.alt,
|
||||
kind: GaugeKind,
|
||||
vk: vk,
|
||||
}
|
||||
checkDescriptor(t, e, d)
|
||||
}
|
||||
for idx, tt := range testcases {
|
||||
t.Logf("Testing gauge case %s (%d)", tt.name, idx)
|
||||
f := NewFloat64Gauge(tt.name, tt.opts...)
|
||||
checkGaugeDescriptor(tt, Float64ValueKind, f.Descriptor())
|
||||
i := NewInt64Gauge(tt.name, tt.opts...)
|
||||
checkGaugeDescriptor(tt, Int64ValueKind, i.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasureOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
opts []MeasureOptionApplier
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{
|
||||
name: "no opts",
|
||||
opts: nil,
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "keys keys keys",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithKeys(key("foo"), key("foo2")),
|
||||
WithKeys(key("bar"), key("bar2")),
|
||||
WithKeys(key("baz"), key("baz2")),
|
||||
},
|
||||
keys: []core.Key{
|
||||
key("foo"), key("foo2"),
|
||||
key("bar"), key("bar2"),
|
||||
key("baz"), key("baz2"),
|
||||
},
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "stuff",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description override",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
WithDescription("things"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "things",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithUnit("s"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "s",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit override",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithUnit("s"),
|
||||
WithUnit("h"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "h",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "signed",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithSigned(true),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
name: "signed, but not really",
|
||||
opts: []MeasureOptionApplier{
|
||||
WithSigned(true),
|
||||
WithSigned(false),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
}
|
||||
checkMeasureDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) {
|
||||
e := descriptor{
|
||||
name: tt.name,
|
||||
keys: tt.keys,
|
||||
desc: tt.desc,
|
||||
unit: tt.unit,
|
||||
alt: tt.alt,
|
||||
kind: MeasureKind,
|
||||
vk: vk,
|
||||
}
|
||||
checkDescriptor(t, e, d)
|
||||
}
|
||||
for idx, tt := range testcases {
|
||||
t.Logf("Testing measure case %s (%d)", tt.name, idx)
|
||||
f := NewFloat64Measure(tt.name, tt.opts...)
|
||||
checkMeasureDescriptor(tt, Float64ValueKind, f.Descriptor())
|
||||
i := NewInt64Measure(tt.name, tt.opts...)
|
||||
checkMeasureDescriptor(tt, Int64ValueKind, i.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestObserverOptions(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
opts []GaugeOptionApplier
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{
|
||||
name: "no opts",
|
||||
opts: nil,
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "keys keys keys",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithKeys(key("foo"), key("foo2")),
|
||||
WithKeys(key("bar"), key("bar2")),
|
||||
WithKeys(key("baz"), key("baz2")),
|
||||
},
|
||||
keys: []core.Key{
|
||||
key("foo"), key("foo2"),
|
||||
key("bar"), key("bar2"),
|
||||
key("baz"), key("baz2"),
|
||||
},
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "stuff",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "description override",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithDescription("stuff"),
|
||||
WithDescription("things"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "things",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithUnit("s"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "s",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "unit override",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithUnit("s"),
|
||||
WithUnit("h"),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "h",
|
||||
alt: false,
|
||||
},
|
||||
{
|
||||
name: "monotonic",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithMonotonic(true),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: true,
|
||||
},
|
||||
{
|
||||
name: "monotonic, but not really",
|
||||
opts: []GaugeOptionApplier{
|
||||
WithMonotonic(true),
|
||||
WithMonotonic(false),
|
||||
},
|
||||
keys: nil,
|
||||
desc: "",
|
||||
unit: "",
|
||||
alt: false,
|
||||
},
|
||||
}
|
||||
checkObserverDescriptor := func(tt testcase, vk ValueKind, d *Descriptor) {
|
||||
e := descriptor{
|
||||
name: tt.name,
|
||||
keys: tt.keys,
|
||||
desc: tt.desc,
|
||||
unit: tt.unit,
|
||||
alt: tt.alt,
|
||||
kind: ObserverKind,
|
||||
vk: vk,
|
||||
}
|
||||
checkDescriptor(t, e, d)
|
||||
}
|
||||
for idx, tt := range testcases {
|
||||
t.Logf("Testing observer case %s (%d)", tt.name, idx)
|
||||
f := NewFloat64Observer(tt.name, tt.opts...)
|
||||
checkObserverDescriptor(tt, Float64ValueKind, f.Descriptor())
|
||||
i := NewInt64Observer(tt.name, tt.opts...)
|
||||
checkObserverDescriptor(tt, Int64ValueKind, i.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func key(name string) core.Key {
|
||||
return core.Key{
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
type descriptor struct {
|
||||
name string
|
||||
keys []core.Key
|
||||
desc string
|
||||
unit unit.Unit
|
||||
alt bool
|
||||
kind Kind
|
||||
vk ValueKind
|
||||
}
|
||||
|
||||
func checkDescriptor(t *testing.T, e descriptor, d *Descriptor) {
|
||||
if e.name != d.Name() {
|
||||
t.Errorf("Expected name %q, got %q", e.name, d.Name())
|
||||
}
|
||||
if len(e.keys) != len(d.Keys()) {
|
||||
t.Errorf("Expected %d key(s), got %d", len(e.keys), len(d.Keys()))
|
||||
}
|
||||
minLen := len(e.keys)
|
||||
if minLen > len(d.Keys()) {
|
||||
minLen = len(d.Keys())
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if e.keys[i].Name != d.Keys()[i].Name {
|
||||
t.Errorf("Expected key %q, got %q", e.keys[i].Name, d.Keys()[i].Name)
|
||||
}
|
||||
}
|
||||
if e.desc != d.Description() {
|
||||
t.Errorf("Expected description %q, got %q", e.desc, d.Description())
|
||||
}
|
||||
if e.unit != d.Unit() {
|
||||
t.Errorf("Expected unit %q, got %q", e.unit, d.Unit())
|
||||
}
|
||||
if e.alt != d.Alternate() {
|
||||
t.Errorf("Expected alternate %v, got %v", e.alt, d.Alternate())
|
||||
}
|
||||
if e.vk != d.ValueKind() {
|
||||
t.Errorf("Expected value kind %q, got %q", e.vk, d.ValueKind())
|
||||
}
|
||||
if e.kind != d.Kind() {
|
||||
t.Errorf("Expected kind %q, got %q", e.kind, d.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounter(t *testing.T) {
|
||||
{
|
||||
c := NewFloat64Counter("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
c.Add(ctx, 42, labels)
|
||||
handle := c.GetHandle(labels)
|
||||
handle.Add(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, c.Measurement(42))
|
||||
t.Log("Testing float counter")
|
||||
checkBatches(t, ctx, labels, meter, c.Descriptor())
|
||||
}
|
||||
{
|
||||
c := NewInt64Counter("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
c.Add(ctx, 42, labels)
|
||||
handle := c.GetHandle(labels)
|
||||
handle.Add(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, c.Measurement(42))
|
||||
t.Log("Testing int counter")
|
||||
checkBatches(t, ctx, labels, meter, c.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGauge(t *testing.T) {
|
||||
{
|
||||
g := NewFloat64Gauge("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
g.Set(ctx, 42, labels)
|
||||
handle := g.GetHandle(labels)
|
||||
handle.Set(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, g.Measurement(42))
|
||||
t.Log("Testing float gauge")
|
||||
checkBatches(t, ctx, labels, meter, g.Descriptor())
|
||||
}
|
||||
{
|
||||
g := NewInt64Gauge("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
g.Set(ctx, 42, labels)
|
||||
handle := g.GetHandle(labels)
|
||||
handle.Set(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, g.Measurement(42))
|
||||
t.Log("Testing int gauge")
|
||||
checkBatches(t, ctx, labels, meter, g.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeasure(t *testing.T) {
|
||||
{
|
||||
m := NewFloat64Measure("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
m.Record(ctx, 42, labels)
|
||||
handle := m.GetHandle(labels)
|
||||
handle.Record(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, m.Measurement(42))
|
||||
t.Log("Testing float measure")
|
||||
checkBatches(t, ctx, labels, meter, m.Descriptor())
|
||||
}
|
||||
{
|
||||
m := NewInt64Measure("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
m.Record(ctx, 42, labels)
|
||||
handle := m.GetHandle(labels)
|
||||
handle.Record(ctx, 42)
|
||||
meter.RecordBatch(ctx, labels, m.Measurement(42))
|
||||
t.Log("Testing int measure")
|
||||
checkBatches(t, ctx, labels, meter, m.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func TestObserver(t *testing.T) {
|
||||
{
|
||||
o := NewFloat64Observer("ajwaj")
|
||||
meter := newMockMeter()
|
||||
ctx := context.Background()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
RegisterFloat64Observer(meter, o, func(meter Meter, o Float64Observer, cb Float64ObservationCallback) {
|
||||
cb(labels, 42)
|
||||
cb(labels, 42)
|
||||
cb(labels, 42)
|
||||
})
|
||||
meter.PerformObservations()
|
||||
t.Log("Testing float observer")
|
||||
checkBatches(t, ctx, labels, meter, o.Descriptor())
|
||||
}
|
||||
{
|
||||
o := NewInt64Observer("ajwaj")
|
||||
ctx := context.Background()
|
||||
meter := newMockMeter()
|
||||
labels := meter.DefineLabels(ctx)
|
||||
RegisterInt64Observer(meter, o, func(meter Meter, o Int64Observer, cb Int64ObservationCallback) {
|
||||
cb(labels, 42)
|
||||
cb(labels, 42)
|
||||
cb(labels, 42)
|
||||
})
|
||||
meter.PerformObservations()
|
||||
t.Log("Testing int observer")
|
||||
checkBatches(t, ctx, labels, meter, o.Descriptor())
|
||||
}
|
||||
}
|
||||
|
||||
func checkBatches(t *testing.T, ctx context.Context, labels LabelSet, meter *mockMeter, descriptor *Descriptor) {
|
||||
if len(meter.measurementBatches) != 3 {
|
||||
t.Errorf("Expected 3 recorded measurement batches, got %d", len(meter.measurementBatches))
|
||||
}
|
||||
ourLabelSet := labels.(*mockLabelSet)
|
||||
minLen := 3
|
||||
if minLen > len(meter.measurementBatches) {
|
||||
minLen = len(meter.measurementBatches)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
got := meter.measurementBatches[i]
|
||||
if got.ctx != ctx {
|
||||
d := func(c context.Context) string {
|
||||
return fmt.Sprintf("(ptr: %p, ctx %#v)", c, c)
|
||||
}
|
||||
t.Errorf("Wrong recorded context in batch %d, expected %s, got %s", i, d(ctx), d(got.ctx))
|
||||
}
|
||||
if got.labelSet != ourLabelSet {
|
||||
d := func(l *mockLabelSet) string {
|
||||
return fmt.Sprintf("(ptr: %p, labels %#v)", l, l.labels)
|
||||
}
|
||||
t.Errorf("Wrong recorded label set in batch %d, expected %s, got %s", i, d(ourLabelSet), d(got.labelSet))
|
||||
}
|
||||
if len(got.measurements) != 1 {
|
||||
t.Errorf("Expected 1 measurement in batch %d, got %d", i, len(got.measurements))
|
||||
}
|
||||
minMLen := 1
|
||||
if minMLen > len(got.measurements) {
|
||||
minMLen = len(got.measurements)
|
||||
}
|
||||
for j := 0; j < minMLen; j++ {
|
||||
measurement := got.measurements[j]
|
||||
if measurement.Descriptor != descriptor {
|
||||
d := func(d *Descriptor) string {
|
||||
return fmt.Sprintf("(ptr: %p, descriptor %#v)", d, d)
|
||||
}
|
||||
t.Errorf("Wrong recorded descriptor in measurement %d in batch %d, expected %s, got %s", j, i, d(descriptor), d(measurement.Descriptor))
|
||||
}
|
||||
ft := fortyTwo(t, descriptor.ValueKind())
|
||||
if measurement.Value.RawCompare(ft.AsRaw(), descriptor.ValueKind()) != 0 {
|
||||
t.Errorf("Wrong recorded value in measurement %d in batch %d, expected %s, got %s", j, i, ft.Emit(descriptor.ValueKind()), measurement.Value.Emit(descriptor.ValueKind()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fortyTwo(t *testing.T, kind ValueKind) MeasurementValue {
|
||||
switch kind {
|
||||
case Int64ValueKind:
|
||||
return NewInt64MeasurementValue(42)
|
||||
case Float64ValueKind:
|
||||
return NewFloat64MeasurementValue(42)
|
||||
}
|
||||
t.Errorf("Invalid value kind %q", kind)
|
||||
return NewInt64MeasurementValue(0)
|
||||
}
|
@@ -23,43 +23,48 @@ var (
|
||||
descriptorID uint64
|
||||
)
|
||||
|
||||
// TODO: Maybe unexport that and document very _very_ clearly, that
|
||||
// you can still get a descriptor with NewInt64Counter(…).Descriptor
|
||||
|
||||
// CommonMetric holds a descriptor. It is used mostly to implement the
|
||||
// common parts for every metric kind.
|
||||
type CommonMetric struct {
|
||||
*Descriptor
|
||||
type commonMetric struct {
|
||||
d *Descriptor
|
||||
}
|
||||
|
||||
func (m CommonMetric) getHandle(labels LabelSet) Handle {
|
||||
return labels.Meter().NewHandle(m.Descriptor, labels)
|
||||
var _ ExplicitReportingMetric = commonMetric{}
|
||||
|
||||
func (m commonMetric) Descriptor() *Descriptor {
|
||||
return m.d
|
||||
}
|
||||
|
||||
func (m CommonMetric) float64Measurement(value float64) Measurement {
|
||||
func (m commonMetric) SupportHandle() hiddenType {
|
||||
return hiddenType{}
|
||||
}
|
||||
|
||||
func (m commonMetric) getHandle(labels LabelSet) Handle {
|
||||
return labels.Meter().NewHandle(m, labels)
|
||||
}
|
||||
|
||||
func (m commonMetric) float64Measurement(value float64) Measurement {
|
||||
return Measurement{
|
||||
Descriptor: m.Descriptor,
|
||||
Descriptor: m.d,
|
||||
Value: NewFloat64MeasurementValue(value),
|
||||
}
|
||||
}
|
||||
|
||||
func (m CommonMetric) int64Measurement(value int64) Measurement {
|
||||
func (m commonMetric) int64Measurement(value int64) Measurement {
|
||||
return Measurement{
|
||||
Descriptor: m.Descriptor,
|
||||
Descriptor: m.d,
|
||||
Value: NewInt64MeasurementValue(value),
|
||||
}
|
||||
}
|
||||
|
||||
func (m CommonMetric) recordOne(ctx context.Context, value MeasurementValue, labels LabelSet) {
|
||||
func (m commonMetric) recordOne(ctx context.Context, value MeasurementValue, labels LabelSet) {
|
||||
labels.Meter().RecordBatch(ctx, labels, Measurement{
|
||||
Descriptor: m.Descriptor,
|
||||
Descriptor: m.d,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
func registerCommonMetric(name string, kind Kind, valueKind ValueKind) CommonMetric {
|
||||
return CommonMetric{
|
||||
Descriptor: registerDescriptor(name, kind, valueKind),
|
||||
func registerCommonMetric(name string, kind Kind, valueKind ValueKind) commonMetric {
|
||||
return commonMetric{
|
||||
d: registerDescriptor(name, kind, valueKind),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,12 +20,12 @@ import (
|
||||
|
||||
// Float64Counter is a metric that accumulates float64 values.
|
||||
type Float64Counter struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Int64Counter is a metric that accumulates int64 values.
|
||||
type Int64Counter struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Float64CounterHandle is a handle for Float64Counter.
|
||||
@@ -50,43 +50,55 @@ type counterOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
var _ CounterOptionApplier = counterOptionWrapper{}
|
||||
var (
|
||||
_ CounterOptionApplier = counterOptionWrapper{}
|
||||
_ ExplicitReportingMetric = Float64Counter{}
|
||||
_ ExplicitReportingMetric = Int64Counter{}
|
||||
)
|
||||
|
||||
func (o counterOptionWrapper) ApplyCounterOption(d *Descriptor) {
|
||||
o.F(d)
|
||||
}
|
||||
|
||||
func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) CommonMetric {
|
||||
func newCounter(name string, valueKind ValueKind, mos ...CounterOptionApplier) commonMetric {
|
||||
m := registerCommonMetric(name, CounterKind, valueKind)
|
||||
for _, opt := range mos {
|
||||
opt.ApplyCounterOption(m.Descriptor)
|
||||
opt.ApplyCounterOption(m.Descriptor())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewFloat64Counter creates a new counter for float64.
|
||||
func NewFloat64Counter(name string, mos ...CounterOptionApplier) (c Float64Counter) {
|
||||
c.CommonMetric = newCounter(name, Float64ValueKind, mos...)
|
||||
c.commonMetric = newCounter(name, Float64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// NewInt64Counter creates a new counter for int64.
|
||||
func NewInt64Counter(name string, mos ...CounterOptionApplier) (c Int64Counter) {
|
||||
c.CommonMetric = newCounter(name, Int64ValueKind, mos...)
|
||||
c.commonMetric = newCounter(name, Int64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this counter. The labels should
|
||||
// contain the keys and values specified in the counter with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the counter
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// counter with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Float64Counter) GetHandle(labels LabelSet) (h Float64CounterHandle) {
|
||||
h.Handle = c.getHandle(labels)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this counter. The labels should
|
||||
// contain the keys and values specified in the counter with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the counter
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// counter with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Int64Counter) GetHandle(labels LabelSet) (h Int64CounterHandle) {
|
||||
h.Handle = c.getHandle(labels)
|
||||
return
|
||||
@@ -105,15 +117,23 @@ func (c *Int64Counter) Measurement(value int64) Measurement {
|
||||
}
|
||||
|
||||
// Add adds the value to the counter's sum. The labels should contain
|
||||
// the keys and values specified in the counter with the WithKeys
|
||||
// option.
|
||||
// the keys and values for each key specified in the counter with the
|
||||
// WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// counter with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Float64Counter) Add(ctx context.Context, value float64, labels LabelSet) {
|
||||
c.recordOne(ctx, NewFloat64MeasurementValue(value), labels)
|
||||
}
|
||||
|
||||
// Add adds the value to the counter's sum. The labels should contain
|
||||
// the keys and values specified in the counter with the WithKeys
|
||||
// option.
|
||||
// the keys and values for each key specified in the counter with the
|
||||
// WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// counter with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Int64Counter) Add(ctx context.Context, value int64, labels LabelSet) {
|
||||
c.recordOne(ctx, NewInt64MeasurementValue(value), labels)
|
||||
}
|
||||
|
@@ -20,12 +20,12 @@ import (
|
||||
|
||||
// Float64Gauge is a metric that stores the last float64 value.
|
||||
type Float64Gauge struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Int64Gauge is a metric that stores the last int64 value.
|
||||
type Int64Gauge struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Float64GaugeHandle is a handle for Float64Gauge.
|
||||
@@ -50,43 +50,55 @@ type gaugeOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
var _ GaugeOptionApplier = gaugeOptionWrapper{}
|
||||
var (
|
||||
_ GaugeOptionApplier = gaugeOptionWrapper{}
|
||||
_ ExplicitReportingMetric = Float64Gauge{}
|
||||
_ ExplicitReportingMetric = Int64Gauge{}
|
||||
)
|
||||
|
||||
func (o gaugeOptionWrapper) ApplyGaugeOption(d *Descriptor) {
|
||||
o.F(d)
|
||||
}
|
||||
|
||||
func newGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) CommonMetric {
|
||||
func newGauge(name string, valueKind ValueKind, mos ...GaugeOptionApplier) commonMetric {
|
||||
m := registerCommonMetric(name, GaugeKind, valueKind)
|
||||
for _, opt := range mos {
|
||||
opt.ApplyGaugeOption(m.Descriptor)
|
||||
opt.ApplyGaugeOption(m.Descriptor())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewFloat64Gauge creates a new gauge for float64.
|
||||
func NewFloat64Gauge(name string, mos ...GaugeOptionApplier) (g Float64Gauge) {
|
||||
g.CommonMetric = newGauge(name, Float64ValueKind, mos...)
|
||||
g.commonMetric = newGauge(name, Float64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// NewInt64Gauge creates a new gauge for int64.
|
||||
func NewInt64Gauge(name string, mos ...GaugeOptionApplier) (g Int64Gauge) {
|
||||
g.CommonMetric = newGauge(name, Int64ValueKind, mos...)
|
||||
g.commonMetric = newGauge(name, Int64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this gauge. The labels should
|
||||
// contain the keys and values specified in the gauge with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the gauge
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g *Float64Gauge) GetHandle(labels LabelSet) (h Float64GaugeHandle) {
|
||||
h.Handle = g.getHandle(labels)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this gauge. The labels should
|
||||
// contain the keys and values specified in the gauge with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the gauge
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g *Int64Gauge) GetHandle(labels LabelSet) (h Int64GaugeHandle) {
|
||||
h.Handle = g.getHandle(labels)
|
||||
return
|
||||
@@ -104,26 +116,34 @@ func (g *Int64Gauge) Measurement(value int64) Measurement {
|
||||
return g.int64Measurement(value)
|
||||
}
|
||||
|
||||
// Set sets the value of the gauge to the passed value. The labels
|
||||
// should contain the keys and values specified in the gauge with the
|
||||
// WithKeys option.
|
||||
// Set assigns the passed value to the value of the gauge. The labels
|
||||
// should contain the keys and values for each key specified in the
|
||||
// gauge with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g *Float64Gauge) Set(ctx context.Context, value float64, labels LabelSet) {
|
||||
g.recordOne(ctx, NewFloat64MeasurementValue(value), labels)
|
||||
}
|
||||
|
||||
// Set sets the value of the gauge to the passed value. The labels
|
||||
// should contain the keys and values specified in the gauge with the
|
||||
// WithKeys option.
|
||||
// Set assigns the passed value to the value of the gauge. The labels
|
||||
// should contain the keys and values for each key specified in the
|
||||
// gauge with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// gauge with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (g *Int64Gauge) Set(ctx context.Context, value int64, labels LabelSet) {
|
||||
g.recordOne(ctx, NewInt64MeasurementValue(value), labels)
|
||||
}
|
||||
|
||||
// Set sets the value of the gauge to the passed value.
|
||||
// Set assigns the passed value to the value of the gauge.
|
||||
func (h *Float64GaugeHandle) Set(ctx context.Context, value float64) {
|
||||
h.RecordOne(ctx, NewFloat64MeasurementValue(value))
|
||||
}
|
||||
|
||||
// Set sets the value of the gauge to the passed value.
|
||||
// Set assigns the passed value to the value of the gauge.
|
||||
func (h *Int64GaugeHandle) Set(ctx context.Context, value int64) {
|
||||
h.RecordOne(ctx, NewInt64MeasurementValue(value))
|
||||
}
|
||||
|
33
api/metric/global_test.go
Normal file
33
api/metric/global_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metric
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGlobalMeter(t *testing.T) {
|
||||
m := GlobalMeter()
|
||||
if _, ok := m.(noopMeter); !ok {
|
||||
t.Errorf("Expected global meter to be a noopMeter instance, got an instance of %T", m)
|
||||
}
|
||||
|
||||
SetGlobalMeter(newMockMeter())
|
||||
|
||||
m = GlobalMeter()
|
||||
if _, ok := m.(*mockMeter); !ok {
|
||||
t.Errorf("Expected global meter to be a *mockMetric.MockMeter instance, got an instance of %T", m)
|
||||
}
|
||||
}
|
@@ -20,12 +20,12 @@ import (
|
||||
|
||||
// Float64Measure is a metric that records float64 values.
|
||||
type Float64Measure struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Int64Measure is a metric that records int64 values.
|
||||
type Int64Measure struct {
|
||||
CommonMetric
|
||||
commonMetric
|
||||
}
|
||||
|
||||
// Float64MeasureHandle is a handle for Float64Measure.
|
||||
@@ -50,43 +50,55 @@ type measureOptionWrapper struct {
|
||||
F Option
|
||||
}
|
||||
|
||||
var _ MeasureOptionApplier = measureOptionWrapper{}
|
||||
var (
|
||||
_ MeasureOptionApplier = measureOptionWrapper{}
|
||||
_ ExplicitReportingMetric = Float64Measure{}
|
||||
_ ExplicitReportingMetric = Int64Measure{}
|
||||
)
|
||||
|
||||
func (o measureOptionWrapper) ApplyMeasureOption(d *Descriptor) {
|
||||
o.F(d)
|
||||
}
|
||||
|
||||
func newMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) CommonMetric {
|
||||
func newMeasure(name string, valueKind ValueKind, mos ...MeasureOptionApplier) commonMetric {
|
||||
m := registerCommonMetric(name, MeasureKind, valueKind)
|
||||
for _, opt := range mos {
|
||||
opt.ApplyMeasureOption(m.Descriptor)
|
||||
opt.ApplyMeasureOption(m.Descriptor())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewFloat64Measure creates a new measure for float64.
|
||||
func NewFloat64Measure(name string, mos ...MeasureOptionApplier) (c Float64Measure) {
|
||||
c.CommonMetric = newMeasure(name, Float64ValueKind, mos...)
|
||||
c.commonMetric = newMeasure(name, Float64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// NewInt64Measure creates a new measure for int64.
|
||||
func NewInt64Measure(name string, mos ...MeasureOptionApplier) (c Int64Measure) {
|
||||
c.CommonMetric = newMeasure(name, Int64ValueKind, mos...)
|
||||
c.commonMetric = newMeasure(name, Int64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this measure. The labels should
|
||||
// contain the keys and values specified in the measure with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the measure
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// measure with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Float64Measure) GetHandle(labels LabelSet) (h Float64MeasureHandle) {
|
||||
h.Handle = c.getHandle(labels)
|
||||
return
|
||||
}
|
||||
|
||||
// GetHandle creates a handle for this measure. The labels should
|
||||
// contain the keys and values specified in the measure with the
|
||||
// WithKeys option.
|
||||
// contain the keys and values for each key specified in the measure
|
||||
// with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// measure with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Int64Measure) GetHandle(labels LabelSet) (h Int64MeasureHandle) {
|
||||
h.Handle = c.getHandle(labels)
|
||||
return
|
||||
@@ -105,15 +117,23 @@ func (c *Int64Measure) Measurement(value int64) Measurement {
|
||||
}
|
||||
|
||||
// Record adds a new value to the list of measure's records. The
|
||||
// labels should contain the keys and values specified in the measure
|
||||
// with the WithKeys option.
|
||||
// labels should contain the keys and values for each key specified in
|
||||
// the measure with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// measure with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Float64Measure) Record(ctx context.Context, value float64, labels LabelSet) {
|
||||
c.recordOne(ctx, NewFloat64MeasurementValue(value), labels)
|
||||
}
|
||||
|
||||
// Record adds a new value to the list of measure's records. The
|
||||
// labels should contain the keys and values specified in the measure
|
||||
// with the WithKeys option.
|
||||
// labels should contain the keys and values for each key specified in
|
||||
// the measure with the WithKeys option.
|
||||
//
|
||||
// If the labels do not contain a value for the key specified in the
|
||||
// measure with the WithKeys option, then the missing value will be
|
||||
// treated as unspecified.
|
||||
func (c *Int64Measure) Record(ctx context.Context, value int64, labels LabelSet) {
|
||||
c.recordOne(ctx, NewInt64MeasurementValue(value), labels)
|
||||
}
|
||||
|
141
api/metric/mock_test.go
Normal file
141
api/metric/mock_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metric
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
type (
|
||||
mockHandle struct {
|
||||
descriptor *Descriptor
|
||||
labelSet *mockLabelSet
|
||||
}
|
||||
|
||||
mockLabelSet struct {
|
||||
meter *mockMeter
|
||||
labels map[core.Key]core.Value
|
||||
}
|
||||
|
||||
batch struct {
|
||||
ctx context.Context
|
||||
labelSet *mockLabelSet
|
||||
measurements []Measurement
|
||||
}
|
||||
|
||||
observerData struct {
|
||||
observer Observer
|
||||
callback ObserverCallback
|
||||
}
|
||||
|
||||
observerMap map[DescriptorID]observerData
|
||||
|
||||
mockMeter struct {
|
||||
measurementBatches []batch
|
||||
observers observerMap
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
_ Handle = &mockHandle{}
|
||||
_ LabelSet = &mockLabelSet{}
|
||||
_ Meter = &mockMeter{}
|
||||
)
|
||||
|
||||
func (h *mockHandle) RecordOne(ctx context.Context, value MeasurementValue) {
|
||||
h.labelSet.meter.RecordBatch(ctx, h.labelSet, Measurement{
|
||||
Descriptor: h.descriptor,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *mockLabelSet) Meter() Meter {
|
||||
return s.meter
|
||||
}
|
||||
|
||||
func newMockMeter() *mockMeter {
|
||||
return &mockMeter{}
|
||||
}
|
||||
|
||||
func (m *mockMeter) DefineLabels(ctx context.Context, labels ...core.KeyValue) LabelSet {
|
||||
ul := make(map[core.Key]core.Value)
|
||||
for _, kv := range labels {
|
||||
ul[kv.Key] = kv.Value
|
||||
}
|
||||
return &mockLabelSet{
|
||||
meter: m,
|
||||
labels: ul,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockMeter) RecordBatch(ctx context.Context, labels LabelSet, measurements ...Measurement) {
|
||||
ourLabelSet := labels.(*mockLabelSet)
|
||||
m.measurementBatches = append(m.measurementBatches, batch{
|
||||
ctx: ctx,
|
||||
labelSet: ourLabelSet,
|
||||
measurements: measurements,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *mockMeter) NewHandle(erm ExplicitReportingMetric, labels LabelSet) Handle {
|
||||
descriptor := erm.Descriptor()
|
||||
ourLabels := labels.(*mockLabelSet)
|
||||
|
||||
return &mockHandle{
|
||||
descriptor: descriptor,
|
||||
labelSet: ourLabels,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockMeter) DeleteHandle(Handle) {
|
||||
}
|
||||
|
||||
func (m *mockMeter) RegisterObserver(o Observer, cb ObserverCallback) {
|
||||
id := o.Descriptor().ID()
|
||||
if _, ok := m.observers[id]; ok {
|
||||
return
|
||||
}
|
||||
data := observerData{
|
||||
observer: o,
|
||||
callback: cb,
|
||||
}
|
||||
if m.observers == nil {
|
||||
m.observers = observerMap{
|
||||
id: data,
|
||||
}
|
||||
} else {
|
||||
m.observers[id] = data
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockMeter) UnregisterObserver(o Observer) {
|
||||
delete(m.observers, o.Descriptor().ID())
|
||||
}
|
||||
|
||||
func (m *mockMeter) PerformObservations() {
|
||||
for _, data := range m.observers {
|
||||
o := data.observer
|
||||
descriptor := o.Descriptor()
|
||||
ocb := func(l LabelSet, v MeasurementValue) {
|
||||
m.RecordBatch(context.Background(), l, Measurement{
|
||||
Descriptor: descriptor,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
data.callback(m, o, ocb)
|
||||
}
|
||||
}
|
@@ -25,7 +25,7 @@ func (noopMeter) DefineLabels(context.Context, ...core.KeyValue) LabelSet {
|
||||
return noopLabelSet{}
|
||||
}
|
||||
|
||||
func (noopMeter) NewHandle(*Descriptor, LabelSet) Handle {
|
||||
func (noopMeter) NewHandle(ExplicitReportingMetric, LabelSet) Handle {
|
||||
return noopHandle{}
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,7 @@ package metric
|
||||
|
||||
// Observer is a base of typed-observers. Shouldn't be used directly.
|
||||
type Observer struct {
|
||||
*Descriptor
|
||||
d *Descriptor
|
||||
}
|
||||
|
||||
// Float64Observer is an observer that reports float64 values.
|
||||
@@ -30,9 +30,9 @@ type Int64Observer struct {
|
||||
}
|
||||
|
||||
func newObserver(name string, valueKind ValueKind, mos ...GaugeOptionApplier) (o Observer) {
|
||||
o.Descriptor = registerDescriptor(name, ObserverKind, valueKind)
|
||||
o.d = registerDescriptor(name, ObserverKind, valueKind)
|
||||
for _, opt := range mos {
|
||||
opt.ApplyGaugeOption(o.Descriptor)
|
||||
opt.ApplyGaugeOption(o.d)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -48,3 +48,7 @@ func NewInt64Observer(name string, mos ...GaugeOptionApplier) (o Int64Observer)
|
||||
o.Observer = newObserver(name, Int64ValueKind, mos...)
|
||||
return
|
||||
}
|
||||
|
||||
func (o Observer) Descriptor() *Descriptor {
|
||||
return o.d
|
||||
}
|
||||
|
@@ -60,11 +60,11 @@ func (s *sdk) DefineLabels(ctx context.Context, labels ...core.KeyValue) metric.
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sdk) NewHandle(descriptor *metric.Descriptor, labels metric.LabelSet) metric.Handle {
|
||||
func (s *sdk) NewHandle(erm metric.ExplicitReportingMetric, labels metric.LabelSet) metric.Handle {
|
||||
mlabels, _ := labels.(metricLabels)
|
||||
|
||||
return &metricHandle{
|
||||
descriptor: descriptor,
|
||||
descriptor: erm.Descriptor(),
|
||||
labels: mlabels,
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (s *sdk) insertNewObserver(observer metric.Observer, callback metric.Observ
|
||||
s.observersLock.Lock()
|
||||
defer s.observersLock.Unlock()
|
||||
old := s.loadObserversMap()
|
||||
id := observer.Descriptor.ID()
|
||||
id := observer.Descriptor().ID()
|
||||
if _, ok := old[id]; ok {
|
||||
return false
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func (s *sdk) UnregisterObserver(observer metric.Observer) {
|
||||
s.observersLock.Lock()
|
||||
defer s.observersLock.Unlock()
|
||||
old := s.loadObserversMap()
|
||||
id := observer.Descriptor.ID()
|
||||
id := observer.Descriptor().ID()
|
||||
if _, ok := old[id]; !ok {
|
||||
return
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func (s *sdk) observersRoutine() {
|
||||
return
|
||||
}
|
||||
for _, data := range m {
|
||||
ocb := s.getObservationCallback(data.observer.Descriptor)
|
||||
ocb := s.getObservationCallback(data.observer.Descriptor())
|
||||
data.callback(s, data.observer, ocb)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user