1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-03-21 21:17:35 +02:00

[API EPIC 4/4] Fix tests and examples (#2587)

* Empty All of the metrics dir

* Add instrument api with documentation

* Add a NoOp implementation.

* Updated to the new config standard

* Address PR comments

* This change moves components to sdk/metrics

The Moved components are:
- metric/metrictest
- metric/number
- metric/internal/registry
- metric/sdkapi

* The SDK changes necessary to satasify the new api

* This fixes the remaing tests.

* Update changelog

* refactor the Noop meter and instruments into one package.

* Renamed pkg.Instruments to pkg.InstrumentProvider

Co-authored-by: Aaron Clawson <MadVikingGod@users.noreply.github.com>
This commit is contained in:
Aaron Clawson 2022-03-02 09:50:29 -06:00 committed by GitHub
parent b66c902777
commit 18f4cb85ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
102 changed files with 1721 additions and 4404 deletions

View File

@ -227,15 +227,6 @@ updates:
schedule: schedule:
day: sunday day: sunday
interval: weekly interval: weekly
- package-ecosystem: gomod
directory: /internal/metric
labels:
- dependencies
- go
- "Skip Changelog"
schedule:
day: sunday
interval: weekly
- package-ecosystem: gomod - package-ecosystem: gomod
directory: /internal/tools directory: /internal/tools
labels: labels:

View File

@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased] ## [Unreleased]
### ⚠️ Notice ⚠️
This update is a breaking change of the unstable Metrics API. Code instrumented with the `go.opentelemetry.io/otel/metric` <= v0.27.0 will need to be modified.
### Added ### Added
- Added support to configure the span limits with environment variables. - Added support to configure the span limits with environment variables.
@ -24,6 +28,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- For tracestate's members, prepend the new element and remove the oldest one, which is over capacity (#2592) - For tracestate's members, prepend the new element and remove the oldest one, which is over capacity (#2592)
- Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601) - Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601)
- The metrics API has been significantly changed. (#2587)
- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` config. (#2639) - Unify path cleaning functionally in the `otlpmetric` and `otlptrace` config. (#2639)
- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640) - Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640)

View File

@ -21,8 +21,8 @@ import (
"go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricdata"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
) )
var ( var (

View File

@ -27,13 +27,13 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -170,17 +170,17 @@ func convertDescriptor(ocDescriptor metricdata.Descriptor) (sdkapi.Descriptor, e
// Includes TypeGaugeDistribution, TypeCumulativeDistribution, TypeSummary // Includes TypeGaugeDistribution, TypeCumulativeDistribution, TypeSummary
return sdkapi.Descriptor{}, fmt.Errorf("%w; descriptor type: %v", errConversion, ocDescriptor.Type) return sdkapi.Descriptor{}, fmt.Errorf("%w; descriptor type: %v", errConversion, ocDescriptor.Type)
} }
opts := []metric.InstrumentOption{ opts := []instrument.Option{
metric.WithDescription(ocDescriptor.Description), instrument.WithDescription(ocDescriptor.Description),
} }
switch ocDescriptor.Unit { switch ocDescriptor.Unit {
case metricdata.UnitDimensionless: case metricdata.UnitDimensionless:
opts = append(opts, metric.WithUnit(unit.Dimensionless)) opts = append(opts, instrument.WithUnit(unit.Dimensionless))
case metricdata.UnitBytes: case metricdata.UnitBytes:
opts = append(opts, metric.WithUnit(unit.Bytes)) opts = append(opts, instrument.WithUnit(unit.Bytes))
case metricdata.UnitMilliseconds: case metricdata.UnitMilliseconds:
opts = append(opts, metric.WithUnit(unit.Milliseconds)) opts = append(opts, instrument.WithUnit(unit.Milliseconds))
} }
cfg := metric.NewInstrumentConfig(opts...) cfg := instrument.NewConfig(opts...)
return sdkapi.NewDescriptor(ocDescriptor.Name, ikind, nkind, cfg.Description(), cfg.Unit()), nil return sdkapi.NewDescriptor(ocDescriptor.Name, ikind, nkind, cfg.Description(), cfg.Unit()), nil
} }

View File

@ -26,15 +26,15 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -400,8 +400,8 @@ func TestConvertDescriptor(t *testing.T) {
"foo", "foo",
sdkapi.GaugeObserverInstrumentKind, sdkapi.GaugeObserverInstrumentKind,
number.Int64Kind, number.Int64Kind,
metric.WithDescription("bar"), instrument.WithDescription("bar"),
metric.WithUnit(unit.Bytes), instrument.WithUnit(unit.Bytes),
), ),
}, },
{ {
@ -416,8 +416,8 @@ func TestConvertDescriptor(t *testing.T) {
"foo", "foo",
sdkapi.GaugeObserverInstrumentKind, sdkapi.GaugeObserverInstrumentKind,
number.Float64Kind, number.Float64Kind,
metric.WithDescription("bar"), instrument.WithDescription("bar"),
metric.WithUnit(unit.Milliseconds), instrument.WithUnit(unit.Milliseconds),
), ),
}, },
{ {
@ -432,8 +432,8 @@ func TestConvertDescriptor(t *testing.T) {
"foo", "foo",
sdkapi.CounterObserverInstrumentKind, sdkapi.CounterObserverInstrumentKind,
number.Int64Kind, number.Int64Kind,
metric.WithDescription("bar"), instrument.WithDescription("bar"),
metric.WithUnit(unit.Dimensionless), instrument.WithUnit(unit.Dimensionless),
), ),
}, },
{ {
@ -448,8 +448,8 @@ func TestConvertDescriptor(t *testing.T) {
"foo", "foo",
sdkapi.CounterObserverInstrumentKind, sdkapi.CounterObserverInstrumentKind,
number.Float64Kind, number.Float64Kind,
metric.WithDescription("bar"), instrument.WithDescription("bar"),
metric.WithUnit(unit.Dimensionless), instrument.WithUnit(unit.Dimensionless),
), ),
}, },
{ {

View File

@ -25,7 +25,7 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@ -35,6 +35,9 @@ import (
var ( var (
lemonsKey = attribute.Key("ex.com/lemons") lemonsKey = attribute.Key("ex.com/lemons")
// TODO Bring back Global package
meterProvider metric.MeterProvider
) )
func initMeter() { func initMeter() {
@ -54,7 +57,9 @@ func initMeter() {
if err != nil { if err != nil {
log.Panicf("failed to initialize prometheus exporter %v", err) log.Panicf("failed to initialize prometheus exporter %v", err)
} }
global.SetMeterProvider(exporter.MeterProvider()) // TODO Bring back Global package
// global.SetMeterProvider(exporter.MeterProvider())
meterProvider = exporter.MeterProvider()
http.HandleFunc("/", exporter.ServeHTTP) http.HandleFunc("/", exporter.ServeHTTP)
go func() { go func() {
@ -67,23 +72,33 @@ func initMeter() {
func main() { func main() {
initMeter() initMeter()
meter := global.Meter("ex.com/basic") // TODO Bring back Global package
// meter := global.Meter("ex.com/basic")
meter := meterProvider.Meter("ex.com/basic")
observerLock := new(sync.RWMutex) observerLock := new(sync.RWMutex)
observerValueToReport := new(float64) observerValueToReport := new(float64)
observerLabelsToReport := new([]attribute.KeyValue) observerLabelsToReport := new([]attribute.KeyValue)
cb := func(_ context.Context, result metric.Float64ObserverResult) {
gaugeObserver, err := meter.AsyncFloat64().Gauge("ex.com.one")
if err != nil {
log.Panicf("failed to initialize instrument: %v", err)
}
_ = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) {
(*observerLock).RLock() (*observerLock).RLock()
value := *observerValueToReport value := *observerValueToReport
labels := *observerLabelsToReport labels := *observerLabelsToReport
(*observerLock).RUnlock() (*observerLock).RUnlock()
result.Observe(value, labels...) gaugeObserver.Observe(ctx, value, labels...)
} })
_ = metric.Must(meter).NewFloat64GaugeObserver("ex.com.one", cb,
metric.WithDescription("A GaugeObserver set to 1.0"),
)
histogram := metric.Must(meter).NewFloat64Histogram("ex.com.two") histogram, err := meter.SyncFloat64().Histogram("ex.com.two")
counter := metric.Must(meter).NewFloat64Counter("ex.com.three") if err != nil {
log.Panicf("failed to initialize instrument: %v", err)
}
counter, err := meter.SyncFloat64().Counter("ex.com.three")
if err != nil {
log.Panicf("failed to initialize instrument: %v", err)
}
commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")} commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")}
notSoCommonLabels := []attribute.KeyValue{lemonsKey.Int(13)} notSoCommonLabels := []attribute.KeyValue{lemonsKey.Int(13)}
@ -94,12 +109,9 @@ func main() {
*observerValueToReport = 1.0 *observerValueToReport = 1.0
*observerLabelsToReport = commonLabels *observerLabelsToReport = commonLabels
(*observerLock).Unlock() (*observerLock).Unlock()
meter.RecordBatch(
ctx, histogram.Record(ctx, 2.0, commonLabels...)
commonLabels, counter.Add(ctx, 12.0, commonLabels...)
histogram.Measurement(2.0),
counter.Measurement(12.0),
)
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
@ -107,12 +119,8 @@ func main() {
*observerValueToReport = 1.0 *observerValueToReport = 1.0
*observerLabelsToReport = notSoCommonLabels *observerLabelsToReport = notSoCommonLabels
(*observerLock).Unlock() (*observerLock).Unlock()
meter.RecordBatch( histogram.Record(ctx, 2.0, notSoCommonLabels...)
ctx, counter.Add(ctx, 22.0, notSoCommonLabels...)
notSoCommonLabels,
histogram.Measurement(2.0),
counter.Measurement(22.0),
)
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
@ -120,12 +128,8 @@ func main() {
*observerValueToReport = 13.0 *observerValueToReport = 13.0
*observerLabelsToReport = commonLabels *observerLabelsToReport = commonLabels
(*observerLock).Unlock() (*observerLock).Unlock()
meter.RecordBatch( histogram.Record(ctx, 12.0, commonLabels...)
ctx, counter.Add(ctx, 13.0, commonLabels...)
commonLabels,
histogram.Measurement(12.0),
counter.Measurement(13.0),
)
fmt.Println("Example finished updating, please visit :2222") fmt.Println("Example finished updating, please visit :2222")

View File

@ -20,9 +20,9 @@ import (
"sync" "sync"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )

View File

@ -29,16 +29,16 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
commonpb "go.opentelemetry.io/proto/otlp/common/v1" commonpb "go.opentelemetry.io/proto/otlp/common/v1"
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"

View File

@ -24,10 +24,10 @@ import (
"sync" "sync"
"time" "time"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
commonpb "go.opentelemetry.io/proto/otlp/common/v1" commonpb "go.opentelemetry.io/proto/otlp/common/v1"
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"

View File

@ -25,14 +25,14 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
commonpb "go.opentelemetry.io/proto/otlp/common/v1" commonpb "go.opentelemetry.io/proto/otlp/common/v1"
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
) )

View File

@ -20,13 +20,13 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// OneRecordReader is a Reader that returns just one // OneRecordReader is a Reader that returns just one

View File

@ -25,12 +25,12 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/selector/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple"
metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" metricpb "go.opentelemetry.io/proto/otlp/metrics/v1"
) )
@ -63,34 +63,37 @@ func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlpmetric.Exporter
case sdkapi.CounterInstrumentKind: case sdkapi.CounterInstrumentKind:
switch data.nKind { switch data.nKind {
case number.Int64Kind: case number.Int64Kind:
metric.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels...) c, _ := meter.SyncInt64().Counter(name)
c.Add(ctx, data.val, labels...)
case number.Float64Kind: case number.Float64Kind:
metric.Must(meter).NewFloat64Counter(name).Add(ctx, float64(data.val), labels...) c, _ := meter.SyncFloat64().Counter(name)
c.Add(ctx, float64(data.val), labels...)
default: default:
assert.Failf(t, "unsupported number testing kind", data.nKind.String()) assert.Failf(t, "unsupported number testing kind", data.nKind.String())
} }
case sdkapi.HistogramInstrumentKind: case sdkapi.HistogramInstrumentKind:
switch data.nKind { switch data.nKind {
case number.Int64Kind: case number.Int64Kind:
metric.Must(meter).NewInt64Histogram(name).Record(ctx, data.val, labels...) c, _ := meter.SyncInt64().Histogram(name)
c.Record(ctx, data.val, labels...)
case number.Float64Kind: case number.Float64Kind:
metric.Must(meter).NewFloat64Histogram(name).Record(ctx, float64(data.val), labels...) c, _ := meter.SyncFloat64().Histogram(name)
c.Record(ctx, float64(data.val), labels...)
default: default:
assert.Failf(t, "unsupported number testing kind", data.nKind.String()) assert.Failf(t, "unsupported number testing kind", data.nKind.String())
} }
case sdkapi.GaugeObserverInstrumentKind: case sdkapi.GaugeObserverInstrumentKind:
switch data.nKind { switch data.nKind {
case number.Int64Kind: case number.Int64Kind:
metric.Must(meter).NewInt64GaugeObserver(name, g, _ := meter.AsyncInt64().Gauge(name)
func(_ context.Context, result metric.Int64ObserverResult) { _ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) {
result.Observe(data.val, labels...) g.Observe(ctx, data.val, labels...)
}, })
)
case number.Float64Kind: case number.Float64Kind:
callback := func(v float64) metric.Float64ObserverFunc { g, _ := meter.AsyncFloat64().Gauge(name)
return metric.Float64ObserverFunc(func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(v, labels...) }) _ = meter.RegisterCallback([]instrument.Asynchronous{g}, func(ctx context.Context) {
}(float64(data.val)) g.Observe(ctx, float64(data.val), labels...)
metric.Must(meter).NewFloat64GaugeObserver(name, callback) })
default: default:
assert.Failf(t, "unsupported number testing kind", data.nKind.String()) assert.Failf(t, "unsupported number testing kind", data.nKind.String())
} }

View File

@ -24,8 +24,7 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/global"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/selector/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple"
@ -54,7 +53,8 @@ func Example_insecure() {
controller.WithExporter(exp), controller.WithExporter(exp),
controller.WithCollectPeriod(2*time.Second), controller.WithCollectPeriod(2*time.Second),
) )
global.SetMeterProvider(pusher) // TODO Bring back Global package
// global.SetMeterProvider(pusher)
if err := pusher.Start(ctx); err != nil { if err := pusher.Start(ctx); err != nil {
log.Fatalf("could not start metric controoler: %v", err) log.Fatalf("could not start metric controoler: %v", err)
@ -68,14 +68,16 @@ func Example_insecure() {
} }
}() }()
meter := global.Meter("test-meter") // TODO Bring Back Global package
// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
// Recorder metric example // Recorder metric example
counter := metric.Must(meter).
NewFloat64Counter( counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
"an_important_metric", if err != nil {
metric.WithDescription("Measures the cumulative epicness of the app"), log.Fatalf("Failed to create the instrument: %v", err)
) }
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
log.Printf("Doing really hard work (%d / 10)\n", i+1) log.Printf("Doing really hard work (%d / 10)\n", i+1)
@ -113,7 +115,8 @@ func Example_withTLS() {
controller.WithExporter(exp), controller.WithExporter(exp),
controller.WithCollectPeriod(2*time.Second), controller.WithCollectPeriod(2*time.Second),
) )
global.SetMeterProvider(pusher) // TODO Bring back Global package
// global.SetMeterProvider(pusher)
if err := pusher.Start(ctx); err != nil { if err := pusher.Start(ctx); err != nil {
log.Fatalf("could not start metric controoler: %v", err) log.Fatalf("could not start metric controoler: %v", err)
@ -128,14 +131,15 @@ func Example_withTLS() {
} }
}() }()
meter := global.Meter("test-meter") // TODO Bring back Global package
// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
// Recorder metric example // Recorder metric example
counter := metric.Must(meter). counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
NewFloat64Counter( if err != nil {
"an_important_metric", log.Fatalf("Failed to create the instrument: %v", err)
metric.WithDescription("Measures the cumulative epicness of the app"), }
)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
log.Printf("Doing really hard work (%d / 10)\n", i+1) log.Printf("Doing really hard work (%d / 10)\n", i+1)
@ -170,7 +174,8 @@ func Example_withDifferentSignalCollectors() {
controller.WithExporter(exp), controller.WithExporter(exp),
controller.WithCollectPeriod(2*time.Second), controller.WithCollectPeriod(2*time.Second),
) )
global.SetMeterProvider(pusher) // TODO Bring back Global package
// global.SetMeterProvider(pusher)
if err := pusher.Start(ctx); err != nil { if err := pusher.Start(ctx); err != nil {
log.Fatalf("could not start metric controoler: %v", err) log.Fatalf("could not start metric controoler: %v", err)
@ -184,14 +189,15 @@ func Example_withDifferentSignalCollectors() {
} }
}() }()
meter := global.Meter("test-meter") // TODO Bring back Global package
// meter := global.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
meter := pusher.Meter("go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc_test")
// Recorder metric example // Recorder metric example
counter := metric.Must(meter). counter, err := meter.SyncFloat64().Counter("an_important_metric", instrument.WithDescription("Measures the cumulative epicness of the app"))
NewFloat64Counter( if err != nil {
"an_important_metric", log.Fatalf("Failed to create the instrument: %v", err)
metric.WithDescription("Measures the cumulative epicness of the app"), }
)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
log.Printf("Doing really hard work (%d / 10)\n", i+1) log.Printf("Doing really hard work (%d / 10)\n", i+1)

View File

@ -29,12 +29,12 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )

View File

@ -26,7 +26,7 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@ -107,9 +107,12 @@ func TestPrometheusExporter(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
meter := exporter.MeterProvider().Meter("test") meter := exporter.MeterProvider().Meter("test")
upDownCounter := metric.Must(meter).NewFloat64UpDownCounter("updowncounter") upDownCounter, err := meter.SyncFloat64().UpDownCounter("updowncounter")
counter := metric.Must(meter).NewFloat64Counter("counter") require.NoError(t, err)
histogram := metric.Must(meter).NewFloat64Histogram("histogram") counter, err := meter.SyncFloat64().Counter("counter")
require.NoError(t, err)
histogram, err := meter.SyncFloat64().Histogram("histogram")
require.NoError(t, err)
labels := []attribute.KeyValue{ labels := []attribute.KeyValue{
attribute.Key("A").String("B"), attribute.Key("A").String("B"),
@ -124,9 +127,13 @@ func TestPrometheusExporter(t *testing.T) {
expected = append(expected, expectCounter("counter", `counter{A="B",C="D",R="V"} 15.3`)) expected = append(expected, expectCounter("counter", `counter{A="B",C="D",R="V"} 15.3`))
_ = metric.Must(meter).NewInt64GaugeObserver("intgaugeobserver", func(_ context.Context, result metric.Int64ObserverResult) { gaugeObserver, err := meter.AsyncInt64().Gauge("intgaugeobserver")
result.Observe(1, labels...) require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{gaugeObserver}, func(ctx context.Context) {
gaugeObserver.Observe(ctx, 1, labels...)
}) })
require.NoError(t, err)
expected = append(expected, expectGauge("intgaugeobserver", `intgaugeobserver{A="B",C="D",R="V"} 1`)) expected = append(expected, expectGauge("intgaugeobserver", `intgaugeobserver{A="B",C="D",R="V"} 1`))
@ -148,15 +155,23 @@ func TestPrometheusExporter(t *testing.T) {
expected = append(expected, expectGauge("updowncounter", `updowncounter{A="B",C="D",R="V"} 6.8`)) expected = append(expected, expectGauge("updowncounter", `updowncounter{A="B",C="D",R="V"} 6.8`))
_ = metric.Must(meter).NewFloat64CounterObserver("floatcounterobserver", func(_ context.Context, result metric.Float64ObserverResult) { counterObserver, err := meter.AsyncFloat64().Counter("floatcounterobserver")
result.Observe(7.7, labels...) require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
counterObserver.Observe(ctx, 7.7, labels...)
}) })
require.NoError(t, err)
expected = append(expected, expectCounter("floatcounterobserver", `floatcounterobserver{A="B",C="D",R="V"} 7.7`)) expected = append(expected, expectCounter("floatcounterobserver", `floatcounterobserver{A="B",C="D",R="V"} 7.7`))
_ = metric.Must(meter).NewFloat64UpDownCounterObserver("floatupdowncounterobserver", func(_ context.Context, result metric.Float64ObserverResult) { upDownCounterObserver, err := meter.AsyncFloat64().UpDownCounter("floatupdowncounterobserver")
result.Observe(-7.7, labels...) require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{upDownCounterObserver}, func(ctx context.Context) {
upDownCounterObserver.Observe(ctx, -7.7, labels...)
}) })
require.NoError(t, err)
expected = append(expected, expectGauge("floatupdowncounterobserver", `floatupdowncounterobserver{A="B",C="D",R="V"} -7.7`)) expected = append(expected, expectGauge("floatupdowncounterobserver", `floatupdowncounterobserver{A="B",C="D",R="V"} -7.7`))
@ -196,10 +211,8 @@ func TestPrometheusStatefulness(t *testing.T) {
ctx := context.Background() ctx := context.Background()
counter := metric.Must(meter).NewInt64Counter( counter, err := meter.SyncInt64().Counter("a.counter", instrument.WithDescription("Counts things"))
"a.counter", require.NoError(t, err)
metric.WithDescription("Counts things"),
)
counter.Add(ctx, 100, attribute.String("key", "value")) counter.Add(ctx, 100, attribute.String("key", "value"))

View File

@ -21,7 +21,7 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/instrument/syncint64"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/selector/simple" "go.opentelemetry.io/otel/sdk/metric/selector/simple"
@ -33,13 +33,15 @@ const (
) )
var ( var (
meter = global.GetMeterProvider().Meter( // TODO Bring back Global package
instrumentationName, // meter = global.GetMeterProvider().Meter(
metric.WithInstrumentationVersion(instrumentationVersion), // instrumentationName,
) // metric.WithInstrumentationVersion(instrumentationVersion),
// )
meter metric.Meter
loopCounter = metric.Must(meter).NewInt64Counter("function.loops") loopCounter syncint64.Counter
paramValue = metric.Must(meter).NewInt64Histogram("function.param") paramValue syncint64.Histogram
nameKey = attribute.Key("function.name") nameKey = attribute.Key("function.name")
) )
@ -80,7 +82,18 @@ func InstallExportPipeline(ctx context.Context) func() {
if err = pusher.Start(ctx); err != nil { if err = pusher.Start(ctx); err != nil {
log.Fatalf("starting push controller: %v", err) log.Fatalf("starting push controller: %v", err)
} }
global.SetMeterProvider(pusher) // TODO Bring back Global package
// global.SetMeterProvider(pusher)
meter = pusher.Meter(instrumentationName, metric.WithInstrumentationVersion(instrumentationVersion))
loopCounter, err = meter.SyncInt64().Counter("function.loops")
if err != nil {
log.Fatalf("creating instrument: %v", err)
}
paramValue, err = meter.SyncInt64().Histogram("function.param")
if err != nil {
log.Fatalf("creating instrument: %v", err)
}
return func() { return func() {
if err := pusher.Stop(ctx); err != nil { if err := pusher.Stop(ctx); err != nil {
@ -92,7 +105,7 @@ func InstallExportPipeline(ctx context.Context) func() {
func Example() { func Example() {
ctx := context.Background() ctx := context.Background()
// Registers a meter Provider globally. // TODO: Registers a meter Provider globally.
cleanup := InstallExportPipeline(ctx) cleanup := InstallExportPipeline(ctx)
defer cleanup() defer cleanup()

View File

@ -22,10 +22,10 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )

View File

@ -101,7 +101,8 @@ func TestStdoutTimestamp(t *testing.T) {
require.NoError(t, cont.Start(ctx)) require.NoError(t, cont.Start(ctx))
meter := cont.Meter("test") meter := cont.Meter("test")
counter := metric.Must(meter).NewInt64Counter("name.lastvalue") counter, err := meter.SyncInt64().Counter("name.lastvalue")
require.NoError(t, err)
before := time.Now() before := time.Now()
// Ensure the timestamp is after before. // Ensure the timestamp is after before.
@ -137,7 +138,8 @@ func TestStdoutTimestamp(t *testing.T) {
func TestStdoutCounterFormat(t *testing.T) { func TestStdoutCounterFormat(t *testing.T) {
fix := newFixture(t) fix := newFixture(t)
counter := metric.Must(fix.meter).NewInt64Counter("name.sum") counter, err := fix.meter.SyncInt64().Counter("name.sum")
require.NoError(t, err)
counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D")) counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D"))
require.NoError(t, fix.cont.Stop(fix.ctx)) require.NoError(t, fix.cont.Stop(fix.ctx))
@ -148,7 +150,8 @@ func TestStdoutCounterFormat(t *testing.T) {
func TestStdoutLastValueFormat(t *testing.T) { func TestStdoutLastValueFormat(t *testing.T) {
fix := newFixture(t) fix := newFixture(t)
counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue")
require.NoError(t, err)
counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D")) counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D"))
require.NoError(t, fix.cont.Stop(fix.ctx)) require.NoError(t, fix.cont.Stop(fix.ctx))
@ -159,7 +162,8 @@ func TestStdoutLastValueFormat(t *testing.T) {
func TestStdoutHistogramFormat(t *testing.T) { func TestStdoutHistogramFormat(t *testing.T) {
fix := newFixture(t, stdoutmetric.WithPrettyPrint()) fix := newFixture(t, stdoutmetric.WithPrettyPrint())
inst := metric.Must(fix.meter).NewFloat64Histogram("name.histogram") inst, err := fix.meter.SyncFloat64().Histogram("name.histogram")
require.NoError(t, err)
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D")) inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D"))
@ -181,7 +185,8 @@ func TestStdoutNoData(t *testing.T) {
t.Parallel() t.Parallel()
fix := newFixture(t) fix := newFixture(t)
_ = metric.Must(fix.meter).NewFloat64Counter(fmt.Sprint("name.", aggName)) _, err := fix.meter.SyncFloat64().Counter(fmt.Sprint("name.", aggName))
require.NoError(t, err)
require.NoError(t, fix.cont.Stop(fix.ctx)) require.NoError(t, fix.cont.Stop(fix.ctx))
require.Equal(t, "", fix.Output()) require.Equal(t, "", fix.Output())
@ -243,7 +248,8 @@ func TestStdoutResource(t *testing.T) {
ctx := context.Background() ctx := context.Background()
fix := newFixtureWithResource(t, tc.res) fix := newFixtureWithResource(t, tc.res)
counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") counter, err := fix.meter.SyncFloat64().Counter("name.lastvalue")
require.NoError(t, err)
counter.Add(ctx, 123.456, tc.attrs...) counter.Add(ctx, 123.456, tc.attrs...)
require.NoError(t, fix.cont.Stop(fix.ctx)) require.NoError(t, fix.cont.Stop(fix.ctx))

View File

@ -1,149 +0,0 @@
// 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/internal/metric"
import (
"context"
"errors"
"fmt"
"sync"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
)
//nolint:revive // ignoring missing comments for exported error in an internal package
var ErrInvalidAsyncRunner = errors.New("unknown async runner type")
// AsyncCollector is an interface used between the MeterImpl and the
// AsyncInstrumentState helper below. This interface is implemented by
// the SDK to provide support for running observer callbacks.
type AsyncCollector interface {
// CollectAsync passes a batch of observations to the MeterImpl.
CollectAsync(labels []attribute.KeyValue, observation ...sdkapi.Observation)
}
// AsyncInstrumentState manages an ordered set of asynchronous
// instruments and the distinct runners, taking into account batch
// observer callbacks.
type AsyncInstrumentState struct {
lock sync.Mutex
// errorOnce will use the otel.Handler to report an error
// once in case of an invalid runner attempting to run.
errorOnce sync.Once
// runnerMap keeps the set of runners that will run each
// collection interval. Singletons are entered with a real
// instrument each, batch observers are entered with a nil
// instrument, ensuring that when a singleton callback is used
// repeatedly, it is executed repeatedly in the interval, while
// when a batch callback is used repeatedly, it only executes
// once per interval.
runnerMap map[asyncRunnerPair]struct{}
// runners maintains the set of runners in the order they were
// registered.
runners []asyncRunnerPair
// instruments maintains the set of instruments in the order
// they were registered.
instruments []sdkapi.AsyncImpl
}
// asyncRunnerPair is a map entry for Observer callback runners.
type asyncRunnerPair struct {
// runner is used as a map key here. The API ensures
// that all callbacks are pointers for this reason.
runner sdkapi.AsyncRunner
// inst refers to a non-nil instrument when `runner` is a
// AsyncSingleRunner.
inst sdkapi.AsyncImpl
}
// NewAsyncInstrumentState returns a new *AsyncInstrumentState, for
// use by MeterImpl to manage running the set of observer callbacks in
// the correct order.
func NewAsyncInstrumentState() *AsyncInstrumentState {
return &AsyncInstrumentState{
runnerMap: map[asyncRunnerPair]struct{}{},
}
}
// Instruments returns the asynchronous instruments managed by this
// object, the set that should be checkpointed after observers are
// run.
func (a *AsyncInstrumentState) Instruments() []sdkapi.AsyncImpl {
a.lock.Lock()
defer a.lock.Unlock()
return a.instruments
}
// Register adds a new asynchronous instrument to by managed by this
// object. This should be called during NewAsyncInstrument() and
// assumes that errors (e.g., duplicate registration) have already
// been checked.
func (a *AsyncInstrumentState) Register(inst sdkapi.AsyncImpl, runner sdkapi.AsyncRunner) {
a.lock.Lock()
defer a.lock.Unlock()
a.instruments = append(a.instruments, inst)
// asyncRunnerPair reflects this callback in the asyncRunners
// list. If this is a batch runner, the instrument is nil.
// If this is a single-Observer runner, the instrument is
// included. This ensures that batch callbacks are called
// once and single callbacks are called once per instrument.
rp := asyncRunnerPair{
runner: runner,
}
if _, ok := runner.(sdkapi.AsyncSingleRunner); ok {
rp.inst = inst
}
if _, ok := a.runnerMap[rp]; !ok {
a.runnerMap[rp] = struct{}{}
a.runners = append(a.runners, rp)
}
}
// Run executes the complete set of observer callbacks.
func (a *AsyncInstrumentState) Run(ctx context.Context, collector AsyncCollector) {
a.lock.Lock()
runners := a.runners
a.lock.Unlock()
for _, rp := range runners {
// The runner must be a single or batch runner, no
// other implementations are possible because the
// interface has un-exported methods.
if singleRunner, ok := rp.runner.(sdkapi.AsyncSingleRunner); ok {
singleRunner.Run(ctx, rp.inst, collector.CollectAsync)
continue
}
if multiRunner, ok := rp.runner.(sdkapi.AsyncBatchRunner); ok {
multiRunner.Run(ctx, collector.CollectAsync)
continue
}
a.errorOnce.Do(func() {
otel.Handle(fmt.Errorf("%w: type %T (reported once)", ErrInvalidAsyncRunner, rp))
})
}
}

View File

@ -1,41 +0,0 @@
// 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 global_test
import (
"context"
"testing"
"go.opentelemetry.io/otel/attribute"
internalglobal "go.opentelemetry.io/otel/internal/metric/global"
metricglobal "go.opentelemetry.io/otel/metric/global"
)
func BenchmarkGlobalInt64CounterAddNoSDK(b *testing.B) {
// Compare with BenchmarkGlobalInt64CounterAddWithSDK() in
// ../../sdk/metric/benchmark_test.go to see the overhead of the
// global no-op system against a registered SDK.
internalglobal.ResetForTest()
ctx := context.Background()
sdk := metricglobal.Meter("test")
labs := []attribute.KeyValue{attribute.String("A", "B")}
cnt := Must(sdk).NewInt64Counter("int64.counter")
b.ResetTimer()
for i := 0; i < b.N; i++ {
cnt.Add(ctx, 1, labs...)
}
}

View File

@ -1,40 +0,0 @@
// 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 global_test
import (
"os"
"testing"
ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/internal/metric/global"
)
// Ensure struct alignment prior to running tests.
func TestMain(m *testing.M) {
fieldsMap := global.AtomicFieldOffsets()
fields := make([]ottest.FieldOffset, 0, len(fieldsMap))
for name, offset := range fieldsMap {
fields = append(fields, ottest.FieldOffset{
Name: name,
Offset: offset,
})
}
if !ottest.Aligned8Byte(fields, os.Stderr) {
os.Exit(1)
}
os.Exit(m.Run())
}

View File

@ -1,287 +0,0 @@
// 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 global // import "go.opentelemetry.io/otel/internal/metric/global"
import (
"context"
"sync"
"sync/atomic"
"unsafe"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/metric/registry"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// This file contains the forwarding implementation of MeterProvider used as
// the default global instance. Metric events using instruments provided by
// this implementation are no-ops until the first Meter implementation is set
// as the global provider.
//
// The implementation here uses Mutexes to maintain a list of active Meters in
// the MeterProvider and Instruments in each Meter, under the assumption that
// these interfaces are not performance-critical.
//
// We have the invariant that setDelegate() will be called before a new
// MeterProvider implementation is registered as the global provider. Mutexes
// in the MeterProvider and Meters ensure that each instrument has a delegate
// before the global provider is set.
//
// Metric uniqueness checking is implemented by calling the exported
// methods of the api/metric/registry package.
type meterKey struct {
InstrumentationName string
InstrumentationVersion string
SchemaURL string
}
type meterProvider struct {
delegate metric.MeterProvider
// lock protects `delegate` and `meters`.
lock sync.Mutex
// meters maintains a unique entry for every named Meter
// that has been registered through the global instance.
meters map[meterKey]*meterEntry
}
type meterImpl struct {
delegate unsafe.Pointer // (*metric.MeterImpl)
lock sync.Mutex
syncInsts []*syncImpl
asyncInsts []*asyncImpl
}
type meterEntry struct {
unique sdkapi.MeterImpl
impl meterImpl
}
type instrument struct {
descriptor sdkapi.Descriptor
}
type syncImpl struct {
delegate unsafe.Pointer // (*sdkapi.SyncImpl)
instrument
}
type asyncImpl struct {
delegate unsafe.Pointer // (*sdkapi.AsyncImpl)
instrument
runner sdkapi.AsyncRunner
}
var _ metric.MeterProvider = &meterProvider{}
var _ sdkapi.MeterImpl = &meterImpl{}
var _ sdkapi.InstrumentImpl = &syncImpl{}
var _ sdkapi.AsyncImpl = &asyncImpl{}
func (inst *instrument) Descriptor() sdkapi.Descriptor {
return inst.descriptor
}
// MeterProvider interface and delegation
func newMeterProvider() *meterProvider {
return &meterProvider{
meters: map[meterKey]*meterEntry{},
}
}
func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
p.lock.Lock()
defer p.lock.Unlock()
p.delegate = provider
for key, entry := range p.meters {
entry.impl.setDelegate(key, provider)
}
p.meters = nil
}
func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
p.lock.Lock()
defer p.lock.Unlock()
if p.delegate != nil {
return p.delegate.Meter(instrumentationName, opts...)
}
cfg := metric.NewMeterConfig(opts...)
key := meterKey{
InstrumentationName: instrumentationName,
InstrumentationVersion: cfg.InstrumentationVersion(),
SchemaURL: cfg.SchemaURL(),
}
entry, ok := p.meters[key]
if !ok {
entry = &meterEntry{}
// Note: This code implements its own MeterProvider
// name-uniqueness logic because there is
// synchronization required at the moment of
// delegation. We use the same instrument-uniqueness
// checking the real SDK uses here:
entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
p.meters[key] = entry
}
return metric.WrapMeterImpl(entry.unique)
}
// Meter interface and delegation
func (m *meterImpl) setDelegate(key meterKey, provider metric.MeterProvider) {
m.lock.Lock()
defer m.lock.Unlock()
d := new(sdkapi.MeterImpl)
*d = provider.Meter(
key.InstrumentationName,
metric.WithInstrumentationVersion(key.InstrumentationVersion),
metric.WithSchemaURL(key.SchemaURL),
).MeterImpl()
m.delegate = unsafe.Pointer(d)
for _, inst := range m.syncInsts {
inst.setDelegate(*d)
}
m.syncInsts = nil
for _, obs := range m.asyncInsts {
obs.setDelegate(*d)
}
m.asyncInsts = nil
}
func (m *meterImpl) NewSyncInstrument(desc sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
m.lock.Lock()
defer m.lock.Unlock()
if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return (*meterPtr).NewSyncInstrument(desc)
}
inst := &syncImpl{
instrument: instrument{
descriptor: desc,
},
}
m.syncInsts = append(m.syncInsts, inst)
return inst, nil
}
// Synchronous delegation
func (inst *syncImpl) setDelegate(d sdkapi.MeterImpl) {
implPtr := new(sdkapi.SyncImpl)
var err error
*implPtr, err = d.NewSyncInstrument(inst.descriptor)
if err != nil {
// TODO: There is no standard way to deliver this error to the user.
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
// Note that the default SDK will not generate any errors yet, this is
// only for added safety.
panic(err)
}
atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
}
func (inst *syncImpl) Implementation() interface{} {
if implPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return inst
}
// Async delegation
func (m *meterImpl) NewAsyncInstrument(
desc sdkapi.Descriptor,
runner sdkapi.AsyncRunner,
) (sdkapi.AsyncImpl, error) {
m.lock.Lock()
defer m.lock.Unlock()
if meterPtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
return (*meterPtr).NewAsyncInstrument(desc, runner)
}
inst := &asyncImpl{
instrument: instrument{
descriptor: desc,
},
runner: runner,
}
m.asyncInsts = append(m.asyncInsts, inst)
return inst, nil
}
func (obs *asyncImpl) Implementation() interface{} {
if implPtr := (*sdkapi.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
return (*implPtr).Implementation()
}
return obs
}
func (obs *asyncImpl) setDelegate(d sdkapi.MeterImpl) {
implPtr := new(sdkapi.AsyncImpl)
var err error
*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner)
if err != nil {
// TODO: There is no standard way to deliver this error to the user.
// See https://github.com/open-telemetry/opentelemetry-go/issues/514
// Note that the default SDK will not generate any errors yet, this is
// only for added safety.
panic(err)
}
atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
}
// Metric updates
func (m *meterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) {
if delegatePtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
(*delegatePtr).RecordBatch(ctx, labels, measurements...)
}
}
func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
if instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
(*instPtr).RecordOne(ctx, number, labels)
}
}
func AtomicFieldOffsets() map[string]uintptr {
return map[string]uintptr{
"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
"meterImpl.delegate": unsafe.Offsetof(meterImpl{}.delegate),
"syncImpl.delegate": unsafe.Offsetof(syncImpl{}.delegate),
"asyncImpl.delegate": unsafe.Offsetof(asyncImpl{}.delegate),
}
}

View File

@ -1,253 +0,0 @@
// 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 global_test
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/metric/global"
"go.opentelemetry.io/otel/metric"
metricglobal "go.opentelemetry.io/otel/metric/global"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
var Must = metric.Must
var asInt = number.NewInt64Number
var asFloat = number.NewFloat64Number
func TestDirect(t *testing.T) {
global.ResetForTest()
ctx := context.Background()
meter1 := metricglobal.Meter("test1", metric.WithInstrumentationVersion("semver:v1.0.0"))
meter2 := metricglobal.Meter("test2", metric.WithSchemaURL("hello"))
library1 := metrictest.Library{
InstrumentationName: "test1",
InstrumentationVersion: "semver:v1.0.0",
}
library2 := metrictest.Library{
InstrumentationName: "test2",
SchemaURL: "hello",
}
labels1 := []attribute.KeyValue{attribute.String("A", "B")}
labels2 := []attribute.KeyValue{attribute.String("C", "D")}
labels3 := []attribute.KeyValue{attribute.String("E", "F")}
counter := Must(meter1).NewInt64Counter("test.counter")
counter.Add(ctx, 1, labels1...)
counter.Add(ctx, 1, labels1...)
histogram := Must(meter1).NewFloat64Histogram("test.histogram")
histogram.Record(ctx, 1, labels1...)
histogram.Record(ctx, 2, labels1...)
_ = Must(meter1).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(1., labels1...)
result.Observe(2., labels2...)
})
_ = Must(meter1).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(1, labels1...)
result.Observe(2, labels2...)
})
second := Must(meter2).NewFloat64Histogram("test.second")
second.Record(ctx, 1, labels3...)
second.Record(ctx, 2, labels3...)
provider := metrictest.NewMeterProvider()
metricglobal.SetMeterProvider(provider)
counter.Add(ctx, 1, labels1...)
histogram.Record(ctx, 3, labels1...)
second.Record(ctx, 3, labels3...)
provider.RunAsyncInstruments()
measurements := metrictest.AsStructs(provider.MeasurementBatches)
require.EqualValues(t,
[]metrictest.Measured{
{
Name: "test.counter",
Library: library1,
Labels: metrictest.LabelsToMap(labels1...),
Number: asInt(1),
},
{
Name: "test.histogram",
Library: library1,
Labels: metrictest.LabelsToMap(labels1...),
Number: asFloat(3),
},
{
Name: "test.second",
Library: library2,
Labels: metrictest.LabelsToMap(labels3...),
Number: asFloat(3),
},
{
Name: "test.gauge.float",
Library: library1,
Labels: metrictest.LabelsToMap(labels1...),
Number: asFloat(1),
},
{
Name: "test.gauge.float",
Library: library1,
Labels: metrictest.LabelsToMap(labels2...),
Number: asFloat(2),
},
{
Name: "test.gauge.int",
Library: library1,
Labels: metrictest.LabelsToMap(labels1...),
Number: asInt(1),
},
{
Name: "test.gauge.int",
Library: library1,
Labels: metrictest.LabelsToMap(labels2...),
Number: asInt(2),
},
},
measurements,
)
}
type meterProviderWithConstructorError struct {
metric.MeterProvider
}
type meterWithConstructorError struct {
sdkapi.MeterImpl
}
func (m *meterProviderWithConstructorError) Meter(iName string, opts ...metric.MeterOption) metric.Meter {
return metric.WrapMeterImpl(&meterWithConstructorError{m.MeterProvider.Meter(iName, opts...).MeterImpl()})
}
func (m *meterWithConstructorError) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
return sdkapi.NewNoopSyncInstrument(), errors.New("constructor error")
}
func TestErrorInDeferredConstructor(t *testing.T) {
global.ResetForTest()
ctx := context.Background()
meter := metricglobal.GetMeterProvider().Meter("builtin")
c1 := Must(meter).NewInt64Counter("test")
c2 := Must(meter).NewInt64Counter("test")
provider := metrictest.NewMeterProvider()
sdk := &meterProviderWithConstructorError{provider}
require.Panics(t, func() {
metricglobal.SetMeterProvider(sdk)
})
c1.Add(ctx, 1)
c2.Add(ctx, 2)
}
func TestImplementationIndirection(t *testing.T) {
global.ResetForTest()
// Test that Implementation() does the proper indirection, i.e.,
// returns the implementation interface not the global, after
// registered.
meter1 := metricglobal.Meter("test1")
// Sync: no SDK yet
counter := Must(meter1).NewInt64Counter("interface.counter")
ival := counter.Measurement(1).SyncImpl().Implementation()
require.NotNil(t, ival)
_, ok := ival.(*metrictest.Sync)
require.False(t, ok)
// Async: no SDK yet
gauge := Must(meter1).NewFloat64GaugeObserver(
"interface.gauge",
func(_ context.Context, result metric.Float64ObserverResult) {},
)
ival = gauge.AsyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Async)
require.False(t, ok)
// Register the SDK
provider := metrictest.NewMeterProvider()
metricglobal.SetMeterProvider(provider)
// Repeat the above tests
// Sync
ival = counter.Measurement(1).SyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Sync)
require.True(t, ok)
// Async
ival = gauge.AsyncImpl().Implementation()
require.NotNil(t, ival)
_, ok = ival.(*metrictest.Async)
require.True(t, ok)
}
func TestRecordBatchMock(t *testing.T) {
global.ResetForTest()
meter := metricglobal.GetMeterProvider().Meter("builtin")
counter := Must(meter).NewInt64Counter("test.counter")
meter.RecordBatch(context.Background(), nil, counter.Measurement(1))
provider := metrictest.NewMeterProvider()
metricglobal.SetMeterProvider(provider)
meter.RecordBatch(context.Background(), nil, counter.Measurement(1))
require.EqualValues(t,
[]metrictest.Measured{
{
Name: "test.counter",
Library: metrictest.Library{
InstrumentationName: "builtin",
},
Labels: metrictest.LabelsToMap(),
Number: asInt(1),
},
},
metrictest.AsStructs(provider.MeasurementBatches))
}

View File

@ -1,66 +0,0 @@
// 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 global // import "go.opentelemetry.io/otel/internal/metric/global"
import (
"sync"
"sync/atomic"
"go.opentelemetry.io/otel/metric"
)
type meterProviderHolder struct {
mp metric.MeterProvider
}
var (
globalMeter = defaultMeterValue()
delegateMeterOnce sync.Once
)
// MeterProvider is the internal implementation for global.MeterProvider.
func MeterProvider() metric.MeterProvider {
return globalMeter.Load().(meterProviderHolder).mp
}
// SetMeterProvider is the internal implementation for global.SetMeterProvider.
func SetMeterProvider(mp metric.MeterProvider) {
delegateMeterOnce.Do(func() {
current := MeterProvider()
if current == mp {
// Setting the provider to the prior default is nonsense, panic.
// Panic is acceptable because we are likely still early in the
// process lifetime.
panic("invalid MeterProvider, the global instance cannot be reinstalled")
} else if def, ok := current.(*meterProvider); ok {
def.setDelegate(mp)
}
})
globalMeter.Store(meterProviderHolder{mp: mp})
}
func defaultMeterValue() *atomic.Value {
v := &atomic.Value{}
v.Store(meterProviderHolder{mp: newMeterProvider()})
return v
}
// ResetForTest restores the initial global state, for testing purposes.
func ResetForTest() {
globalMeter = defaultMeterValue()
delegateMeterOnce = sync.Once{}
}

View File

@ -1,131 +0,0 @@
// 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 global
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/internal/metric/registry"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/sdkapi"
)
type (
newFunc func(name, libraryName string) (sdkapi.InstrumentImpl, error)
)
var (
allNew = map[string]newFunc{
"counter.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewInt64Counter(name))
},
"counter.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewFloat64Counter(name))
},
"histogram.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewInt64Histogram(name))
},
"histogram.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewFloat64Histogram(name))
},
"gauge.int64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {}))
},
"gauge.float64": func(name, libraryName string) (sdkapi.InstrumentImpl, error) {
return unwrap(MeterProvider().Meter(libraryName).NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {}))
},
}
)
func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) {
if impl == nil {
return nil, err
}
if s, ok := impl.(interface {
SyncImpl() sdkapi.SyncImpl
}); ok {
return s.SyncImpl(), err
}
if a, ok := impl.(interface {
AsyncImpl() sdkapi.AsyncImpl
}); ok {
return a.AsyncImpl(), err
}
return nil, err
}
func TestRegistrySameInstruments(t *testing.T) {
for _, nf := range allNew {
ResetForTest()
inst1, err1 := nf("this", "meter")
inst2, err2 := nf("this", "meter")
require.NoError(t, err1)
require.NoError(t, err2)
require.Equal(t, inst1, inst2)
SetMeterProvider(metrictest.NewMeterProvider())
require.Equal(t, inst1, inst2)
}
}
func TestRegistryDifferentNamespace(t *testing.T) {
for _, nf := range allNew {
ResetForTest()
inst1, err1 := nf("this", "meter1")
inst2, err2 := nf("this", "meter2")
require.NoError(t, err1)
require.NoError(t, err2)
if inst1.Descriptor().InstrumentKind().Synchronous() {
// They're equal because of a `nil` pointer at this point.
// (Only for synchronous instruments, which lack callacks.)
require.EqualValues(t, inst1, inst2)
}
SetMeterProvider(metrictest.NewMeterProvider())
// They're different after the deferred setup.
require.NotEqual(t, inst1, inst2)
}
}
func TestRegistryDiffInstruments(t *testing.T) {
for origName, origf := range allNew {
ResetForTest()
_, err := origf("this", "super")
require.NoError(t, err)
for newName, nf := range allNew {
if newName == origName {
continue
}
other, err := nf("this", "super")
require.Error(t, err)
require.NotNil(t, other)
require.True(t, errors.Is(err, registry.ErrMetricKindMismatch))
require.Contains(t, err.Error(), "by this name with another kind or number type")
}
}
}

View File

@ -1,45 +0,0 @@
// 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 global_test
import (
"testing"
internalglobal "go.opentelemetry.io/otel/internal/metric/global"
metricglobal "go.opentelemetry.io/otel/metric/global"
)
func TestResetsOfGlobalsPanic(t *testing.T) {
internalglobal.ResetForTest()
tests := map[string]func(){
"SetMeterProvider": func() {
metricglobal.SetMeterProvider(metricglobal.GetMeterProvider())
},
}
for name, test := range tests {
shouldPanic(t, name, test)
}
}
func shouldPanic(t *testing.T, name string, f func()) {
defer func() {
if r := recover(); r == nil {
t.Errorf("calling %s with default global did not panic", name)
}
}()
f()
}

View File

@ -1,73 +0,0 @@
module go.opentelemetry.io/otel/internal/metric
go 1.16
require (
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/metric v0.27.0
)
replace go.opentelemetry.io/otel => ../..
replace go.opentelemetry.io/otel/metric => ../../metric
replace go.opentelemetry.io/otel/internal/metric => ./
replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus
replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing
replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger
replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer
replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus
replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector
replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough
replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus
replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin
replace go.opentelemetry.io/otel/exporters/prometheus => ../../exporters/prometheus
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp
replace go.opentelemetry.io/otel/exporters/jaeger => ../../exporters/jaeger
replace go.opentelemetry.io/otel/exporters/zipkin => ../../exporters/zipkin
replace go.opentelemetry.io/otel/internal/tools => ../tools
replace go.opentelemetry.io/otel/sdk => ../../sdk
replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric
replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric
replace go.opentelemetry.io/otel/trace => ../../trace
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc
replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric
replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp => ../../exporters/otlp/otlpmetric/otlpmetrichttp
replace go.opentelemetry.io/otel/bridge/opencensus/test => ../../bridge/opencensus/test
replace go.opentelemetry.io/otel/example/fib => ../../example/fib
replace go.opentelemetry.io/otel/schema => ../../schema
replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry

View File

@ -1,18 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -14,65 +14,6 @@
package metric // import "go.opentelemetry.io/otel/metric" package metric // import "go.opentelemetry.io/otel/metric"
import (
"go.opentelemetry.io/otel/metric/unit"
)
// InstrumentConfig contains options for metric instrument descriptors.
type InstrumentConfig struct {
description string
unit unit.Unit
}
// Description describes the instrument in human-readable terms.
func (cfg *InstrumentConfig) Description() string {
return cfg.description
}
// Unit describes the measurement unit for a instrument.
func (cfg *InstrumentConfig) Unit() unit.Unit {
return cfg.unit
}
// InstrumentOption is an interface for applying metric instrument options.
type InstrumentOption interface {
// ApplyMeter is used to set a InstrumentOption value of a
// InstrumentConfig.
applyInstrument(InstrumentConfig) InstrumentConfig
}
// NewInstrumentConfig creates a new InstrumentConfig
// and applies all the given options.
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
var config InstrumentConfig
for _, o := range opts {
config = o.applyInstrument(config)
}
return config
}
type instrumentOptionFunc func(InstrumentConfig) InstrumentConfig
func (fn instrumentOptionFunc) applyInstrument(cfg InstrumentConfig) InstrumentConfig {
return fn(cfg)
}
// WithDescription applies provided description.
func WithDescription(desc string) InstrumentOption {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.description = desc
return cfg
})
}
// WithUnit applies provided unit.
func WithUnit(unit unit.Unit) InstrumentOption {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.unit = unit
return cfg
})
}
// MeterConfig contains options for Meters. // MeterConfig contains options for Meters.
type MeterConfig struct { type MeterConfig struct {
instrumentationVersion string instrumentationVersion string
@ -80,18 +21,18 @@ type MeterConfig struct {
} }
// InstrumentationVersion is the version of the library providing instrumentation. // InstrumentationVersion is the version of the library providing instrumentation.
func (cfg *MeterConfig) InstrumentationVersion() string { func (cfg MeterConfig) InstrumentationVersion() string {
return cfg.instrumentationVersion return cfg.instrumentationVersion
} }
// SchemaURL is the schema_url of the library providing instrumentation. // SchemaURL is the schema_url of the library providing instrumentation.
func (cfg *MeterConfig) SchemaURL() string { func (cfg MeterConfig) SchemaURL() string {
return cfg.schemaURL return cfg.schemaURL
} }
// MeterOption is an interface for applying Meter options. // MeterOption is an interface for applying Meter options.
type MeterOption interface { type MeterOption interface {
// ApplyMeter is used to set a MeterOption value of a MeterConfig. // applyMeter is used to set a MeterOption value of a MeterConfig.
applyMeter(MeterConfig) MeterConfig applyMeter(MeterConfig) MeterConfig
} }

View File

@ -19,49 +19,5 @@ OpenTelemetry API.
This package is currently in a pre-GA phase. Backwards incompatible changes This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback. evolving OpenTelemetry specification and user feedback.
Measurements can be made about an operation being performed or the state of a
system in general. These measurements can be crucial to the reliable operation
of code and provide valuable insights about the inner workings of a system.
Measurements are made using instruments provided by this package. The type of
instrument used will depend on the type of measurement being made and of what
part of a system is being measured.
Instruments are categorized as Synchronous or Asynchronous and independently
as Adding or Grouping. Synchronous instruments are called by the user with a
Context. Asynchronous instruments are called by the SDK during collection.
Adding instruments are semantically intended for capturing a sum. Grouping
instruments are intended for capturing a distribution.
Adding instruments may be monotonic, in which case they are non-decreasing
and naturally define a rate.
The synchronous instrument names are:
Counter: adding, monotonic
UpDownCounter: adding
Histogram: grouping
and the asynchronous instruments are:
CounterObserver: adding, monotonic
UpDownCounterObserver: adding
GaugeObserver: grouping
All instruments are provided with support for either float64 or int64 input
values.
An instrument is created using a Meter. Additionally, a Meter is used to
record batches of synchronous measurements or asynchronous observations. A
Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
the instrumentation it instruments and must be named and versioned when
created with a MeterProvider with the name and version of the instrumentation
library.
Instrumentation should be designed to accept a MeterProvider from which it can
create its own unique Meter. Alternatively, the registered global
MeterProvider from the go.opentelemetry.io/otel package can be used as a
default.
*/ */
package metric // import "go.opentelemetry.io/otel/metric" package metric // import "go.opentelemetry.io/otel/metric"

116
metric/example_test.go Normal file
View File

@ -0,0 +1,116 @@
// 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_test
import (
"context"
"fmt"
"runtime"
"time"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/nonrecording"
"go.opentelemetry.io/otel/metric/unit"
)
//nolint:govet // Meter doesn't register for go vet
func ExampleMeter_synchronous() {
// In a library or program this would be provided by otel.GetMeterProvider().
meterProvider := nonrecording.NewNoopMeterProvider()
workDuration, err := meterProvider.Meter("go.opentelemetry.io/otel/metric#SyncExample").SyncInt64().Histogram(
"workDuration",
instrument.WithUnit(unit.Milliseconds))
if err != nil {
fmt.Println("Failed to register instrument")
panic(err)
}
startTime := time.Now()
ctx := context.Background()
// Do work
// ...
workDuration.Record(ctx, time.Since(startTime).Milliseconds())
}
//nolint:govet // Meter doesn't register for go vet
func ExampleMeter_asynchronous_single() {
// In a library or program this would be provided by otel.GetMeterProvider().
meterProvider := nonrecording.NewNoopMeterProvider()
meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#AsyncExample")
memoryUsage, err := meter.AsyncInt64().Gauge(
"MemoryUsage",
instrument.WithUnit(unit.Bytes),
)
if err != nil {
fmt.Println("Failed to register instrument")
panic(err)
}
err = meter.RegisterCallback([]instrument.Asynchronous{memoryUsage},
func(ctx context.Context) {
// instrument.WithCallbackFunc(func(ctx context.Context) {
//Do Work to get the real memoryUsage
// mem := GatherMemory(ctx)
mem := 75000
memoryUsage.Observe(ctx, int64(mem))
})
if err != nil {
fmt.Println("Failed to register callback")
panic(err)
}
}
//nolint:govet // Meter doesn't register for go vet
func ExampleMeter_asynchronous_multiple() {
meterProvider := nonrecording.NewNoopMeterProvider()
meter := meterProvider.Meter("go.opentelemetry.io/otel/metric#MultiAsyncExample")
// This is just a sample of memory stats to record from the Memstats
heapAlloc, _ := meter.AsyncInt64().UpDownCounter("heapAllocs")
gcCount, _ := meter.AsyncInt64().Counter("gcCount")
gcPause, _ := meter.SyncFloat64().Histogram("gcPause")
err := meter.RegisterCallback([]instrument.Asynchronous{
heapAlloc,
gcCount,
},
func(ctx context.Context) {
memStats := &runtime.MemStats{}
// This call does work
runtime.ReadMemStats(memStats)
heapAlloc.Observe(ctx, int64(memStats.HeapAlloc))
gcCount.Observe(ctx, int64(memStats.NumGC))
// This function synchronously records the pauses
computeGCPauses(ctx, gcPause, memStats.PauseNs[:])
},
)
if err != nil {
fmt.Println("Failed to register callback")
panic(err)
}
}
//This is just an example, see the the contrib runtime instrumentation for real implementation
func computeGCPauses(ctx context.Context, recorder syncfloat64.Histogram, pauseBuff []uint64) {
}

View File

@ -1,49 +0,0 @@
// 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 global // import "go.opentelemetry.io/otel/metric/global"
import (
"go.opentelemetry.io/otel/internal/metric/global"
"go.opentelemetry.io/otel/metric"
)
// Meter creates an implementation of the Meter interface from the global
// MeterProvider. The instrumentationName must be the name of the library
// providing instrumentation. This name may be the same as the instrumented
// code only if that code provides built-in instrumentation. If the
// instrumentationName is empty, then a implementation defined default name
// will be used instead.
//
// This is short for MeterProvider().Meter(name)
func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
return GetMeterProvider().Meter(instrumentationName, opts...)
}
// GetMeterProvider returns the registered global meter provider. If
// none is registered then a default meter provider is returned that
// forwards the Meter interface to the first registered Meter.
//
// Use the meter provider to create a named meter. E.g.
// meter := global.MeterProvider().Meter("example.com/foo")
// or
// meter := global.Meter("example.com/foo")
func GetMeterProvider() metric.MeterProvider {
return global.MeterProvider()
}
// SetMeterProvider registers `mp` as the global meter provider.
func SetMeterProvider(mp metric.MeterProvider) {
global.SetMeterProvider(mp)
}

View File

@ -1,42 +0,0 @@
// 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 global
import (
"testing"
"go.opentelemetry.io/otel/metric"
)
type testMeterProvider struct{}
var _ metric.MeterProvider = &testMeterProvider{}
func (*testMeterProvider) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
return metric.Meter{}
}
func TestMultipleGlobalMeterProvider(t *testing.T) {
p1 := testMeterProvider{}
p2 := metric.NewNoopMeterProvider()
SetMeterProvider(&p1)
SetMeterProvider(p2)
got := GetMeterProvider()
want := p2
if got != want {
t.Fatalf("MeterProvider: got %p, want %p\n", got, want)
}
}

View File

@ -2,6 +2,8 @@ module go.opentelemetry.io/otel/metric
go 1.16 go 1.16
require go.opentelemetry.io/otel v1.4.1
replace go.opentelemetry.io/otel => ../ replace go.opentelemetry.io/otel => ../
replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus replace go.opentelemetry.io/otel/bridge/opencensus => ../bridge/opencensus
@ -38,13 +40,6 @@ replace go.opentelemetry.io/otel/sdk/metric => ../sdk/metric
replace go.opentelemetry.io/otel/trace => ../trace replace go.opentelemetry.io/otel/trace => ../trace
require (
github.com/google/go-cmp v0.5.7
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/internal/metric v0.27.0
)
replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough replace go.opentelemetry.io/otel/example/passthrough => ../example/passthrough
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../exporters/otlp/otlptrace
@ -53,8 +48,6 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../ex
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../exporters/otlp/otlptrace/otlptracehttp
replace go.opentelemetry.io/otel/internal/metric => ../internal/metric
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../exporters/otlp/otlpmetric
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../exporters/otlp/otlpmetric/otlpmetricgrpc

View File

@ -1,8 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
@ -11,9 +9,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,70 @@
// 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 asyncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Gauge creates an instrument for recording the current value.
Gauge(name string, opts ...instrument.Option) (Gauge, error)
}
// Counter is an instrument that records increasing values.
type Counter interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// UpDownCounter is an instrument that records increasing or decresing values.
type UpDownCounter interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// Gauge is an instrument that records independent readings.
type Gauge interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x float64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}

View File

@ -0,0 +1,70 @@
// 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 asyncint64 // import "go.opentelemetry.io/otel/metric/instrument/asyncint64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Gauge creates an instrument for recording the current value.
Gauge(name string, opts ...instrument.Option) (Gauge, error)
}
// Counter is an instrument that records increasing values.
type Counter interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// UpDownCounter is an instrument that records increasing or decresing values.
type UpDownCounter interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}
// Gauge is an instrument that records independent readings.
type Gauge interface {
// Observe records the state of the instrument.
//
// It is only valid to call this within a callback. If called outside of the
// registered callback it should have no effect on the instrument, and an
// error will be reported via the error handler.
Observe(ctx context.Context, x int64, attrs ...attribute.KeyValue)
instrument.Asynchronous
}

View File

@ -0,0 +1,69 @@
// 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 instrument // import "go.opentelemetry.io/otel/metric/instrument"
import "go.opentelemetry.io/otel/metric/unit"
// Config contains options for metric instrument descriptors.
type Config struct {
description string
unit unit.Unit
}
// Description describes the instrument in human-readable terms.
func (cfg Config) Description() string {
return cfg.description
}
// Unit describes the measurement unit for a instrument.
func (cfg Config) Unit() unit.Unit {
return cfg.unit
}
// Option is an interface for applying metric instrument options.
type Option interface {
applyInstrument(Config) Config
}
// NewConfig creates a new Config and applies all the given options.
func NewConfig(opts ...Option) Config {
var config Config
for _, o := range opts {
config = o.applyInstrument(config)
}
return config
}
type optionFunc func(Config) Config
func (fn optionFunc) applyInstrument(cfg Config) Config {
return fn(cfg)
}
// WithDescription applies provided description.
func WithDescription(desc string) Option {
return optionFunc(func(cfg Config) Config {
cfg.description = desc
return cfg
})
}
// WithUnit applies provided unit.
func WithUnit(unit unit.Unit) Option {
return optionFunc(func(cfg Config) Config {
cfg.unit = unit
return cfg
})
}

View File

@ -12,23 +12,19 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package metric package instrument // import "go.opentelemetry.io/otel/metric/instrument"
import ( // Asynchronous instruments are instruments that are updated within a Callback.
"testing" // If an instrument is observed outside of it's callback it should be an error.
) //
// This interface is used as a grouping mechanism.
func TestNewNoopMeterProvider(t *testing.T) { type Asynchronous interface {
got, want := NewNoopMeterProvider(), noopMeterProvider{} asynchronous()
if got != want {
t.Errorf("NewNoopMeterProvider() returned %#v, want %#v", got, want)
}
} }
func TestNoopMeterProviderMeter(t *testing.T) { // Synchronous instruments are updated in line with application code.
mp := NewNoopMeterProvider() //
got, want := mp.Meter(""), Meter{} // This interface is used as a grouping mechanism.
if got != want { type Synchronous interface {
t.Errorf("noopMeterProvider.Meter() returned %#v, want %#v", got, want) synchronous()
}
} }

View File

@ -0,0 +1,56 @@
// 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 syncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/syncfloat64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Histogram creates an instrument for recording a distribution of values.
Histogram(name string, opts ...instrument.Option) (Histogram, error)
}
// Counter is an instrument that records increasing values.
type Counter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// UpDownCounter is an instrument that records increasing or decresing values.
type UpDownCounter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// Histogram is an instrument that records a distribution of values.
type Histogram interface {
// Record adds an additional value to the distribution.
Record(ctx context.Context, incr float64, attrs ...attribute.KeyValue)
instrument.Synchronous
}

View File

@ -0,0 +1,56 @@
// 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 syncint64 // import "go.opentelemetry.io/otel/metric/instrument/syncint64"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
)
// InstrumentProvider provides access to individual instruments.
type InstrumentProvider interface {
// Counter creates an instrument for recording increasing values.
Counter(name string, opts ...instrument.Option) (Counter, error)
// UpDownCounter creates an instrument for recording changes of a value.
UpDownCounter(name string, opts ...instrument.Option) (UpDownCounter, error)
// Histogram creates an instrument for recording a distribution of values.
Histogram(name string, opts ...instrument.Option) (Histogram, error)
}
// Counter is an instrument that records increasing values.
type Counter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// UpDownCounter is an instrument that records increasing or decresing values.
type UpDownCounter interface {
// Add records a change to the counter.
Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}
// Histogram is an instrument that records a distribution of values.
type Histogram interface {
// Record adds an additional value to the distribution.
Record(ctx context.Context, incr int64, attrs ...attribute.KeyValue)
instrument.Synchronous
}

60
metric/meter.go Normal file
View File

@ -0,0 +1,60 @@
// 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/metric"
import (
"context"
"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"
)
// MeterProvider provides access to named Meter instances, for instrumenting
// an application or library.
type MeterProvider interface {
// Meter creates an instance of a `Meter` interface. The instrumentationName
// must be the name of the library providing instrumentation. This name may
// be the same as the instrumented code only if that code provides built-in
// instrumentation. If the instrumentationName is empty, then a
// implementation defined default name will be used instead.
Meter(instrumentationName string, opts ...MeterOption) Meter
}
// Meter provides access to instrument instances for recording metrics.
type Meter interface {
// AsyncInt64 is the namespace for the Asynchronous Integer instruments.
//
// To Observe data with instruments it must be registered in a callback.
AsyncInt64() asyncint64.InstrumentProvider
// AsyncFloat64 is the namespace for the Asynchronous Float instruments
//
// To Observe data with instruments it must be registered in a callback.
AsyncFloat64() asyncfloat64.InstrumentProvider
// RegisterCallback captures the function that will be called during Collect.
//
// It is only valid to call Observe within the scope of the passed function,
// and only on the instruments that were registered with this call.
RegisterCallback(insts []instrument.Asynchronous, function func(context.Context)) error
// SyncInt64 is the namespace for the Synchronous Integer instruments
SyncInt64() syncint64.InstrumentProvider
// SyncFloat64 is the namespace for the Synchronous Float instruments
SyncFloat64() syncfloat64.InstrumentProvider
}

View File

@ -1,538 +0,0 @@
// 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/metric"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// MeterProvider supports named Meter instances.
type MeterProvider interface {
// Meter creates an implementation of the Meter interface.
// The instrumentationName must be the name of the library providing
// instrumentation. This name may be the same as the instrumented code
// only if that code provides built-in instrumentation. If the
// instrumentationName is empty, then a implementation defined default
// name will be used instead.
Meter(instrumentationName string, opts ...MeterOption) Meter
}
// Meter is the creator of metric instruments.
//
// An uninitialized Meter is a no-op implementation.
type Meter struct {
impl sdkapi.MeterImpl
}
// WrapMeterImpl constructs a `Meter` implementation from a
// `MeterImpl` implementation.
func WrapMeterImpl(impl sdkapi.MeterImpl) Meter {
return Meter{
impl: impl,
}
}
// Measurement is used for reporting a synchronous batch of metric
// values. Instances of this type should be created by synchronous
// instruments (e.g., Int64Counter.Measurement()).
//
// Note: This is an alias because it is a first-class member of the
// API but is also part of the lower-level sdkapi interface.
type Measurement = sdkapi.Measurement
// Observation is used for reporting an asynchronous batch of metric
// values. Instances of this type should be created by asynchronous
// instruments (e.g., Int64GaugeObserver.Observation()).
//
// Note: This is an alias because it is a first-class member of the
// API but is also part of the lower-level sdkapi interface.
type Observation = sdkapi.Observation
// RecordBatch atomically records a batch of measurements.
func (m Meter) RecordBatch(ctx context.Context, ls []attribute.KeyValue, ms ...Measurement) {
if m.impl == nil {
return
}
m.impl.RecordBatch(ctx, ls, ms...)
}
// NewBatchObserver creates a new BatchObserver that supports
// making batches of observations for multiple instruments.
func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver {
return BatchObserver{
meter: m,
runner: newBatchAsyncRunner(callback),
}
}
// NewInt64Counter creates a new integer Counter instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) {
return wrapInt64CounterInstrument(
m.newSync(name, sdkapi.CounterInstrumentKind, number.Int64Kind, options))
}
// NewFloat64Counter creates a new floating point Counter with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) {
return wrapFloat64CounterInstrument(
m.newSync(name, sdkapi.CounterInstrumentKind, number.Float64Kind, options))
}
// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) {
return wrapInt64UpDownCounterInstrument(
m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Int64Kind, options))
}
// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) {
return wrapFloat64UpDownCounterInstrument(
m.newSync(name, sdkapi.UpDownCounterInstrumentKind, number.Float64Kind, options))
}
// NewInt64Histogram creates a new integer Histogram instrument with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewInt64Histogram(name string, opts ...InstrumentOption) (Int64Histogram, error) {
return wrapInt64HistogramInstrument(
m.newSync(name, sdkapi.HistogramInstrumentKind, number.Int64Kind, opts))
}
// NewFloat64Histogram creates a new floating point Histogram with the
// given name, customized with options. May return an error if the
// name is invalid (e.g., empty) or improperly registered (e.g.,
// duplicate registration).
func (m Meter) NewFloat64Histogram(name string, opts ...InstrumentOption) (Float64Histogram, error) {
return wrapFloat64HistogramInstrument(
m.newSync(name, sdkapi.HistogramInstrumentKind, number.Float64Kind, opts))
}
// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64GaugeObserver, error) {
if callback == nil {
return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64GaugeObserverInstrument(
m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64GaugeObserver, error) {
if callback == nil {
return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64GaugeObserverInstrument(
m.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64CounterObserver creates a new integer CounterObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64CounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64CounterObserver, error) {
if callback == nil {
return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64CounterObserverInstrument(
m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64CounterObserver creates a new floating point CounterObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64CounterObserver, error) {
if callback == nil {
return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64CounterObserverInstrument(
m.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
// with the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
if callback == nil {
return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64UpDownCounterObserverInstrument(
m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts,
newInt64AsyncRunner(callback)))
}
// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
// the given name, running a given callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (m Meter) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
if callback == nil {
return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64UpDownCounterObserverInstrument(
m.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
newFloat64AsyncRunner(callback)))
}
// NewInt64GaugeObserver creates a new integer GaugeObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64GaugeObserver(name string, opts ...InstrumentOption) (Int64GaugeObserver, error) {
if b.runner == nil {
return wrapInt64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64GaugeObserverInstrument(
b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64GaugeObserver creates a new floating point GaugeObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64GaugeObserver(name string, opts ...InstrumentOption) (Float64GaugeObserver, error) {
if b.runner == nil {
return wrapFloat64GaugeObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64GaugeObserverInstrument(
b.meter.newAsync(name, sdkapi.GaugeObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// NewInt64CounterObserver creates a new integer CounterObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64CounterObserver(name string, opts ...InstrumentOption) (Int64CounterObserver, error) {
if b.runner == nil {
return wrapInt64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64CounterObserverInstrument(
b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64CounterObserver creates a new floating point CounterObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64CounterObserver(name string, opts ...InstrumentOption) (Float64CounterObserver, error) {
if b.runner == nil {
return wrapFloat64CounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64CounterObserverInstrument(
b.meter.newAsync(name, sdkapi.CounterObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// NewInt64UpDownCounterObserver creates a new integer UpDownCounterObserver instrument
// with the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewInt64UpDownCounterObserver(name string, opts ...InstrumentOption) (Int64UpDownCounterObserver, error) {
if b.runner == nil {
return wrapInt64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapInt64UpDownCounterObserverInstrument(
b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Int64Kind, opts, b.runner))
}
// NewFloat64UpDownCounterObserver creates a new floating point UpDownCounterObserver with
// the given name, running in a batch callback, and customized with
// options. May return an error if the name is invalid (e.g., empty)
// or improperly registered (e.g., duplicate registration).
func (b BatchObserver) NewFloat64UpDownCounterObserver(name string, opts ...InstrumentOption) (Float64UpDownCounterObserver, error) {
if b.runner == nil {
return wrapFloat64UpDownCounterObserverInstrument(sdkapi.NewNoopAsyncInstrument(), nil)
}
return wrapFloat64UpDownCounterObserverInstrument(
b.meter.newAsync(name, sdkapi.UpDownCounterObserverInstrumentKind, number.Float64Kind, opts,
b.runner))
}
// MeterImpl returns the underlying MeterImpl of this Meter.
func (m Meter) MeterImpl() sdkapi.MeterImpl {
return m.impl
}
// newAsync constructs one new asynchronous instrument.
func (m Meter) newAsync(
name string,
mkind sdkapi.InstrumentKind,
nkind number.Kind,
opts []InstrumentOption,
runner sdkapi.AsyncRunner,
) (
sdkapi.AsyncImpl,
error,
) {
if m.impl == nil {
return sdkapi.NewNoopAsyncInstrument(), nil
}
cfg := NewInstrumentConfig(opts...)
desc := sdkapi.NewDescriptor(name, mkind, nkind, cfg.description, cfg.unit)
return m.impl.NewAsyncInstrument(desc, runner)
}
// newSync constructs one new synchronous instrument.
func (m Meter) newSync(
name string,
metricKind sdkapi.InstrumentKind,
numberKind number.Kind,
opts []InstrumentOption,
) (
sdkapi.SyncImpl,
error,
) {
if m.impl == nil {
return sdkapi.NewNoopSyncInstrument(), nil
}
cfg := NewInstrumentConfig(opts...)
desc := sdkapi.NewDescriptor(name, metricKind, numberKind, cfg.description, cfg.unit)
return m.impl.NewSyncInstrument(desc)
}
// MeterMust is a wrapper for Meter interfaces that panics when any
// instrument constructor encounters an error.
type MeterMust struct {
meter Meter
}
// BatchObserverMust is a wrapper for BatchObserver that panics when
// any instrument constructor encounters an error.
type BatchObserverMust struct {
batch BatchObserver
}
// Must constructs a MeterMust implementation from a Meter, allowing
// the application to panic when any instrument constructor yields an
// error.
func Must(meter Meter) MeterMust {
return MeterMust{meter: meter}
}
// NewInt64Counter calls `Meter.NewInt64Counter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter {
if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter {
if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter {
if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter {
if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64Histogram calls `Meter.NewInt64Histogram` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64Histogram(name string, mos ...InstrumentOption) Int64Histogram {
if inst, err := mm.meter.NewInt64Histogram(name, mos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64Histogram calls `Meter.NewFloat64Histogram` and returns the
// instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64Histogram(name string, mos ...InstrumentOption) Float64Histogram {
if inst, err := mm.meter.NewFloat64Histogram(name, mos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64GaugeObserver calls `Meter.NewInt64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64GaugeObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64GaugeObserver {
if inst, err := mm.meter.NewInt64GaugeObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64GaugeObserver calls `Meter.NewFloat64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64GaugeObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64GaugeObserver {
if inst, err := mm.meter.NewFloat64GaugeObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64CounterObserver calls `Meter.NewInt64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64CounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64CounterObserver {
if inst, err := mm.meter.NewInt64CounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64CounterObserver calls `Meter.NewFloat64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64CounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64CounterObserver {
if inst, err := mm.meter.NewFloat64CounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounterObserver calls `Meter.NewInt64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewInt64UpDownCounterObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownCounterObserver {
if inst, err := mm.meter.NewInt64UpDownCounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounterObserver calls `Meter.NewFloat64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (mm MeterMust) NewFloat64UpDownCounterObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownCounterObserver {
if inst, err := mm.meter.NewFloat64UpDownCounterObserver(name, callback, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewBatchObserver returns a wrapper around BatchObserver that panics
// when any instrument constructor returns an error.
func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust {
return BatchObserverMust{
batch: mm.meter.NewBatchObserver(callback),
}
}
// NewInt64GaugeObserver calls `BatchObserver.NewInt64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64GaugeObserver(name string, oos ...InstrumentOption) Int64GaugeObserver {
if inst, err := bm.batch.NewInt64GaugeObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64GaugeObserver calls `BatchObserver.NewFloat64GaugeObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64GaugeObserver(name string, oos ...InstrumentOption) Float64GaugeObserver {
if inst, err := bm.batch.NewFloat64GaugeObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64CounterObserver calls `BatchObserver.NewInt64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64CounterObserver(name string, oos ...InstrumentOption) Int64CounterObserver {
if inst, err := bm.batch.NewInt64CounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64CounterObserver calls `BatchObserver.NewFloat64CounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64CounterObserver(name string, oos ...InstrumentOption) Float64CounterObserver {
if inst, err := bm.batch.NewFloat64CounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewInt64UpDownCounterObserver calls `BatchObserver.NewInt64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewInt64UpDownCounterObserver(name string, oos ...InstrumentOption) Int64UpDownCounterObserver {
if inst, err := bm.batch.NewInt64UpDownCounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}
// NewFloat64UpDownCounterObserver calls `BatchObserver.NewFloat64UpDownCounterObserver` and
// returns the instrument, panicking if it encounters an error.
func (bm BatchObserverMust) NewFloat64UpDownCounterObserver(name string, oos ...InstrumentOption) Float64UpDownCounterObserver {
if inst, err := bm.batch.NewFloat64UpDownCounterObserver(name, oos...); err != nil {
panic(err)
} else {
return inst
}
}

View File

@ -1,464 +0,0 @@
// 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/metric"
import (
"context"
"errors"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
// ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil.
var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
// Int64ObserverFunc is a type of callback that integral
// observers run.
type Int64ObserverFunc func(context.Context, Int64ObserverResult)
// Float64ObserverFunc is a type of callback that floating point
// observers run.
type Float64ObserverFunc func(context.Context, Float64ObserverResult)
// BatchObserverFunc is a callback argument for use with any
// Observer instrument that will be reported as a batch of
// observations.
type BatchObserverFunc func(context.Context, BatchObserverResult)
// Int64ObserverResult is passed to an observer callback to capture
// observations for one asynchronous integer metric instrument.
type Int64ObserverResult struct {
instrument sdkapi.AsyncImpl
function func([]attribute.KeyValue, ...Observation)
}
// Float64ObserverResult is passed to an observer callback to capture
// observations for one asynchronous floating point metric instrument.
type Float64ObserverResult struct {
instrument sdkapi.AsyncImpl
function func([]attribute.KeyValue, ...Observation)
}
// BatchObserverResult is passed to a batch observer callback to
// capture observations for multiple asynchronous instruments.
type BatchObserverResult struct {
function func([]attribute.KeyValue, ...Observation)
}
// Observe captures a single integer value from the associated
// instrument callback, with the given labels.
func (ir Int64ObserverResult) Observe(value int64, labels ...attribute.KeyValue) {
ir.function(labels, sdkapi.NewObservation(ir.instrument, number.NewInt64Number(value)))
}
// Observe captures a single floating point value from the associated
// instrument callback, with the given labels.
func (fr Float64ObserverResult) Observe(value float64, labels ...attribute.KeyValue) {
fr.function(labels, sdkapi.NewObservation(fr.instrument, number.NewFloat64Number(value)))
}
// Observe captures a multiple observations from the associated batch
// instrument callback, with the given labels.
func (br BatchObserverResult) Observe(labels []attribute.KeyValue, obs ...Observation) {
br.function(labels, obs...)
}
var _ sdkapi.AsyncSingleRunner = (*Int64ObserverFunc)(nil)
var _ sdkapi.AsyncSingleRunner = (*Float64ObserverFunc)(nil)
var _ sdkapi.AsyncBatchRunner = (*BatchObserverFunc)(nil)
// newInt64AsyncRunner returns a single-observer callback for integer Observer instruments.
func newInt64AsyncRunner(c Int64ObserverFunc) sdkapi.AsyncSingleRunner {
return &c
}
// newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments.
func newFloat64AsyncRunner(c Float64ObserverFunc) sdkapi.AsyncSingleRunner {
return &c
}
// newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments.
func newBatchAsyncRunner(c BatchObserverFunc) sdkapi.AsyncBatchRunner {
return &c
}
// AnyRunner implements AsyncRunner.
func (*Int64ObserverFunc) AnyRunner() {}
// AnyRunner implements AsyncRunner.
func (*Float64ObserverFunc) AnyRunner() {}
// AnyRunner implements AsyncRunner.
func (*BatchObserverFunc) AnyRunner() {}
// Run implements AsyncSingleRunner.
func (i *Int64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
(*i)(ctx, Int64ObserverResult{
instrument: impl,
function: function,
})
}
// Run implements AsyncSingleRunner.
func (f *Float64ObserverFunc) Run(ctx context.Context, impl sdkapi.AsyncImpl, function func([]attribute.KeyValue, ...Observation)) {
(*f)(ctx, Float64ObserverResult{
instrument: impl,
function: function,
})
}
// Run implements AsyncBatchRunner.
func (b *BatchObserverFunc) Run(ctx context.Context, function func([]attribute.KeyValue, ...Observation)) {
(*b)(ctx, BatchObserverResult{
function: function,
})
}
// wrapInt64GaugeObserverInstrument converts an AsyncImpl into Int64GaugeObserver.
func wrapInt64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64GaugeObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64GaugeObserver{asyncInstrument: common}, err
}
// wrapFloat64GaugeObserverInstrument converts an AsyncImpl into Float64GaugeObserver.
func wrapFloat64GaugeObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64GaugeObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64GaugeObserver{asyncInstrument: common}, err
}
// wrapInt64CounterObserverInstrument converts an AsyncImpl into Int64CounterObserver.
func wrapInt64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64CounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64CounterObserver{asyncInstrument: common}, err
}
// wrapFloat64CounterObserverInstrument converts an AsyncImpl into Float64CounterObserver.
func wrapFloat64CounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64CounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64CounterObserver{asyncInstrument: common}, err
}
// wrapInt64UpDownCounterObserverInstrument converts an AsyncImpl into Int64UpDownCounterObserver.
func wrapInt64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Int64UpDownCounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Int64UpDownCounterObserver{asyncInstrument: common}, err
}
// wrapFloat64UpDownCounterObserverInstrument converts an AsyncImpl into Float64UpDownCounterObserver.
func wrapFloat64UpDownCounterObserverInstrument(asyncInst sdkapi.AsyncImpl, err error) (Float64UpDownCounterObserver, error) {
common, err := checkNewAsync(asyncInst, err)
return Float64UpDownCounterObserver{asyncInstrument: common}, err
}
// BatchObserver represents an Observer callback that can report
// observations for multiple instruments.
type BatchObserver struct {
meter Meter
runner sdkapi.AsyncBatchRunner
}
// Int64GaugeObserver is a metric that captures a set of int64 values at a
// point in time.
type Int64GaugeObserver struct {
asyncInstrument
}
// Float64GaugeObserver is a metric that captures a set of float64 values
// at a point in time.
type Float64GaugeObserver struct {
asyncInstrument
}
// Int64CounterObserver is a metric that captures a precomputed sum of
// int64 values at a point in time.
type Int64CounterObserver struct {
asyncInstrument
}
// Float64CounterObserver is a metric that captures a precomputed sum of
// float64 values at a point in time.
type Float64CounterObserver struct {
asyncInstrument
}
// Int64UpDownCounterObserver is a metric that captures a precomputed sum of
// int64 values at a point in time.
type Int64UpDownCounterObserver struct {
asyncInstrument
}
// Float64UpDownCounterObserver is a metric that captures a precomputed sum of
// float64 values at a point in time.
type Float64UpDownCounterObserver struct {
asyncInstrument
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64GaugeObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64GaugeObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64CounterObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64CounterObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (i Int64UpDownCounterObserver) Observation(v int64) Observation {
return sdkapi.NewObservation(i.instrument, number.NewInt64Number(v))
}
// Observation returns an Observation, a BatchObserverFunc
// argument, for an asynchronous integer instrument.
// This returns an implementation-level object for use by the SDK,
// users should not refer to this.
func (f Float64UpDownCounterObserver) Observation(v float64) Observation {
return sdkapi.NewObservation(f.instrument, number.NewFloat64Number(v))
}
// syncInstrument contains a SyncImpl.
type syncInstrument struct {
instrument sdkapi.SyncImpl
}
// asyncInstrument contains a AsyncImpl.
type asyncInstrument struct {
instrument sdkapi.AsyncImpl
}
// AsyncImpl implements AsyncImpl.
func (a asyncInstrument) AsyncImpl() sdkapi.AsyncImpl {
return a.instrument
}
// SyncImpl returns the implementation object for synchronous instruments.
func (s syncInstrument) SyncImpl() sdkapi.SyncImpl {
return s.instrument
}
func (s syncInstrument) float64Measurement(value float64) Measurement {
return sdkapi.NewMeasurement(s.instrument, number.NewFloat64Number(value))
}
func (s syncInstrument) int64Measurement(value int64) Measurement {
return sdkapi.NewMeasurement(s.instrument, number.NewInt64Number(value))
}
func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
s.instrument.RecordOne(ctx, number, labels)
}
// checkNewAsync receives an AsyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewAsync(instrument sdkapi.AsyncImpl, err error) (asyncInstrument, error) {
if instrument == nil {
if err == nil {
err = ErrSDKReturnedNilImpl
}
instrument = sdkapi.NewNoopAsyncInstrument()
}
return asyncInstrument{
instrument: instrument,
}, err
}
// checkNewSync receives an SyncImpl and potential
// error, and returns the same types, checking for and ensuring that
// the returned interface is not nil.
func checkNewSync(instrument sdkapi.SyncImpl, err error) (syncInstrument, error) {
if instrument == nil {
if err == nil {
err = ErrSDKReturnedNilImpl
}
// Note: an alternate behavior would be to synthesize a new name
// or group all duplicately-named instruments of a certain type
// together and use a tag for the original name, e.g.,
// name = 'invalid.counter.int64'
// label = 'original-name=duplicate-counter-name'
instrument = sdkapi.NewNoopSyncInstrument()
}
return syncInstrument{
instrument: instrument,
}, err
}
// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter.
func wrapInt64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Int64Counter{syncInstrument: common}, err
}
// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter.
func wrapFloat64CounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Counter, error) {
common, err := checkNewSync(syncInst, err)
return Float64Counter{syncInstrument: common}, err
}
// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter.
func wrapInt64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Int64UpDownCounter, error) {
common, err := checkNewSync(syncInst, err)
return Int64UpDownCounter{syncInstrument: common}, err
}
// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter.
func wrapFloat64UpDownCounterInstrument(syncInst sdkapi.SyncImpl, err error) (Float64UpDownCounter, error) {
common, err := checkNewSync(syncInst, err)
return Float64UpDownCounter{syncInstrument: common}, err
}
// wrapInt64HistogramInstrument converts a SyncImpl into Int64Histogram.
func wrapInt64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Int64Histogram, error) {
common, err := checkNewSync(syncInst, err)
return Int64Histogram{syncInstrument: common}, err
}
// wrapFloat64HistogramInstrument converts a SyncImpl into Float64Histogram.
func wrapFloat64HistogramInstrument(syncInst sdkapi.SyncImpl, err error) (Float64Histogram, error) {
common, err := checkNewSync(syncInst, err)
return Float64Histogram{syncInstrument: common}, err
}
// Float64Counter is a metric that accumulates float64 values.
type Float64Counter struct {
syncInstrument
}
// Int64Counter is a metric that accumulates int64 values.
type Int64Counter struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64Counter) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64Counter) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Float64Counter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Int64Counter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}
// Float64UpDownCounter is a metric instrument that sums floating
// point values.
type Float64UpDownCounter struct {
syncInstrument
}
// Int64UpDownCounter is a metric instrument that sums integer values.
type Int64UpDownCounter struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64UpDownCounter) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64UpDownCounter) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Add adds the value to the counter's sum. The labels should contain
// the keys and values to be associated with this value.
func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}
// Float64Histogram is a metric that records float64 values.
type Float64Histogram struct {
syncInstrument
}
// Int64Histogram is a metric that records int64 values.
type Int64Histogram struct {
syncInstrument
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Float64Histogram) Measurement(value float64) Measurement {
return c.float64Measurement(value)
}
// Measurement creates a Measurement object to use with batch
// recording.
func (c Int64Histogram) Measurement(value int64) Measurement {
return c.int64Measurement(value)
}
// Record adds a new value to the list of Histogram's records. The
// labels should contain the keys and values to be associated with
// this value.
func (c Float64Histogram) Record(ctx context.Context, value float64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewFloat64Number(value), labels)
}
// Record adds a new value to the Histogram's distribution. The
// labels should contain the keys and values to be associated with
// this value.
func (c Int64Histogram) Record(ctx context.Context, value int64, labels ...attribute.KeyValue) {
c.directRecord(ctx, number.NewInt64Number(value), labels)
}

View File

@ -1,497 +0,0 @@
// 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_test
import (
"context"
"errors"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/metric/unit"
)
var Must = metric.Must
var (
syncKinds = []sdkapi.InstrumentKind{
sdkapi.HistogramInstrumentKind,
sdkapi.CounterInstrumentKind,
sdkapi.UpDownCounterInstrumentKind,
}
asyncKinds = []sdkapi.InstrumentKind{
sdkapi.GaugeObserverInstrumentKind,
sdkapi.CounterObserverInstrumentKind,
sdkapi.UpDownCounterObserverInstrumentKind,
}
addingKinds = []sdkapi.InstrumentKind{
sdkapi.CounterInstrumentKind,
sdkapi.UpDownCounterInstrumentKind,
sdkapi.CounterObserverInstrumentKind,
sdkapi.UpDownCounterObserverInstrumentKind,
}
groupingKinds = []sdkapi.InstrumentKind{
sdkapi.HistogramInstrumentKind,
sdkapi.GaugeObserverInstrumentKind,
}
monotonicKinds = []sdkapi.InstrumentKind{
sdkapi.CounterInstrumentKind,
sdkapi.CounterObserverInstrumentKind,
}
nonMonotonicKinds = []sdkapi.InstrumentKind{
sdkapi.UpDownCounterInstrumentKind,
sdkapi.UpDownCounterObserverInstrumentKind,
sdkapi.HistogramInstrumentKind,
sdkapi.GaugeObserverInstrumentKind,
}
precomputedSumKinds = []sdkapi.InstrumentKind{
sdkapi.CounterObserverInstrumentKind,
sdkapi.UpDownCounterObserverInstrumentKind,
}
nonPrecomputedSumKinds = []sdkapi.InstrumentKind{
sdkapi.CounterInstrumentKind,
sdkapi.UpDownCounterInstrumentKind,
sdkapi.HistogramInstrumentKind,
sdkapi.GaugeObserverInstrumentKind,
}
)
func TestSynchronous(t *testing.T) {
for _, k := range syncKinds {
require.True(t, k.Synchronous())
require.False(t, k.Asynchronous())
}
for _, k := range asyncKinds {
require.True(t, k.Asynchronous())
require.False(t, k.Synchronous())
}
}
func TestGrouping(t *testing.T) {
for _, k := range groupingKinds {
require.True(t, k.Grouping())
require.False(t, k.Adding())
}
for _, k := range addingKinds {
require.True(t, k.Adding())
require.False(t, k.Grouping())
}
}
func TestMonotonic(t *testing.T) {
for _, k := range monotonicKinds {
require.True(t, k.Monotonic())
}
for _, k := range nonMonotonicKinds {
require.False(t, k.Monotonic())
}
}
func TestPrecomputedSum(t *testing.T) {
for _, k := range precomputedSumKinds {
require.True(t, k.PrecomputedSum())
}
for _, k := range nonPrecomputedSumKinds {
require.False(t, k.PrecomputedSum())
}
}
func checkSyncBatches(ctx context.Context, t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, instrument sdkapi.InstrumentImpl, expected ...float64) {
t.Helper()
batchesCount := len(provider.MeasurementBatches)
if len(provider.MeasurementBatches) != len(expected) {
t.Errorf("Expected %d recorded measurement batches, got %d", batchesCount, len(provider.MeasurementBatches))
}
recorded := metrictest.AsStructs(provider.MeasurementBatches)
for i, batch := range provider.MeasurementBatches {
if len(batch.Measurements) != 1 {
t.Errorf("Expected 1 measurement in batch %d, got %d", i, len(batch.Measurements))
}
measurement := batch.Measurements[0]
descriptor := measurement.Instrument.Descriptor()
expected := metrictest.Measured{
Name: descriptor.Name(),
Library: metrictest.Library{
InstrumentationName: "apitest",
},
Labels: metrictest.LabelsToMap(labels...),
Number: metrictest.ResolveNumberByKind(t, nkind, expected[i]),
}
require.Equal(t, expected, recorded[i])
}
}
func TestOptions(t *testing.T) {
type testcase struct {
name string
opts []metric.InstrumentOption
desc string
unit unit.Unit
}
testcases := []testcase{
{
name: "no opts",
opts: nil,
desc: "",
unit: "",
},
{
name: "description",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
},
desc: "stuff",
unit: "",
},
{
name: "description override",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
metric.WithDescription("things"),
},
desc: "things",
unit: "",
},
{
name: "unit",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
},
desc: "",
unit: "s",
},
{
name: "description override",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
metric.WithDescription("things"),
},
desc: "things",
unit: "",
},
{
name: "unit",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
},
desc: "",
unit: "s",
},
{
name: "unit override",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
metric.WithUnit("h"),
},
desc: "",
unit: "h",
},
{
name: "all",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
metric.WithUnit("s"),
},
desc: "stuff",
unit: "s",
},
}
for idx, tt := range testcases {
t.Logf("Testing counter case %s (%d)", tt.name, idx)
cfg := metric.NewInstrumentConfig(tt.opts...)
if diff := cmp.Diff(cfg.Description(), tt.desc); diff != "" {
t.Errorf("Compare Description: -got +want %s", diff)
}
if diff := cmp.Diff(cfg.Unit(), tt.unit); diff != "" {
t.Errorf("Compare Unit: -got +want %s", diff)
}
}
}
func testPair() (*metrictest.MeterProvider, metric.Meter) {
provider := metrictest.NewMeterProvider()
return provider, provider.Meter("apitest")
}
func TestCounter(t *testing.T) {
// N.B. the API does not check for negative
// values, that's the SDK's responsibility.
t.Run("float64 counter", func(t *testing.T) {
provider, meter := testPair()
c := Must(meter).NewFloat64Counter("test.counter.float")
ctx := context.Background()
labels := []attribute.KeyValue{attribute.String("A", "B")}
c.Add(ctx, 1994.1, labels...)
meter.RecordBatch(ctx, labels, c.Measurement(42))
checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(),
1994.1, 42,
)
})
t.Run("int64 counter", func(t *testing.T) {
provider, meter := testPair()
c := Must(meter).NewInt64Counter("test.counter.int")
ctx := context.Background()
labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
c.Add(ctx, 42, labels...)
meter.RecordBatch(ctx, labels, c.Measurement(420000))
checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.CounterInstrumentKind, c.SyncImpl(),
42, 420000,
)
})
t.Run("int64 updowncounter", func(t *testing.T) {
provider, meter := testPair()
c := Must(meter).NewInt64UpDownCounter("test.updowncounter.int")
ctx := context.Background()
labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
c.Add(ctx, 100, labels...)
meter.RecordBatch(ctx, labels, c.Measurement(42))
checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(),
100, 42,
)
})
t.Run("float64 updowncounter", func(t *testing.T) {
provider, meter := testPair()
c := Must(meter).NewFloat64UpDownCounter("test.updowncounter.float")
ctx := context.Background()
labels := []attribute.KeyValue{attribute.String("A", "B"), attribute.String("C", "D")}
c.Add(ctx, 100.1, labels...)
meter.RecordBatch(ctx, labels, c.Measurement(-100.1))
checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterInstrumentKind, c.SyncImpl(),
100.1, -100.1,
)
})
}
func TestHistogram(t *testing.T) {
t.Run("float64 histogram", func(t *testing.T) {
provider, meter := testPair()
m := Must(meter).NewFloat64Histogram("test.histogram.float")
ctx := context.Background()
labels := []attribute.KeyValue{}
m.Record(ctx, 42, labels...)
meter.RecordBatch(ctx, labels, m.Measurement(-100.5))
checkSyncBatches(ctx, t, labels, provider, number.Float64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(),
42, -100.5,
)
})
t.Run("int64 histogram", func(t *testing.T) {
provider, meter := testPair()
m := Must(meter).NewInt64Histogram("test.histogram.int")
ctx := context.Background()
labels := []attribute.KeyValue{attribute.Int("I", 1)}
m.Record(ctx, 173, labels...)
meter.RecordBatch(ctx, labels, m.Measurement(0))
checkSyncBatches(ctx, t, labels, provider, number.Int64Kind, sdkapi.HistogramInstrumentKind, m.SyncImpl(),
173, 0,
)
})
}
func TestObserverInstruments(t *testing.T) {
t.Run("float gauge", func(t *testing.T) {
labels := []attribute.KeyValue{attribute.String("O", "P")}
provider, meter := testPair()
o := Must(meter).NewFloat64GaugeObserver("test.gauge.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(),
42.1,
)
})
t.Run("int gauge", func(t *testing.T) {
labels := []attribute.KeyValue{}
provider, meter := testPair()
o := Must(meter).NewInt64GaugeObserver("test.gauge.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.GaugeObserverInstrumentKind, o.AsyncImpl(),
-142,
)
})
t.Run("float counterobserver", func(t *testing.T) {
labels := []attribute.KeyValue{attribute.String("O", "P")}
provider, meter := testPair()
o := Must(meter).NewFloat64CounterObserver("test.counter.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(),
42.1,
)
})
t.Run("int counterobserver", func(t *testing.T) {
labels := []attribute.KeyValue{}
provider, meter := testPair()
o := Must(meter).NewInt64CounterObserver("test.counter.int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.CounterObserverInstrumentKind, o.AsyncImpl(),
-142,
)
})
t.Run("float updowncounterobserver", func(t *testing.T) {
labels := []attribute.KeyValue{attribute.String("O", "P")}
provider, meter := testPair()
o := Must(meter).NewFloat64UpDownCounterObserver("test.updowncounter.float", func(_ context.Context, result metric.Float64ObserverResult) {
result.Observe(42.1, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Float64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(),
42.1,
)
})
t.Run("int updowncounterobserver", func(t *testing.T) {
labels := []attribute.KeyValue{}
provider, meter := testPair()
o := Must(meter).NewInt64UpDownCounterObserver("test..int", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(-142, labels...)
})
provider.RunAsyncInstruments()
checkObserverBatch(t, labels, provider, number.Int64Kind, sdkapi.UpDownCounterObserverInstrumentKind, o.AsyncImpl(),
-142,
)
})
}
func TestBatchObserverInstruments(t *testing.T) {
provider, meter := testPair()
var obs1 metric.Int64GaugeObserver
var obs2 metric.Float64GaugeObserver
labels := []attribute.KeyValue{
attribute.String("A", "B"),
attribute.String("C", "D"),
}
cb := Must(meter).NewBatchObserver(
func(_ context.Context, result metric.BatchObserverResult) {
result.Observe(labels,
obs1.Observation(42),
obs2.Observation(42.0),
)
},
)
obs1 = cb.NewInt64GaugeObserver("test.gauge.int")
obs2 = cb.NewFloat64GaugeObserver("test.gauge.float")
provider.RunAsyncInstruments()
require.Len(t, provider.MeasurementBatches, 1)
impl1 := obs1.AsyncImpl().Implementation().(*metrictest.Async)
impl2 := obs2.AsyncImpl().Implementation().(*metrictest.Async)
require.NotNil(t, impl1)
require.NotNil(t, impl2)
got := provider.MeasurementBatches[0]
require.Equal(t, labels, got.Labels)
require.Len(t, got.Measurements, 2)
m1 := got.Measurements[0]
require.Equal(t, impl1, m1.Instrument.Implementation().(*metrictest.Async))
require.Equal(t, 0, m1.Number.CompareNumber(number.Int64Kind, metrictest.ResolveNumberByKind(t, number.Int64Kind, 42)))
m2 := got.Measurements[1]
require.Equal(t, impl2, m2.Instrument.Implementation().(*metrictest.Async))
require.Equal(t, 0, m2.Number.CompareNumber(number.Float64Kind, metrictest.ResolveNumberByKind(t, number.Float64Kind, 42)))
}
func checkObserverBatch(t *testing.T, labels []attribute.KeyValue, provider *metrictest.MeterProvider, nkind number.Kind, mkind sdkapi.InstrumentKind, observer sdkapi.AsyncImpl, expected float64) {
t.Helper()
assert.Len(t, provider.MeasurementBatches, 1)
if len(provider.MeasurementBatches) < 1 {
return
}
o := observer.Implementation().(*metrictest.Async)
if !assert.NotNil(t, o) {
return
}
got := provider.MeasurementBatches[0]
assert.Equal(t, labels, got.Labels)
assert.Len(t, got.Measurements, 1)
if len(got.Measurements) < 1 {
return
}
measurement := got.Measurements[0]
require.Equal(t, mkind, measurement.Instrument.Descriptor().InstrumentKind())
assert.Equal(t, o, measurement.Instrument.Implementation().(*metrictest.Async))
ft := metrictest.ResolveNumberByKind(t, nkind, expected)
assert.Equal(t, 0, measurement.Number.CompareNumber(nkind, ft))
}
type testWrappedMeter struct {
}
var _ sdkapi.MeterImpl = testWrappedMeter{}
func (testWrappedMeter) RecordBatch(context.Context, []attribute.KeyValue, ...sdkapi.Measurement) {
}
func (testWrappedMeter) NewSyncInstrument(_ sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
return nil, nil
}
func (testWrappedMeter) NewAsyncInstrument(_ sdkapi.Descriptor, _ sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) {
return nil, errors.New("Test wrap error")
}
func TestWrappedInstrumentError(t *testing.T) {
impl := &testWrappedMeter{}
meter := metric.WrapMeterImpl(impl)
histogram, err := meter.NewInt64Histogram("test.histogram")
require.Equal(t, err, metric.ErrSDKReturnedNilImpl)
require.NotNil(t, histogram.SyncImpl())
observer, err := meter.NewInt64GaugeObserver("test.observer", func(_ context.Context, result metric.Int64ObserverResult) {})
require.NotNil(t, err)
require.NotNil(t, observer.AsyncImpl())
}
func TestNilCallbackObserverNoop(t *testing.T) {
// Tests that a nil callback yields a no-op observer without error.
_, meter := testPair()
observer := Must(meter).NewInt64GaugeObserver("test.observer", nil)
impl := observer.AsyncImpl().Implementation()
desc := observer.AsyncImpl().Descriptor()
require.Equal(t, nil, impl)
require.Equal(t, "", desc.Name())
}

View File

@ -1,290 +0,0 @@
// 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 metrictest // import "go.opentelemetry.io/otel/metric/metrictest"
import (
"context"
"sync"
"testing"
"go.opentelemetry.io/otel/attribute"
internalmetric "go.opentelemetry.io/otel/internal/metric"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
)
type (
Handle struct {
Instrument *Sync
Labels []attribute.KeyValue
}
// Library is the same as "sdk/instrumentation".Library but there is
// a package cycle to use it.
Library struct {
InstrumentationName string
InstrumentationVersion string
SchemaURL string
}
Batch struct {
// Measurement needs to be aligned for 64-bit atomic operations.
Measurements []Measurement
Ctx context.Context
Labels []attribute.KeyValue
Library Library
}
// MeterImpl is an OpenTelemetry Meter implementation used for testing.
MeterImpl struct {
library Library
provider *MeterProvider
asyncInstruments *internalmetric.AsyncInstrumentState
}
// MeterProvider is a collection of named MeterImpls used for testing.
MeterProvider struct {
lock sync.Mutex
MeasurementBatches []Batch
impls []*MeterImpl
}
Measurement struct {
// Number needs to be aligned for 64-bit atomic operations.
Number number.Number
Instrument sdkapi.InstrumentImpl
}
Instrument struct {
meter *MeterImpl
descriptor sdkapi.Descriptor
}
Async struct {
Instrument
runner sdkapi.AsyncRunner
}
Sync struct {
Instrument
}
)
var (
_ sdkapi.SyncImpl = &Sync{}
_ sdkapi.MeterImpl = &MeterImpl{}
_ sdkapi.AsyncImpl = &Async{}
)
// NewDescriptor is a test helper for constructing test metric
// descriptors using standard options.
func NewDescriptor(name string, ikind sdkapi.InstrumentKind, nkind number.Kind, opts ...metric.InstrumentOption) sdkapi.Descriptor {
cfg := metric.NewInstrumentConfig(opts...)
return sdkapi.NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit())
}
func (i Instrument) Descriptor() sdkapi.Descriptor {
return i.descriptor
}
func (a *Async) Implementation() interface{} {
return a
}
func (s *Sync) Implementation() interface{} {
return s
}
func (s *Sync) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
s.meter.doRecordSingle(ctx, labels, s, number)
}
func (h *Handle) RecordOne(ctx context.Context, number number.Number) {
h.Instrument.meter.doRecordSingle(ctx, h.Labels, h.Instrument, number)
}
func (h *Handle) Unbind() {
}
func (m *MeterImpl) doRecordSingle(ctx context.Context, labels []attribute.KeyValue, instrument sdkapi.InstrumentImpl, number number.Number) {
m.collect(ctx, labels, []Measurement{{
Instrument: instrument,
Number: number,
}})
}
// NewMeterProvider returns a MeterProvider suitable for testing.
// When the test is complete, consult MeterProvider.MeasurementBatches.
func NewMeterProvider() *MeterProvider {
return &MeterProvider{}
}
// Meter implements metric.MeterProvider.
func (p *MeterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter {
p.lock.Lock()
defer p.lock.Unlock()
cfg := metric.NewMeterConfig(opts...)
impl := &MeterImpl{
library: Library{
InstrumentationName: name,
InstrumentationVersion: cfg.InstrumentationVersion(),
SchemaURL: cfg.SchemaURL(),
},
provider: p,
asyncInstruments: internalmetric.NewAsyncInstrumentState(),
}
p.impls = append(p.impls, impl)
return metric.WrapMeterImpl(impl)
}
// NewSyncInstrument implements sdkapi.MeterImpl.
func (m *MeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
return &Sync{
Instrument{
descriptor: descriptor,
meter: m,
},
}, nil
}
// NewAsyncInstrument implements sdkapi.MeterImpl.
func (m *MeterImpl) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) {
a := &Async{
Instrument: Instrument{
descriptor: descriptor,
meter: m,
},
runner: runner,
}
m.provider.registerAsyncInstrument(a, m, runner)
return a, nil
}
// RecordBatch implements sdkapi.MeterImpl.
func (m *MeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurements ...sdkapi.Measurement) {
mm := make([]Measurement, len(measurements))
for i := 0; i < len(measurements); i++ {
m := measurements[i]
mm[i] = Measurement{
Instrument: m.SyncImpl().Implementation().(*Sync),
Number: m.Number(),
}
}
m.collect(ctx, labels, mm)
}
// CollectAsync is called from asyncInstruments.Run() with the lock held.
func (m *MeterImpl) CollectAsync(labels []attribute.KeyValue, obs ...sdkapi.Observation) {
mm := make([]Measurement, len(obs))
for i := 0; i < len(obs); i++ {
o := obs[i]
mm[i] = Measurement{
Instrument: o.AsyncImpl(),
Number: o.Number(),
}
}
m.collect(context.Background(), labels, mm)
}
// collect is called from CollectAsync() or RecordBatch() with the lock held.
func (m *MeterImpl) collect(ctx context.Context, labels []attribute.KeyValue, measurements []Measurement) {
m.provider.addMeasurement(Batch{
Ctx: ctx,
Labels: labels,
Measurements: measurements,
Library: m.library,
})
}
// registerAsyncInstrument locks the provider and registers the new Async instrument.
func (p *MeterProvider) registerAsyncInstrument(a *Async, m *MeterImpl, runner sdkapi.AsyncRunner) {
p.lock.Lock()
defer p.lock.Unlock()
m.asyncInstruments.Register(a, runner)
}
// addMeasurement locks the provider and adds the new measurement batch.
func (p *MeterProvider) addMeasurement(b Batch) {
p.lock.Lock()
defer p.lock.Unlock()
p.MeasurementBatches = append(p.MeasurementBatches, b)
}
// copyImpls locks the provider and copies the current list of *MeterImpls.
func (p *MeterProvider) copyImpls() []*MeterImpl {
p.lock.Lock()
defer p.lock.Unlock()
cpy := make([]*MeterImpl, len(p.impls))
copy(cpy, p.impls)
return cpy
}
// RunAsyncInstruments is used in tests to trigger collection from
// asynchronous instruments.
func (p *MeterProvider) RunAsyncInstruments() {
for _, impl := range p.copyImpls() {
impl.asyncInstruments.Run(context.Background(), impl)
}
}
// Measured is the helper struct which provides flat representation of recorded measurements
// to simplify testing
type Measured struct {
Name string
Labels map[attribute.Key]attribute.Value
Number number.Number
Library Library
}
// LabelsToMap converts label set to keyValue map, to be easily used in tests
func LabelsToMap(kvs ...attribute.KeyValue) map[attribute.Key]attribute.Value {
m := map[attribute.Key]attribute.Value{}
for _, label := range kvs {
m[label.Key] = label.Value
}
return m
}
// AsStructs converts recorded batches to array of flat, readable Measured helper structures
func AsStructs(batches []Batch) []Measured {
var r []Measured
for _, batch := range batches {
for _, m := range batch.Measurements {
r = append(r, Measured{
Name: m.Instrument.Descriptor().Name(),
Labels: LabelsToMap(batch.Labels...),
Number: m.Number,
Library: batch.Library,
})
}
}
return r
}
// ResolveNumberByKind takes defined metric descriptor creates a concrete typed metric number
func ResolveNumberByKind(t *testing.T, kind number.Kind, value float64) number.Number {
t.Helper()
switch kind {
case number.Int64Kind:
return number.NewInt64Number(int64(value))
case number.Float64Kind:
return number.NewFloat64Number(value)
}
panic("invalid number kind")
}

View File

@ -0,0 +1,138 @@
// 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 nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording"
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"
)
type nonrecordingAsyncFloat64Instrument struct {
instrument.Asynchronous
}
var (
_ asyncfloat64.InstrumentProvider = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.Counter = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.UpDownCounter = nonrecordingAsyncFloat64Instrument{}
_ asyncfloat64.Gauge = nonrecordingAsyncFloat64Instrument{}
)
func (n nonrecordingAsyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) {
return n, nil
}
func (n nonrecordingAsyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingAsyncFloat64Instrument) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) {
return n, nil
}
func (nonrecordingAsyncFloat64Instrument) Observe(context.Context, float64, ...attribute.KeyValue) {
}
type nonrecordingAsyncInt64Instrument struct {
instrument.Asynchronous
}
var (
_ asyncint64.InstrumentProvider = nonrecordingAsyncInt64Instrument{}
_ asyncint64.Counter = nonrecordingAsyncInt64Instrument{}
_ asyncint64.UpDownCounter = nonrecordingAsyncInt64Instrument{}
_ asyncint64.Gauge = nonrecordingAsyncInt64Instrument{}
)
func (n nonrecordingAsyncInt64Instrument) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) {
return n, nil
}
func (n nonrecordingAsyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingAsyncInt64Instrument) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) {
return n, nil
}
func (nonrecordingAsyncInt64Instrument) Observe(context.Context, int64, ...attribute.KeyValue) {
}
type nonrecordingSyncFloat64Instrument struct {
instrument.Synchronous
}
var (
_ syncfloat64.InstrumentProvider = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.Counter = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.UpDownCounter = nonrecordingSyncFloat64Instrument{}
_ syncfloat64.Histogram = nonrecordingSyncFloat64Instrument{}
)
func (n nonrecordingSyncFloat64Instrument) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) {
return n, nil
}
func (n nonrecordingSyncFloat64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingSyncFloat64Instrument) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) {
return n, nil
}
func (nonrecordingSyncFloat64Instrument) Add(context.Context, float64, ...attribute.KeyValue) {
}
func (nonrecordingSyncFloat64Instrument) Record(context.Context, float64, ...attribute.KeyValue) {
}
type nonrecordingSyncInt64Instrument struct {
instrument.Synchronous
}
var (
_ syncint64.InstrumentProvider = nonrecordingSyncInt64Instrument{}
_ syncint64.Counter = nonrecordingSyncInt64Instrument{}
_ syncint64.UpDownCounter = nonrecordingSyncInt64Instrument{}
_ syncint64.Histogram = nonrecordingSyncInt64Instrument{}
)
func (n nonrecordingSyncInt64Instrument) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) {
return n, nil
}
func (n nonrecordingSyncInt64Instrument) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) {
return n, nil
}
func (n nonrecordingSyncInt64Instrument) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) {
return n, nil
}
func (nonrecordingSyncInt64Instrument) Add(context.Context, int64, ...attribute.KeyValue) {
}
func (nonrecordingSyncInt64Instrument) Record(context.Context, int64, ...attribute.KeyValue) {
}

View File

@ -0,0 +1,64 @@
// 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 nonrecording // import "go.opentelemetry.io/otel/metric/nonrecording"
import (
"context"
"go.opentelemetry.io/otel/metric"
"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"
)
// NewNoopMeterProvider creates a MeterProvider that does not record any metrics.
func NewNoopMeterProvider() metric.MeterProvider {
return noopMeterProvider{}
}
type noopMeterProvider struct{}
var _ metric.MeterProvider = noopMeterProvider{}
func (noopMeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
return noopMeter{}
}
// NewNoopMeter creates a Meter that does not record any metrics.
func NewNoopMeter() metric.Meter {
return noopMeter{}
}
type noopMeter struct{}
var _ metric.Meter = noopMeter{}
func (noopMeter) AsyncInt64() asyncint64.InstrumentProvider {
return nonrecordingAsyncInt64Instrument{}
}
func (noopMeter) AsyncFloat64() asyncfloat64.InstrumentProvider {
return nonrecordingAsyncFloat64Instrument{}
}
func (noopMeter) SyncInt64() syncint64.InstrumentProvider {
return nonrecordingSyncInt64Instrument{}
}
func (noopMeter) SyncFloat64() syncfloat64.InstrumentProvider {
return nonrecordingSyncFloat64Instrument{}
}
func (noopMeter) RegisterCallback([]instrument.Asynchronous, func(context.Context)) error {
return nil
}

View File

@ -1,30 +0,0 @@
// 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/metric"
type noopMeterProvider struct{}
// NewNoopMeterProvider returns an implementation of MeterProvider that
// performs no operations. The Meter and Instrument created from the returned
// MeterProvider also perform no operations.
func NewNoopMeterProvider() MeterProvider {
return noopMeterProvider{}
}
var _ MeterProvider = noopMeterProvider{}
func (noopMeterProvider) Meter(instrumentationName string, opts ...MeterOption) Meter {
return Meter{}
}

View File

@ -15,8 +15,8 @@
package aggregation // import "go.opentelemetry.io/otel/sdk/export/metric/aggregation" package aggregation // import "go.opentelemetry.io/otel/sdk/export/metric/aggregation"
import ( import (
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
) )
// Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export/aggregation" // Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export/aggregation"

View File

@ -41,7 +41,6 @@ replace go.opentelemetry.io/otel/trace => ../../../trace
require ( require (
go.opentelemetry.io/otel v1.4.1 go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/metric v0.27.0
go.opentelemetry.io/otel/sdk/metric v0.27.0 go.opentelemetry.io/otel/sdk/metric v0.27.0
) )

View File

@ -18,10 +18,10 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export" // Deprecated: use module "go.opentelemetry.io/otel/sdk/metric/export"

View File

@ -19,9 +19,9 @@ import (
"fmt" "fmt"
"math" "math"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// Aggregator implements a specific aggregation behavior, e.g., a // Aggregator implements a specific aggregation behavior, e.g., a

View File

@ -21,13 +21,13 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
func TestInconsistentAggregatorErr(t *testing.T) { func TestInconsistentAggregatorErr(t *testing.T) {

View File

@ -26,11 +26,11 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ottest "go.opentelemetry.io/otel/internal/internaltest" ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
const Magnitude = 1000 const Magnitude = 1000

View File

@ -19,10 +19,10 @@ import (
"math/rand" "math/rand"
"testing" "testing"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
const inputRange = 1e6 const inputRange = 1e6

View File

@ -19,10 +19,10 @@ import (
"sort" "sort"
"sync" "sync"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// Note: This code uses a Mutex to govern access to the exclusive // Note: This code uses a Mutex to govern access to the exclusive

View File

@ -23,11 +23,11 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
const count = 100 const count = 100

View File

@ -20,10 +20,10 @@ import (
"time" "time"
"unsafe" "unsafe"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (

View File

@ -25,11 +25,11 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ottest "go.opentelemetry.io/otel/internal/internaltest" ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
const count = 100 const count = 100

View File

@ -17,10 +17,10 @@ package sum // import "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
import ( import (
"context" "context"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// Aggregator aggregates counter events. // Aggregator aggregates counter events.

View File

@ -22,10 +22,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ottest "go.opentelemetry.io/otel/internal/internaltest" ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
const count = 100 const count = 100

View File

@ -22,11 +22,13 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/metric/instrument/syncfloat64"
"go.opentelemetry.io/otel/metric/instrument/syncint64"
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type benchFixture struct { type benchFixture struct {
@ -44,7 +46,7 @@ func newFixture(b *testing.B) *benchFixture {
} }
bf.accumulator = sdk.NewAccumulator(bf) bf.accumulator = sdk.NewAccumulator(bf)
bf.meter = metric.WrapMeterImpl(bf.accumulator) bf.meter = sdkapi.WrapMeterImpl(bf.accumulator)
return bf return bf
} }
@ -56,8 +58,33 @@ func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
return f.meter return f.meter
} }
func (f *benchFixture) meterMust() metric.MeterMust { func (f *benchFixture) iCounter(name string) syncint64.Counter {
return metric.Must(f.meter) ctr, err := f.meter.SyncInt64().Counter(name)
if err != nil {
f.B.Error(err)
}
return ctr
}
func (f *benchFixture) fCounter(name string) syncfloat64.Counter {
ctr, err := f.meter.SyncFloat64().Counter(name)
if err != nil {
f.B.Error(err)
}
return ctr
}
func (f *benchFixture) iHistogram(name string) syncint64.Histogram {
ctr, err := f.meter.SyncInt64().Histogram(name)
if err != nil {
f.B.Error(err)
}
return ctr
}
func (f *benchFixture) fHistogram(name string) syncfloat64.Histogram {
ctr, err := f.meter.SyncFloat64().Histogram(name)
if err != nil {
f.B.Error(err)
}
return ctr
} }
func makeLabels(n int) []attribute.KeyValue { func makeLabels(n int) []attribute.KeyValue {
@ -81,7 +108,7 @@ func benchmarkLabels(b *testing.B, n int) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(n) labs := makeLabels(n)
cnt := fix.meterMust().NewInt64Counter("int64.sum") cnt := fix.iCounter("int64.sum")
b.ResetTimer() b.ResetTimer()
@ -154,31 +181,33 @@ func BenchmarkIterator_16(b *testing.B) {
// Counters // Counters
func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) { // TODO readd global
// Compare with BenchmarkInt64CounterAdd() to see overhead of global
// package. This is in the SDK to avoid the API from depending on the
// SDK.
ctx := context.Background()
fix := newFixture(b)
sdk := global.Meter("test") // func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
global.SetMeterProvider(fix) // // Compare with BenchmarkInt64CounterAdd() to see overhead of global
// // package. This is in the SDK to avoid the API from depending on the
// // SDK.
// ctx := context.Background()
// fix := newFixture(b)
labs := []attribute.KeyValue{attribute.String("A", "B")} // sdk := global.Meter("test")
cnt := Must(sdk).NewInt64Counter("int64.sum") // global.SetMeterProvider(fix)
b.ResetTimer() // labs := []attribute.KeyValue{attribute.String("A", "B")}
// cnt := Must(sdk).NewInt64Counter("int64.sum")
for i := 0; i < b.N; i++ { // b.ResetTimer()
cnt.Add(ctx, 1, labs...)
} // for i := 0; i < b.N; i++ {
} // cnt.Add(ctx, 1, labs...)
// }
// }
func BenchmarkInt64CounterAdd(b *testing.B) { func BenchmarkInt64CounterAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
cnt := fix.meterMust().NewInt64Counter("int64.sum") cnt := fix.iCounter("int64.sum")
b.ResetTimer() b.ResetTimer()
@ -191,7 +220,7 @@ func BenchmarkFloat64CounterAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
cnt := fix.meterMust().NewFloat64Counter("float64.sum") cnt := fix.fCounter("float64.sum")
b.ResetTimer() b.ResetTimer()
@ -206,7 +235,7 @@ func BenchmarkInt64LastValueAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
mea := fix.meterMust().NewInt64Histogram("int64.lastvalue") mea := fix.iHistogram("int64.lastvalue")
b.ResetTimer() b.ResetTimer()
@ -219,7 +248,7 @@ func BenchmarkFloat64LastValueAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
mea := fix.meterMust().NewFloat64Histogram("float64.lastvalue") mea := fix.fHistogram("float64.lastvalue")
b.ResetTimer() b.ResetTimer()
@ -234,7 +263,7 @@ func BenchmarkInt64HistogramAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
mea := fix.meterMust().NewInt64Histogram("int64.histogram") mea := fix.iHistogram("int64.histogram")
b.ResetTimer() b.ResetTimer()
@ -247,7 +276,7 @@ func BenchmarkFloat64HistogramAdd(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
mea := fix.meterMust().NewFloat64Histogram("float64.histogram") mea := fix.fHistogram("float64.histogram")
b.ResetTimer() b.ResetTimer()
@ -264,12 +293,12 @@ func BenchmarkObserverRegistration(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
names = append(names, fmt.Sprintf("test.%d.lastvalue", i)) names = append(names, fmt.Sprintf("test.%d.lastvalue", i))
} }
cb := func(_ context.Context, result metric.Int64ObserverResult) {}
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
fix.meterMust().NewInt64GaugeObserver(names[i], cb) ctr, _ := fix.meter.AsyncInt64().Counter(names[i])
_ = fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(context.Context) {})
} }
} }
@ -277,11 +306,16 @@ func BenchmarkGaugeObserverObservationInt64(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
_ = fix.meterMust().NewInt64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) { ctr, _ := fix.meter.AsyncInt64().Counter("test.lastvalue")
err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
result.Observe((int64)(i), labs...) ctr.Observe(ctx, (int64)(i), labs...)
} }
}) })
if err != nil {
b.Errorf("could not register callback: %v", err)
b.FailNow()
}
b.ResetTimer() b.ResetTimer()
@ -292,11 +326,16 @@ func BenchmarkGaugeObserverObservationFloat64(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(1) labs := makeLabels(1)
_ = fix.meterMust().NewFloat64GaugeObserver("test.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) { ctr, _ := fix.meter.AsyncFloat64().Counter("test.lastvalue")
err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
result.Observe((float64)(i), labs...) ctr.Observe(ctx, (float64)(i), labs...)
} }
}) })
if err != nil {
b.Errorf("could not register callback: %v", err)
b.FailNow()
}
b.ResetTimer() b.ResetTimer()
@ -310,17 +349,18 @@ func benchmarkBatchRecord8Labels(b *testing.B, numInst int) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
labs := makeLabels(numLabels) labs := makeLabels(numLabels)
var meas []sdkapi.Measurement var meas []syncint64.Counter
for i := 0; i < numInst; i++ { for i := 0; i < numInst; i++ {
inst := fix.meterMust().NewInt64Counter(fmt.Sprintf("int64.%d.sum", i)) meas = append(meas, fix.iCounter(fmt.Sprintf("int64.%d.sum", i)))
meas = append(meas, inst.Measurement(1))
} }
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
fix.accumulator.RecordBatch(ctx, labs, meas...) for _, ctr := range meas {
ctr.Add(ctx, 1, labs...)
}
} }
} }
@ -346,7 +386,7 @@ func BenchmarkRepeatedDirectCalls(b *testing.B) {
ctx := context.Background() ctx := context.Background()
fix := newFixture(b) fix := newFixture(b)
c := fix.meterMust().NewInt64Counter("int64.sum") c := fix.iCounter("int64.sum")
k := attribute.String("bench", "true") k := attribute.String("bench", "true")
b.ResetTimer() b.ResetTimer()

View File

@ -21,12 +21,13 @@ import (
"time" "time"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/internal/metric/registry"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time" controllerTime "go.opentelemetry.io/otel/sdk/metric/controller/time"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/registry"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -99,7 +100,7 @@ func (c *Controller) Meter(instrumentationName string, opts ...metric.MeterOptio
library: library, library: library,
})) }))
} }
return metric.WrapMeterImpl(m.(*registry.UniqueInstrumentMeterImpl)) return sdkapi.WrapMeterImpl(m.(*registry.UniqueInstrumentMeterImpl))
} }
type accumulatorCheckpointer struct { type accumulatorCheckpointer struct {
@ -108,6 +109,8 @@ type accumulatorCheckpointer struct {
library instrumentation.Library library instrumentation.Library
} }
var _ sdkapi.MeterImpl = &accumulatorCheckpointer{}
// New constructs a Controller using the provided checkpointer factory // New constructs a Controller using the provided checkpointer factory
// and options (including optional exporter) to configure a metric // and options (including optional exporter) to configure a metric
// export pipeline. // export pipeline.

View File

@ -25,8 +25,7 @@ import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
ottest "go.opentelemetry.io/otel/internal/internaltest" ottest "go.opentelemetry.io/otel/internal/internaltest"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
@ -34,6 +33,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -127,7 +127,7 @@ func TestControllerUsesResource(t *testing.T) {
ctx := context.Background() ctx := context.Background()
require.NoError(t, cont.Start(ctx)) require.NoError(t, cont.Start(ctx))
ctr := metric.Must(cont.Meter("named")).NewFloat64Counter("calls.sum") ctr, _ := cont.Meter("named").SyncFloat64().Counter("calls.sum")
ctr.Add(context.Background(), 1.) ctr.Add(context.Background(), 1.)
// Collect once // Collect once
@ -152,16 +152,19 @@ func TestStartNoExporter(t *testing.T) {
) )
mock := controllertest.NewMockClock() mock := controllertest.NewMockClock()
cont.SetClock(mock) cont.SetClock(mock)
meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#StartNoExporter")
calls := int64(0) calls := int64(0)
_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("calls.lastvalue", counterObserver, err := meter.AsyncInt64().Counter("calls.lastvalue")
func(ctx context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
calls++
checkTestContext(t, ctx) err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
result.Observe(calls, attribute.String("A", "B")) calls++
}, checkTestContext(t, ctx)
) counterObserver.Observe(ctx, calls, attribute.String("A", "B"))
})
require.NoError(t, err)
// Collect() has not been called. The controller is unstarted. // Collect() has not been called. The controller is unstarted.
expect := map[string]float64{} expect := map[string]float64{}
@ -220,18 +223,22 @@ func TestObserverCanceled(t *testing.T) {
controller.WithCollectTimeout(time.Millisecond), controller.WithCollectTimeout(time.Millisecond),
controller.WithResource(resource.Empty()), controller.WithResource(resource.Empty()),
) )
meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ObserverCanceled")
calls := int64(0) calls := int64(0)
_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue", counterObserver, err := meter.AsyncInt64().Counter("done.lastvalue")
func(ctx context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
<-ctx.Done()
calls++ err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
result.Observe(calls) <-ctx.Done()
}, calls++
) counterObserver.Observe(ctx, calls)
})
require.NoError(t, err)
// This relies on the context timing out // This relies on the context timing out
err := cont.Collect(context.Background()) err = cont.Collect(context.Background())
require.Error(t, err) require.Error(t, err)
require.True(t, errors.Is(err, context.DeadlineExceeded)) require.True(t, errors.Is(err, context.DeadlineExceeded))
@ -251,14 +258,18 @@ func TestObserverContext(t *testing.T) {
controller.WithCollectTimeout(0), controller.WithCollectTimeout(0),
controller.WithResource(resource.Empty()), controller.WithResource(resource.Empty()),
) )
meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ObserverContext")
counterObserver, err := meter.AsyncInt64().Counter("done.lastvalue")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
time.Sleep(10 * time.Millisecond)
checkTestContext(t, ctx)
counterObserver.Observe(ctx, 1)
})
require.NoError(t, err)
_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("done.lastvalue",
func(ctx context.Context, result metric.Int64ObserverResult) {
time.Sleep(10 * time.Millisecond)
checkTestContext(t, ctx)
result.Observe(1)
},
)
ctx := testContext() ctx := testContext()
require.NoError(t, cont.Collect(ctx)) require.NoError(t, cont.Collect(ctx))
@ -314,14 +325,17 @@ func TestExportTimeout(t *testing.T) {
) )
mock := controllertest.NewMockClock() mock := controllertest.NewMockClock()
cont.SetClock(mock) cont.SetClock(mock)
meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#ExportTimeout")
calls := int64(0) calls := int64(0)
_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue", counterObserver, err := meter.AsyncInt64().Counter("one.lastvalue")
func(ctx context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
calls++
result.Observe(calls) err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
}, calls++
) counterObserver.Observe(ctx, calls)
})
require.NoError(t, err)
require.NoError(t, cont.Start(context.Background())) require.NoError(t, cont.Start(context.Background()))
@ -332,7 +346,7 @@ func TestExportTimeout(t *testing.T) {
// Collect after 1s, timeout // Collect after 1s, timeout
mock.Add(time.Second) mock.Add(time.Second)
err := testHandler.Flush() err = testHandler.Flush()
require.Error(t, err) require.Error(t, err)
require.True(t, errors.Is(err, context.DeadlineExceeded)) require.True(t, errors.Is(err, context.DeadlineExceeded))
@ -369,13 +383,17 @@ func TestCollectAfterStopThenStartAgain(t *testing.T) {
mock := controllertest.NewMockClock() mock := controllertest.NewMockClock()
cont.SetClock(mock) cont.SetClock(mock)
meter := cont.Meter("go.opentelemetry.io/otel/sdk/metric/controller/basic_test#CollectAfterStopThenStartAgain")
calls := 0 calls := 0
_ = metric.Must(cont.Meter("named")).NewInt64CounterObserver("one.lastvalue", counterObserver, err := meter.AsyncInt64().Counter("one.lastvalue")
func(ctx context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
calls++
result.Observe(int64(calls)) err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
}, calls++
) counterObserver.Observe(ctx, int64(calls))
})
require.NoError(t, err)
// No collections happen (because mock clock does not advance): // No collections happen (because mock clock does not advance):
require.NoError(t, cont.Start(context.Background())) require.NoError(t, cont.Start(context.Background()))
@ -403,7 +421,7 @@ func TestCollectAfterStopThenStartAgain(t *testing.T) {
// explicit collection should still fail. // explicit collection should still fail.
require.NoError(t, cont.Start(context.Background())) require.NoError(t, cont.Start(context.Background()))
require.True(t, cont.IsRunning()) require.True(t, cont.IsRunning())
err := cont.Collect(context.Background()) err = cont.Collect(context.Background())
require.Error(t, err) require.Error(t, err)
require.Equal(t, controller.ErrControllerStarted, err) require.Equal(t, controller.ErrControllerStarted, err)
@ -452,10 +470,10 @@ func TestRegistryFunction(t *testing.T) {
require.NotNil(t, m1) require.NotNil(t, m1)
require.Equal(t, m1, m2) require.Equal(t, m1, m2)
c1, err := m1.NewInt64Counter("counter.sum") c1, err := m1.SyncInt64().Counter("counter.sum")
require.NoError(t, err) require.NoError(t, err)
c2, err := m1.NewInt64Counter("counter.sum") c2, err := m1.SyncInt64().Counter("counter.sum")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, c1, c2) require.Equal(t, c1, c2)

View File

@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@ -45,7 +44,8 @@ func TestPullNoCollect(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter := puller.Meter("nocache") meter := puller.Meter("nocache")
counter := metric.Must(meter).NewInt64Counter("counter.sum") counter, err := meter.SyncInt64().Counter("counter.sum")
require.NoError(t, err)
counter.Add(ctx, 10, attribute.String("A", "B")) counter.Add(ctx, 10, attribute.String("A", "B"))
@ -83,7 +83,8 @@ func TestPullWithCollect(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter := puller.Meter("nocache") meter := puller.Meter("nocache")
counter := metric.Must(meter).NewInt64Counter("counter.sum") counter, err := meter.SyncInt64().Counter("counter.sum")
require.NoError(t, err)
counter.Add(ctx, 10, attribute.String("A", "B")) counter.Add(ctx, 10, attribute.String("A", "B"))

View File

@ -27,7 +27,6 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
"go.opentelemetry.io/otel/sdk/metric/controller/controllertest" "go.opentelemetry.io/otel/sdk/metric/controller/controllertest"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
@ -117,7 +116,8 @@ func TestPushTicker(t *testing.T) {
ctx := context.Background() ctx := context.Background()
counter := metric.Must(meter).NewInt64Counter("counter.sum") counter, err := meter.SyncInt64().Counter("counter.sum")
require.NoError(t, err)
require.NoError(t, p.Start(ctx)) require.NoError(t, p.Start(ctx))
@ -197,8 +197,10 @@ func TestPushExportError(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter := p.Meter("name") meter := p.Meter("name")
counter1 := metric.Must(meter).NewInt64Counter("counter1.sum") counter1, err := meter.SyncInt64().Counter("counter1.sum")
counter2 := metric.Must(meter).NewInt64Counter("counter2.sum") require.NoError(t, err)
counter2, err := meter.SyncInt64().Counter("counter2.sum")
require.NoError(t, err)
require.NoError(t, p.Start(ctx)) require.NoError(t, p.Start(ctx))
runtime.Gosched() runtime.Gosched()

View File

@ -26,16 +26,17 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/nonrecording"
metricsdk "go.opentelemetry.io/otel/sdk/metric" metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
var Must = metric.Must
type handler struct { type handler struct {
sync.Mutex sync.Mutex
err error err error
@ -88,7 +89,7 @@ func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector,
accum := metricsdk.NewAccumulator( accum := metricsdk.NewAccumulator(
processor, processor,
) )
meter := metric.WrapMeterImpl(accum) meter := sdkapi.WrapMeterImpl(accum)
return meter, accum, testSelector, processor return meter, accum, testSelector, processor
} }
@ -96,7 +97,8 @@ func TestInputRangeCounter(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64Counter("name.sum") counter, err := meter.SyncInt64().Counter("name.sum")
require.NoError(t, err)
counter.Add(ctx, -1) counter.Add(ctx, -1)
require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
@ -118,7 +120,8 @@ func TestInputRangeUpDownCounter(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64UpDownCounter("name.sum") counter, err := meter.SyncInt64().UpDownCounter("name.sum")
require.NoError(t, err)
counter.Add(ctx, -1) counter.Add(ctx, -1)
counter.Add(ctx, -1) counter.Add(ctx, -1)
@ -137,7 +140,8 @@ func TestInputRangeHistogram(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
histogram := Must(meter).NewFloat64Histogram("name.histogram") histogram, err := meter.SyncFloat64().Histogram("name.histogram")
require.NoError(t, err)
histogram.Record(ctx, math.NaN()) histogram.Record(ctx, math.NaN())
require.Equal(t, aggregation.ErrNaNInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNaNInput, testHandler.Flush())
@ -162,7 +166,8 @@ func TestDisabledInstrument(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
histogram := Must(meter).NewFloat64Histogram("name.disabled") histogram, err := meter.SyncFloat64().Histogram("name.disabled")
require.NoError(t, err)
histogram.Record(ctx, -1) histogram.Record(ctx, -1)
checkpointed := sdk.Collect(ctx) checkpointed := sdk.Collect(ctx)
@ -175,7 +180,8 @@ func TestRecordNaN(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, _, _, _ := newSDK(t) meter, _, _, _ := newSDK(t)
c := Must(meter).NewFloat64Counter("name.sum") c, err := meter.SyncFloat64().Counter("name.sum")
require.NoError(t, err)
require.Nil(t, testHandler.Flush()) require.Nil(t, testHandler.Flush())
c.Add(ctx, math.NaN()) c.Add(ctx, math.NaN())
@ -186,7 +192,8 @@ func TestSDKLabelsDeduplication(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64Counter("name.sum") counter, err := meter.SyncInt64().Counter("name.sum")
require.NoError(t, err)
const ( const (
maxKeys = 21 maxKeys = 21
@ -277,48 +284,86 @@ func TestObserverCollection(t *testing.T) {
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
mult := 1 mult := 1
_ = Must(meter).NewFloat64GaugeObserver("float.gauge.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) { gaugeF, err := meter.AsyncFloat64().Gauge("float.gauge.lastvalue")
result.Observe(float64(mult), attribute.String("A", "B")) require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{
gaugeF,
}, func(ctx context.Context) {
gaugeF.Observe(ctx, float64(mult), attribute.String("A", "B"))
// last value wins // last value wins
result.Observe(float64(-mult), attribute.String("A", "B")) gaugeF.Observe(ctx, float64(-mult), attribute.String("A", "B"))
result.Observe(float64(-mult), attribute.String("C", "D")) gaugeF.Observe(ctx, float64(-mult), attribute.String("C", "D"))
})
_ = Must(meter).NewInt64GaugeObserver("int.gauge.lastvalue", func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(int64(-mult), attribute.String("A", "B"))
result.Observe(int64(mult))
// last value wins
result.Observe(int64(mult), attribute.String("A", "B"))
result.Observe(int64(mult))
}) })
require.NoError(t, err)
_ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { gaugeI, err := meter.AsyncInt64().Gauge("int.gauge.lastvalue")
result.Observe(float64(mult), attribute.String("A", "B")) require.NoError(t, err)
result.Observe(float64(2*mult), attribute.String("A", "B")) err = meter.RegisterCallback([]instrument.Asynchronous{
result.Observe(float64(mult), attribute.String("C", "D")) gaugeI,
}) }, func(ctx context.Context) {
_ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { gaugeI.Observe(ctx, int64(-mult), attribute.String("A", "B"))
result.Observe(int64(2*mult), attribute.String("A", "B")) gaugeI.Observe(ctx, int64(mult))
result.Observe(int64(mult))
// last value wins // last value wins
result.Observe(int64(mult), attribute.String("A", "B")) gaugeI.Observe(ctx, int64(mult), attribute.String("A", "B"))
result.Observe(int64(mult)) gaugeI.Observe(ctx, int64(mult))
}) })
require.NoError(t, err)
_ = Must(meter).NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { counterF, err := meter.AsyncFloat64().Counter("float.counterobserver.sum")
result.Observe(float64(mult), attribute.String("A", "B")) require.NoError(t, err)
result.Observe(float64(-2*mult), attribute.String("A", "B")) err = meter.RegisterCallback([]instrument.Asynchronous{
result.Observe(float64(mult), attribute.String("C", "D")) counterF,
}, func(ctx context.Context) {
counterF.Observe(ctx, float64(mult), attribute.String("A", "B"))
counterF.Observe(ctx, float64(2*mult), attribute.String("A", "B"))
counterF.Observe(ctx, float64(mult), attribute.String("C", "D"))
}) })
_ = Must(meter).NewInt64UpDownCounterObserver("int.updowncounterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
result.Observe(int64(2*mult), attribute.String("A", "B"))
result.Observe(int64(mult)) counterI, err := meter.AsyncInt64().Counter("int.counterobserver.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{
counterI,
}, func(ctx context.Context) {
counterI.Observe(ctx, int64(2*mult), attribute.String("A", "B"))
counterI.Observe(ctx, int64(mult))
// last value wins // last value wins
result.Observe(int64(mult), attribute.String("A", "B")) counterI.Observe(ctx, int64(mult), attribute.String("A", "B"))
result.Observe(int64(-mult)) counterI.Observe(ctx, int64(mult))
}) })
require.NoError(t, err)
_ = Must(meter).NewInt64GaugeObserver("empty.gauge.sum", func(_ context.Context, result metric.Int64ObserverResult) { updowncounterF, err := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{
updowncounterF,
}, func(ctx context.Context) {
updowncounterF.Observe(ctx, float64(mult), attribute.String("A", "B"))
updowncounterF.Observe(ctx, float64(-2*mult), attribute.String("A", "B"))
updowncounterF.Observe(ctx, float64(mult), attribute.String("C", "D"))
}) })
require.NoError(t, err)
updowncounterI, err := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{
updowncounterI,
}, func(ctx context.Context) {
updowncounterI.Observe(ctx, int64(2*mult), attribute.String("A", "B"))
updowncounterI.Observe(ctx, int64(mult))
// last value wins
updowncounterI.Observe(ctx, int64(mult), attribute.String("A", "B"))
updowncounterI.Observe(ctx, int64(-mult))
})
require.NoError(t, err)
unused, err := meter.AsyncInt64().Gauge("empty.gauge.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{
unused,
}, func(ctx context.Context) {
})
require.NoError(t, err)
for mult = 0; mult < 3; mult++ { for mult = 0; mult < 3; mult++ {
processor.Reset() processor.Reset()
@ -333,15 +378,15 @@ func TestObserverCollection(t *testing.T) {
"int.gauge.lastvalue//": mult, "int.gauge.lastvalue//": mult,
"int.gauge.lastvalue/A=B/": mult, "int.gauge.lastvalue/A=B/": mult,
"float.counterobserver.sum/A=B/": 2 * mult, "float.counterobserver.sum/A=B/": 3 * mult,
"float.counterobserver.sum/C=D/": mult, "float.counterobserver.sum/C=D/": mult,
"int.counterobserver.sum//": mult, "int.counterobserver.sum//": 2 * mult,
"int.counterobserver.sum/A=B/": mult, "int.counterobserver.sum/A=B/": 3 * mult,
"float.updowncounterobserver.sum/A=B/": -2 * mult, "float.updowncounterobserver.sum/A=B/": -mult,
"float.updowncounterobserver.sum/C=D/": mult, "float.updowncounterobserver.sum/C=D/": mult,
"int.updowncounterobserver.sum//": -mult, "int.updowncounterobserver.sum//": 0,
"int.updowncounterobserver.sum/A=B/": mult, "int.updowncounterobserver.sum/A=B/": 3 * mult,
}, processor.Values()) }, processor.Values())
} }
} }
@ -351,18 +396,26 @@ func TestCounterObserverInputRange(t *testing.T) {
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
// TODO: these tests are testing for negative values, not for _descending values_. Fix. // TODO: these tests are testing for negative values, not for _descending values_. Fix.
_ = Must(meter).NewFloat64CounterObserver("float.counterobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) { counterF, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum")
result.Observe(-2, attribute.String("A", "B")) err := meter.RegisterCallback([]instrument.Asynchronous{
counterF,
}, func(ctx context.Context) {
counterF.Observe(ctx, -2, attribute.String("A", "B"))
require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
result.Observe(-1, attribute.String("C", "D")) counterF.Observe(ctx, -1, attribute.String("C", "D"))
require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
}) })
_ = Must(meter).NewInt64CounterObserver("int.counterobserver.sum", func(_ context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
result.Observe(-1, attribute.String("A", "B")) counterI, _ := meter.AsyncInt64().Counter("int.counterobserver.sum")
err = meter.RegisterCallback([]instrument.Asynchronous{
counterI,
}, func(ctx context.Context) {
counterI.Observe(ctx, -1, attribute.String("A", "B"))
require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
result.Observe(-1) counterI.Observe(ctx, -1)
require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush()) require.Equal(t, aggregation.ErrNegativeInput, testHandler.Flush())
}) })
require.NoError(t, err)
collected := sdk.Collect(ctx) collected := sdk.Collect(ctx)
@ -377,51 +430,43 @@ func TestObserverBatch(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
var floatGaugeObs metric.Float64GaugeObserver floatGaugeObs, _ := meter.AsyncFloat64().Gauge("float.gauge.lastvalue")
var intGaugeObs metric.Int64GaugeObserver intGaugeObs, _ := meter.AsyncInt64().Gauge("int.gauge.lastvalue")
var floatCounterObs metric.Float64CounterObserver floatCounterObs, _ := meter.AsyncFloat64().Counter("float.counterobserver.sum")
var intCounterObs metric.Int64CounterObserver intCounterObs, _ := meter.AsyncInt64().Counter("int.counterobserver.sum")
var floatUpDownCounterObs metric.Float64UpDownCounterObserver floatUpDownCounterObs, _ := meter.AsyncFloat64().UpDownCounter("float.updowncounterobserver.sum")
var intUpDownCounterObs metric.Int64UpDownCounterObserver intUpDownCounterObs, _ := meter.AsyncInt64().UpDownCounter("int.updowncounterobserver.sum")
var batch = Must(meter).NewBatchObserver( err := meter.RegisterCallback([]instrument.Asynchronous{
func(_ context.Context, result metric.BatchObserverResult) { floatGaugeObs,
result.Observe( intGaugeObs,
[]attribute.KeyValue{ floatCounterObs,
attribute.String("A", "B"), intCounterObs,
}, floatUpDownCounterObs,
floatGaugeObs.Observation(1), intUpDownCounterObs,
floatGaugeObs.Observation(-1), }, func(ctx context.Context) {
intGaugeObs.Observation(-1), ab := attribute.String("A", "B")
intGaugeObs.Observation(1), floatGaugeObs.Observe(ctx, 1, ab)
floatCounterObs.Observation(1000), floatGaugeObs.Observe(ctx, -1, ab)
intCounterObs.Observation(100), intGaugeObs.Observe(ctx, -1, ab)
floatUpDownCounterObs.Observation(-1000), intGaugeObs.Observe(ctx, 1, ab)
intUpDownCounterObs.Observation(-100), floatCounterObs.Observe(ctx, 1000, ab)
) intCounterObs.Observe(ctx, 100, ab)
result.Observe( floatUpDownCounterObs.Observe(ctx, -1000, ab)
[]attribute.KeyValue{ intUpDownCounterObs.Observe(ctx, -100, ab)
attribute.String("C", "D"),
}, cd := attribute.String("C", "D")
floatGaugeObs.Observation(-1), floatGaugeObs.Observe(ctx, -1, cd)
floatCounterObs.Observation(-1), floatCounterObs.Observe(ctx, -1, cd)
floatUpDownCounterObs.Observation(-1), floatUpDownCounterObs.Observe(ctx, -1, cd)
)
result.Observe( intGaugeObs.Observe(ctx, 1)
nil, intGaugeObs.Observe(ctx, 1)
intGaugeObs.Observation(1), intCounterObs.Observe(ctx, 10)
intGaugeObs.Observation(1), floatCounterObs.Observe(ctx, 1.1)
intCounterObs.Observation(10), intUpDownCounterObs.Observe(ctx, 10)
floatCounterObs.Observation(1.1), })
intUpDownCounterObs.Observation(10), require.NoError(t, err)
)
})
floatGaugeObs = batch.NewFloat64GaugeObserver("float.gauge.lastvalue")
intGaugeObs = batch.NewInt64GaugeObserver("int.gauge.lastvalue")
floatCounterObs = batch.NewFloat64CounterObserver("float.counterobserver.sum")
intCounterObs = batch.NewInt64CounterObserver("int.counterobserver.sum")
floatUpDownCounterObs = batch.NewFloat64UpDownCounterObserver("float.updowncounterobserver.sum")
intUpDownCounterObs = batch.NewInt64UpDownCounterObserver("int.updowncounterobserver.sum")
collected := sdk.Collect(ctx) collected := sdk.Collect(ctx)
@ -445,37 +490,6 @@ func TestObserverBatch(t *testing.T) {
}, processor.Values()) }, processor.Values())
} }
func TestRecordBatch(t *testing.T) {
ctx := context.Background()
meter, sdk, _, processor := newSDK(t)
counter1 := Must(meter).NewInt64Counter("int64.sum")
counter2 := Must(meter).NewFloat64Counter("float64.sum")
histogram1 := Must(meter).NewInt64Histogram("int64.histogram")
histogram2 := Must(meter).NewFloat64Histogram("float64.histogram")
sdk.RecordBatch(
ctx,
[]attribute.KeyValue{
attribute.String("A", "B"),
attribute.String("C", "D"),
},
counter1.Measurement(1),
counter2.Measurement(2),
histogram1.Measurement(3),
histogram2.Measurement(4),
)
sdk.Collect(ctx)
require.EqualValues(t, map[string]float64{
"int64.sum/A=B,C=D/": 1,
"float64.sum/A=B,C=D/": 2,
"int64.histogram/A=B,C=D/": 3,
"float64.histogram/A=B,C=D/": 4,
}, processor.Values())
}
// TestRecordPersistence ensures that a direct-called instrument that // TestRecordPersistence ensures that a direct-called instrument that
// is repeatedly used each interval results in a persistent record, so // is repeatedly used each interval results in a persistent record, so
// that its encoded labels will be cached across collection intervals. // that its encoded labels will be cached across collection intervals.
@ -483,7 +497,9 @@ func TestRecordPersistence(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, selector, _ := newSDK(t) meter, sdk, selector, _ := newSDK(t)
c := Must(meter).NewFloat64Counter("name.sum") c, err := meter.SyncFloat64().Counter("name.sum")
require.NoError(t, err)
uk := attribute.String("bound", "false") uk := attribute.String("bound", "false")
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
@ -497,51 +513,58 @@ func TestRecordPersistence(t *testing.T) {
func TestIncorrectInstruments(t *testing.T) { func TestIncorrectInstruments(t *testing.T) {
// The Batch observe/record APIs are susceptible to // The Batch observe/record APIs are susceptible to
// uninitialized instruments. // uninitialized instruments.
var counter metric.Int64Counter var observer asyncint64.Gauge
var observer metric.Int64GaugeObserver
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
// Now try with uninitialized instruments. // Now try with uninitialized instruments.
meter.RecordBatch(ctx, nil, counter.Measurement(1)) err := meter.RegisterCallback([]instrument.Asynchronous{
meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { observer,
result.Observe(nil, observer.Observation(1)) }, func(ctx context.Context) {
observer.Observe(ctx, 1)
}) })
require.ErrorIs(t, err, metricsdk.ErrBadInstrument)
collected := sdk.Collect(ctx) collected := sdk.Collect(ctx)
require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush()) err = testHandler.Flush()
require.NoError(t, err)
require.Equal(t, 0, collected) require.Equal(t, 0, collected)
// Now try with instruments from another SDK. // Now try with instruments from another SDK.
var noopMeter metric.Meter noopMeter := nonrecording.NewNoopMeter()
counter = metric.Must(noopMeter).NewInt64Counter("name.sum") observer, _ = noopMeter.AsyncInt64().Gauge("observer")
observer = metric.Must(noopMeter).NewBatchObserver(
func(context.Context, metric.BatchObserverResult) {},
).NewInt64GaugeObserver("observer")
meter.RecordBatch(ctx, nil, counter.Measurement(1)) err = meter.RegisterCallback(
meter.NewBatchObserver(func(_ context.Context, result metric.BatchObserverResult) { []instrument.Asynchronous{observer},
result.Observe(nil, observer.Observation(1)) func(ctx context.Context) {
}) observer.Observe(ctx, 1)
},
)
require.ErrorIs(t, err, metricsdk.ErrBadInstrument)
collected = sdk.Collect(ctx) collected = sdk.Collect(ctx)
require.Equal(t, 0, collected) require.Equal(t, 0, collected)
require.EqualValues(t, map[string]float64{}, processor.Values()) require.EqualValues(t, map[string]float64{}, processor.Values())
require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush())
err = testHandler.Flush()
require.NoError(t, err)
} }
func TestSyncInAsync(t *testing.T) { func TestSyncInAsync(t *testing.T) {
ctx := context.Background() ctx := context.Background()
meter, sdk, _, processor := newSDK(t) meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewFloat64Counter("counter.sum") counter, _ := meter.SyncFloat64().Counter("counter.sum")
_ = Must(meter).NewInt64GaugeObserver("observer.lastvalue", gauge, _ := meter.AsyncInt64().Gauge("observer.lastvalue")
func(ctx context.Context, result metric.Int64ObserverResult) {
result.Observe(10) err := meter.RegisterCallback([]instrument.Asynchronous{
counter.Add(ctx, 100) gauge,
}, }, func(ctx context.Context) {
) gauge.Observe(ctx, 10)
counter.Add(ctx, 100)
})
require.NoError(t, err)
sdk.Collect(ctx) sdk.Collect(ctx)

View File

@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"time" "time"
"go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/sdk/metric/number"
) )
// These interfaces describe the various ways to access state from an // These interfaces describe the various ways to access state from an

View File

@ -17,7 +17,7 @@
package aggregation // import "go.opentelemetry.io/otel/sdk/metric/export/aggregation" package aggregation // import "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
import ( import (
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// Temporality indicates the temporal aggregation exported by an exporter. // Temporality indicates the temporal aggregation exported by an exporter.

View File

@ -19,9 +19,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/metrictest" "go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
func TestTemporalityIncludes(t *testing.T) { func TestTemporalityIncludes(t *testing.T) {

View File

@ -20,10 +20,10 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )

View File

@ -42,7 +42,6 @@ require (
github.com/benbjohnson/clock v1.3.0 github.com/benbjohnson/clock v1.3.0
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
go.opentelemetry.io/otel v1.4.1 go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/internal/metric v0.27.0
go.opentelemetry.io/otel/metric v0.27.0 go.opentelemetry.io/otel/metric v0.27.0
go.opentelemetry.io/otel/sdk v1.4.1 go.opentelemetry.io/otel/sdk v1.4.1
) )
@ -55,8 +54,6 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../..
replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp
replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric => ../../exporters/otlp/otlpmetric
replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc replace go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc => ../../exporters/otlp/otlpmetric/otlpmetricgrpc

View File

@ -22,10 +22,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
func TestStressInt64Histogram(t *testing.T) { func TestStressInt64Histogram(t *testing.T) {

View File

@ -0,0 +1,56 @@
// 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 metrictest // import "go.opentelemetry.io/otel/sdk/metric/metrictest"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
)
type (
// Library is the same as "sdk/instrumentation".Library but there is
// a package cycle to use it.
Library struct {
InstrumentationName string
InstrumentationVersion string
SchemaURL string
}
Batch struct {
// Measurement needs to be aligned for 64-bit atomic operations.
Measurements []Measurement
Ctx context.Context
Labels []attribute.KeyValue
Library Library
}
Measurement struct {
// Number needs to be aligned for 64-bit atomic operations.
Number number.Number
Instrument sdkapi.InstrumentImpl
}
)
// NewDescriptor is a test helper for constructing test metric
// descriptors using standard options.
func NewDescriptor(name string, ikind sdkapi.InstrumentKind, nkind number.Kind, opts ...instrument.Option) sdkapi.Descriptor {
cfg := instrument.NewConfig(opts...)
return sdkapi.NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit())
}

View File

@ -20,4 +20,4 @@ This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback. evolving OpenTelemetry specification and user feedback.
*/ */
package number // import "go.opentelemetry.io/otel/metric/number" package number // import "go.opentelemetry.io/otel/sdk/metric/number"

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package number // import "go.opentelemetry.io/otel/metric/number" package number // import "go.opentelemetry.io/otel/sdk/metric/number"
//go:generate stringer -type=Kind //go:generate stringer -type=Kind

View File

@ -21,10 +21,10 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (

View File

@ -25,19 +25,19 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/metrictest"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
sdk "go.opentelemetry.io/otel/sdk/metric" sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest" "go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/processor/basic" "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -450,15 +450,16 @@ func TestCounterObserverEndToEnd(t *testing.T) {
eselector, eselector,
) )
accum := sdk.NewAccumulator(proc) accum := sdk.NewAccumulator(proc)
meter := metric.WrapMeterImpl(accum) meter := sdkapi.WrapMeterImpl(accum)
var calls int64 var calls int64
metric.Must(meter).NewInt64CounterObserver("observer.sum", ctr, err := meter.AsyncInt64().Counter("observer.sum")
func(_ context.Context, result metric.Int64ObserverResult) { require.NoError(t, err)
calls++ err = meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
result.Observe(calls) calls++
}, ctr.Observe(ctx, calls)
) })
require.NoError(t, err)
reader := proc.Reader() reader := proc.Reader()
var startTime [3]time.Time var startTime [3]time.Time

View File

@ -22,7 +22,6 @@ import (
"time" "time"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
@ -30,6 +29,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )

View File

@ -21,33 +21,37 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
metricsdk "go.opentelemetry.io/otel/sdk/metric" metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
func generateTestData(proc export.Processor) { func generateTestData(t *testing.T, proc export.Processor) {
ctx := context.Background() ctx := context.Background()
accum := metricsdk.NewAccumulator(proc) accum := metricsdk.NewAccumulator(proc)
meter := metric.WrapMeterImpl(accum) meter := sdkapi.WrapMeterImpl(accum)
counter := metric.Must(meter).NewFloat64Counter("counter.sum") counter, err := meter.SyncFloat64().Counter("counter.sum")
require.NoError(t, err)
_ = metric.Must(meter).NewInt64CounterObserver("observer.sum",
func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(10, attribute.String("K1", "V1"))
result.Observe(11, attribute.String("K1", "V2"))
},
)
counter.Add(ctx, 100, attribute.String("K1", "V1")) counter.Add(ctx, 100, attribute.String("K1", "V1"))
counter.Add(ctx, 101, attribute.String("K1", "V2")) counter.Add(ctx, 101, attribute.String("K1", "V2"))
counterObserver, err := meter.AsyncInt64().Counter("observer.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
counterObserver.Observe(ctx, 10, attribute.String("K1", "V1"))
counterObserver.Observe(ctx, 11, attribute.String("K1", "V2"))
})
require.NoError(t, err)
accum.Collect(ctx) accum.Collect(ctx)
} }
@ -60,7 +64,7 @@ func TestProcessorTesting(t *testing.T) {
attribute.DefaultEncoder(), attribute.DefaultEncoder(),
), ),
) )
generateTestData(checkpointer) generateTestData(t, checkpointer)
res := resource.NewSchemaless(attribute.String("R", "V")) res := resource.NewSchemaless(attribute.String("R", "V"))
expect := map[string]float64{ expect := map[string]float64{

View File

@ -16,8 +16,8 @@ package reducer // import "go.opentelemetry.io/otel/sdk/metric/processor/reducer
import ( import (
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (

View File

@ -21,8 +21,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/instrumentation"
metricsdk "go.opentelemetry.io/otel/sdk/metric" metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/export/aggregation" "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
@ -30,6 +29,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/processor/processortest" "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest" processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/metric/processor/reducer" "go.opentelemetry.io/otel/sdk/metric/processor/reducer"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource"
) )
@ -54,21 +54,22 @@ func (testFilter) LabelFilterFor(_ *sdkapi.Descriptor) attribute.Filter {
} }
} }
func generateData(impl sdkapi.MeterImpl) { func generateData(t *testing.T, impl sdkapi.MeterImpl) {
ctx := context.Background() ctx := context.Background()
meter := metric.WrapMeterImpl(impl) meter := sdkapi.WrapMeterImpl(impl)
counter := metric.Must(meter).NewFloat64Counter("counter.sum")
_ = metric.Must(meter).NewInt64CounterObserver("observer.sum",
func(_ context.Context, result metric.Int64ObserverResult) {
result.Observe(10, kvs1...)
result.Observe(10, kvs2...)
},
)
counter, err := meter.SyncFloat64().Counter("counter.sum")
require.NoError(t, err)
counter.Add(ctx, 100, kvs1...) counter.Add(ctx, 100, kvs1...)
counter.Add(ctx, 100, kvs2...) counter.Add(ctx, 100, kvs2...)
counterObserver, err := meter.AsyncInt64().Counter("observer.sum")
require.NoError(t, err)
err = meter.RegisterCallback([]instrument.Asynchronous{counterObserver}, func(ctx context.Context) {
counterObserver.Observe(ctx, 10, kvs1...)
counterObserver.Observe(ctx, 10, kvs2...)
})
require.NoError(t, err)
} }
func TestFilterProcessor(t *testing.T) { func TestFilterProcessor(t *testing.T) {
@ -79,7 +80,7 @@ func TestFilterProcessor(t *testing.T) {
accum := metricsdk.NewAccumulator( accum := metricsdk.NewAccumulator(
reducer.New(testFilter{}, processorTest.NewCheckpointer(testProc)), reducer.New(testFilter{}, processorTest.NewCheckpointer(testProc)),
) )
generateData(accum) generateData(t, accum)
accum.Collect(context.Background()) accum.Collect(context.Background())
@ -97,7 +98,7 @@ func TestFilterBasicProcessor(t *testing.T) {
) )
exporter := processorTest.New(basicProc, attribute.DefaultEncoder()) exporter := processorTest.New(basicProc, attribute.DefaultEncoder())
generateData(accum) generateData(t, accum)
basicProc.StartCollection() basicProc.StartCollection()
accum.Collect(context.Background()) accum.Collect(context.Background())

View File

@ -21,4 +21,4 @@ This package is currently in a pre-GA phase. Backwards incompatible changes
may be introduced in subsequent minor version releases as we work to track the may be introduced in subsequent minor version releases as we work to track the
evolving OpenTelemetry specification and user feedback. evolving OpenTelemetry specification and user feedback.
*/ */
package registry // import "go.opentelemetry.io/otel/internal/metric/registry" package registry // import "go.opentelemetry.io/otel/sdk/metric/registry"

View File

@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package registry // import "go.opentelemetry.io/otel/internal/metric/registry" package registry // import "go.opentelemetry.io/otel/sdk/metric/registry"
import ( import (
"context" "context"
"fmt" "fmt"
"sync" "sync"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
// UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding // UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
@ -53,11 +53,6 @@ func (u *UniqueInstrumentMeterImpl) MeterImpl() sdkapi.MeterImpl {
return u.impl return u.impl
} }
// RecordBatch implements sdkapi.MeterImpl.
func (u *UniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...sdkapi.Measurement) {
u.impl.RecordBatch(ctx, labels, ms...)
}
// NewMetricKindMismatchError formats an error that describes a // NewMetricKindMismatchError formats an error that describes a
// mismatched metric instrument definition. // mismatched metric instrument definition.
func NewMetricKindMismatchError(desc sdkapi.Descriptor) error { func NewMetricKindMismatchError(desc sdkapi.Descriptor) error {
@ -115,10 +110,7 @@ func (u *UniqueInstrumentMeterImpl) NewSyncInstrument(descriptor sdkapi.Descript
} }
// NewAsyncInstrument implements sdkapi.MeterImpl. // NewAsyncInstrument implements sdkapi.MeterImpl.
func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument( func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.AsyncImpl, error) {
descriptor sdkapi.Descriptor,
runner sdkapi.AsyncRunner,
) (sdkapi.AsyncImpl, error) {
u.lock.Lock() u.lock.Lock()
defer u.lock.Unlock() defer u.lock.Unlock()
@ -130,10 +122,17 @@ func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(
return impl.(sdkapi.AsyncImpl), nil return impl.(sdkapi.AsyncImpl), nil
} }
asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner) asyncInst, err := u.impl.NewAsyncInstrument(descriptor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
u.state[descriptor.Name()] = asyncInst u.state[descriptor.Name()] = asyncInst
return asyncInst, nil return asyncInst, nil
} }
func (u *UniqueInstrumentMeterImpl) RegisterCallback(insts []instrument.Asynchronous, callback func(context.Context)) error {
u.lock.Lock()
defer u.lock.Unlock()
return u.impl.RegisterCallback(insts, callback)
}

View File

@ -15,16 +15,15 @@
package registry_test package registry_test
import ( import (
"context"
"errors" "errors"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/internal/metric/registry"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/metrictest" metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/registry"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (
@ -34,22 +33,22 @@ type (
var ( var (
allNew = map[string]newFunc{ allNew = map[string]newFunc{
"counter.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "counter.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewInt64Counter(name)) return unwrap(m.SyncInt64().Counter(name))
}, },
"counter.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "counter.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewFloat64Counter(name)) return unwrap(m.SyncFloat64().Counter(name))
}, },
"histogram.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "histogram.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewInt64Histogram(name)) return unwrap(m.SyncInt64().Histogram(name))
}, },
"histogram.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "histogram.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewFloat64Histogram(name)) return unwrap(m.SyncFloat64().Histogram(name))
}, },
"gaugeobserver.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "gaugeobserver.int64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewInt64GaugeObserver(name, func(context.Context, metric.Int64ObserverResult) {})) return unwrap(m.AsyncInt64().Gauge(name))
}, },
"gaugeobserver.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) { "gaugeobserver.float64": func(m metric.Meter, name string) (sdkapi.InstrumentImpl, error) {
return unwrap(m.NewFloat64GaugeObserver(name, func(context.Context, metric.Float64ObserverResult) {})) return unwrap(m.AsyncFloat64().Gauge(name))
}, },
} }
) )
@ -71,10 +70,11 @@ func unwrap(impl interface{}, err error) (sdkapi.InstrumentImpl, error) {
return nil, err return nil, err
} }
// TODO Replace with controller
func testMeterWithRegistry(name string) metric.Meter { func testMeterWithRegistry(name string) metric.Meter {
return metric.WrapMeterImpl( return sdkapi.WrapMeterImpl(
registry.NewUniqueInstrumentMeterImpl( registry.NewUniqueInstrumentMeterImpl(
metrictest.NewMeterProvider().Meter(name).MeterImpl(), metricsdk.NewAccumulator(nil),
), ),
) )
} }
@ -91,21 +91,6 @@ func TestRegistrySameInstruments(t *testing.T) {
} }
} }
func TestRegistryDifferentNamespace(t *testing.T) {
for _, nf := range allNew {
provider := metrictest.NewMeterProvider()
meter1 := provider.Meter("meter1")
meter2 := provider.Meter("meter2")
inst1, err1 := nf(meter1, "this")
inst2, err2 := nf(meter2, "this")
require.NoError(t, err1)
require.NoError(t, err2)
require.NotEqual(t, inst1, inst2)
}
}
func TestRegistryDiffInstruments(t *testing.T) { func TestRegistryDiffInstruments(t *testing.T) {
for origName, origf := range allNew { for origName, origf := range allNew {
meter := testMeterWithRegistry("meter") meter := testMeterWithRegistry("meter")
@ -120,7 +105,7 @@ func TestRegistryDiffInstruments(t *testing.T) {
other, err := nf(meter, "this") other, err := nf(meter, "this")
require.Error(t, err) require.Error(t, err)
require.NotNil(t, other) require.Nil(t, other)
require.True(t, errors.Is(err, registry.ErrMetricKindMismatch)) require.True(t, errors.Is(err, registry.ErrMetricKindMismatch))
} }
} }

View File

@ -23,11 +23,11 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
internal "go.opentelemetry.io/otel/internal/metric" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/number"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (
@ -44,10 +44,8 @@ type (
// current maps `mapkey` to *record. // current maps `mapkey` to *record.
current sync.Map current sync.Map
// asyncInstruments is a set of callbackLock sync.Mutex
// `*asyncInstrument` instances callbacks map[*callback]struct{}
asyncLock sync.Mutex
asyncInstruments *internal.AsyncInstrumentState
// currentEpoch is the current epoch number. It is // currentEpoch is the current epoch number. It is
// incremented in `Collect()`. // incremented in `Collect()`.
@ -58,15 +56,23 @@ type (
// collectLock prevents simultaneous calls to Collect(). // collectLock prevents simultaneous calls to Collect().
collectLock sync.Mutex collectLock sync.Mutex
}
// asyncSortSlice has a single purpose - as a temporary callback struct {
// place for sorting during labels creation to avoid insts map[*asyncInstrument]struct{}
// allocation. It is cleared after use. f func(context.Context)
asyncSortSlice attribute.Sortable }
asyncContextKey struct{}
asyncInstrument struct {
baseInstrument
instrument.Asynchronous
} }
syncInstrument struct { syncInstrument struct {
instrument baseInstrument
instrument.Synchronous
} }
// mapkey uniquely describes a metric instrument in terms of // mapkey uniquely describes a metric instrument in terms of
@ -92,16 +98,10 @@ type (
// supports checking for no updates during a round. // supports checking for no updates during a round.
collectedCount int64 collectedCount int64
// storage is the stored label set for this record, // labels is the stored label set for this record,
// except in cases where a label set is shared due to // except in cases where a label set is shared due to
// batch recording. // batch recording.
storage attribute.Set labels attribute.Set
// labels is the processed label set for this record.
// this may refer to the `storage` field in another
// record if this label set is shared resulting from
// `RecordBatch`.
labels *attribute.Set
// sortSlice has a single purpose - as a temporary // sortSlice has a single purpose - as a temporary
// place for sorting during labels creation to avoid // place for sorting during labels creation to avoid
@ -109,7 +109,7 @@ type (
sortSlice attribute.Sortable sortSlice attribute.Sortable
// inst is a pointer to the corresponding instrument. // inst is a pointer to the corresponding instrument.
inst *syncInstrument inst *baseInstrument
// current implements the actual RecordOne() API, // current implements the actual RecordOne() API,
// depending on the type of aggregation. If nil, the // depending on the type of aggregation. If nil, the
@ -118,36 +118,23 @@ type (
checkpoint aggregator.Aggregator checkpoint aggregator.Aggregator
} }
instrument struct { baseInstrument struct {
meter *Accumulator meter *Accumulator
descriptor sdkapi.Descriptor descriptor sdkapi.Descriptor
} }
asyncInstrument struct {
instrument
// recorders maps ordered labels to the pair of
// labelset and recorder
recorders map[attribute.Distinct]*labeledRecorder
}
labeledRecorder struct {
observedEpoch int64
labels *attribute.Set
observed aggregator.Aggregator
}
) )
var ( var (
_ sdkapi.MeterImpl = &Accumulator{} _ sdkapi.MeterImpl = &Accumulator{}
_ sdkapi.AsyncImpl = &asyncInstrument{}
_ sdkapi.SyncImpl = &syncInstrument{}
// ErrUninitializedInstrument is returned when an instrument is used when uninitialized. // ErrUninitializedInstrument is returned when an instrument is used when uninitialized.
ErrUninitializedInstrument = fmt.Errorf("use of an uninitialized instrument") ErrUninitializedInstrument = fmt.Errorf("use of an uninitialized instrument")
ErrBadInstrument = fmt.Errorf("use of a instrument from another SDK")
) )
func (inst *instrument) Descriptor() sdkapi.Descriptor { func (b *baseInstrument) Descriptor() sdkapi.Descriptor {
return inst.descriptor return b.descriptor
} }
func (a *asyncInstrument) Implementation() interface{} { func (a *asyncInstrument) Implementation() interface{} {
@ -158,77 +145,24 @@ func (s *syncInstrument) Implementation() interface{} {
return s return s
} }
func (a *asyncInstrument) observe(num number.Number, labels *attribute.Set) {
if err := aggregator.RangeTest(num, &a.descriptor); err != nil {
otel.Handle(err)
return
}
recorder := a.getRecorder(labels)
if recorder == nil {
// The instrument is disabled according to the
// AggregatorSelector.
return
}
if err := recorder.Update(context.Background(), num, &a.descriptor); err != nil {
otel.Handle(err)
return
}
}
func (a *asyncInstrument) getRecorder(labels *attribute.Set) aggregator.Aggregator {
lrec, ok := a.recorders[labels.Equivalent()]
if ok {
// Note: SynchronizedMove(nil) can't return an error
_ = lrec.observed.SynchronizedMove(nil, &a.descriptor)
lrec.observedEpoch = a.meter.currentEpoch
a.recorders[labels.Equivalent()] = lrec
return lrec.observed
}
var rec aggregator.Aggregator
a.meter.processor.AggregatorFor(&a.descriptor, &rec)
if a.recorders == nil {
a.recorders = make(map[attribute.Distinct]*labeledRecorder)
}
// This may store nil recorder in the map, thus disabling the
// asyncInstrument for the labelset for good. This is intentional,
// but will be revisited later.
a.recorders[labels.Equivalent()] = &labeledRecorder{
observed: rec,
labels: labels,
observedEpoch: a.meter.currentEpoch,
}
return rec
}
// acquireHandle gets or creates a `*record` corresponding to `kvs`, // acquireHandle gets or creates a `*record` corresponding to `kvs`,
// the input labels. The second argument `labels` is passed in to // the input labels.
// support re-use of the orderedLabels computed by a previous func (b *baseInstrument) acquireHandle(kvs []attribute.KeyValue) *record {
// measurement in the same batch. This performs two allocations
// in the common case.
func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attribute.Set) *record {
var rec *record
var equiv attribute.Distinct
if labelPtr == nil { // This memory allocation may not be used, but it's
// This memory allocation may not be used, but it's // needed for the `sortSlice` field, to avoid an
// needed for the `sortSlice` field, to avoid an // allocation while sorting.
// allocation while sorting. rec := &record{}
rec = &record{} rec.labels = attribute.NewSetWithSortable(kvs, &rec.sortSlice)
rec.storage = attribute.NewSetWithSortable(kvs, &rec.sortSlice)
rec.labels = &rec.storage
equiv = rec.storage.Equivalent()
} else {
equiv = labelPtr.Equivalent()
}
// Create lookup key for sync.Map (one allocation, as this // Create lookup key for sync.Map (one allocation, as this
// passes through an interface{}) // passes through an interface{})
mk := mapkey{ mk := mapkey{
descriptor: &s.descriptor, descriptor: &b.descriptor,
ordered: equiv, ordered: rec.labels.Equivalent(),
} }
if actual, ok := s.meter.current.Load(mk); ok { if actual, ok := b.meter.current.Load(mk); ok {
// Existing record case. // Existing record case.
existingRec := actual.(*record) existingRec := actual.(*record)
if existingRec.refMapped.ref() { if existingRec.refMapped.ref() {
@ -239,19 +173,15 @@ func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attri
// This entry is no longer mapped, try to add a new entry. // This entry is no longer mapped, try to add a new entry.
} }
if rec == nil {
rec = &record{}
rec.labels = labelPtr
}
rec.refMapped = refcountMapped{value: 2} rec.refMapped = refcountMapped{value: 2}
rec.inst = s rec.inst = b
s.meter.processor.AggregatorFor(&s.descriptor, &rec.current, &rec.checkpoint) b.meter.processor.AggregatorFor(&b.descriptor, &rec.current, &rec.checkpoint)
for { for {
// Load/Store: there's a memory allocation to place `mk` into // Load/Store: there's a memory allocation to place `mk` into
// an interface here. // an interface here.
if actual, loaded := s.meter.current.LoadOrStore(mk, rec); loaded { if actual, loaded := b.meter.current.LoadOrStore(mk, rec); loaded {
// Existing record case. Cannot change rec here because if fail // Existing record case. Cannot change rec here because if fail
// will try to add rec again to avoid new allocations. // will try to add rec again to avoid new allocations.
oldRec := actual.(*record) oldRec := actual.(*record)
@ -278,11 +208,22 @@ func (s *syncInstrument) acquireHandle(kvs []attribute.KeyValue, labelPtr *attri
} }
} }
// RecordOne captures a single synchronous metric event.
//
// The order of the input array `kvs` may be sorted after the function is called. // The order of the input array `kvs` may be sorted after the function is called.
func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs []attribute.KeyValue) { func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs []attribute.KeyValue) {
h := s.acquireHandle(kvs, nil) h := s.acquireHandle(kvs)
defer h.unbind() defer h.unbind()
h.RecordOne(ctx, num) h.captureOne(ctx, num)
}
// ObserveOne captures a single asynchronous metric event.
// The order of the input array `kvs` may be sorted after the function is called.
func (a *asyncInstrument) ObserveOne(ctx context.Context, num number.Number, attrs []attribute.KeyValue) {
h := a.acquireHandle(attrs)
defer h.unbind()
h.captureOne(ctx, num)
} }
// NewAccumulator constructs a new Accumulator for the given // NewAccumulator constructs a new Accumulator for the given
@ -296,15 +237,17 @@ func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs [
// own periodic collection. // own periodic collection.
func NewAccumulator(processor export.Processor) *Accumulator { func NewAccumulator(processor export.Processor) *Accumulator {
return &Accumulator{ return &Accumulator{
processor: processor, processor: processor,
asyncInstruments: internal.NewAsyncInstrumentState(), callbacks: map[*callback]struct{}{},
} }
} }
var _ sdkapi.MeterImpl = &Accumulator{}
// NewSyncInstrument implements sdkapi.MetricImpl. // NewSyncInstrument implements sdkapi.MetricImpl.
func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) { func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
return &syncInstrument{ return &syncInstrument{
instrument: instrument{ baseInstrument: baseInstrument{
descriptor: descriptor, descriptor: descriptor,
meter: m, meter: m,
}, },
@ -312,19 +255,40 @@ func (m *Accumulator) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.Sy
} }
// NewAsyncInstrument implements sdkapi.MetricImpl. // NewAsyncInstrument implements sdkapi.MetricImpl.
func (m *Accumulator) NewAsyncInstrument(descriptor sdkapi.Descriptor, runner sdkapi.AsyncRunner) (sdkapi.AsyncImpl, error) { func (m *Accumulator) NewAsyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.AsyncImpl, error) {
a := &asyncInstrument{ a := &asyncInstrument{
instrument: instrument{ baseInstrument: baseInstrument{
descriptor: descriptor, descriptor: descriptor,
meter: m, meter: m,
}, },
} }
m.asyncLock.Lock()
defer m.asyncLock.Unlock()
m.asyncInstruments.Register(a, runner)
return a, nil return a, nil
} }
func (m *Accumulator) RegisterCallback(insts []instrument.Asynchronous, f func(context.Context)) error {
cb := &callback{
insts: map[*asyncInstrument]struct{}{},
f: f,
}
for _, inst := range insts {
impl, ok := inst.(sdkapi.AsyncImpl)
if !ok {
return ErrBadInstrument
}
ai, err := m.fromAsync(impl)
if err != nil {
return err
}
cb.insts[ai] = struct{}{}
}
m.callbackLock.Lock()
defer m.callbackLock.Unlock()
m.callbacks[cb] = struct{}{}
return nil
}
// Collect traverses the list of active records and observers and // Collect traverses the list of active records and observers and
// exports data for each active instrument. Collect() may not be // exports data for each active instrument. Collect() may not be
// called concurrently. // called concurrently.
@ -337,14 +301,14 @@ func (m *Accumulator) Collect(ctx context.Context) int {
m.collectLock.Lock() m.collectLock.Lock()
defer m.collectLock.Unlock() defer m.collectLock.Unlock()
checkpointed := m.observeAsyncInstruments(ctx) m.runAsyncCallbacks(ctx)
checkpointed += m.collectSyncInstruments() checkpointed := m.collectInstruments()
m.currentEpoch++ m.currentEpoch++
return checkpointed return checkpointed
} }
func (m *Accumulator) collectSyncInstruments() int { func (m *Accumulator) collectInstruments() int {
checkpointed := 0 checkpointed := 0
m.current.Range(func(key interface{}, value interface{}) bool { m.current.Range(func(key interface{}, value interface{}) bool {
@ -387,35 +351,17 @@ func (m *Accumulator) collectSyncInstruments() int {
return checkpointed return checkpointed
} }
// CollectAsync implements internal.AsyncCollector. func (m *Accumulator) runAsyncCallbacks(ctx context.Context) {
// The order of the input array `kvs` may be sorted after the function is called. m.callbackLock.Lock()
func (m *Accumulator) CollectAsync(kv []attribute.KeyValue, obs ...sdkapi.Observation) { defer m.callbackLock.Unlock()
labels := attribute.NewSetWithSortable(kv, &m.asyncSortSlice)
for _, ob := range obs { ctx = context.WithValue(ctx, asyncContextKey{}, m)
if a := m.fromAsync(ob.AsyncImpl()); a != nil {
a.observe(ob.Number(), &labels) for cb := range m.callbacks {
} cb.f(ctx)
} }
} }
func (m *Accumulator) observeAsyncInstruments(ctx context.Context) int {
m.asyncLock.Lock()
defer m.asyncLock.Unlock()
asyncCollected := 0
m.asyncInstruments.Run(ctx, m)
for _, inst := range m.asyncInstruments.Instruments() {
if a := m.fromAsync(inst); a != nil {
asyncCollected += m.checkpointAsync(a)
}
}
return asyncCollected
}
func (m *Accumulator) checkpointRecord(r *record) int { func (m *Accumulator) checkpointRecord(r *record) int {
if r.current == nil { if r.current == nil {
return 0 return 0
@ -426,7 +372,7 @@ func (m *Accumulator) checkpointRecord(r *record) int {
return 0 return 0
} }
a := export.NewAccumulation(&r.inst.descriptor, r.labels, r.checkpoint) a := export.NewAccumulation(&r.inst.descriptor, &r.labels, r.checkpoint)
err = m.processor.Process(a) err = m.processor.Process(a)
if err != nil { if err != nil {
otel.Handle(err) otel.Handle(err)
@ -434,63 +380,7 @@ func (m *Accumulator) checkpointRecord(r *record) int {
return 1 return 1
} }
func (m *Accumulator) checkpointAsync(a *asyncInstrument) int { func (r *record) captureOne(ctx context.Context, num number.Number) {
if len(a.recorders) == 0 {
return 0
}
checkpointed := 0
for encodedLabels, lrec := range a.recorders {
lrec := lrec
epochDiff := m.currentEpoch - lrec.observedEpoch
if epochDiff == 0 {
if lrec.observed != nil {
a := export.NewAccumulation(&a.descriptor, lrec.labels, lrec.observed)
err := m.processor.Process(a)
if err != nil {
otel.Handle(err)
}
checkpointed++
}
} else if epochDiff > 1 {
// This is second collection cycle with no
// observations for this labelset. Remove the
// recorder.
delete(a.recorders, encodedLabels)
}
}
if len(a.recorders) == 0 {
a.recorders = nil
}
return checkpointed
}
// RecordBatch enters a batch of metric events.
// The order of the input array `kvs` may be sorted after the function is called.
func (m *Accumulator) RecordBatch(ctx context.Context, kvs []attribute.KeyValue, measurements ...sdkapi.Measurement) {
// Labels will be computed the first time acquireHandle is
// called. Subsequent calls to acquireHandle will re-use the
// previously computed value instead of recomputing the
// ordered labels.
var labelsPtr *attribute.Set
for i, meas := range measurements {
s := m.fromSync(meas.SyncImpl())
if s == nil {
continue
}
h := s.acquireHandle(kvs, labelsPtr)
// Re-use labels for the next measurement.
if i == 0 {
labelsPtr = h.labels
}
defer h.unbind()
h.RecordOne(ctx, meas.Number())
}
}
// RecordOne implements sdkapi.SyncImpl.
func (r *record) RecordOne(ctx context.Context, num number.Number) {
if r.current == nil { if r.current == nil {
// The instrument is disabled according to the AggregatorSelector. // The instrument is disabled according to the AggregatorSelector.
return return
@ -519,26 +409,16 @@ func (r *record) mapkey() mapkey {
} }
} }
// fromSync gets a sync implementation object, checking for
// uninitialized instruments and instruments created by another SDK.
func (m *Accumulator) fromSync(sync sdkapi.SyncImpl) *syncInstrument {
if sync != nil {
if inst, ok := sync.Implementation().(*syncInstrument); ok {
return inst
}
}
otel.Handle(ErrUninitializedInstrument)
return nil
}
// fromSync gets an async implementation object, checking for // fromSync gets an async implementation object, checking for
// uninitialized instruments and instruments created by another SDK. // uninitialized instruments and instruments created by another SDK.
func (m *Accumulator) fromAsync(async sdkapi.AsyncImpl) *asyncInstrument { func (m *Accumulator) fromAsync(async sdkapi.AsyncImpl) (*asyncInstrument, error) {
if async != nil { if async == nil {
if inst, ok := async.Implementation().(*asyncInstrument); ok { return nil, ErrUninitializedInstrument
return inst
}
} }
otel.Handle(ErrUninitializedInstrument) inst, ok := async.Implementation().(*asyncInstrument)
return nil if !ok {
return nil, ErrBadInstrument
}
return inst, nil
} }

View File

@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
import ( import (
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/metric/number"
) )
// Descriptor contains all the settings that describe an instrument, // Descriptor contains all the settings that describe an instrument,

View File

@ -19,8 +19,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/number"
"go.opentelemetry.io/otel/metric/unit" "go.opentelemetry.io/otel/metric/unit"
"go.opentelemetry.io/otel/sdk/metric/number"
) )
func TestDescriptorGetters(t *testing.T) { func TestDescriptorGetters(t *testing.T) {

View File

@ -14,7 +14,7 @@
//go:generate stringer -type=InstrumentKind //go:generate stringer -type=InstrumentKind
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
// InstrumentKind describes the kind of instrument. // InstrumentKind describes the kind of instrument.
type InstrumentKind int8 type InstrumentKind int8

View File

@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/sdkapi" "go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
func TestInstrumentKinds(t *testing.T) { func TestInstrumentKinds(t *testing.T) {

View File

@ -12,20 +12,34 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
import ( import (
"context" "context"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/instrument"
) "go.opentelemetry.io/otel/sdk/metric/number"
) // import (
// "context"
// "go.opentelemetry.io/otel/attribute"
// "go.opentelemetry.io/otel/sdk/metric/number"
// )
type noopInstrument struct { type noopInstrument struct {
descriptor Descriptor descriptor Descriptor
} }
type noopSyncInstrument struct{ noopInstrument } type noopSyncInstrument struct {
type noopAsyncInstrument struct{ noopInstrument } noopInstrument
instrument.Synchronous
}
type noopAsyncInstrument struct {
noopInstrument
instrument.Asynchronous
}
var _ SyncImpl = noopSyncInstrument{} var _ SyncImpl = noopSyncInstrument{}
var _ AsyncImpl = noopAsyncInstrument{} var _ AsyncImpl = noopAsyncInstrument{}
@ -34,7 +48,7 @@ var _ AsyncImpl = noopAsyncInstrument{}
// synchronous instrument interface. // synchronous instrument interface.
func NewNoopSyncInstrument() SyncImpl { func NewNoopSyncInstrument() SyncImpl {
return noopSyncInstrument{ return noopSyncInstrument{
noopInstrument{ noopInstrument: noopInstrument{
descriptor: Descriptor{ descriptor: Descriptor{
instrumentKind: CounterInstrumentKind, instrumentKind: CounterInstrumentKind,
}, },
@ -46,7 +60,7 @@ func NewNoopSyncInstrument() SyncImpl {
// asynchronous instrument interface. // asynchronous instrument interface.
func NewNoopAsyncInstrument() AsyncImpl { func NewNoopAsyncInstrument() AsyncImpl {
return noopAsyncInstrument{ return noopAsyncInstrument{
noopInstrument{ noopInstrument: noopInstrument{
descriptor: Descriptor{ descriptor: Descriptor{
instrumentKind: CounterObserverInstrumentKind, instrumentKind: CounterObserverInstrumentKind,
}, },
@ -64,3 +78,6 @@ func (n noopInstrument) Descriptor() Descriptor {
func (noopSyncInstrument) RecordOne(context.Context, number.Number, []attribute.KeyValue) { func (noopSyncInstrument) RecordOne(context.Context, number.Number, []attribute.KeyValue) {
} }
func (noopAsyncInstrument) ObserveOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
}

View File

@ -12,21 +12,19 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package sdkapi // import "go.opentelemetry.io/otel/metric/sdkapi" package sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
import ( import (
"context" "context"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/metric/number"
) )
// MeterImpl is the interface an SDK must implement to supply a Meter // MeterImpl is the interface an SDK must implement to supply a Meter
// implementation. // implementation.
type MeterImpl interface { type MeterImpl interface {
// RecordBatch atomically records a batch of measurements.
RecordBatch(ctx context.Context, labels []attribute.KeyValue, measurement ...Measurement)
// NewSyncInstrument returns a newly constructed // NewSyncInstrument returns a newly constructed
// synchronous instrument implementation or an error, should // synchronous instrument implementation or an error, should
// one occur. // one occur.
@ -35,10 +33,10 @@ type MeterImpl interface {
// NewAsyncInstrument returns a newly constructed // NewAsyncInstrument returns a newly constructed
// asynchronous instrument implementation or an error, should // asynchronous instrument implementation or an error, should
// one occur. // one occur.
NewAsyncInstrument( NewAsyncInstrument(descriptor Descriptor) (AsyncImpl, error)
descriptor Descriptor,
runner AsyncRunner, // Etc.
) (AsyncImpl, error) RegisterCallback(insts []instrument.Asynchronous, callback func(context.Context)) error
} }
// InstrumentImpl is a common interface for synchronous and // InstrumentImpl is a common interface for synchronous and
@ -57,6 +55,7 @@ type InstrumentImpl interface {
// synchronous instrument (e.g., Histogram and Counter instruments). // synchronous instrument (e.g., Histogram and Counter instruments).
type SyncImpl interface { type SyncImpl interface {
InstrumentImpl InstrumentImpl
instrument.Synchronous
// RecordOne captures a single synchronous metric event. // RecordOne captures a single synchronous metric event.
RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
@ -66,6 +65,10 @@ type SyncImpl interface {
// asynchronous instrument (e.g., Observer instruments). // asynchronous instrument (e.g., Observer instruments).
type AsyncImpl interface { type AsyncImpl interface {
InstrumentImpl InstrumentImpl
instrument.Asynchronous
// ObserveOne captures a single synchronous metric event.
ObserveOne(ctx context.Context, number number.Number, labels []attribute.KeyValue)
} }
// AsyncRunner is expected to convert into an AsyncSingleRunner or an // AsyncRunner is expected to convert into an AsyncSingleRunner or an

View File

@ -19,7 +19,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/number" "go.opentelemetry.io/otel/sdk/metric/number"
) )
func TestMeasurementGetters(t *testing.T) { func TestMeasurementGetters(t *testing.T) {

181
sdk/metric/sdkapi/wrap.go Normal file
View File

@ -0,0 +1,181 @@
// 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 sdkapi // import "go.opentelemetry.io/otel/sdk/metric/sdkapi"
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"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/sdk/metric/number"
)
type (
meter struct{ MeterImpl }
sfMeter struct{ meter }
siMeter struct{ meter }
afMeter struct{ meter }
aiMeter struct{ meter }
iAdder struct{ SyncImpl }
fAdder struct{ SyncImpl }
iRecorder struct{ SyncImpl }
fRecorder struct{ SyncImpl }
iObserver struct{ AsyncImpl }
fObserver struct{ AsyncImpl }
)
func WrapMeterImpl(impl MeterImpl) metric.Meter {
return meter{impl}
}
func UnwrapMeterImpl(m metric.Meter) MeterImpl {
mm, ok := m.(meter)
if !ok {
return nil
}
return mm.MeterImpl
}
func (m meter) AsyncFloat64() asyncfloat64.InstrumentProvider {
return afMeter{m}
}
func (m meter) AsyncInt64() asyncint64.InstrumentProvider {
return aiMeter{m}
}
func (m meter) SyncFloat64() syncfloat64.InstrumentProvider {
return sfMeter{m}
}
func (m meter) SyncInt64() syncint64.InstrumentProvider {
return siMeter{m}
}
func (m meter) RegisterCallback(insts []instrument.Asynchronous, cb func(ctx context.Context)) error {
return m.MeterImpl.RegisterCallback(insts, cb)
}
func (m meter) newSync(name string, ikind InstrumentKind, nkind number.Kind, opts []instrument.Option) (SyncImpl, error) {
cfg := instrument.NewConfig(opts...)
return m.NewSyncInstrument(NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit()))
}
func (m meter) newAsync(name string, ikind InstrumentKind, nkind number.Kind, opts []instrument.Option) (AsyncImpl, error) {
cfg := instrument.NewConfig(opts...)
return m.NewAsyncInstrument(NewDescriptor(name, ikind, nkind, cfg.Description(), cfg.Unit()))
}
func (m afMeter) Counter(name string, opts ...instrument.Option) (asyncfloat64.Counter, error) {
inst, err := m.newAsync(name, CounterObserverInstrumentKind, number.Float64Kind, opts)
return fObserver{inst}, err
}
func (m afMeter) UpDownCounter(name string, opts ...instrument.Option) (asyncfloat64.UpDownCounter, error) {
inst, err := m.newAsync(name, UpDownCounterObserverInstrumentKind, number.Float64Kind, opts)
return fObserver{inst}, err
}
func (m afMeter) Gauge(name string, opts ...instrument.Option) (asyncfloat64.Gauge, error) {
inst, err := m.newAsync(name, GaugeObserverInstrumentKind, number.Float64Kind, opts)
return fObserver{inst}, err
}
func (m aiMeter) Counter(name string, opts ...instrument.Option) (asyncint64.Counter, error) {
inst, err := m.newAsync(name, CounterObserverInstrumentKind, number.Int64Kind, opts)
return iObserver{inst}, err
}
func (m aiMeter) UpDownCounter(name string, opts ...instrument.Option) (asyncint64.UpDownCounter, error) {
inst, err := m.newAsync(name, UpDownCounterObserverInstrumentKind, number.Int64Kind, opts)
return iObserver{inst}, err
}
func (m aiMeter) Gauge(name string, opts ...instrument.Option) (asyncint64.Gauge, error) {
inst, err := m.newAsync(name, GaugeObserverInstrumentKind, number.Int64Kind, opts)
return iObserver{inst}, err
}
func (m sfMeter) Counter(name string, opts ...instrument.Option) (syncfloat64.Counter, error) {
inst, err := m.newSync(name, CounterInstrumentKind, number.Float64Kind, opts)
return fAdder{inst}, err
}
func (m sfMeter) UpDownCounter(name string, opts ...instrument.Option) (syncfloat64.UpDownCounter, error) {
inst, err := m.newSync(name, UpDownCounterInstrumentKind, number.Float64Kind, opts)
return fAdder{inst}, err
}
func (m sfMeter) Histogram(name string, opts ...instrument.Option) (syncfloat64.Histogram, error) {
inst, err := m.newSync(name, HistogramInstrumentKind, number.Float64Kind, opts)
return fRecorder{inst}, err
}
func (m siMeter) Counter(name string, opts ...instrument.Option) (syncint64.Counter, error) {
inst, err := m.newSync(name, CounterInstrumentKind, number.Int64Kind, opts)
return iAdder{inst}, err
}
func (m siMeter) UpDownCounter(name string, opts ...instrument.Option) (syncint64.UpDownCounter, error) {
inst, err := m.newSync(name, UpDownCounterInstrumentKind, number.Int64Kind, opts)
return iAdder{inst}, err
}
func (m siMeter) Histogram(name string, opts ...instrument.Option) (syncint64.Histogram, error) {
inst, err := m.newSync(name, HistogramInstrumentKind, number.Int64Kind, opts)
return iRecorder{inst}, err
}
func (a fAdder) Add(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
if a.SyncImpl != nil {
a.SyncImpl.RecordOne(ctx, number.NewFloat64Number(value), attrs)
}
}
func (a iAdder) Add(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
if a.SyncImpl != nil {
a.SyncImpl.RecordOne(ctx, number.NewInt64Number(value), attrs)
}
}
func (a fRecorder) Record(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
if a.SyncImpl != nil {
a.SyncImpl.RecordOne(ctx, number.NewFloat64Number(value), attrs)
}
}
func (a iRecorder) Record(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
if a.SyncImpl != nil {
a.SyncImpl.RecordOne(ctx, number.NewInt64Number(value), attrs)
}
}
func (a fObserver) Observe(ctx context.Context, value float64, attrs ...attribute.KeyValue) {
if a.AsyncImpl != nil {
a.AsyncImpl.ObserveOne(ctx, number.NewFloat64Number(value), attrs)
}
}
func (a iObserver) Observe(ctx context.Context, value int64, attrs ...attribute.KeyValue) {
if a.AsyncImpl != nil {
a.AsyncImpl.ObserveOne(ctx, number.NewInt64Number(value), attrs)
}
}

View File

@ -15,12 +15,12 @@
package simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple" package simple // import "go.opentelemetry.io/otel/sdk/metric/selector/simple"
import ( import (
"go.opentelemetry.io/otel/metric/sdkapi"
"go.opentelemetry.io/otel/sdk/metric/aggregator" "go.opentelemetry.io/otel/sdk/metric/aggregator"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" "go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/metric/export" "go.opentelemetry.io/otel/sdk/metric/export"
"go.opentelemetry.io/otel/sdk/metric/sdkapi"
) )
type ( type (

Some files were not shown because too many files have changed in this diff Show More