Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add oterror package and implementation in api/global #778

Merged
merged 13 commits into from
Jun 2, 2020
78 changes: 78 additions & 0 deletions api/global/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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"

"go.opentelemetry.io/otel/api/oterror"
)

var defaultHandler = &handler{
l: log.New(os.Stderr, "", log.LstdFlags),
}

// Global Handler (used for testing).
var globalHandler oterror.Handler = defaultHandler

// handler logs all errors to STDERR.
type handler struct {
sync.Mutex
delegate oterror.Handler

l *log.Logger
}

func (h *handler) setDelegate(d oterror.Handler) {
h.Lock()
defer h.Unlock()
if h.delegate != nil {
// delegate already registered
return
}

h.delegate = d
}

// Handle implements oterror.Handler.
func (h *handler) Handle(err error) {
h.Lock()
defer h.Unlock()
if h.delegate != nil {
MrAlias marked this conversation as resolved.
Show resolved Hide resolved
jmacd marked this conversation as resolved.
Show resolved Hide resolved
h.delegate.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) {
defaultHandler.setDelegate(h)
}

// Handle is a convience function for Handler().Handle(err)
func Handle(err error) {
globalHandler.Handle(err)
}
126 changes: 126 additions & 0 deletions api/global/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// 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 (
"errors"
"testing"
"time"

"github.com/stretchr/testify/suite"

"go.opentelemetry.io/otel/api/oterror"
)

type mock []error

func (m *mock) Handle(err error) {
(*m) = append(*m, err)
}

type HandlerTestSuite struct {
suite.Suite

origHandler oterror.Handler

errs []error
}

func (s *HandlerTestSuite) Handle(err error) {
s.errs = append(s.errs, err)
}

func (s *HandlerTestSuite) SetupSuite() {
s.origHandler = globalHandler
globalHandler = s
}

func (s *HandlerTestSuite) TearDownSuite() {
globalHandler = s.origHandler
}

func (s *HandlerTestSuite) SetupTest() {
s.errs = []error{}
}

func (s *HandlerTestSuite) TestGlocalHandler() {
err1 := errors.New("one")
err2 := errors.New("two")
Handler().Handle(err1)
Handle(err2)
s.Assert().Equal([]error{err1, err2}, s.errs)
}

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.
secondary := new(mock)
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

s.Assert().Greater(len(s.errs), 1, "at least 2 errors should have been sent")
s.Assert().Len(s.errs, sent)
}

func TestHandlerTestSuite(t *testing.T) {
suite.Run(t, new(HandlerTestSuite))
}
38 changes: 0 additions & 38 deletions api/global/global.go → api/global/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,8 @@ package global
import (
"go.opentelemetry.io/otel/api/global/internal"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/propagation"
"go.opentelemetry.io/otel/api/trace"
)

// Tracer creates a named tracer that implements Tracer interface.
// If the name is an empty string then provider uses default name.
//
// This is short for TraceProvider().Tracer(name)
func Tracer(name string) trace.Tracer {
return TraceProvider().Tracer(name)
}

// TraceProvider returns the registered global trace provider.
// If none is registered then an instance of trace.NoopProvider is returned.
//
// Use the trace provider to create a named tracer. E.g.
// tracer := global.TraceProvider().Tracer("example.com/foo")
// or
// tracer := global.Tracer("example.com/foo")
func TraceProvider() trace.Provider {
return internal.TraceProvider()
}

// SetTraceProvider registers `tp` as the global trace provider.
func SetTraceProvider(tp trace.Provider) {
internal.SetTraceProvider(tp)
}

// Meter gets a named Meter interface. If the name is an
// empty string, the provider uses a default name.
//
Expand All @@ -69,15 +43,3 @@ func MeterProvider() metric.Provider {
func SetMeterProvider(mp metric.Provider) {
internal.SetMeterProvider(mp)
}

// 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)
}
43 changes: 43 additions & 0 deletions api/global/metric_test.go
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)
}
}
32 changes: 32 additions & 0 deletions api/global/propagation.go
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)
}
44 changes: 44 additions & 0 deletions api/global/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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/trace"
)

// Tracer creates a named tracer that implements Tracer interface.
// If the name is an empty string then provider uses default name.
//
// This is short for TraceProvider().Tracer(name)
func Tracer(name string) trace.Tracer {
return TraceProvider().Tracer(name)
}

// TraceProvider returns the registered global trace provider.
// If none is registered then an instance of trace.NoopProvider is returned.
//
// Use the trace provider to create a named tracer. E.g.
// tracer := global.TraceProvider().Tracer("example.com/foo")
// or
// tracer := global.Tracer("example.com/foo")
func TraceProvider() trace.Provider {
return internal.TraceProvider()
}

// SetTraceProvider registers `tp` as the global trace provider.
func SetTraceProvider(tp trace.Provider) {
internal.SetTraceProvider(tp)
}
Loading