1// Copyright The OpenTelemetry Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package trace // import "go.opentelemetry.io/otel/sdk/trace" 16 17import ( 18 "context" 19 "sync" 20 21 "go.opentelemetry.io/otel" 22) 23 24// simpleSpanProcessor is a SpanProcessor that synchronously sends all 25// completed Spans to a trace.Exporter immediately. 26type simpleSpanProcessor struct { 27 exporterMu sync.RWMutex 28 exporter SpanExporter 29 stopOnce sync.Once 30} 31 32var _ SpanProcessor = (*simpleSpanProcessor)(nil) 33 34// NewSimpleSpanProcessor returns a new SpanProcessor that will synchronously 35// send completed spans to the exporter immediately. 36// 37// This SpanProcessor is not recommended for production use. The synchronous 38// nature of this SpanProcessor make it good for testing, debugging, or 39// showing examples of other feature, but it will be slow and have a high 40// computation resource usage overhead. The BatchSpanProcessor is recommended 41// for production use instead. 42func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { 43 ssp := &simpleSpanProcessor{ 44 exporter: exporter, 45 } 46 return ssp 47} 48 49// OnStart does nothing. 50func (ssp *simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} 51 52// OnEnd immediately exports a ReadOnlySpan. 53func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { 54 ssp.exporterMu.RLock() 55 defer ssp.exporterMu.RUnlock() 56 57 if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { 58 if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { 59 otel.Handle(err) 60 } 61 } 62} 63 64// Shutdown shuts down the exporter this SimpleSpanProcessor exports to. 65func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error { 66 var err error 67 ssp.stopOnce.Do(func() { 68 stopFunc := func(exp SpanExporter) (<-chan error, func()) { 69 done := make(chan error) 70 return done, func() { done <- exp.Shutdown(ctx) } 71 } 72 73 // The exporter field of the simpleSpanProcessor needs to be zeroed to 74 // signal it is shut down, meaning all subsequent calls to OnEnd will 75 // be gracefully ignored. This needs to be done synchronously to avoid 76 // any race condition. 77 // 78 // A closure is used to keep reference to the exporter and then the 79 // field is zeroed. This ensures the simpleSpanProcessor is shut down 80 // before the exporter. This order is important as it avoids a 81 // potential deadlock. If the exporter shut down operation generates a 82 // span, that span would need to be exported. Meaning, OnEnd would be 83 // called and try acquiring the lock that is held here. 84 ssp.exporterMu.Lock() 85 done, shutdown := stopFunc(ssp.exporter) 86 ssp.exporter = nil 87 ssp.exporterMu.Unlock() 88 89 go shutdown() 90 91 // Wait for the exporter to shut down or the deadline to expire. 92 select { 93 case err = <-done: 94 case <-ctx.Done(): 95 // It is possible for the exporter to have immediately shut down 96 // and the context to be done simultaneously. In that case this 97 // outer select statement will randomly choose a case. This will 98 // result in a different returned error for similar scenarios. 99 // Instead, double check if the exporter shut down at the same 100 // time and return that error if so. This will ensure consistency 101 // as well as ensure the caller knows the exporter shut down 102 // successfully (they can already determine if the deadline is 103 // expired given they passed the context). 104 select { 105 case err = <-done: 106 default: 107 err = ctx.Err() 108 } 109 } 110 }) 111 return err 112} 113 114// ForceFlush does nothing as there is no data to flush. 115func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { 116 return nil 117} 118