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/instrument.go
Tyler Yahn 2e780d8e39
Add View, NewView, Instrument, Stream, and InstrumentKind to sdk/metric with unit tests (#3459)
* Add the InstrumentKind type and vars to sdk/metric

* Add the Instrument type to sdk/metric

* Add the Stream type to sdk/metric

* Add the View type to sdk/metric

* Add NewView to create Views matching OTel spec

* Add unit tests for NewView

* Add changes to changelog

* Apply suggestions from code review

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Update CHANGELOG.md

* Update match and mask comments of Instrument

* Explain wildcard logic in NewView with comment

* Drop views that replace name for multi-inst match

* Comment how users are expected to match zero-vals

* Remove InstrumentKind and Scope from Stream

* Fix redundant word in NewView comment

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
Co-authored-by: Chester Cheung <cheung.zhy.csu@gmail.com>
2022-11-16 08:18:02 -08:00

220 lines
8.0 KiB
Go

// Copyright The 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 "go.opentelemetry.io/otel/sdk/metric"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
"go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregation"
"go.opentelemetry.io/otel/sdk/metric/internal"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
)
var (
zeroUnit unit.Unit
zeroInstrumentKind InstrumentKind
zeroScope instrumentation.Scope
)
// InstrumentKind is the identifier of a group of instruments that all
// performing the same function.
type InstrumentKind uint8
const (
// instrumentKindUndefined is an undefined instrument kind, it should not
// be used by any initialized type.
instrumentKindUndefined InstrumentKind = iota // nolint:deadcode,varcheck,unused
// InstrumentKindSyncCounter identifies a group of instruments that record
// increasing values synchronously with the code path they are measuring.
InstrumentKindSyncCounter
// InstrumentKindSyncUpDownCounter identifies a group of instruments that
// record increasing and decreasing values synchronously with the code path
// they are measuring.
InstrumentKindSyncUpDownCounter
// InstrumentKindSyncHistogram identifies a group of instruments that
// record a distribution of values synchronously with the code path they
// are measuring.
InstrumentKindSyncHistogram
// InstrumentKindAsyncCounter identifies a group of instruments that record
// increasing values in an asynchronous callback.
InstrumentKindAsyncCounter
// InstrumentKindAsyncUpDownCounter identifies a group of instruments that
// record increasing and decreasing values in an asynchronous callback.
InstrumentKindAsyncUpDownCounter
// InstrumentKindAsyncGauge identifies a group of instruments that record
// current values in an asynchronous callback.
InstrumentKindAsyncGauge
)
type nonComparable [0]func() // nolint: unused // This is indeed used.
// Instrument describes properties an instrument is created with.
type Instrument struct {
// Name is the human-readable identifier of the instrument.
Name string
// Description describes the purpose of the instrument.
Description string
// Kind defines the functional group of the instrument.
Kind InstrumentKind
// Unit is the unit of measurement recorded by the instrument.
Unit unit.Unit
// Scope identifies the instrumentation that created the instrument.
Scope instrumentation.Scope
// Ensure forward compatibility if non-comparable fields need to be added.
nonComparable // nolint: unused
}
// empty returns if all fields of i are their zero-value.
func (i Instrument) empty() bool {
return i.Name == "" &&
i.Description == "" &&
i.Kind == zeroInstrumentKind &&
i.Unit == zeroUnit &&
i.Scope == zeroScope
}
// matches returns whether all the non-zero-value fields of i match the
// corresponding fields of other. If i is empty it will match all other, and
// true will always be returned.
func (i Instrument) matches(other Instrument) bool {
return i.matchesName(other) &&
i.matchesDescription(other) &&
i.matchesKind(other) &&
i.matchesUnit(other) &&
i.matchesScope(other)
}
// matchesName returns true if the Name of i is "" or it equals the Name of
// other, otherwise false.
func (i Instrument) matchesName(other Instrument) bool {
return i.Name == "" || i.Name == other.Name
}
// matchesDescription returns true if the Description of i is "" or it equals
// the Description of other, otherwise false.
func (i Instrument) matchesDescription(other Instrument) bool {
return i.Description == "" || i.Description == other.Description
}
// matchesKind returns true if the Kind of i is its zero-value or it equals the
// Kind of other, otherwise false.
func (i Instrument) matchesKind(other Instrument) bool {
return i.Kind == zeroInstrumentKind || i.Kind == other.Kind
}
// matchesUnit returns true if the Unit of i is its zero-value or it equals the
// Unit of other, otherwise false.
func (i Instrument) matchesUnit(other Instrument) bool {
return i.Unit == zeroUnit || i.Unit == other.Unit
}
// matchesScope returns true if the Scope of i is its zero-value or it equals
// the Scope of other, otherwise false.
func (i Instrument) matchesScope(other Instrument) bool {
return (i.Scope.Name == "" || i.Scope.Name == other.Scope.Name) &&
(i.Scope.Version == "" || i.Scope.Version == other.Scope.Version) &&
(i.Scope.SchemaURL == "" || i.Scope.SchemaURL == other.Scope.SchemaURL)
}
// Stream describes the stream of data an instrument produces.
type Stream struct {
// Name is the human-readable identifier of the stream.
Name string
// Description describes the purpose of the data.
Description string
// Unit is the unit of measurement recorded.
Unit unit.Unit
// Aggregation the stream uses for an instrument.
Aggregation aggregation.Aggregation
// AttributeFilter applied to all attributes recorded for an instrument.
AttributeFilter attribute.Filter
}
// instrumentID are the identifying properties of an instrument.
type instrumentID struct {
// Name is the name of the instrument.
Name string
// Description is the description of the instrument.
Description string
// Unit is the unit of the instrument.
Unit unit.Unit
// Aggregation is the aggregation data type of the instrument.
Aggregation string
// Monotonic is the monotonicity of an instruments data type. This field is
// not used for all data types, so a zero value needs to be understood in the
// context of Aggregation.
Monotonic bool
// Temporality is the temporality of an instrument's data type. This field
// is not used by some data types.
Temporality metricdata.Temporality
// Number is the number type of the instrument.
Number string
}
type instrumentImpl[N int64 | float64] struct {
instrument.Asynchronous
instrument.Synchronous
aggregators []internal.Aggregator[N]
}
var _ asyncfloat64.Counter = &instrumentImpl[float64]{}
var _ asyncfloat64.UpDownCounter = &instrumentImpl[float64]{}
var _ asyncfloat64.Gauge = &instrumentImpl[float64]{}
var _ asyncint64.Counter = &instrumentImpl[int64]{}
var _ asyncint64.UpDownCounter = &instrumentImpl[int64]{}
var _ asyncint64.Gauge = &instrumentImpl[int64]{}
var _ syncfloat64.Counter = &instrumentImpl[float64]{}
var _ syncfloat64.UpDownCounter = &instrumentImpl[float64]{}
var _ syncfloat64.Histogram = &instrumentImpl[float64]{}
var _ syncint64.Counter = &instrumentImpl[int64]{}
var _ syncint64.UpDownCounter = &instrumentImpl[int64]{}
var _ syncint64.Histogram = &instrumentImpl[int64]{}
func (i *instrumentImpl[N]) Observe(ctx context.Context, val N, attrs ...attribute.KeyValue) {
// Only record a value if this is being called from the MetricProvider.
_, ok := ctx.Value(produceKey).(struct{})
if !ok {
return
}
i.aggregate(ctx, val, attrs)
}
func (i *instrumentImpl[N]) Add(ctx context.Context, val N, attrs ...attribute.KeyValue) {
i.aggregate(ctx, val, attrs)
}
func (i *instrumentImpl[N]) Record(ctx context.Context, val N, attrs ...attribute.KeyValue) {
i.aggregate(ctx, val, attrs)
}
func (i *instrumentImpl[N]) aggregate(ctx context.Context, val N, attrs []attribute.KeyValue) {
if err := ctx.Err(); err != nil {
return
}
for _, agg := range i.aggregators {
agg.Aggregate(val, attribute.NewSet(attrs...))
}
}