You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			348 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| // SPDX-License-Identifier: Apache-2.0
 | |
| 
 | |
| //go:generate stringer -type=InstrumentKind -trimprefix=InstrumentKind
 | |
| 
 | |
| package metric // import "go.opentelemetry.io/otel/sdk/metric"
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"go.opentelemetry.io/otel/attribute"
 | |
| 	"go.opentelemetry.io/otel/metric"
 | |
| 	"go.opentelemetry.io/otel/metric/embedded"
 | |
| 	"go.opentelemetry.io/otel/sdk/instrumentation"
 | |
| 	"go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
 | |
| )
 | |
| 
 | |
| var 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 = 0 // nolint:deadcode,varcheck,unused
 | |
| 	// InstrumentKindCounter identifies a group of instruments that record
 | |
| 	// increasing values synchronously with the code path they are measuring.
 | |
| 	InstrumentKindCounter InstrumentKind = 1
 | |
| 	// InstrumentKindUpDownCounter identifies a group of instruments that
 | |
| 	// record increasing and decreasing values synchronously with the code path
 | |
| 	// they are measuring.
 | |
| 	InstrumentKindUpDownCounter InstrumentKind = 2
 | |
| 	// InstrumentKindHistogram identifies a group of instruments that record a
 | |
| 	// distribution of values synchronously with the code path they are
 | |
| 	// measuring.
 | |
| 	InstrumentKindHistogram InstrumentKind = 3
 | |
| 	// InstrumentKindObservableCounter identifies a group of instruments that
 | |
| 	// record increasing values in an asynchronous callback.
 | |
| 	InstrumentKindObservableCounter InstrumentKind = 4
 | |
| 	// InstrumentKindObservableUpDownCounter identifies a group of instruments
 | |
| 	// that record increasing and decreasing values in an asynchronous
 | |
| 	// callback.
 | |
| 	InstrumentKindObservableUpDownCounter InstrumentKind = 5
 | |
| 	// InstrumentKindObservableGauge identifies a group of instruments that
 | |
| 	// record current values in an asynchronous callback.
 | |
| 	InstrumentKindObservableGauge InstrumentKind = 6
 | |
| 	// InstrumentKindGauge identifies a group of instruments that record
 | |
| 	// instantaneous values synchronously with the code path they are
 | |
| 	// measuring.
 | |
| 	InstrumentKindGauge InstrumentKind = 7
 | |
| )
 | |
| 
 | |
| 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 string
 | |
| 	// 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
 | |
| }
 | |
| 
 | |
| // IsEmpty returns if all Instrument fields are their zero-value.
 | |
| func (i Instrument) IsEmpty() bool {
 | |
| 	return i.Name == "" &&
 | |
| 		i.Description == "" &&
 | |
| 		i.Kind == instrumentKindUndefined &&
 | |
| 		i.Unit == "" &&
 | |
| 		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 == instrumentKindUndefined || 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 == "" || 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 string
 | |
| 	// Aggregation the stream uses for an instrument.
 | |
| 	Aggregation Aggregation
 | |
| 	// AttributeFilter is an attribute Filter applied to the attributes
 | |
| 	// recorded for an instrument's measurement. If the filter returns false
 | |
| 	// the attribute will not be recorded, otherwise, if it returns true, it
 | |
| 	// will record the attribute.
 | |
| 	//
 | |
| 	// Use NewAllowKeysFilter from "go.opentelemetry.io/otel/attribute" to
 | |
| 	// provide an allow-list of attribute keys here.
 | |
| 	AttributeFilter attribute.Filter
 | |
| }
 | |
| 
 | |
| // instID are the identifying properties of a instrument.
 | |
| type instID struct {
 | |
| 	// Name is the name of the stream.
 | |
| 	Name string
 | |
| 	// Description is the description of the stream.
 | |
| 	Description string
 | |
| 	// Kind defines the functional group of the instrument.
 | |
| 	Kind InstrumentKind
 | |
| 	// Unit is the unit of the stream.
 | |
| 	Unit string
 | |
| 	// Number is the number type of the stream.
 | |
| 	Number string
 | |
| }
 | |
| 
 | |
| // Returns a normalized copy of the instID i.
 | |
| //
 | |
| // Instrument names are considered case-insensitive. Standardize the instrument
 | |
| // name to always be lowercase for the returned instID so it can be compared
 | |
| // without the name casing affecting the comparison.
 | |
| func (i instID) normalize() instID {
 | |
| 	i.Name = strings.ToLower(i.Name)
 | |
| 	return i
 | |
| }
 | |
| 
 | |
| type int64Inst struct {
 | |
| 	measures []aggregate.Measure[int64]
 | |
| 
 | |
| 	embedded.Int64Counter
 | |
| 	embedded.Int64UpDownCounter
 | |
| 	embedded.Int64Histogram
 | |
| 	embedded.Int64Gauge
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ metric.Int64Counter       = (*int64Inst)(nil)
 | |
| 	_ metric.Int64UpDownCounter = (*int64Inst)(nil)
 | |
| 	_ metric.Int64Histogram     = (*int64Inst)(nil)
 | |
| 	_ metric.Int64Gauge         = (*int64Inst)(nil)
 | |
| )
 | |
| 
 | |
| func (i *int64Inst) Add(ctx context.Context, val int64, opts ...metric.AddOption) {
 | |
| 	c := metric.NewAddConfig(opts)
 | |
| 	i.aggregate(ctx, val, c.Attributes())
 | |
| }
 | |
| 
 | |
| func (i *int64Inst) Record(ctx context.Context, val int64, opts ...metric.RecordOption) {
 | |
| 	c := metric.NewRecordConfig(opts)
 | |
| 	i.aggregate(ctx, val, c.Attributes())
 | |
| }
 | |
| 
 | |
| func (i *int64Inst) aggregate(ctx context.Context, val int64, s attribute.Set) { // nolint:revive  // okay to shadow pkg with method.
 | |
| 	for _, in := range i.measures {
 | |
| 		in(ctx, val, s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type float64Inst struct {
 | |
| 	measures []aggregate.Measure[float64]
 | |
| 
 | |
| 	embedded.Float64Counter
 | |
| 	embedded.Float64UpDownCounter
 | |
| 	embedded.Float64Histogram
 | |
| 	embedded.Float64Gauge
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ metric.Float64Counter       = (*float64Inst)(nil)
 | |
| 	_ metric.Float64UpDownCounter = (*float64Inst)(nil)
 | |
| 	_ metric.Float64Histogram     = (*float64Inst)(nil)
 | |
| 	_ metric.Float64Gauge         = (*float64Inst)(nil)
 | |
| )
 | |
| 
 | |
| func (i *float64Inst) Add(ctx context.Context, val float64, opts ...metric.AddOption) {
 | |
| 	c := metric.NewAddConfig(opts)
 | |
| 	i.aggregate(ctx, val, c.Attributes())
 | |
| }
 | |
| 
 | |
| func (i *float64Inst) Record(ctx context.Context, val float64, opts ...metric.RecordOption) {
 | |
| 	c := metric.NewRecordConfig(opts)
 | |
| 	i.aggregate(ctx, val, c.Attributes())
 | |
| }
 | |
| 
 | |
| func (i *float64Inst) aggregate(ctx context.Context, val float64, s attribute.Set) {
 | |
| 	for _, in := range i.measures {
 | |
| 		in(ctx, val, s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // observablID is a comparable unique identifier of an observable.
 | |
| type observablID[N int64 | float64] struct {
 | |
| 	name        string
 | |
| 	description string
 | |
| 	kind        InstrumentKind
 | |
| 	unit        string
 | |
| 	scope       instrumentation.Scope
 | |
| }
 | |
| 
 | |
| type float64Observable struct {
 | |
| 	metric.Float64Observable
 | |
| 	*observable[float64]
 | |
| 
 | |
| 	embedded.Float64ObservableCounter
 | |
| 	embedded.Float64ObservableUpDownCounter
 | |
| 	embedded.Float64ObservableGauge
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ metric.Float64ObservableCounter       = float64Observable{}
 | |
| 	_ metric.Float64ObservableUpDownCounter = float64Observable{}
 | |
| 	_ metric.Float64ObservableGauge         = float64Observable{}
 | |
| )
 | |
| 
 | |
| func newFloat64Observable(m *meter, kind InstrumentKind, name, desc, u string) float64Observable {
 | |
| 	return float64Observable{
 | |
| 		observable: newObservable[float64](m, kind, name, desc, u),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type int64Observable struct {
 | |
| 	metric.Int64Observable
 | |
| 	*observable[int64]
 | |
| 
 | |
| 	embedded.Int64ObservableCounter
 | |
| 	embedded.Int64ObservableUpDownCounter
 | |
| 	embedded.Int64ObservableGauge
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ metric.Int64ObservableCounter       = int64Observable{}
 | |
| 	_ metric.Int64ObservableUpDownCounter = int64Observable{}
 | |
| 	_ metric.Int64ObservableGauge         = int64Observable{}
 | |
| )
 | |
| 
 | |
| func newInt64Observable(m *meter, kind InstrumentKind, name, desc, u string) int64Observable {
 | |
| 	return int64Observable{
 | |
| 		observable: newObservable[int64](m, kind, name, desc, u),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type observable[N int64 | float64] struct {
 | |
| 	metric.Observable
 | |
| 	observablID[N]
 | |
| 
 | |
| 	meter           *meter
 | |
| 	measures        measures[N]
 | |
| 	dropAggregation bool
 | |
| }
 | |
| 
 | |
| func newObservable[N int64 | float64](m *meter, kind InstrumentKind, name, desc, u string) *observable[N] {
 | |
| 	return &observable[N]{
 | |
| 		observablID: observablID[N]{
 | |
| 			name:        name,
 | |
| 			description: desc,
 | |
| 			kind:        kind,
 | |
| 			unit:        u,
 | |
| 			scope:       m.scope,
 | |
| 		},
 | |
| 		meter: m,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // observe records the val for the set of attrs.
 | |
| func (o *observable[N]) observe(val N, s attribute.Set) {
 | |
| 	o.measures.observe(val, s)
 | |
| }
 | |
| 
 | |
| func (o *observable[N]) appendMeasures(meas []aggregate.Measure[N]) {
 | |
| 	o.measures = append(o.measures, meas...)
 | |
| }
 | |
| 
 | |
| type measures[N int64 | float64] []aggregate.Measure[N]
 | |
| 
 | |
| // observe records the val for the set of attrs.
 | |
| func (m measures[N]) observe(val N, s attribute.Set) {
 | |
| 	for _, in := range m {
 | |
| 		in(context.Background(), val, s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var errEmptyAgg = errors.New("no aggregators for observable instrument")
 | |
| 
 | |
| // registerable returns an error if the observable o should not be registered,
 | |
| // and nil if it should. An errEmptyAgg error is returned if o is effectively a
 | |
| // no-op because it does not have any aggregators. Also, an error is returned
 | |
| // if scope defines a Meter other than the one o was created by.
 | |
| func (o *observable[N]) registerable(m *meter) error {
 | |
| 	if len(o.measures) == 0 {
 | |
| 		return errEmptyAgg
 | |
| 	}
 | |
| 	if m != o.meter {
 | |
| 		return fmt.Errorf(
 | |
| 			"invalid registration: observable %q from Meter %q, registered with Meter %q",
 | |
| 			o.name,
 | |
| 			o.scope.Name,
 | |
| 			m.scope.Name,
 | |
| 		)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |