1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2024-12-14 10:13:10 +02:00
opentelemetry-go/sdk/metric/correct_test.go
Joshua MacDonald 9878f3b700 Metrics stdout export pipeline (#265)
* Add MetricAggregator.Merge() implementations

* Update from feedback

* Type

* Ckpt

* Ckpt

* Add push controller

* Ckpt

* Add aggregator interfaces, stdout encoder

* Modify basic main.go

* Main is working

* Batch stdout output

* Sum udpate

* Rename stdout

* Add stateless/stateful Batcher options

* Undo a for-loop in the example, remove a done TODO

* Update imports

* Add note

* Rename defaultkeys

* Support variable label encoder to speed OpenMetrics/Statsd export

* Lint

* Doc

* Precommit/lint

* Simplify Aggregator API

* Record->Identifier

* Remove export.Record a.k.a. Identifier

* Checkpoint

* Propagate errors to the SDK, remove a bunch of 'TODO warn'

* Checkpoint

* Introduce export.Labels

* Comments in export/metric.go

* Comment

* More merge

* More doc

* Complete example

* Lint fixes

* Add a testable example

* Lint

* Let Export return an error

* add a basic stdout exporter test

* Add measure test; fix aggregator APIs

* Use JSON numbers, not strings

* Test stdout exporter error

* Add a test for the call to RangeTest

* Add error handler API to improve correctness test; return errors from RecordOne

* Undo the previous -- do not expose errors

* Add simple selector variations, test

* Repair examples

* Test push controller error handling

* Add SDK label encoder tests

* Add a defaultkeys batcher test

* Add an ungrouped batcher test

* Lint new tests

* Respond to krnowak's feedback

* Undo comment

* Use concrete receivers for export records and labels, since the constructors return structs not pointers

* Bug fix for stateful batchers; clone an aggregator for long term storage

* Remove TODO addressed in #318

* Add errors to all aggregator interfaces

* Handle ErrNoLastValue case in stdout exporter

* Move aggregator API into sdk/export/metric/aggregator

* Update all aggregator exported-method comments

* Document the aggregator APIs

* More aggregator comments

* Add multiple updates to the ungrouped test

* Fixes for feedback from Gustavo and Liz

* Producer->CheckpointSet; add FinishedCollection

* Process takes an export.Record

* ReadCheckpoint->CheckpointSet

* EncodeLabels->Encode

* Format a better inconsistent type error; add more aggregator API tests

* More RangeTest test coverage

* Make benbjohnson/clock a test-only dependency

* Handle ErrNoLastValue in stress_test
2019-11-15 13:01:20 -08:00

195 lines
4.8 KiB
Go

// 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_test
import (
"context"
"fmt"
"math"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
)
type correctnessBatcher struct {
t *testing.T
agg export.Aggregator
records []export.Record
}
type testLabelEncoder struct{}
func (cb *correctnessBatcher) AggregatorFor(*export.Descriptor) export.Aggregator {
return cb.agg
}
func (cb *correctnessBatcher) CheckpointSet() export.CheckpointSet {
cb.t.Fatal("Should not be called")
return nil
}
func (*correctnessBatcher) FinishedCollection() {
}
func (cb *correctnessBatcher) Process(_ context.Context, record export.Record) error {
cb.records = append(cb.records, record)
return nil
}
func (testLabelEncoder) Encode(labels []core.KeyValue) string {
return fmt.Sprint(labels)
}
func TestInputRangeTestCounter(t *testing.T) {
ctx := context.Background()
cagg := counter.New()
batcher := &correctnessBatcher{
t: t,
agg: cagg,
}
sdk := sdk.New(batcher, sdk.DefaultLabelEncoder())
var sdkErr error
sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr
})
counter := sdk.NewInt64Counter("counter.name", metric.WithMonotonic(true))
counter.Add(ctx, -1, sdk.Labels())
require.Equal(t, aggregator.ErrNegativeInput, sdkErr)
sdkErr = nil
sdk.Collect(ctx)
sum, err := cagg.Sum()
require.Equal(t, int64(0), sum.AsInt64())
require.Nil(t, err)
counter.Add(ctx, 1, sdk.Labels())
checkpointed := sdk.Collect(ctx)
sum, err = cagg.Sum()
require.Equal(t, int64(1), sum.AsInt64())
require.Equal(t, 1, checkpointed)
require.Nil(t, err)
require.Nil(t, sdkErr)
}
func TestInputRangeTestMeasure(t *testing.T) {
ctx := context.Background()
magg := array.New()
batcher := &correctnessBatcher{
t: t,
agg: magg,
}
sdk := sdk.New(batcher, sdk.DefaultLabelEncoder())
var sdkErr error
sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr
})
measure := sdk.NewFloat64Measure("measure.name", metric.WithAbsolute(true))
measure.Record(ctx, -1, sdk.Labels())
require.Equal(t, aggregator.ErrNegativeInput, sdkErr)
sdkErr = nil
sdk.Collect(ctx)
count, err := magg.Count()
require.Equal(t, int64(0), count)
require.Nil(t, err)
measure.Record(ctx, 1, sdk.Labels())
measure.Record(ctx, 2, sdk.Labels())
checkpointed := sdk.Collect(ctx)
count, err = magg.Count()
require.Equal(t, int64(2), count)
require.Equal(t, 1, checkpointed)
require.Nil(t, sdkErr)
require.Nil(t, err)
}
func TestDisabledInstrument(t *testing.T) {
ctx := context.Background()
batcher := &correctnessBatcher{
t: t,
agg: nil,
}
sdk := sdk.New(batcher, sdk.DefaultLabelEncoder())
measure := sdk.NewFloat64Measure("measure.name", metric.WithAbsolute(true))
measure.Record(ctx, -1, sdk.Labels())
checkpointed := sdk.Collect(ctx)
require.Equal(t, 0, checkpointed)
}
func TestRecordNaN(t *testing.T) {
ctx := context.Background()
batcher := &correctnessBatcher{
t: t,
agg: gauge.New(),
}
sdk := sdk.New(batcher, sdk.DefaultLabelEncoder())
var sdkErr error
sdk.SetErrorHandler(func(handleErr error) {
sdkErr = handleErr
})
g := sdk.NewFloat64Gauge("gauge.name")
require.Nil(t, sdkErr)
g.Set(ctx, math.NaN(), sdk.Labels())
require.Error(t, sdkErr)
}
func TestSDKLabelEncoder(t *testing.T) {
ctx := context.Background()
cagg := counter.New()
batcher := &correctnessBatcher{
t: t,
agg: cagg,
}
sdk := sdk.New(batcher, testLabelEncoder{})
measure := sdk.NewFloat64Measure("measure")
measure.Record(ctx, 1, sdk.Labels(key.String("A", "B"), key.String("C", "D")))
sdk.Collect(ctx)
require.Equal(t, 1, len(batcher.records))
labels := batcher.records[0].Labels()
require.Equal(t, `[{A {8 0 B}} {C {8 0 D}}]`, labels.Encoded())
}
func TestDefaultLabelEncoder(t *testing.T) {
encoder := sdk.DefaultLabelEncoder()
encoded := encoder.Encode([]core.KeyValue{key.String("A", "B"), key.String("C", "D")})
require.Equal(t, `A=B,C=D`, encoded)
}