From 813936187e46d48924b717ed9f63767eef26f55a Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Wed, 1 Mar 2023 11:16:03 -0800 Subject: [PATCH] Support a global MeterProvider in `go.opentelemetry.io/otel` (#3818) * Move ErrorHandler impl to internal To avoid the import cycle, the otel/metric package needs to not import otel. To achieve this, the error handling implementation is moved to the otel/internal/global package where both can import the needed functionality. * Add global metric to go.opentelemetry.io/otel * Crosslink and update to global metric in otel * Add changes to changelog * Set PR number in changelog * Add global metric unit tests * Rename MeterProivder() to GetMeterProivder() * Add TODO to remove nolint comments --- CHANGELOG.md | 12 + bridge/opentracing/go.mod | 3 + bridge/opentracing/test/go.mod | 3 + example/fib/go.mod | 3 + example/jaeger/go.mod | 3 + example/namedtracer/go.mod | 3 + example/otel-collector/go.mod | 3 + example/passthrough/go.mod | 3 + example/zipkin/go.mod | 3 + exporters/jaeger/go.mod | 3 + .../otlpmetric/otlpmetricgrpc/example_test.go | 4 +- .../otlp/otlpmetric/otlpmetricgrpc/go.mod | 2 +- .../otlpmetric/otlpmetrichttp/example_test.go | 4 +- .../otlp/otlpmetric/otlpmetrichttp/go.mod | 2 +- exporters/otlp/otlptrace/go.mod | 3 + exporters/otlp/otlptrace/otlptracegrpc/go.mod | 3 + exporters/otlp/otlptrace/otlptracehttp/go.mod | 3 + exporters/stdout/stdouttrace/go.mod | 3 + exporters/zipkin/go.mod | 3 + go.mod | 3 + handler.go | 64 +---- handler_test.go | 214 ++--------------- internal/global/handler.go | 103 ++++++++ internal/global/handler_test.go | 226 ++++++++++++++++++ metric.go | 61 +++++ metric/global/global.go | 9 + metric/internal/global/instruments.go | 26 +- metric/internal/global/meter.go | 4 +- metric_test.go | 41 ++++ sdk/go.mod | 3 + sdk/metric/example_test.go | 4 +- sdk/metric/meter_test.go | 7 +- trace/go.mod | 2 + 33 files changed, 551 insertions(+), 282 deletions(-) create mode 100644 internal/global/handler.go create mode 100644 internal/global/handler_test.go create mode 100644 metric.go create mode 100644 metric_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 131f932d78d..c774008a2c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Added + +- Support global `MeterProvider` in `go.opentelemetry.io/otel`. (#3818) + - Use `Meter` for a `metric.Meter` from the global `metric.MeterProvider`. + - Use `GetMeterProivder` for a global `metric.MeterProvider`. + - Use `SetMeterProivder` to set the global `metric.MeterProvider`. + ### Changed - Dropped compatibility testing for [Go 1.18]. @@ -17,6 +24,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Handle empty environment variable as it they were not set. (#3764) +### Deprecated + +- The `go.opentelemetry.io/otel/metric/global` package is deprecated. + Use `go.opentelemetry.io/otel` instead. (#3818) + ### Removed - The deprecated `go.opentelemetry.io/otel/metric/unit` package is removed. (#3814) diff --git a/bridge/opentracing/go.mod b/bridge/opentracing/go.mod index eb9887d5a01..38f15dcd752 100644 --- a/bridge/opentracing/go.mod +++ b/bridge/opentracing/go.mod @@ -18,5 +18,8 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/bridge/opentracing/test/go.mod b/bridge/opentracing/test/go.mod index 2cb63f89916..3edb93674d0 100644 --- a/bridge/opentracing/test/go.mod +++ b/bridge/opentracing/test/go.mod @@ -23,6 +23,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect @@ -31,3 +32,5 @@ require ( google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace go.opentelemetry.io/otel/metric => ../../../metric diff --git a/example/fib/go.mod b/example/fib/go.mod index 5fe869fefea..7d6f35a96c8 100644 --- a/example/fib/go.mod +++ b/example/fib/go.mod @@ -12,6 +12,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect ) @@ -22,3 +23,5 @@ replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters replace go.opentelemetry.io/otel/sdk => ../../sdk replace go.opentelemetry.io/otel/trace => ../../trace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index b33cea0614b..b99aae53537 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -17,8 +17,11 @@ require ( require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/sys v0.5.0 // indirect ) replace go.opentelemetry.io/otel/trace => ../../trace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index 467324eb6fb..8b6a5dc3cd5 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -17,9 +17,12 @@ require ( require ( github.com/go-logr/logr v1.2.3 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect ) replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index 3573d666da1..e5cc970943c 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -23,6 +23,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect @@ -38,3 +39,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otl replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../exporters/otlp/internal/retry + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/passthrough/go.mod b/example/passthrough/go.mod index 0d78d317afb..2934867637d 100644 --- a/example/passthrough/go.mod +++ b/example/passthrough/go.mod @@ -12,6 +12,7 @@ require ( require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect ) @@ -22,3 +23,5 @@ replace ( ) replace go.opentelemetry.io/otel/exporters/stdout/stdouttrace => ../../exporters/stdout/stdouttrace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index a027a189e77..811e4c700a5 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -19,7 +19,10 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/openzipkin/zipkin-go v0.4.1 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect ) replace go.opentelemetry.io/otel/trace => ../../trace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/exporters/jaeger/go.mod b/exporters/jaeger/go.mod index f5225228db9..4d1fd8b3a1c 100644 --- a/exporters/jaeger/go.mod +++ b/exporters/jaeger/go.mod @@ -16,6 +16,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -25,3 +26,5 @@ replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/sdk => ../../sdk + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go index f7630760678..8307b51fc69 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go @@ -17,8 +17,8 @@ package otlpmetricgrpc_test import ( "context" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" - "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/sdk/metric" ) @@ -35,7 +35,7 @@ func Example() { panic(err) } }() - global.SetMeterProvider(meterProvider) + otel.SetMeterProvider(meterProvider) // From here, the meterProvider can be used by instrumentation to collect // telemetry. diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod index bfc61ce141a..bf30fb30dac 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/go.mod @@ -9,7 +9,6 @@ require ( go.opentelemetry.io/otel v1.14.0 go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.37.0 - go.opentelemetry.io/otel/metric v0.37.0 go.opentelemetry.io/otel/sdk/metric v0.37.0 go.opentelemetry.io/proto/otlp v0.19.0 google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f @@ -26,6 +25,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/otel/sdk v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/net v0.7.0 // indirect diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/example_test.go b/exporters/otlp/otlpmetric/otlpmetrichttp/example_test.go index d3397167c70..398c834a5f1 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/example_test.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/example_test.go @@ -17,8 +17,8 @@ package otlpmetrichttp_test import ( "context" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" - "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/sdk/metric" ) @@ -35,7 +35,7 @@ func Example() { panic(err) } }() - global.SetMeterProvider(meterProvider) + otel.SetMeterProvider(meterProvider) // From here, the meterProvider can be used by instrumentation to collect // telemetry. diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod index b1df2a44f34..3b47c210709 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/go.mod @@ -9,7 +9,6 @@ require ( go.opentelemetry.io/otel v1.14.0 go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.37.0 - go.opentelemetry.io/otel/metric v0.37.0 go.opentelemetry.io/otel/sdk/metric v0.37.0 go.opentelemetry.io/proto/otlp v0.19.0 google.golang.org/protobuf v1.28.1 @@ -24,6 +23,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/otel/sdk v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/net v0.7.0 // indirect diff --git a/exporters/otlp/otlptrace/go.mod b/exporters/otlp/otlptrace/go.mod index 54aa3a03bb5..3be0bbf9aea 100644 --- a/exporters/otlp/otlptrace/go.mod +++ b/exporters/otlp/otlptrace/go.mod @@ -22,6 +22,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect @@ -36,3 +37,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../sdk replace go.opentelemetry.io/otel/trace => ../../../trace replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../internal/retry + +replace go.opentelemetry.io/otel/metric => ../../../metric diff --git a/exporters/otlp/otlptrace/otlptracegrpc/go.mod b/exporters/otlp/otlptrace/otlptracegrpc/go.mod index de850c10831..7099a5e7a5f 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/go.mod +++ b/exporters/otlp/otlptrace/otlptracegrpc/go.mod @@ -23,6 +23,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect @@ -39,3 +40,5 @@ replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../ replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry + +replace go.opentelemetry.io/otel/metric => ../../../../metric diff --git a/exporters/otlp/otlptrace/otlptracehttp/go.mod b/exporters/otlp/otlptrace/otlptracehttp/go.mod index 76e44c182c4..0fcbeee6fae 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/go.mod +++ b/exporters/otlp/otlptrace/otlptracehttp/go.mod @@ -21,6 +21,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect @@ -38,3 +39,5 @@ replace go.opentelemetry.io/otel/sdk => ../../../../sdk replace go.opentelemetry.io/otel/trace => ../../../../trace replace go.opentelemetry.io/otel/exporters/otlp/internal/retry => ../../internal/retry + +replace go.opentelemetry.io/otel/metric => ../../../../metric diff --git a/exporters/stdout/stdouttrace/go.mod b/exporters/stdout/stdouttrace/go.mod index 43587994dcc..c83008d96e7 100644 --- a/exporters/stdout/stdouttrace/go.mod +++ b/exporters/stdout/stdouttrace/go.mod @@ -19,8 +19,11 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) replace go.opentelemetry.io/otel/trace => ../../../trace + +replace go.opentelemetry.io/otel/metric => ../../../metric diff --git a/exporters/zipkin/go.mod b/exporters/zipkin/go.mod index 3af17aaf690..090cb404466 100644 --- a/exporters/zipkin/go.mod +++ b/exporters/zipkin/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect golang.org/x/sys v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -25,3 +26,5 @@ replace go.opentelemetry.io/otel/trace => ../../trace replace go.opentelemetry.io/otel => ../.. replace go.opentelemetry.io/otel/sdk => ../../sdk + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/go.mod b/go.mod index 89fc125f9ff..a4013cf63d6 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-logr/stdr v1.2.2 github.com/google/go-cmp v0.5.9 github.com/stretchr/testify v1.8.2 + go.opentelemetry.io/otel/metric v0.37.0 go.opentelemetry.io/otel/trace v1.14.0 ) @@ -17,3 +18,5 @@ require ( ) replace go.opentelemetry.io/otel/trace => ./trace + +replace go.opentelemetry.io/otel/metric => ./metric diff --git a/handler.go b/handler.go index ecd363ab516..4115fe3bbb5 100644 --- a/handler.go +++ b/handler.go @@ -15,58 +15,16 @@ package otel // import "go.opentelemetry.io/otel" import ( - "log" - "os" - "sync/atomic" - "unsafe" + "go.opentelemetry.io/otel/internal/global" ) var ( - // globalErrorHandler provides an ErrorHandler that can be used - // throughout an OpenTelemetry instrumented project. When a user - // specified ErrorHandler is registered (`SetErrorHandler`) all calls to - // `Handle` and will be delegated to the registered ErrorHandler. - globalErrorHandler = defaultErrorHandler() - - // Compile-time check that delegator implements ErrorHandler. - _ ErrorHandler = (*delegator)(nil) - // Compile-time check that errLogger implements ErrorHandler. - _ ErrorHandler = (*errLogger)(nil) + // Compile-time check global.ErrDelegator implements ErrorHandler. + _ ErrorHandler = (*global.ErrDelegator)(nil) + // Compile-time check global.ErrLogger implements ErrorHandler. + _ ErrorHandler = (*global.ErrLogger)(nil) ) -type delegator struct { - delegate unsafe.Pointer -} - -func (d *delegator) Handle(err error) { - d.getDelegate().Handle(err) -} - -func (d *delegator) getDelegate() ErrorHandler { - return *(*ErrorHandler)(atomic.LoadPointer(&d.delegate)) -} - -// setDelegate sets the ErrorHandler delegate. -func (d *delegator) setDelegate(eh ErrorHandler) { - atomic.StorePointer(&d.delegate, unsafe.Pointer(&eh)) -} - -func defaultErrorHandler() *delegator { - d := &delegator{} - d.setDelegate(&errLogger{l: log.New(os.Stderr, "", log.LstdFlags)}) - return d -} - -// errLogger logs errors if no delegate is set, otherwise they are delegated. -type errLogger struct { - l *log.Logger -} - -// Handle logs err if no delegate is set, otherwise it is delegated. -func (h *errLogger) Handle(err error) { - h.l.Print(err) -} - // GetErrorHandler returns the global ErrorHandler instance. // // The default ErrorHandler instance returned will log all errors to STDERR @@ -76,9 +34,7 @@ func (h *errLogger) Handle(err error) { // // Subsequent calls to SetErrorHandler after the first will not forward errors // to the new ErrorHandler for prior returned instances. -func GetErrorHandler() ErrorHandler { - return globalErrorHandler -} +func GetErrorHandler() ErrorHandler { return global.GetErrorHandler() } // SetErrorHandler sets the global ErrorHandler to h. // @@ -86,11 +42,7 @@ func GetErrorHandler() ErrorHandler { // GetErrorHandler will send errors to h instead of the default logging // ErrorHandler. Subsequent calls will set the global ErrorHandler, but not // delegate errors to h. -func SetErrorHandler(h ErrorHandler) { - globalErrorHandler.setDelegate(h) -} +func SetErrorHandler(h ErrorHandler) { global.SetErrorHandler(h) } // Handle is a convenience function for ErrorHandler().Handle(err). -func Handle(err error) { - GetErrorHandler().Handle(err) -} +func Handle(err error) { global.Handle(err) } diff --git a/handler_test.go b/handler_test.go index b6b9b20cae0..c522d61e661 100644 --- a/handler_test.go +++ b/handler_test.go @@ -15,212 +15,28 @@ package otel import ( - "bytes" - "errors" - "io" - "log" - "os" "testing" - "github.com/stretchr/testify/suite" + "github.com/stretchr/testify/assert" ) -type testErrCatcher []string - -func (l *testErrCatcher) Write(p []byte) (int, error) { - msg := bytes.TrimRight(p, "\n") - (*l) = append(*l, string(msg)) - return len(msg), nil -} - -func (l *testErrCatcher) Reset() { - *l = testErrCatcher([]string{}) -} - -func (l *testErrCatcher) Got() []string { - return []string(*l) -} - -func causeErr(text string) { - Handle(errors.New(text)) -} - -type HandlerTestSuite struct { - suite.Suite - - origHandler ErrorHandler - errCatcher *testErrCatcher -} - -func (s *HandlerTestSuite) SetupSuite() { - s.errCatcher = new(testErrCatcher) - s.origHandler = globalErrorHandler.getDelegate() - - globalErrorHandler.setDelegate(&errLogger{l: log.New(s.errCatcher, "", 0)}) -} - -func (s *HandlerTestSuite) TearDownSuite() { - globalErrorHandler.setDelegate(s.origHandler) -} - -func (s *HandlerTestSuite) SetupTest() { - s.errCatcher.Reset() -} - -func (s *HandlerTestSuite) TearDownTest() { - globalErrorHandler.setDelegate(&errLogger{l: log.New(s.errCatcher, "", 0)}) -} - -func (s *HandlerTestSuite) TestGlobalHandler() { - errs := []string{"one", "two"} - GetErrorHandler().Handle(errors.New(errs[0])) - Handle(errors.New(errs[1])) - s.Assert().Equal(errs, s.errCatcher.Got()) -} - -func (s *HandlerTestSuite) TestDelegatedHandler() { - eh := GetErrorHandler() - - newErrLogger := new(testErrCatcher) - SetErrorHandler(&errLogger{l: log.New(newErrLogger, "", 0)}) - - errs := []string{"TestDelegatedHandler"} - eh.Handle(errors.New(errs[0])) - s.Assert().Equal(errs, newErrLogger.Got()) -} - -func (s *HandlerTestSuite) TestNoDropsOnDelegate() { - causeErr("") - s.Require().Len(s.errCatcher.Got(), 1) - - // Change to another Handler. We are testing this is loss-less. - newErrLogger := new(testErrCatcher) - secondary := &errLogger{ - l: log.New(newErrLogger, "", 0), - } - SetErrorHandler(secondary) - - causeErr("") - s.Assert().Len(s.errCatcher.Got(), 1, "original Handler used after delegation") - s.Assert().Len(newErrLogger.Got(), 1, "new Handler not used after delegation") +type testErrHandler struct { + err error } -func (s *HandlerTestSuite) TestAllowMultipleSets() { - notUsed := new(testErrCatcher) - - secondary := &errLogger{l: log.New(notUsed, "", 0)} - SetErrorHandler(secondary) - s.Require().Same(GetErrorHandler(), globalErrorHandler, "set changed globalErrorHandler") - s.Require().Same(globalErrorHandler.getDelegate(), secondary, "new Handler not set") +var _ ErrorHandler = &testErrHandler{} - tertiary := &errLogger{l: log.New(notUsed, "", 0)} - SetErrorHandler(tertiary) - s.Require().Same(GetErrorHandler(), globalErrorHandler, "set changed globalErrorHandler") - s.Assert().Same(globalErrorHandler.getDelegate(), tertiary, "user Handler not overridden") -} +func (eh *testErrHandler) Handle(err error) { eh.err = err } -func TestHandlerTestSuite(t *testing.T) { - suite.Run(t, new(HandlerTestSuite)) -} - -func TestHandlerRace(t *testing.T) { - go SetErrorHandler(&errLogger{log.New(os.Stderr, "", 0)}) - go Handle(errors.New("error")) -} - -func BenchmarkErrorHandler(b *testing.B) { - primary := &errLogger{l: log.New(io.Discard, "", 0)} - secondary := &errLogger{l: log.New(io.Discard, "", 0)} - tertiary := &errLogger{l: log.New(io.Discard, "", 0)} - - globalErrorHandler.setDelegate(primary) - - err := errors.New("benchmark error handler") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - GetErrorHandler().Handle(err) - Handle(err) - - SetErrorHandler(secondary) - GetErrorHandler().Handle(err) - Handle(err) - - SetErrorHandler(tertiary) - GetErrorHandler().Handle(err) - Handle(err) - - globalErrorHandler.setDelegate(primary) - } - - reset() -} - -var eh ErrorHandler - -func BenchmarkGetDefaultErrorHandler(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - eh = GetErrorHandler() - } -} - -func BenchmarkGetDelegatedErrorHandler(b *testing.B) { - SetErrorHandler(&errLogger{l: log.New(io.Discard, "", 0)}) - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - eh = GetErrorHandler() - } - - reset() -} - -func BenchmarkDefaultErrorHandlerHandle(b *testing.B) { - globalErrorHandler.setDelegate( - &errLogger{l: log.New(io.Discard, "", 0)}, - ) - - eh := GetErrorHandler() - err := errors.New("benchmark default error handler handle") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - eh.Handle(err) - } - - reset() -} - -func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) { - eh := GetErrorHandler() - SetErrorHandler(&errLogger{l: log.New(io.Discard, "", 0)}) - err := errors.New("benchmark delegated error handler handle") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - eh.Handle(err) - } - - reset() -} - -func BenchmarkSetErrorHandlerDelegation(b *testing.B) { - alt := &errLogger{l: log.New(io.Discard, "", 0)} - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - SetErrorHandler(alt) - - reset() - } -} +func TestGlobalErrorHandler(t *testing.T) { + e1 := &testErrHandler{} + SetErrorHandler(e1) + Handle(assert.AnError) + assert.ErrorIs(t, e1.err, assert.AnError) + e1.err = nil -func reset() { - globalErrorHandler = defaultErrorHandler() + e2 := &testErrHandler{} + SetErrorHandler(e2) + GetErrorHandler().Handle(assert.AnError) + assert.ErrorIs(t, e2.err, assert.AnError) } diff --git a/internal/global/handler.go b/internal/global/handler.go new file mode 100644 index 00000000000..3dcd1caae69 --- /dev/null +++ b/internal/global/handler.go @@ -0,0 +1,103 @@ +// 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/global" + +import ( + "log" + "os" + "sync/atomic" + "unsafe" +) + +var ( + // GlobalErrorHandler provides an ErrorHandler that can be used + // throughout an OpenTelemetry instrumented project. When a user + // specified ErrorHandler is registered (`SetErrorHandler`) all calls to + // `Handle` and will be delegated to the registered ErrorHandler. + GlobalErrorHandler = defaultErrorHandler() + + // Compile-time check that delegator implements ErrorHandler. + _ ErrorHandler = (*ErrDelegator)(nil) + // Compile-time check that errLogger implements ErrorHandler. + _ ErrorHandler = (*ErrLogger)(nil) +) + +// ErrorHandler handles irremediable events. +type ErrorHandler interface { + // Handle handles any error deemed irremediable by an OpenTelemetry + // component. + Handle(error) +} + +type ErrDelegator struct { + delegate unsafe.Pointer +} + +func (d *ErrDelegator) Handle(err error) { + d.getDelegate().Handle(err) +} + +func (d *ErrDelegator) getDelegate() ErrorHandler { + return *(*ErrorHandler)(atomic.LoadPointer(&d.delegate)) +} + +// setDelegate sets the ErrorHandler delegate. +func (d *ErrDelegator) setDelegate(eh ErrorHandler) { + atomic.StorePointer(&d.delegate, unsafe.Pointer(&eh)) +} + +func defaultErrorHandler() *ErrDelegator { + d := &ErrDelegator{} + d.setDelegate(&ErrLogger{l: log.New(os.Stderr, "", log.LstdFlags)}) + return d +} + +// ErrLogger logs errors if no delegate is set, otherwise they are delegated. +type ErrLogger struct { + l *log.Logger +} + +// Handle logs err if no delegate is set, otherwise it is delegated. +func (h *ErrLogger) Handle(err error) { + h.l.Print(err) +} + +// GetErrorHandler returns the global ErrorHandler instance. +// +// The default ErrorHandler instance returned will log all errors to STDERR +// until an override ErrorHandler is set with SetErrorHandler. All +// ErrorHandler returned prior to this will automatically forward errors to +// the set instance instead of logging. +// +// Subsequent calls to SetErrorHandler after the first will not forward errors +// to the new ErrorHandler for prior returned instances. +func GetErrorHandler() ErrorHandler { + return GlobalErrorHandler +} + +// SetErrorHandler sets the global ErrorHandler to h. +// +// The first time this is called all ErrorHandler previously returned from +// GetErrorHandler will send errors to h instead of the default logging +// ErrorHandler. Subsequent calls will set the global ErrorHandler, but not +// delegate errors to h. +func SetErrorHandler(h ErrorHandler) { + GlobalErrorHandler.setDelegate(h) +} + +// Handle is a convenience function for ErrorHandler().Handle(err). +func Handle(err error) { + GetErrorHandler().Handle(err) +} diff --git a/internal/global/handler_test.go b/internal/global/handler_test.go new file mode 100644 index 00000000000..08074bcc0ff --- /dev/null +++ b/internal/global/handler_test.go @@ -0,0 +1,226 @@ +// 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 ( + "bytes" + "errors" + "io" + "log" + "os" + "testing" + + "github.com/stretchr/testify/suite" +) + +type testErrCatcher []string + +func (l *testErrCatcher) Write(p []byte) (int, error) { + msg := bytes.TrimRight(p, "\n") + (*l) = append(*l, string(msg)) + return len(msg), nil +} + +func (l *testErrCatcher) Reset() { + *l = testErrCatcher([]string{}) +} + +func (l *testErrCatcher) Got() []string { + return []string(*l) +} + +func causeErr(text string) { + Handle(errors.New(text)) +} + +type HandlerTestSuite struct { + suite.Suite + + origHandler ErrorHandler + errCatcher *testErrCatcher +} + +func (s *HandlerTestSuite) SetupSuite() { + s.errCatcher = new(testErrCatcher) + s.origHandler = GlobalErrorHandler.getDelegate() + + GlobalErrorHandler.setDelegate(&ErrLogger{l: log.New(s.errCatcher, "", 0)}) +} + +func (s *HandlerTestSuite) TearDownSuite() { + GlobalErrorHandler.setDelegate(s.origHandler) +} + +func (s *HandlerTestSuite) SetupTest() { + s.errCatcher.Reset() +} + +func (s *HandlerTestSuite) TearDownTest() { + GlobalErrorHandler.setDelegate(&ErrLogger{l: log.New(s.errCatcher, "", 0)}) +} + +func (s *HandlerTestSuite) TestGlobalHandler() { + errs := []string{"one", "two"} + GetErrorHandler().Handle(errors.New(errs[0])) + Handle(errors.New(errs[1])) + s.Assert().Equal(errs, s.errCatcher.Got()) +} + +func (s *HandlerTestSuite) TestDelegatedHandler() { + eh := GetErrorHandler() + + newErrLogger := new(testErrCatcher) + SetErrorHandler(&ErrLogger{l: log.New(newErrLogger, "", 0)}) + + errs := []string{"TestDelegatedHandler"} + eh.Handle(errors.New(errs[0])) + s.Assert().Equal(errs, newErrLogger.Got()) +} + +func (s *HandlerTestSuite) TestNoDropsOnDelegate() { + causeErr("") + s.Require().Len(s.errCatcher.Got(), 1) + + // Change to another Handler. We are testing this is loss-less. + newErrLogger := new(testErrCatcher) + secondary := &ErrLogger{ + l: log.New(newErrLogger, "", 0), + } + SetErrorHandler(secondary) + + causeErr("") + s.Assert().Len(s.errCatcher.Got(), 1, "original Handler used after delegation") + s.Assert().Len(newErrLogger.Got(), 1, "new Handler not used after delegation") +} + +func (s *HandlerTestSuite) TestAllowMultipleSets() { + notUsed := new(testErrCatcher) + + secondary := &ErrLogger{l: log.New(notUsed, "", 0)} + SetErrorHandler(secondary) + s.Require().Same(GetErrorHandler(), GlobalErrorHandler, "set changed globalErrorHandler") + s.Require().Same(GlobalErrorHandler.getDelegate(), secondary, "new Handler not set") + + tertiary := &ErrLogger{l: log.New(notUsed, "", 0)} + SetErrorHandler(tertiary) + s.Require().Same(GetErrorHandler(), GlobalErrorHandler, "set changed globalErrorHandler") + s.Assert().Same(GlobalErrorHandler.getDelegate(), tertiary, "user Handler not overridden") +} + +func TestHandlerTestSuite(t *testing.T) { + suite.Run(t, new(HandlerTestSuite)) +} + +func TestHandlerRace(t *testing.T) { + go SetErrorHandler(&ErrLogger{log.New(os.Stderr, "", 0)}) + go Handle(errors.New("error")) +} + +func BenchmarkErrorHandler(b *testing.B) { + primary := &ErrLogger{l: log.New(io.Discard, "", 0)} + secondary := &ErrLogger{l: log.New(io.Discard, "", 0)} + tertiary := &ErrLogger{l: log.New(io.Discard, "", 0)} + + GlobalErrorHandler.setDelegate(primary) + + err := errors.New("benchmark error handler") + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + GetErrorHandler().Handle(err) + Handle(err) + + SetErrorHandler(secondary) + GetErrorHandler().Handle(err) + Handle(err) + + SetErrorHandler(tertiary) + GetErrorHandler().Handle(err) + Handle(err) + + GlobalErrorHandler.setDelegate(primary) + } + + reset() +} + +var eh ErrorHandler + +func BenchmarkGetDefaultErrorHandler(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + eh = GetErrorHandler() + } +} + +func BenchmarkGetDelegatedErrorHandler(b *testing.B) { + SetErrorHandler(&ErrLogger{l: log.New(io.Discard, "", 0)}) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + eh = GetErrorHandler() + } + + reset() +} + +func BenchmarkDefaultErrorHandlerHandle(b *testing.B) { + GlobalErrorHandler.setDelegate( + &ErrLogger{l: log.New(io.Discard, "", 0)}, + ) + + eh := GetErrorHandler() + err := errors.New("benchmark default error handler handle") + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + eh.Handle(err) + } + + reset() +} + +func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) { + eh := GetErrorHandler() + SetErrorHandler(&ErrLogger{l: log.New(io.Discard, "", 0)}) + err := errors.New("benchmark delegated error handler handle") + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + eh.Handle(err) + } + + reset() +} + +func BenchmarkSetErrorHandlerDelegation(b *testing.B) { + alt := &ErrLogger{l: log.New(io.Discard, "", 0)} + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + SetErrorHandler(alt) + + reset() + } +} + +func reset() { + GlobalErrorHandler = defaultErrorHandler() +} diff --git a/metric.go b/metric.go new file mode 100644 index 00000000000..dcf7b7931fe --- /dev/null +++ b/metric.go @@ -0,0 +1,61 @@ +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "go.opentelemetry.io/otel/metric" + // TODO (#3819): Remove this disablement. + // nolint: staticcheck // Temporary, while metric/global is deprecated. + "go.opentelemetry.io/otel/metric/global" +) + +// Meter returns a Meter from the global MeterProvider. The name 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 name is empty, then a implementation defined default name will be +// used instead. +// +// If this is called before a global MeterProvider is registered the returned +// Meter will be a No-op implementation of a Meter. When a global MeterProvider +// is registered for the first time, the returned Meter, and all the +// instruments it has created or will create, are recreated automatically from +// the new MeterProvider. +// +// This is short for GetMeterProvider().Meter(name). +func Meter(name string, opts ...metric.MeterOption) metric.Meter { + // TODO (#3819): Remove this disablement. + // nolint: staticcheck // Temporary, while metric/global is deprecated. + return GetMeterProvider().Meter(name, opts...) +} + +// GetMeterProvider returns the registered global meter provider. +// +// If no global GetMeterProvider has been registered, a No-op GetMeterProvider +// implementation is returned. When a global GetMeterProvider is registered for +// the first time, the returned GetMeterProvider, and all the Meters it has +// created or will create, are recreated automatically from the new +// GetMeterProvider. +func GetMeterProvider() metric.MeterProvider { + // TODO (#3819): Remove this disablement. + // nolint: staticcheck // Temporary, while metric/global is deprecated. + return global.MeterProvider() +} + +// SetMeterProvider registers mp as the global MeterProvider. +func SetMeterProvider(mp metric.MeterProvider) { + // TODO (#3819): Remove this disablement. + // nolint: staticcheck // Temporary, while metric/global is deprecated. + global.SetMeterProvider(mp) +} diff --git a/metric/global/global.go b/metric/global/global.go index cb0896d38ac..09817388e20 100644 --- a/metric/global/global.go +++ b/metric/global/global.go @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package global provides a global MeterProvider for OpenTelemetry. +// +// Deprecated: Use go.opentelemetry.io/otel instead. package global // import "go.opentelemetry.io/otel/metric/global" import ( @@ -26,17 +29,23 @@ import ( // empty, then a implementation defined default name will be used instead. // // This is short for MeterProvider().Meter(name). +// +// Deprecated: Use Meter from go.opentelemetry.io/otel instead. func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { return MeterProvider().Meter(instrumentationName, opts...) } // MeterProvider returns the registered global meter provider. // If none is registered then a No-op MeterProvider is returned. +// +// Deprecated: Use MeterProvider from go.opentelemetry.io/otel instead. func MeterProvider() metric.MeterProvider { return global.MeterProvider() } // SetMeterProvider registers `mp` as the global meter provider. +// +// Deprecated: Use SetMeterProvider from go.opentelemetry.io/otel instead. func SetMeterProvider(mp metric.MeterProvider) { global.SetMeterProvider(mp) } diff --git a/metric/internal/global/instruments.go b/metric/internal/global/instruments.go index d1480fa5f3e..edc60033841 100644 --- a/metric/internal/global/instruments.go +++ b/metric/internal/global/instruments.go @@ -18,8 +18,8 @@ import ( "context" "sync/atomic" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + oGlob "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument" ) @@ -44,7 +44,7 @@ var _ instrument.Float64ObservableCounter = (*afCounter)(nil) func (i *afCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -72,7 +72,7 @@ var _ instrument.Float64ObservableUpDownCounter = (*afUpDownCounter)(nil) func (i *afUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableUpDownCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -100,7 +100,7 @@ var _ instrument.Float64ObservableGauge = (*afGauge)(nil) func (i *afGauge) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableGauge(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -128,7 +128,7 @@ var _ instrument.Int64ObservableCounter = (*aiCounter)(nil) func (i *aiCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -156,7 +156,7 @@ var _ instrument.Int64ObservableUpDownCounter = (*aiUpDownCounter)(nil) func (i *aiUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableUpDownCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -184,7 +184,7 @@ var _ instrument.Int64ObservableGauge = (*aiGauge)(nil) func (i *aiGauge) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableGauge(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -212,7 +212,7 @@ var _ instrument.Float64Counter = (*sfCounter)(nil) func (i *sfCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64Counter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -238,7 +238,7 @@ var _ instrument.Float64UpDownCounter = (*sfUpDownCounter)(nil) func (i *sfUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64UpDownCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -264,7 +264,7 @@ var _ instrument.Float64Histogram = (*sfHistogram)(nil) func (i *sfHistogram) setDelegate(m metric.Meter) { ctr, err := m.Float64Histogram(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -290,7 +290,7 @@ var _ instrument.Int64Counter = (*siCounter)(nil) func (i *siCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64Counter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -316,7 +316,7 @@ var _ instrument.Int64UpDownCounter = (*siUpDownCounter)(nil) func (i *siUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64UpDownCounter(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) @@ -342,7 +342,7 @@ var _ instrument.Int64Histogram = (*siHistogram)(nil) func (i *siHistogram) setDelegate(m metric.Meter) { ctr, err := m.Int64Histogram(i.name, i.opts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) diff --git a/metric/internal/global/meter.go b/metric/internal/global/meter.go index 8acf632863c..0064ed8fec7 100644 --- a/metric/internal/global/meter.go +++ b/metric/internal/global/meter.go @@ -19,7 +19,7 @@ import ( "sync" "sync/atomic" - "go.opentelemetry.io/otel" + oGlob "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/instrument" ) @@ -334,7 +334,7 @@ func (c *registration) setDelegate(m metric.Meter) { reg, err := m.RegisterCallback(c.function, insts...) if err != nil { - otel.Handle(err) + oGlob.GetErrorHandler().Handle(err) } c.unreg = reg.Unregister diff --git a/metric_test.go b/metric_test.go new file mode 100644 index 00000000000..646bb108368 --- /dev/null +++ b/metric_test.go @@ -0,0 +1,41 @@ +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/metric" +) + +type testMeterProvider struct{} + +var _ metric.MeterProvider = &testMeterProvider{} + +func (*testMeterProvider) Meter(_ string, _ ...metric.MeterOption) metric.Meter { + return metric.NewNoopMeterProvider().Meter("") +} + +func TestMultipleGlobalMeterProvider(t *testing.T) { + p1 := testMeterProvider{} + p2 := metric.NewNoopMeterProvider() + SetMeterProvider(&p1) + SetMeterProvider(p2) + + got := GetMeterProvider() + assert.Equal(t, p2, got) +} diff --git a/sdk/go.mod b/sdk/go.mod index 38a150da424..81022a6a137 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -17,7 +17,10 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) replace go.opentelemetry.io/otel/trace => ../trace + +replace go.opentelemetry.io/otel/metric => ../metric diff --git a/sdk/metric/example_test.go b/sdk/metric/example_test.go index c0704509a26..0086dba7687 100644 --- a/sdk/metric/example_test.go +++ b/sdk/metric/example_test.go @@ -18,7 +18,7 @@ import ( "context" "log" - "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" @@ -42,7 +42,7 @@ func Example() { metric.WithResource(res), metric.WithReader(reader), ) - global.SetMeterProvider(meterProvider) + otel.SetMeterProvider(meterProvider) defer func() { err := meterProvider.Shutdown(context.Background()) if err != nil { diff --git a/sdk/metric/meter_test.go b/sdk/metric/meter_test.go index d86be80c308..31f291c06e2 100644 --- a/sdk/metric/meter_test.go +++ b/sdk/metric/meter_test.go @@ -29,7 +29,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" "go.opentelemetry.io/otel/metric/instrument" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric/aggregation" @@ -631,7 +630,7 @@ func TestGlobalInstRegisterCallback(t *testing.T) { otel.SetLogger(logr.New(l)) const mtrName = "TestGlobalInstRegisterCallback" - preMtr := global.Meter(mtrName) + preMtr := otel.Meter(mtrName) preInt64Ctr, err := preMtr.Int64ObservableCounter("pre.int64.counter") require.NoError(t, err) preFloat64Ctr, err := preMtr.Float64ObservableCounter("pre.float64.counter") @@ -639,9 +638,9 @@ func TestGlobalInstRegisterCallback(t *testing.T) { rdr := NewManualReader() mp := NewMeterProvider(WithReader(rdr), WithResource(resource.Empty())) - global.SetMeterProvider(mp) + otel.SetMeterProvider(mp) - postMtr := global.Meter(mtrName) + postMtr := otel.Meter(mtrName) postInt64Ctr, err := postMtr.Int64ObservableCounter("post.int64.counter") require.NoError(t, err) postFloat64Ctr, err := postMtr.Float64ObservableCounter("post.float64.counter") diff --git a/trace/go.mod b/trace/go.mod index eaaf00035ae..05af8f6e729 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -15,3 +15,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace go.opentelemetry.io/otel/metric => ../metric