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 15 package trace // import "go.opentelemetry.io/otel/sdk/trace" 16 17 import ( 18 "context" 19 "sync" 20 21 "go.opentelemetry.io/otel" 22 "go.opentelemetry.io/otel/internal/global" 23 ) 24 25 // simpleSpanProcessor is a SpanProcessor that synchronously sends all 26 // completed Spans to a trace.Exporter immediately. 27 type simpleSpanProcessor struct { 28 exporterMu sync.Mutex 29 exporter SpanExporter 30 stopOnce sync.Once 31 } 32 33 var _ SpanProcessor = (*simpleSpanProcessor)(nil) 34 35 // NewSimpleSpanProcessor returns a new SpanProcessor that will synchronously 36 // send completed spans to the exporter immediately. 37 // 38 // This SpanProcessor is not recommended for production use. The synchronous 39 // nature of this SpanProcessor make it good for testing, debugging, or 40 // showing examples of other feature, but it will be slow and have a high 41 // computation resource usage overhead. The BatchSpanProcessor is recommended 42 // for production use instead. 43 func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { 44 ssp := &simpleSpanProcessor{ 45 exporter: exporter, 46 } 47 global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") 48 49 return ssp 50 } 51 52 // OnStart does nothing. 53 func (ssp *simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} 54 55 // OnEnd immediately exports a ReadOnlySpan. 56 func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { 57 ssp.exporterMu.Lock() 58 defer ssp.exporterMu.Unlock() 59 60 if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { 61 if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { 62 otel.Handle(err) 63 } 64 } 65 } 66 67 // Shutdown shuts down the exporter this SimpleSpanProcessor exports to. 68 func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error { 69 var err error 70 ssp.stopOnce.Do(func() { 71 stopFunc := func(exp SpanExporter) (<-chan error, func()) { 72 done := make(chan error) 73 return done, func() { done <- exp.Shutdown(ctx) } 74 } 75 76 // The exporter field of the simpleSpanProcessor needs to be zeroed to 77 // signal it is shut down, meaning all subsequent calls to OnEnd will 78 // be gracefully ignored. This needs to be done synchronously to avoid 79 // any race condition. 80 // 81 // A closure is used to keep reference to the exporter and then the 82 // field is zeroed. This ensures the simpleSpanProcessor is shut down 83 // before the exporter. This order is important as it avoids a 84 // potential deadlock. If the exporter shut down operation generates a 85 // span, that span would need to be exported. Meaning, OnEnd would be 86 // called and try acquiring the lock that is held here. 87 ssp.exporterMu.Lock() 88 done, shutdown := stopFunc(ssp.exporter) 89 ssp.exporter = nil 90 ssp.exporterMu.Unlock() 91 92 go shutdown() 93 94 // Wait for the exporter to shut down or the deadline to expire. 95 select { 96 case err = <-done: 97 case <-ctx.Done(): 98 // It is possible for the exporter to have immediately shut down 99 // and the context to be done simultaneously. In that case this 100 // outer select statement will randomly choose a case. This will 101 // result in a different returned error for similar scenarios. 102 // Instead, double check if the exporter shut down at the same 103 // time and return that error if so. This will ensure consistency 104 // as well as ensure the caller knows the exporter shut down 105 // successfully (they can already determine if the deadline is 106 // expired given they passed the context). 107 select { 108 case err = <-done: 109 default: 110 err = ctx.Err() 111 } 112 } 113 }) 114 return err 115 } 116 117 // ForceFlush does nothing as there is no data to flush. 118 func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { 119 return nil 120 } 121 122 // MarshalLog is the marshaling function used by the logging system to represent this Span Processor. 123 func (ssp *simpleSpanProcessor) MarshalLog() interface{} { 124 return struct { 125 Type string 126 Exporter SpanExporter 127 }{ 128 Type: "SimpleSpanProcessor", 129 Exporter: ssp.exporter, 130 } 131 } 132