1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-11-27 22:49:15 +02:00

simplifies error handling, uses internal logger by default

This commit is contained in:
Aaron Clawson
2021-11-17 16:17:04 +00:00
committed by GitHub
parent 8d0b098602
commit 8b210d20d4
5 changed files with 108 additions and 130 deletions

View File

@@ -17,12 +17,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- The following struct types moved and are replaced with type aliases, since they are exposed to the user: `Observation`, `Measurement`. - The following struct types moved and are replaced with type aliases, since they are exposed to the user: `Observation`, `Measurement`.
- The No-op implementations of sync and async instruments are no longer exported, new functions `sdkapi.NewNoopAsyncInstrument()` and `sdkapi.NewNoopSyncInstrument()` are provided instead. (#2271) - The No-op implementations of sync and async instruments are no longer exported, new functions `sdkapi.NewNoopAsyncInstrument()` and `sdkapi.NewNoopSyncInstrument()` are provided instead. (#2271)
- Update the SDK `BatchSpanProcessor` to export all queued spans when `ForceFlush` is called. (#2080, #2335) - Update the SDK `BatchSpanProcessor` to export all queued spans when `ForceFlush` is called. (#2080, #2335)
- The default ErrorHandler now uses the internal logger. The default of logging to stderr is unchanged.
### Added ### Added
- Add the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) - Add the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002)
- Added a new `schema` module to help parse Schema Files in OTEP 0152 format. (#2267) - Added a new `schema` module to help parse Schema Files in OTEP 0152 format. (#2267)
- Added a new `MapCarrier` to the `go.opentelemetry.io/otel/propagation` package to hold propagated coss-cutting concerns as a `map[string]string` held in memory. (#2334) - Added a new `MapCarrier` to the `go.opentelemetry.io/otel/propagation` package to hold propagated coss-cutting concerns as a `map[string]string` held in memory. (#2334)
- Added an internal Logger. This can be used by the SDK and API to provide users with feedback of the internal state. To enable verbose logs configure the logger which will print V(1) logs. For debugging information confgure to print V(5) logs.
## [1.1.0] - 2021-10-27 ## [1.1.0] - 2021-10-27

View File

@@ -15,10 +15,9 @@
package otel // import "go.opentelemetry.io/otel" package otel // import "go.opentelemetry.io/otel"
import ( import (
"log"
"os"
"sync" "sync"
"sync/atomic"
"go.opentelemetry.io/otel/internal/debug"
) )
var ( var (
@@ -26,79 +25,61 @@ var (
// throughout an OpenTelemetry instrumented project. When a user // throughout an OpenTelemetry instrumented project. When a user
// specified ErrorHandler is registered (`SetErrorHandler`) all calls to // specified ErrorHandler is registered (`SetErrorHandler`) all calls to
// `Handle` and will be delegated to the registered ErrorHandler. // `Handle` and will be delegated to the registered ErrorHandler.
globalErrorHandler = defaultErrorHandler() globalErrorHandler = &errorHandlerDelegate{
delegate: &defaultErrorHandler{},
}
// delegateErrorHandlerOnce ensures that a user provided ErrorHandler is // delegateErrorHandlerOnce ensures that a user provided ErrorHandler is
// only ever registered once. // only ever registered once.
delegateErrorHandlerOnce sync.Once delegateErrorHandlerOnce sync.Once
// Compile-time check that delegator implements ErrorHandler. // Compile-time check that delegator implements ErrorHandler.
_ ErrorHandler = (*delegator)(nil) _ ErrorHandler = (*errorHandlerDelegate)(nil)
_ ErrorHandler = (*defaultErrorHandler)(nil)
) )
type holder struct { // errorHandlerDelegate is a box type to enable updating of all handlers returned by GetErrorHandler()
eh ErrorHandler type errorHandlerDelegate struct {
delegate ErrorHandler
} }
func defaultErrorHandler() *atomic.Value { func (h *errorHandlerDelegate) setDelegate(d ErrorHandler) {
v := &atomic.Value{} h.delegate = d
v.Store(holder{eh: &delegator{l: log.New(os.Stderr, "", log.LstdFlags)}})
return v
} }
// delegator logs errors if no delegate is set, otherwise they are delegated. // Handle handles any error deemed irremediable by an OpenTelemetry component.
type delegator struct { func (h *errorHandlerDelegate) Handle(err error) {
delegate atomic.Value if h == nil {
l *log.Logger
}
// setDelegate sets the ErrorHandler delegate.
func (h *delegator) setDelegate(d ErrorHandler) {
// It is critical this is guarded with delegateErrorHandlerOnce, if it is
// called again with a different concrete type it will panic.
h.delegate.Store(d)
}
// Handle logs err if no delegate is set, otherwise it is delegated.
func (h *delegator) Handle(err error) {
if d := h.delegate.Load(); d != nil {
d.(ErrorHandler).Handle(err)
return return
} }
h.l.Print(err) h.delegate.Handle(err)
} }
// defaultErrorHandler utilizes the internal logger to manage the messages.
type defaultErrorHandler struct{}
func (h *defaultErrorHandler) Handle(err error) {
debug.Error(err, "")
}
// DiscardErrorHandler drops all errors.
// Use `SetErrorHandler(DiscardErrorHandler{})` to disable error handling
type DiscardErrorHandler struct{}
func (DiscardErrorHandler) Handle(error) {}
// GetErrorHandler returns the global ErrorHandler instance. // GetErrorHandler returns the global ErrorHandler instance.
// //
// The default ErrorHandler instance returned will log all errors to STDERR // The default ErrorHandler instance returned will log all errors to STDERR
// until an override ErrorHandler is set with SetErrorHandler. All // until an override ErrorHandler is set with SetErrorHandler. All
// ErrorHandler returned prior to this will automatically forward errors to // ErrorHandler returned prior to this will automatically forward errors to
// the set instance instead of logging. // 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 { func GetErrorHandler() ErrorHandler {
return globalErrorHandler.Load().(holder).eh return globalErrorHandler
} }
// SetErrorHandler sets the global ErrorHandler to h. // 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) { func SetErrorHandler(h ErrorHandler) {
delegateErrorHandlerOnce.Do(func() { globalErrorHandler.setDelegate(h)
current := GetErrorHandler()
if current == h {
return
}
if internalHandler, ok := current.(*delegator); ok {
internalHandler.setDelegate(h)
}
})
globalErrorHandler.Store(holder{eh: h})
} }
// Handle is a convenience function for ErrorHandler().Handle(err) // Handle is a convenience function for ErrorHandler().Handle(err)

View File

@@ -15,39 +15,20 @@
package otel package otel
import ( import (
"bytes"
"errors" "errors"
"io/ioutil" "strings"
"log"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
type errLogger []string type errLogger struct {
errs []string
func (l *errLogger) Write(p []byte) (int, error) {
msg := bytes.TrimRight(p, "\n")
(*l) = append(*l, string(msg))
return len(msg), nil
} }
func (l *errLogger) Reset() { func (l *errLogger) Handle(err error) {
*l = errLogger([]string{}) l.errs = append(l.errs, err.Error())
}
func (l *errLogger) Got() []string {
return []string(*l)
}
type logger struct {
l *log.Logger
}
func (l *logger) Handle(err error) {
l.l.Print(err)
} }
func causeErr(text string) { func causeErr(text string) {
@@ -62,76 +43,85 @@ type HandlerTestSuite struct {
} }
func (s *HandlerTestSuite) SetupSuite() { func (s *HandlerTestSuite) SetupSuite() {
s.errLogger = new(errLogger) s.errLogger = &errLogger{errs: []string{}}
s.origHandler = globalErrorHandler.Load().(holder).eh s.origHandler = globalErrorHandler.delegate
globalErrorHandler.Store(holder{eh: &delegator{l: log.New(s.errLogger, "", 0)}}) globalErrorHandler.delegate = s.errLogger
} }
func (s *HandlerTestSuite) TearDownSuite() { func (s *HandlerTestSuite) TearDownSuite() {
globalErrorHandler.Store(holder{eh: s.origHandler}) globalErrorHandler.delegate = s.origHandler
delegateErrorHandlerOnce = sync.Once{} delegateErrorHandlerOnce = sync.Once{}
} }
func (s *HandlerTestSuite) SetupTest() { func (s *HandlerTestSuite) SetupTest() {
s.errLogger.Reset() s.errLogger.errs = []string{}
SetErrorHandler(s.errLogger)
} }
func (s *HandlerTestSuite) TearDownTest() { func (s *HandlerTestSuite) TearDownTest() {}
globalErrorHandler.Store(holder{eh: &delegator{l: log.New(s.errLogger, "", 0)}})
delegateErrorHandlerOnce = sync.Once{} type bufferedErrorHandler struct {
buf *strings.Builder
}
func (h *bufferedErrorHandler) Handle(err error) {
if h.buf != nil {
h.buf = &strings.Builder{}
}
h.buf.WriteString(err.Error())
} }
func (s *HandlerTestSuite) TestGlobalHandler() { func (s *HandlerTestSuite) TestGlobalHandler() {
errs := []string{"one", "two"} errs := []string{"one", "two"}
GetErrorHandler().Handle(errors.New(errs[0])) GetErrorHandler().Handle(errors.New(errs[0]))
Handle(errors.New(errs[1])) Handle(errors.New(errs[1]))
s.Assert().Equal(errs, s.errLogger.Got()) s.Assert().Equal(errs, s.errLogger.errs)
} }
func (s *HandlerTestSuite) TestDelegatedHandler() { func (s *HandlerTestSuite) TestDelegatedHandler() {
eh := GetErrorHandler() eh := GetErrorHandler()
newErrLogger := new(errLogger) newErrLogger := &errLogger{errs: []string{}}
SetErrorHandler(&logger{l: log.New(newErrLogger, "", 0)}) SetErrorHandler(newErrLogger)
errs := []string{"TestDelegatedHandler"} errs := []string{"TestDelegatedHandler"}
eh.Handle(errors.New(errs[0])) eh.Handle(errors.New(errs[0]))
s.Assert().Equal(errs, newErrLogger.Got()) s.Assert().Equal(errs, newErrLogger.errs)
}
func (s *HandlerTestSuite) TestSettingDefaultIsANoOp() {
SetErrorHandler(GetErrorHandler())
d := globalErrorHandler.Load().(holder).eh.(*delegator)
s.Assert().Nil(d.delegate.Load())
} }
func (s *HandlerTestSuite) TestNoDropsOnDelegate() { func (s *HandlerTestSuite) TestNoDropsOnDelegate() {
causeErr("") causeErr("")
s.Require().Len(s.errLogger.Got(), 1) s.Require().Len(s.errLogger.errs, 1)
// Change to another Handler. We are testing this is loss-less. // Change to another Handler. We are testing this is loss-less.
newErrLogger := new(errLogger) newErrLogger := &errLogger{errs: []string{}}
secondary := &logger{ SetErrorHandler(newErrLogger)
l: log.New(newErrLogger, "", 0),
}
SetErrorHandler(secondary)
causeErr("") causeErr("")
s.Assert().Len(s.errLogger.Got(), 1, "original Handler used after delegation") s.Assert().Len(s.errLogger.errs, 1, "original Handler used after delegation")
s.Assert().Len(newErrLogger.Got(), 1, "new Handler not used after delegation") s.Assert().Len(newErrLogger.errs, 1, "new Handler not used after delegation")
} }
func (s *HandlerTestSuite) TestAllowMultipleSets() { func (s *HandlerTestSuite) TestAllowMultipleSets() {
notUsed := new(errLogger) secondary := &errLogger{errs: []string{}}
secondary := &logger{l: log.New(notUsed, "", 0)}
SetErrorHandler(secondary) SetErrorHandler(secondary)
s.Require().Same(GetErrorHandler(), secondary, "new Handler not set") s.Require().Same(GetErrorHandler().(*errorHandlerDelegate).delegate, secondary, "new Handler not set")
tertiary := &logger{l: log.New(notUsed, "", 0)} tertiary := &errLogger{errs: []string{}}
SetErrorHandler(tertiary) SetErrorHandler(tertiary)
s.Assert().Same(GetErrorHandler(), tertiary, "user Handler not overridden") s.Assert().Same(GetErrorHandler().(*errorHandlerDelegate).delegate, tertiary, "user Handler not overridden")
}
func (s *HandlerTestSuite) TestGetErrorHandlerAlwaysIsCurrent() {
orig := GetErrorHandler()
newErrLogger := &errLogger{errs: []string{}}
SetErrorHandler(newErrLogger)
orig.Handle(errors.New("error"))
s.Assert().Len(newErrLogger.errs, 1, "original Handler did not update")
} }
func TestHandlerTestSuite(t *testing.T) { func TestHandlerTestSuite(t *testing.T) {
@@ -139,17 +129,16 @@ func TestHandlerTestSuite(t *testing.T) {
} }
func BenchmarkErrorHandler(b *testing.B) { func BenchmarkErrorHandler(b *testing.B) {
primary := &delegator{l: log.New(ioutil.Discard, "", 0)} primary := DiscardErrorHandler{}
secondary := &logger{l: log.New(ioutil.Discard, "", 0)} secondary := DiscardErrorHandler{}
tertiary := &logger{l: log.New(ioutil.Discard, "", 0)} tertiary := DiscardErrorHandler{}
globalErrorHandler.Store(holder{eh: primary})
err := errors.New("BenchmarkErrorHandler") err := errors.New("BenchmarkErrorHandler")
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
SetErrorHandler(primary)
GetErrorHandler().Handle(err) GetErrorHandler().Handle(err)
Handle(err) Handle(err)
@@ -161,11 +150,6 @@ func BenchmarkErrorHandler(b *testing.B) {
GetErrorHandler().Handle(err) GetErrorHandler().Handle(err)
Handle(err) Handle(err)
b.StopTimer()
primary.delegate = atomic.Value{}
globalErrorHandler.Store(holder{eh: primary})
delegateErrorHandlerOnce = sync.Once{}
b.StartTimer()
} }
b.StopTimer() b.StopTimer()
@@ -182,7 +166,7 @@ func BenchmarkGetDefaultErrorHandler(b *testing.B) {
} }
func BenchmarkGetDelegatedErrorHandler(b *testing.B) { func BenchmarkGetDelegatedErrorHandler(b *testing.B) {
SetErrorHandler(&logger{l: log.New(ioutil.Discard, "", 0)}) SetErrorHandler(DiscardErrorHandler{})
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@@ -195,9 +179,7 @@ func BenchmarkGetDelegatedErrorHandler(b *testing.B) {
} }
func BenchmarkDefaultErrorHandlerHandle(b *testing.B) { func BenchmarkDefaultErrorHandlerHandle(b *testing.B) {
globalErrorHandler.Store(holder{ SetErrorHandler(DiscardErrorHandler{})
eh: &delegator{l: log.New(ioutil.Discard, "", 0)},
})
eh := GetErrorHandler() eh := GetErrorHandler()
err := errors.New("BenchmarkDefaultErrorHandlerHandle") err := errors.New("BenchmarkDefaultErrorHandlerHandle")
@@ -214,7 +196,7 @@ func BenchmarkDefaultErrorHandlerHandle(b *testing.B) {
func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) { func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) {
eh := GetErrorHandler() eh := GetErrorHandler()
SetErrorHandler(&logger{l: log.New(ioutil.Discard, "", 0)}) SetErrorHandler(DiscardErrorHandler{})
err := errors.New("BenchmarkDelegatedErrorHandlerHandle") err := errors.New("BenchmarkDelegatedErrorHandlerHandle")
b.ReportAllocs() b.ReportAllocs()
@@ -228,7 +210,7 @@ func BenchmarkDelegatedErrorHandlerHandle(b *testing.B) {
} }
func BenchmarkSetErrorHandlerDelegation(b *testing.B) { func BenchmarkSetErrorHandlerDelegation(b *testing.B) {
alt := &logger{l: log.New(ioutil.Discard, "", 0)} alt := DiscardErrorHandler{}
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@@ -243,8 +225,8 @@ func BenchmarkSetErrorHandlerDelegation(b *testing.B) {
func BenchmarkSetErrorHandlerNoDelegation(b *testing.B) { func BenchmarkSetErrorHandlerNoDelegation(b *testing.B) {
eh := []ErrorHandler{ eh := []ErrorHandler{
&logger{l: log.New(ioutil.Discard, "", 0)}, DiscardErrorHandler{},
&logger{l: log.New(ioutil.Discard, "", 0)}, DiscardErrorHandler{},
} }
mod := len(eh) mod := len(eh)
// Do not measure delegation. // Do not measure delegation.
@@ -261,6 +243,7 @@ func BenchmarkSetErrorHandlerNoDelegation(b *testing.B) {
} }
func reset() { func reset() {
globalErrorHandler = defaultErrorHandler() globalErrorHandler = &errorHandlerDelegate{
delegateErrorHandlerOnce = sync.Once{} delegate: &defaultErrorHandler{},
}
} }

View File

@@ -15,15 +15,27 @@
package debug // import "go.opentelemetry.io/otel/internal/debug" package debug // import "go.opentelemetry.io/otel/internal/debug"
import ( import (
"log"
"os"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/go-logr/stdr"
) )
var Log logr.Logger = logr.Discard() var globalLoggger logr.Logger = stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))
func SetLogger(l logr.Logger) {
globalLoggger = l
}
func Info(msg string, keysAndValues ...interface{}) { func Info(msg string, keysAndValues ...interface{}) {
Log.Info(msg, keysAndValues...) globalLoggger.V(1).Info(msg, keysAndValues...)
} }
func Error(err error, msg string, keysAndValues ...interface{}) { func Error(err error, msg string, keysAndValues ...interface{}) {
Log.Error(err, msg, keysAndValues...) globalLoggger.Error(err, msg, keysAndValues...)
}
func Debug(msg string, keysAndValues ...interface{}) {
globalLoggger.V(5).Info(msg, keysAndValues...)
} }

View File

@@ -238,7 +238,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
} }
if l := len(bsp.batch); l > 0 { if l := len(bsp.batch); l > 0 {
debug.Log.V(5).Info("exporting spans", "count", len(bsp.batch)) debug.Info("exporting spans", "count", len(bsp.batch))
err := bsp.e.ExportSpans(ctx, bsp.batch) err := bsp.e.ExportSpans(ctx, bsp.batch)
// A new batch is always created after exporting, even if the batch failed to be exported. // A new batch is always created after exporting, even if the batch failed to be exported.