You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-06-14 23:45:20 +02:00
SDK: span processor interface and simple span processor. (#117)
* SDK: SpanProcessor Interface. * add simple span processor. * rename span processor. * fix logic to export or process span data.
This commit is contained in:
47
sdk/trace/simple_span_processor.go
Normal file
47
sdk/trace/simple_span_processor.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2019, 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 trace
|
||||||
|
|
||||||
|
// SimpleSpanProcessor implements SpanProcessor interfaces. It is used by
|
||||||
|
// exporters to receive SpanData synchronously when span is finished.
|
||||||
|
type SimpleSpanProcessor struct {
|
||||||
|
exporter Exporter
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SpanProcessor = (*SimpleSpanProcessor)(nil)
|
||||||
|
|
||||||
|
// NewSimpleSpanProcessor creates a new instance of SimpleSpanProcessor
|
||||||
|
// for a given exporter.
|
||||||
|
func NewSimpleSpanProcessor(exporter Exporter) *SimpleSpanProcessor {
|
||||||
|
ssp := &SimpleSpanProcessor{
|
||||||
|
exporter: exporter,
|
||||||
|
}
|
||||||
|
return ssp
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnStart method does nothing.
|
||||||
|
func (ssp *SimpleSpanProcessor) OnStart(sd *SpanData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEnd method exports SpanData using associated exporter.
|
||||||
|
func (ssp *SimpleSpanProcessor) OnEnd(sd *SpanData) {
|
||||||
|
if ssp.exporter != nil {
|
||||||
|
ssp.exporter.ExportSpan(sd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown method does nothing. There is no data to cleanup.
|
||||||
|
func (ssp *SimpleSpanProcessor) Shutdown() {
|
||||||
|
}
|
77
sdk/trace/simple_span_processor_test.go
Normal file
77
sdk/trace/simple_span_processor_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2019, 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 trace_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/api/core"
|
||||||
|
apitrace "go.opentelemetry.io/api/trace"
|
||||||
|
sdktrace "go.opentelemetry.io/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testExporter struct {
|
||||||
|
spans []*sdktrace.SpanData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testExporter) ExportSpan(s *sdktrace.SpanData) {
|
||||||
|
t.spans = append(t.spans, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdktrace.Exporter = (*testExporter)(nil)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sdktrace.Register()
|
||||||
|
sdktrace.ApplyConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSimpleSpanProcessor(t *testing.T) {
|
||||||
|
ssp := sdktrace.NewSimpleSpanProcessor(&testExporter{})
|
||||||
|
if ssp == nil {
|
||||||
|
t.Errorf("Error creating new instance of SimpleSpanProcessor\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSimpleSpanProcessorWithNilExporter(t *testing.T) {
|
||||||
|
ssp := sdktrace.NewSimpleSpanProcessor(nil)
|
||||||
|
if ssp == nil {
|
||||||
|
t.Errorf("Error creating new instance of SimpleSpanProcessor with nil Exporter\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimpleSpanProcessorOnEnd(t *testing.T) {
|
||||||
|
te := testExporter{}
|
||||||
|
ssp := sdktrace.NewSimpleSpanProcessor(&te)
|
||||||
|
if ssp == nil {
|
||||||
|
t.Errorf("Error creating new instance of SimpleSpanProcessor with nil Exporter\n")
|
||||||
|
}
|
||||||
|
sdktrace.RegisterSpanProcessor(ssp)
|
||||||
|
tid := core.TraceID{High: 0x0102030405060708, Low: 0x0102040810203040}
|
||||||
|
sid := uint64(0x0102040810203040)
|
||||||
|
sc := core.SpanContext{
|
||||||
|
TraceID: tid,
|
||||||
|
SpanID: sid,
|
||||||
|
TraceOptions: 0x1,
|
||||||
|
}
|
||||||
|
_, span := apitrace.GlobalTracer().Start(context.Background(), "OnEnd", apitrace.ChildOf(sc))
|
||||||
|
span.Finish()
|
||||||
|
|
||||||
|
wantTraceID := tid
|
||||||
|
gotTraceID := te.spans[0].SpanContext.TraceID
|
||||||
|
if wantTraceID != gotTraceID {
|
||||||
|
t.Errorf("SimplerSpanProcessor OnEnd() check: got %+v, want %+v\n", gotTraceID, wantTraceID)
|
||||||
|
}
|
||||||
|
}
|
@ -123,23 +123,26 @@ func (s *span) Finish(options ...apitrace.FinishOption) {
|
|||||||
}
|
}
|
||||||
s.endOnce.Do(func() {
|
s.endOnce.Do(func() {
|
||||||
exp, _ := exporters.Load().(exportersMap)
|
exp, _ := exporters.Load().(exportersMap)
|
||||||
mustExport := s.spanContext.IsSampled() && len(exp) > 0
|
sps, _ := spanProcessors.Load().(spanProcessorMap)
|
||||||
//if s.spanStore != nil || mustExport {
|
mustExportOrProcess := len(sps) > 0 || (s.spanContext.IsSampled() && len(exp) > 0)
|
||||||
if mustExport {
|
// TODO(rghetia): when exporter is migrated to use processors simply check for the number
|
||||||
|
// of processors. Exporter will export based on sampling.
|
||||||
|
if mustExportOrProcess {
|
||||||
sd := s.makeSpanData()
|
sd := s.makeSpanData()
|
||||||
if opts.FinishTime.IsZero() {
|
if opts.FinishTime.IsZero() {
|
||||||
sd.EndTime = internal.MonotonicEndTime(sd.StartTime)
|
sd.EndTime = internal.MonotonicEndTime(sd.StartTime)
|
||||||
} else {
|
} else {
|
||||||
sd.EndTime = opts.FinishTime
|
sd.EndTime = opts.FinishTime
|
||||||
}
|
}
|
||||||
//if s.spanStore != nil {
|
// Sampling check would be in the processor if the processor is used for exporting.
|
||||||
// s.spanStore.finished(s, sd)
|
if s.spanContext.IsSampled() {
|
||||||
//}
|
|
||||||
if mustExport {
|
|
||||||
for e := range exp {
|
for e := range exp {
|
||||||
e.ExportSpan(sd)
|
e.ExportSpan(sd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for sp := range sps {
|
||||||
|
sp.OnEnd(sd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
74
sdk/trace/span_processor.go
Normal file
74
sdk/trace/span_processor.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2019, 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 trace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SpanProcessor is interface to add hooks to start and end method invocations.
|
||||||
|
type SpanProcessor interface {
|
||||||
|
|
||||||
|
// OnStart method is invoked when span is started. It is a synchronous call
|
||||||
|
// and hence should not block.
|
||||||
|
OnStart(sd *SpanData)
|
||||||
|
|
||||||
|
// OnEnd method is invoked when span is finished. It is a synchronous call
|
||||||
|
// and hence should not block.
|
||||||
|
OnEnd(sd *SpanData)
|
||||||
|
|
||||||
|
// Shutdown is invoked when SDK shutsdown. Use this call to cleanup any processor
|
||||||
|
// data. No calls to OnStart and OnEnd method is invoked after Shutdown call is
|
||||||
|
// made. It should not be blocked indefinitely.
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
type spanProcessorMap map[SpanProcessor]struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
spanProcessors atomic.Value
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterSpanProcessor adds to the list of SpanProcessors that will receive sampled
|
||||||
|
// trace spans.
|
||||||
|
func RegisterSpanProcessor(e SpanProcessor) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
new := make(spanProcessorMap)
|
||||||
|
if old, ok := spanProcessors.Load().(spanProcessorMap); ok {
|
||||||
|
for k, v := range old {
|
||||||
|
new[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new[e] = struct{}{}
|
||||||
|
spanProcessors.Store(new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterSpanProcessor removes from the list of SpanProcessors the SpanProcessor that was
|
||||||
|
// registered with the given name.
|
||||||
|
func UnregisterSpanProcessor(e SpanProcessor) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
new := make(spanProcessorMap)
|
||||||
|
if old, ok := spanProcessors.Load().(spanProcessorMap); ok {
|
||||||
|
for k, v := range old {
|
||||||
|
new[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(new, e)
|
||||||
|
spanProcessors.Store(new)
|
||||||
|
}
|
118
sdk/trace/span_processor_test.go
Normal file
118
sdk/trace/span_processor_test.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2019, 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 trace_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
apitrace "go.opentelemetry.io/api/trace"
|
||||||
|
sdktrace "go.opentelemetry.io/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testSpanProcesor struct {
|
||||||
|
spansStarted []*sdktrace.SpanData
|
||||||
|
spansEnded []*sdktrace.SpanData
|
||||||
|
shutdownCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sdktrace.Register()
|
||||||
|
sdktrace.ApplyConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSpanProcesor) OnStart(s *sdktrace.SpanData) {
|
||||||
|
t.spansStarted = append(t.spansStarted, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSpanProcesor) OnEnd(s *sdktrace.SpanData) {
|
||||||
|
t.spansEnded = append(t.spansEnded, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSpanProcesor) Shutdown() {
|
||||||
|
t.shutdownCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterSpanProcessort(t *testing.T) {
|
||||||
|
name := "Register span processor before span starts"
|
||||||
|
sp := NewTestSpanProcessor()
|
||||||
|
sdktrace.RegisterSpanProcessor(sp)
|
||||||
|
defer sdktrace.UnregisterSpanProcessor(sp)
|
||||||
|
_, span := apitrace.GlobalTracer().Start(context.Background(), "OnStart")
|
||||||
|
span.Finish()
|
||||||
|
wantCount := 1
|
||||||
|
gotCount := len(sp.spansStarted)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
gotCount = len(sp.spansEnded)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnregisterSpanProcessor(t *testing.T) {
|
||||||
|
name := "Start span after unregistering span processor"
|
||||||
|
sp := NewTestSpanProcessor()
|
||||||
|
sdktrace.RegisterSpanProcessor(sp)
|
||||||
|
_, span := apitrace.GlobalTracer().Start(context.Background(), "OnStart")
|
||||||
|
span.Finish()
|
||||||
|
sdktrace.UnregisterSpanProcessor(sp)
|
||||||
|
|
||||||
|
// start another span after unregistering span processor.
|
||||||
|
_, span = apitrace.GlobalTracer().Start(context.Background(), "Start span after unregister")
|
||||||
|
span.Finish()
|
||||||
|
|
||||||
|
wantCount := 1
|
||||||
|
gotCount := len(sp.spansStarted)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotCount = len(sp.spansEnded)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnregisterSpanProcessorWhileSpanIsActive(t *testing.T) {
|
||||||
|
name := "Unregister span processor while span is active"
|
||||||
|
sp := NewTestSpanProcessor()
|
||||||
|
sdktrace.RegisterSpanProcessor(sp)
|
||||||
|
_, span := apitrace.GlobalTracer().Start(context.Background(), "OnStart")
|
||||||
|
sdktrace.UnregisterSpanProcessor(sp)
|
||||||
|
|
||||||
|
span.Finish()
|
||||||
|
|
||||||
|
wantCount := 1
|
||||||
|
gotCount := len(sp.spansStarted)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: started count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantCount = 0
|
||||||
|
gotCount = len(sp.spansEnded)
|
||||||
|
if gotCount != wantCount {
|
||||||
|
t.Errorf("%s: ended count: got %d, want %d\n", name, gotCount, wantCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(rghetia): Add Shutdown test when it is implemented.
|
||||||
|
func TestShutdown(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestSpanProcessor() *testSpanProcesor {
|
||||||
|
return &testSpanProcesor{}
|
||||||
|
}
|
@ -61,6 +61,13 @@ func (tr *tracer) Start(ctx context.Context, name string, o ...apitrace.SpanOpti
|
|||||||
span := startSpanInternal(name, parent, remoteParent, opts)
|
span := startSpanInternal(name, parent, remoteParent, opts)
|
||||||
span.tracer = tr
|
span.tracer = tr
|
||||||
|
|
||||||
|
if span.IsRecordingEvents() {
|
||||||
|
sps, _ := spanProcessors.Load().(spanProcessorMap)
|
||||||
|
for sp := range sps {
|
||||||
|
sp.OnStart(span.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx, end := startExecutionTracerTask(ctx, name)
|
ctx, end := startExecutionTracerTask(ctx, name)
|
||||||
span.executionTracerTaskEnd = end
|
span.executionTracerTaskEnd = end
|
||||||
return newContext(ctx, span), span
|
return newContext(ctx, span), span
|
||||||
|
Reference in New Issue
Block a user