提交 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) { ...@@ -28,6 +28,7 @@ func TestSyncSegment(t *testing.T) {
WaitGroup: wg, WaitGroup: wg,
} }
tracer, _ := NewTracer("segmentTest", WithReporter(&mr)) tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background() ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor) span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
eSpan, _ := tracer.CreateExitSpan(ctx, "", "", MockInjector) eSpan, _ := tracer.CreateExitSpan(ctx, "", "", MockInjector)
...@@ -48,6 +49,7 @@ func TestAsyncSingleSegment(t *testing.T) { ...@@ -48,6 +49,7 @@ func TestAsyncSingleSegment(t *testing.T) {
WaitGroup: reportWg, WaitGroup: reportWg,
} }
tracer, _ := NewTracer("segmentTest", WithReporter(&mr)) tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background() ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor) span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
go func() { go func() {
...@@ -75,6 +77,7 @@ func TestAsyncMultipleSegments(t *testing.T) { ...@@ -75,6 +77,7 @@ func TestAsyncMultipleSegments(t *testing.T) {
WaitGroup: reportWg, WaitGroup: reportWg,
} }
tracer, _ := NewTracer("segmentTest", WithReporter(&mr)) tracer, _ := NewTracer("segmentTest", WithReporter(&mr))
tracer.WaitUntilRegister()
ctx := context.Background() ctx := context.Background()
span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor) span, ctx, _ := tracer.CreateEntrySpan(ctx, "", MockExtractor)
span.End() span.End()
......
...@@ -16,6 +16,9 @@ package go2sky ...@@ -16,6 +16,9 @@ package go2sky
import ( import (
"context" "context"
"sync"
"sync/atomic"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -31,6 +34,7 @@ type Tracer struct { ...@@ -31,6 +34,7 @@ type Tracer struct {
initFlag int32 initFlag int32
serviceID int32 serviceID int32
instanceID int32 instanceID int32
wg *sync.WaitGroup
} }
// TracerOption allows for functional options to adjust behaviour // TracerOption allows for functional options to adjust behaviour
...@@ -40,8 +44,10 @@ type TracerOption func(t *Tracer) ...@@ -40,8 +44,10 @@ type TracerOption func(t *Tracer)
// NewTracer return a new go2sky Tracer // NewTracer return a new go2sky Tracer
func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) { func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) {
t := &Tracer{ t := &Tracer{
service: service, service: service,
initFlag: 0, initFlag: 0,
serviceID: 0,
instanceID: 0,
} }
for _, opt := range opts { for _, opt := range opts {
opt(t) opt(t)
...@@ -54,19 +60,38 @@ func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) ...@@ -54,19 +60,38 @@ func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error)
t.instance = id.String() t.instance = id.String()
} }
if t.reporter != nil { if t.reporter != nil {
serviceID, instanceID, err := t.reporter.Register(t.service, t.instance) t.wg = &sync.WaitGroup{}
if err != nil { t.wg.Add(1)
return nil, err go func() {
} defer t.wg.Done()
t.initFlag = 1 for {
t.serviceID = serviceID serviceID, instanceID, err := t.reporter.Register(t.service, t.instance)
t.instanceID = instanceID 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 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 // 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) { 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() header, err := extractor()
if err != nil { if err != nil {
return return
...@@ -108,6 +133,9 @@ func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extr ...@@ -108,6 +133,9 @@ func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extr
// CreateLocalSpan creates and starts a span for local usage // 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) { 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) ds := newLocalSpan(t)
for _, opt := range opts { for _, opt := range opts {
opt(ds) opt(ds)
...@@ -122,6 +150,9 @@ func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Spa ...@@ -122,6 +150,9 @@ func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Spa
// CreateExitSpan creates and starts an exit span for client // 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) { 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)) s, _, err := t.CreateLocalSpan(ctx, WithSpanType(SpanTypeExit))
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -156,6 +187,20 @@ func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer ...@@ -156,6 +187,20 @@ func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer
return s, nil 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 ctxKey struct{}
type refKey struct{} type refKey struct{}
......
...@@ -32,6 +32,7 @@ func TestTracer_EntryAndExit(t *testing.T) { ...@@ -32,6 +32,7 @@ func TestTracer_EntryAndExit(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
tracer.WaitUntilRegister()
entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) { entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return "", nil return "", nil
}) })
...@@ -63,6 +64,7 @@ func TestTracer_Entry(t *testing.T) { ...@@ -63,6 +64,7 @@ func TestTracer_Entry(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
tracer.WaitUntilRegister()
entrySpan, _, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) { entrySpan, _, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return header, nil return header, nil
}) })
...@@ -88,6 +90,7 @@ func TestTracer_EntryAndExitInTrace(t *testing.T) { ...@@ -88,6 +90,7 @@ func TestTracer_EntryAndExitInTrace(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
tracer.WaitUntilRegister()
entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) { entrySpan, ctx, err := tracer.CreateEntrySpan(context.Background(), "/rest/api", func() (string, error) {
return header, nil return header, nil
}) })
......
...@@ -33,12 +33,6 @@ func TestTracerInit(t *testing.T) { ...@@ -33,12 +33,6 @@ func TestTracerInit(t *testing.T) {
if err != nil { if err != nil {
t.Fail() t.Fail()
} }
_, err = NewTracer("", WithReporter(&mockRegisterReporter{
success: false,
}))
if err != errRegister {
t.Fail()
}
} }
func TestTracer_CreateLocalSpan(t *testing.T) { func TestTracer_CreateLocalSpan(t *testing.T) {
...@@ -46,6 +40,7 @@ func TestTracer_CreateLocalSpan(t *testing.T) { ...@@ -46,6 +40,7 @@ func TestTracer_CreateLocalSpan(t *testing.T) {
success: true, success: true,
} }
tracer, _ := NewTracer("", WithReporter(reporter)) tracer, _ := NewTracer("", WithReporter(reporter))
tracer.WaitUntilRegister()
span, ctx, err := tracer.CreateLocalSpan(context.Background()) span, ctx, err := tracer.CreateLocalSpan(context.Background())
if err != nil { if err != nil {
t.Error(err) t.Error(err)
...@@ -65,6 +60,7 @@ func TestTracer_CreateLocalSpanAsync(t *testing.T) { ...@@ -65,6 +60,7 @@ func TestTracer_CreateLocalSpanAsync(t *testing.T) {
success: true, success: true,
} }
tracer, _ := NewTracer("", WithReporter(reporter)) tracer, _ := NewTracer("", WithReporter(reporter))
tracer.WaitUntilRegister()
span, ctx, err := tracer.CreateLocalSpan(context.Background()) span, ctx, err := tracer.CreateLocalSpan(context.Background())
if err != nil { if err != nil {
t.Error(err) t.Error(err)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册