提交 a0c2769a 编写于 作者: G Gao Hongtao 提交者: wu-sheng

Adding Noop Span (#7)

* Creating noop span when tracer is not inited
 * If the current context contans noop span, other spans are all noop spans
上级 6d44c59d
// Copyright 2019 Tetrate Labs
//
// 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 go2sky
import (
"time"
"github.com/tetratelabs/go2sky/reporter/grpc/common"
)
type NoopSpan struct {
}
func (*NoopSpan) SetOperationName(string) {
}
func (*NoopSpan) SetPeer(string) {
}
func (*NoopSpan) SetSpanLayer(common.SpanLayer) {
}
func (*NoopSpan) Tag(string, string) {
}
func (*NoopSpan) Log(time.Time, ...string) {
}
func (*NoopSpan) Error(time.Time, ...string) {
}
func (*NoopSpan) End() {
}
// Copyright 2019 Tetrate Labs
//
// 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 go2sky
import (
"context"
"sync"
"testing"
)
type createFunc func() (Span, context.Context, error)
func TestCreateNoopSpan(t *testing.T) {
tracer, _ := NewTracer("")
tests := []struct {
name string
n createFunc
}{
{
"Entry",
func() (Span, context.Context, error) {
return tracer.CreateEntrySpan(context.Background(), "", nil)
},
},
{
"Exit",
func() (s Span, c context.Context, err error) {
s, err = tracer.CreateExitSpan(context.Background(), "", "", nil)
return
},
},
{
"Local",
func() (Span, context.Context, error) {
return tracer.CreateLocalSpan(context.Background())
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, _, _ := tt.n()
if _, ok := s.(*NoopSpan); !ok {
t.Error("Should create noop span")
}
})
}
}
func TestNoopSpanFromBegin(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
r := &registerReporter{
wg: wg,
}
tracer, _ := NewTracer("", WithReporter(r))
span, ctx, _ := tracer.CreateEntrySpan(context.Background(), "", nil)
if _, ok := span.(*NoopSpan); !ok {
t.Error("Should create noop span")
}
wg.Done()
tracer.WaitUntilRegister()
exitSpan, _ := tracer.CreateExitSpan(ctx, "", "", nil)
if _, ok := exitSpan.(*NoopSpan); !ok {
t.Error("Should create noop span")
}
exitSpan.End()
span.End()
}
type registerReporter struct {
wg *sync.WaitGroup
}
func (r *registerReporter) Send(spans []ReportedSpan) {
}
func (r *registerReporter) Close() {
}
func (r *registerReporter) Register(service string, instance string) (int32, int32, error) {
r.wg.Wait()
return 0, 0, nil
}
......@@ -28,6 +28,7 @@ func TestSyncSegment(t *testing.T) {
WaitGroup: wg,
}
tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
eSpan, _ := tracer.CreateExitSpan(ctx, "", "", MockInjector)
......@@ -48,6 +49,7 @@ func TestAsyncSingleSegment(t *testing.T) {
WaitGroup: reportWg,
}
tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
go func() {
......@@ -75,6 +77,7 @@ func TestAsyncMultipleSegments(t *testing.T) {
WaitGroup: reportWg,
}
tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
span.End()
......
......@@ -16,6 +16,9 @@ package go2sky
import (
"context"
"sync"
"sync/atomic"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
......@@ -31,6 +34,7 @@ type Tracer struct {
initFlag int32
serviceID int32
instanceID int32
wg *sync.WaitGroup
}
// TracerOption allows for functional options to adjust behaviour
......@@ -40,8 +44,10 @@ type TracerOption func(t *Tracer)
// NewTracer return a new go2sky Tracer
func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) {
t := &Tracer{
service: service,
initFlag: 0,
service: service,
initFlag: 0,
serviceID: 0,
instanceID: 0,
}
for _, opt := range opts {
opt(t)
......@@ -54,19 +60,38 @@ func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error)
t.instance = id.String()
}
if t.reporter != nil {
serviceID, instanceID, err := t.reporter.Register(t.service, t.instance)
if err != nil {
return nil, err
}
t.initFlag = 1
t.serviceID = serviceID
t.instanceID = instanceID
t.wg = &sync.WaitGroup{}
t.wg.Add(1)
go func() {
defer t.wg.Done()
for {
serviceID, instanceID, err := t.reporter.Register(t.service, t.instance)
if err != nil {
time.Sleep(5 * time.Second)
continue
}
if atomic.SwapInt32(&t.serviceID, serviceID) == 0 && atomic.SwapInt32(&t.instanceID, instanceID) == 0 {
atomic.SwapInt32(&t.initFlag, 1)
break
}
}
}()
}
return t, nil
}
//WaitUntilRegister is a tool helps user to wait until register process has finished
func (t *Tracer) WaitUntilRegister() {
if t.wg != nil {
t.wg.Wait()
}
}
// CreateEntrySpan creates and starts an entry span for incoming request
func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extractor propagation.Extractor) (s Span, nCtx context.Context, err error) {
if s, nCtx = t.createNoop(ctx); s != nil {
return
}
header, err := extractor()
if err != nil {
return
......@@ -108,6 +133,9 @@ func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extr
// CreateLocalSpan creates and starts a span for local usage
func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Span, c context.Context, err error) {
if s, _ = t.createNoop(ctx); s != nil {
return
}
ds := newLocalSpan(t)
for _, opt := range opts {
opt(ds)
......@@ -122,6 +150,9 @@ func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Spa
// CreateExitSpan creates and starts an exit span for client
func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer string, injector propagation.Injector) (Span, error) {
if s, _ := t.createNoop(ctx); s != nil {
return s, nil
}
s, _, err := t.CreateLocalSpan(ctx, WithSpanType(SpanTypeExit))
if err != nil {
return nil, err
......@@ -156,6 +187,20 @@ func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer
return s, nil
}
func (t *Tracer) createNoop(ctx context.Context) (s Span, nCtx context.Context) {
if ns, ok := ctx.Value(ctxKeyInstance).(*NoopSpan); ok {
nCtx = ctx
s = ns
return
}
if t.initFlag == 0 {
s = &NoopSpan{}
nCtx = context.WithValue(ctx, ctxKeyInstance, s)
return
}
return
}
type ctxKey struct{}
type refKey struct{}
......
......@@ -32,6 +32,7 @@ func TestTracer_EntryAndExit(t *testing.T) {
if err != nil {
t.Error(err)
}
tracer.WaitUntilRegister()
entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return "", nil
})
......@@ -63,6 +64,7 @@ func TestTracer_Entry(t *testing.T) {
if err != nil {
t.Error(err)
}
tracer.WaitUntilRegister()
entrySpan, _, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return header, nil
})
......@@ -88,6 +90,7 @@ func TestTracer_EntryAndExitInTrace(t *testing.T) {
if err != nil {
t.Error(err)
}
tracer.WaitUntilRegister()
entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return header, nil
})
......
......@@ -33,12 +33,6 @@ func TestTracerInit(t *testing.T) {
if err != nil {
t.Fail()
}
_, err = NewTracer("", WithReporter(&mockRegisterReporter{
success: false,
}))
if err != errRegister {
t.Fail()
}
}
func TestTracer_CreateLocalSpan(t *testing.T) {
......@@ -46,6 +40,7 @@ func TestTracer_CreateLocalSpan(t *testing.T) {
success: true,
}
tracer, _ := NewTracer("", WithReporter(reporter))
tracer.WaitUntilRegister()
span, ctx, err := tracer.CreateLocalSpan(context.Background())
if err != nil {
t.Error(err)
......@@ -65,6 +60,7 @@ func TestTracer_CreateLocalSpanAsync(t *testing.T) {
success: true,
}
tracer, _ := NewTracer("", WithReporter(reporter))
tracer.WaitUntilRegister()
span, ctx, err := tracer.CreateLocalSpan(context.Background())
if err != nil {
t.Error(err)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册