forked from open-telemetry/opentelemetry-go-contrib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #778 from MrAlias/oterror
Add oterror package and implementation in api/global
- Loading branch information
Showing
12 changed files
with
425 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// 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 ( | ||
"log" | ||
"os" | ||
"sync" | ||
"sync/atomic" | ||
|
||
"go.opentelemetry.io/otel/api/oterror" | ||
) | ||
|
||
var ( | ||
// globalHandler provides an oterror.Handler that can be used throughout | ||
// an OpenTelemetry instrumented project. When a user specified Handler | ||
// is registered (`SetHandler`) all calls to `Handle` will be delegated | ||
// to the registered Handler. | ||
globalHandler = &handler{ | ||
l: log.New(os.Stderr, "", log.LstdFlags), | ||
} | ||
|
||
// delegateHanderOnce ensures that a user provided Handler is only ever | ||
// registered once. | ||
delegateHanderOnce sync.Once | ||
|
||
// Ensure the handler implements oterror.Handle at build time. | ||
_ oterror.Handler = (*handler)(nil) | ||
) | ||
|
||
// handler logs all errors to STDERR. | ||
type handler struct { | ||
delegate atomic.Value | ||
|
||
l *log.Logger | ||
} | ||
|
||
// setDelegate sets the handler delegate if one is not already set. | ||
func (h *handler) setDelegate(d oterror.Handler) { | ||
if h.delegate.Load() != nil { | ||
// Delegate already registered | ||
return | ||
} | ||
h.delegate.Store(d) | ||
} | ||
|
||
// Handle implements oterror.Handler. | ||
func (h *handler) Handle(err error) { | ||
if d := h.delegate.Load(); d != nil { | ||
d.(oterror.Handler).Handle(err) | ||
return | ||
} | ||
h.l.Print(err) | ||
} | ||
|
||
// Handler returns the global Handler instance. If no Handler instance has | ||
// be explicitly set yet, a default Handler is returned that logs to STDERR | ||
// until an Handler is set (all functionality is delegated to the set | ||
// Handler once it is set). | ||
func Handler() oterror.Handler { | ||
return globalHandler | ||
} | ||
|
||
// SetHandler sets the global Handler to be h. | ||
func SetHandler(h oterror.Handler) { | ||
delegateHanderOnce.Do(func() { | ||
current := Handler() | ||
if current == h { | ||
return | ||
} | ||
if internalHandler, ok := current.(*handler); ok { | ||
internalHandler.setDelegate(h) | ||
} | ||
}) | ||
} | ||
|
||
// Handle is a convience function for Handler().Handle(err) | ||
func Handle(err error) { | ||
globalHandler.Handle(err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// 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" | ||
"log" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
type errLogger []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() { | ||
*l = errLogger([]string{}) | ||
} | ||
|
||
func (l *errLogger) Got() []string { | ||
return []string(*l) | ||
} | ||
|
||
type HandlerTestSuite struct { | ||
suite.Suite | ||
|
||
origHandler *handler | ||
errLogger *errLogger | ||
} | ||
|
||
func (s *HandlerTestSuite) SetupSuite() { | ||
s.errLogger = new(errLogger) | ||
s.origHandler = globalHandler | ||
globalHandler = &handler{ | ||
l: log.New(s.errLogger, "", 0), | ||
} | ||
} | ||
|
||
func (s *HandlerTestSuite) TearDownSuite() { | ||
globalHandler = s.origHandler | ||
} | ||
|
||
func (s *HandlerTestSuite) SetupTest() { | ||
s.errLogger.Reset() | ||
} | ||
|
||
func (s *HandlerTestSuite) TestGlobalHandler() { | ||
errs := []string{"one", "two"} | ||
Handler().Handle(errors.New(errs[0])) | ||
Handle(errors.New(errs[1])) | ||
s.Assert().Equal(errs, s.errLogger.Got()) | ||
} | ||
|
||
func (s *HandlerTestSuite) TestNoDropsOnDelegate() { | ||
var sent int | ||
err := errors.New("") | ||
stop := make(chan struct{}) | ||
beat := make(chan struct{}) | ||
done := make(chan struct{}) | ||
|
||
go func() { | ||
for { | ||
select { | ||
case <-stop: | ||
done <- struct{}{} | ||
return | ||
default: | ||
sent++ | ||
Handle(err) | ||
} | ||
|
||
select { | ||
case beat <- struct{}{}: | ||
default: | ||
} | ||
} | ||
}() | ||
|
||
// Wait for the spice to flow | ||
select { | ||
case <-time.Tick(2 * time.Millisecond): | ||
s.T().Fatal("no errors were sent in 2ms") | ||
case <-beat: | ||
} | ||
|
||
// Change to another Handler. We are testing this is loss-less. | ||
newErrLogger := new(errLogger) | ||
secondary := &handler{ | ||
l: log.New(newErrLogger, "", 0), | ||
} | ||
SetHandler(secondary) | ||
|
||
select { | ||
case <-time.Tick(2 * time.Millisecond): | ||
s.T().Fatal("no errors were sent within 2ms after SetHandler") | ||
case <-beat: | ||
} | ||
|
||
// Now beat is clear, wait for a fresh send. | ||
select { | ||
case <-time.Tick(2 * time.Millisecond): | ||
s.T().Fatal("no fresh errors were sent within 2ms after SetHandler") | ||
case <-beat: | ||
} | ||
|
||
// Stop sending errors. | ||
stop <- struct{}{} | ||
// Ensure we do not lose any straglers. | ||
<-done | ||
|
||
got := append(s.errLogger.Got(), newErrLogger.Got()...) | ||
s.Assert().Greater(len(got), 1, "at least 2 errors should have been sent") | ||
s.Assert().Len(got, sent) | ||
} | ||
|
||
func TestHandlerTestSuite(t *testing.T) { | ||
suite.Run(t, new(HandlerTestSuite)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// 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" | ||
|
||
"go.opentelemetry.io/otel/api/global" | ||
"go.opentelemetry.io/otel/api/metric" | ||
) | ||
|
||
type testMeterProvider struct{} | ||
|
||
var _ metric.Provider = &testMeterProvider{} | ||
|
||
func (*testMeterProvider) Meter(_ string) metric.Meter { | ||
return metric.Meter{} | ||
} | ||
|
||
func TestMultipleGlobalMeterProvider(t *testing.T) { | ||
p1 := testMeterProvider{} | ||
p2 := metric.NoopProvider{} | ||
global.SetMeterProvider(&p1) | ||
global.SetMeterProvider(&p2) | ||
|
||
got := global.MeterProvider() | ||
want := &p2 | ||
if got != want { | ||
t.Fatalf("Provider: got %p, want %p\n", got, want) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// 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/api/global/internal" | ||
"go.opentelemetry.io/otel/api/propagation" | ||
) | ||
|
||
// Propagators returns the registered global propagators instance. If | ||
// none is registered then an instance of propagators.NoopPropagators | ||
// is returned. | ||
func Propagators() propagation.Propagators { | ||
return internal.Propagators() | ||
} | ||
|
||
// SetPropagators registers `p` as the global propagators instance. | ||
func SetPropagators(p propagation.Propagators) { | ||
internal.SetPropagators(p) | ||
} |
Oops, something went wrong.