...

Source file src/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go

Documentation: go.opentelemetry.io/otel/sdk/trace

     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  

View as plain text