Copyright 2017, OpenCensus 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 stackdriver

import (
	
	
	
	
	

	tracingclient 
	
	
	
	tracepb 

	commonpb 
	resourcepb 
)
traceExporter is an implementation of trace.Exporter that uploads spans to Stackdriver.
uploadFn defaults to uploadSpans; it can be replaced for tests.
	uploadFn func(spans []*tracepb.Span)
	overflowLogger
	client *tracingclient.Client
}

var _ trace.Exporter = (*traceExporter)(nil)

func ( Options) (*traceExporter, error) {
	 := .Context
	if  == nil {
		 = context.Background()
	}
	,  := tracingclient.NewClient(, .TraceClientOptions...)
	if  != nil {
		return nil, fmt.Errorf("stackdriver: couldn't initialize trace client: %v", )
	}
	return newTraceExporterWithClient(, ), nil
}

const defaultBufferedByteLimit = 8 * 1024 * 1024

func ( Options,  *tracingclient.Client) *traceExporter {
	 := &traceExporter{
		projectID: .ProjectID,
		client:    ,
		o:         ,
	}
	 := bundler.NewBundler((*tracepb.Span)(nil), func( interface{}) {
		.uploadFn(.([]*tracepb.Span))
	})
	if .BundleDelayThreshold > 0 {
		.DelayThreshold = .BundleDelayThreshold
	} else {
		.DelayThreshold = 2 * time.Second
	}
	if .BundleCountThreshold > 0 {
		.BundleCountThreshold = .BundleCountThreshold
	} else {
		.BundleCountThreshold = 50
	}
	if .NumberOfWorkers > 0 {
		.HandlerLimit = .NumberOfWorkers
The measured "bytes" are not really bytes, see exportReceiver.
ExportSpan exports a SpanData to Stackdriver Trace.
func ( *traceExporter) ( *trace.SpanData) {
	 := protoFromSpanData(, .projectID, .o.Resource, .o.UserAgent)
	 := proto.Size()
	 := .bundler.Add(, )
	switch  {
	case nil:
		return
	case bundler.ErrOversizedItem:
	case bundler.ErrOverflow:
		.overflowLogger.log()
	default:
		.o.handleError()
	}
}
Flush waits for exported trace spans to be uploaded. This is useful if your program is ending and you do not want to lose recent spans.
func ( *traceExporter) () {
	.bundler.Flush()
}

func ( *traceExporter) ( context.Context,  *commonpb.Node,  *resourcepb.Resource,  []*trace.SpanData) (int, error) {
	,  := trace.StartSpan(
		,
		"contrib.go.opencensus.io/exporter/stackdriver.PushTraceSpans",
		trace.WithSampler(trace.NeverSample()),
	)
	defer .End()
	.AddAttributes(trace.Int64Attribute("num_spans", int64(len())))

	 := make([]*tracepb.Span, 0, len())

	 := .o.Resource
	if  != nil {
		 = .o.MapResource(resourcepbToResource())
	}

	for ,  := range  {
		 = append(, protoFromSpanData(, .projectID, , .o.UserAgent))
	}

	 := tracepb.BatchWriteSpansRequest{
		Name:  "projects/" + .projectID,
		Spans: ,
Create a never-sampled span to prevent traces associated with exporter.
	,  := newContextWithTimeout(, .o.Timeout)
	defer ()

	 := .client.BatchWriteSpans(, &)

	if  != nil {
		return len(), 
	}
	return 0, nil
}
uploadSpans uploads a set of spans to Stackdriver.
func ( *traceExporter) ( []*tracepb.Span) {
	 := tracepb.BatchWriteSpansRequest{
		Name:  "projects/" + .projectID,
		Spans: ,
Create a never-sampled span to prevent traces associated with exporter.
	,  := newContextWithTimeout(.o.Context, .o.Timeout)
	defer ()
	,  := trace.StartSpan(
		,
		"contrib.go.opencensus.io/exporter/stackdriver.uploadSpans",
		trace.WithSampler(trace.NeverSample()),
	)
	defer .End()
	.AddAttributes(trace.Int64Attribute("num_spans", int64(len())))

	 := .client.BatchWriteSpans(, &)
	if  != nil {
		.SetStatus(trace.Status{Code: 2, Message: .Error()})
		.o.handleError()
	}
}
overflowLogger ensures that at most one overflow error log message is written every 5 seconds.
type overflowLogger struct {
	mu    sync.Mutex
	pause bool
	accum int
}

func ( *overflowLogger) () {
	.pause = true
	time.AfterFunc(5*time.Second, func() {
		.mu.Lock()
		defer .mu.Unlock()
		switch {
		case .accum == 0:
			.pause = false
		case .accum == 1:
			log.Println("OpenCensus Stackdriver exporter: failed to upload span: buffer full")
			.accum = 0
			.()
		default:
			log.Printf("OpenCensus Stackdriver exporter: failed to upload %d spans: buffer full", .accum)
			.accum = 0
			.()
		}
	})
}

func ( *overflowLogger) () {
	.mu.Lock()
	defer .mu.Unlock()
	if !.pause {
		log.Println("OpenCensus Stackdriver exporter: failed to upload span: buffer full")
		.delay()
	} else {
		.accum++
	}