1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2024-12-12 10:04:29 +02:00
opentelemetry-go/sdk/metric/aggregator/lastvalue/lastvalue.go
Joshua MacDonald 9674c81cb7
Drop the gauge instrument (#537)
* drop gauge instrument

* Restore the benchmark and stress test for lastvalue aggregator, but remove monotonic last-value support

* Rename gauge->lastvalue and remove remaining uses of the word 'gauge'

Co-authored-by: Krzesimir Nowak <krzesimir@kinvolk.io>
2020-03-10 16:00:37 -07:00

115 lines
3.5 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 lastvalue // import "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
import (
"context"
"sync/atomic"
"time"
"unsafe"
"go.opentelemetry.io/otel/api/core"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
)
type (
// Aggregator aggregates lastValue events.
Aggregator struct {
// current is an atomic pointer to *lastValueData. It is never nil.
current unsafe.Pointer
// checkpoint is a copy of the current value taken in Checkpoint()
checkpoint unsafe.Pointer
}
// lastValueData stores the current value of a lastValue along with
// a sequence number to determine the winner of a race.
lastValueData struct {
// value is the int64- or float64-encoded Set() data
//
// value needs to be aligned for 64-bit atomic operations.
value core.Number
// timestamp indicates when this record was submitted.
// this can be used to pick a winner when multiple
// records contain lastValue data for the same labels due
// to races.
timestamp time.Time
}
)
var _ export.Aggregator = &Aggregator{}
var _ aggregator.LastValue = &Aggregator{}
// An unset lastValue has zero timestamp and zero value.
var unsetLastValue = &lastValueData{}
// New returns a new lastValue aggregator. This aggregator retains the
// last value and timestamp that were recorded.
func New() *Aggregator {
return &Aggregator{
current: unsafe.Pointer(unsetLastValue),
checkpoint: unsafe.Pointer(unsetLastValue),
}
}
// LastValue returns the last-recorded lastValue value and the
// corresponding timestamp. The error value aggregator.ErrNoLastValue
// will be returned if (due to a race condition) the checkpoint was
// computed before the first value was set.
func (g *Aggregator) LastValue() (core.Number, time.Time, error) {
gd := (*lastValueData)(g.checkpoint)
if gd == unsetLastValue {
return core.Number(0), time.Time{}, aggregator.ErrNoLastValue
}
return gd.value.AsNumber(), gd.timestamp, nil
}
// Checkpoint atomically saves the current value.
func (g *Aggregator) Checkpoint(ctx context.Context, _ *export.Descriptor) {
g.checkpoint = atomic.LoadPointer(&g.current)
}
// Update atomically sets the current "last" value.
func (g *Aggregator) Update(_ context.Context, number core.Number, desc *export.Descriptor) error {
ngd := &lastValueData{
value: number,
timestamp: time.Now(),
}
atomic.StorePointer(&g.current, unsafe.Pointer(ngd))
return nil
}
// Merge combines state from two aggregators. The most-recently set
// value is chosen.
func (g *Aggregator) Merge(oa export.Aggregator, desc *export.Descriptor) error {
o, _ := oa.(*Aggregator)
if o == nil {
return aggregator.NewInconsistentMergeError(g, oa)
}
ggd := (*lastValueData)(atomic.LoadPointer(&g.checkpoint))
ogd := (*lastValueData)(atomic.LoadPointer(&o.checkpoint))
if ggd.timestamp.After(ogd.timestamp) {
return nil
}
g.checkpoint = unsafe.Pointer(ogd)
return nil
}