mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			307 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 trace
 | 
						|
 | 
						|
import (
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"go.opencensus.io/internal"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	maxBucketSize     = 100000
 | 
						|
	defaultBucketSize = 10
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	ssmu       sync.RWMutex // protects spanStores
 | 
						|
	spanStores = make(map[string]*spanStore)
 | 
						|
)
 | 
						|
 | 
						|
// This exists purely to avoid exposing internal methods used by z-Pages externally.
 | 
						|
type internalOnly struct{}
 | 
						|
 | 
						|
func init() {
 | 
						|
	//TODO(#412): remove
 | 
						|
	internal.Trace = &internalOnly{}
 | 
						|
}
 | 
						|
 | 
						|
// ReportActiveSpans returns the active spans for the given name.
 | 
						|
func (i internalOnly) ReportActiveSpans(name string) []*SpanData {
 | 
						|
	s := spanStoreForName(name)
 | 
						|
	if s == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var out []*SpanData
 | 
						|
	s.mu.Lock()
 | 
						|
	defer s.mu.Unlock()
 | 
						|
	for span := range s.active {
 | 
						|
		out = append(out, span.makeSpanData())
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// ReportSpansByError returns a sample of error spans.
 | 
						|
//
 | 
						|
// If code is nonzero, only spans with that status code are returned.
 | 
						|
func (i internalOnly) ReportSpansByError(name string, code int32) []*SpanData {
 | 
						|
	s := spanStoreForName(name)
 | 
						|
	if s == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var out []*SpanData
 | 
						|
	s.mu.Lock()
 | 
						|
	defer s.mu.Unlock()
 | 
						|
	if code != 0 {
 | 
						|
		if b, ok := s.errors[code]; ok {
 | 
						|
			for _, sd := range b.buffer {
 | 
						|
				if sd == nil {
 | 
						|
					break
 | 
						|
				}
 | 
						|
				out = append(out, sd)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for _, b := range s.errors {
 | 
						|
			for _, sd := range b.buffer {
 | 
						|
				if sd == nil {
 | 
						|
					break
 | 
						|
				}
 | 
						|
				out = append(out, sd)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// ConfigureBucketSizes sets the number of spans to keep per latency and error
 | 
						|
// bucket for different span names.
 | 
						|
func (i internalOnly) ConfigureBucketSizes(bcs []internal.BucketConfiguration) {
 | 
						|
	for _, bc := range bcs {
 | 
						|
		latencyBucketSize := bc.MaxRequestsSucceeded
 | 
						|
		if latencyBucketSize < 0 {
 | 
						|
			latencyBucketSize = 0
 | 
						|
		}
 | 
						|
		if latencyBucketSize > maxBucketSize {
 | 
						|
			latencyBucketSize = maxBucketSize
 | 
						|
		}
 | 
						|
		errorBucketSize := bc.MaxRequestsErrors
 | 
						|
		if errorBucketSize < 0 {
 | 
						|
			errorBucketSize = 0
 | 
						|
		}
 | 
						|
		if errorBucketSize > maxBucketSize {
 | 
						|
			errorBucketSize = maxBucketSize
 | 
						|
		}
 | 
						|
		spanStoreSetSize(bc.Name, latencyBucketSize, errorBucketSize)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ReportSpansPerMethod returns a summary of what spans are being stored for each span name.
 | 
						|
func (i internalOnly) ReportSpansPerMethod() map[string]internal.PerMethodSummary {
 | 
						|
	out := make(map[string]internal.PerMethodSummary)
 | 
						|
	ssmu.RLock()
 | 
						|
	defer ssmu.RUnlock()
 | 
						|
	for name, s := range spanStores {
 | 
						|
		s.mu.Lock()
 | 
						|
		p := internal.PerMethodSummary{
 | 
						|
			Active: len(s.active),
 | 
						|
		}
 | 
						|
		for code, b := range s.errors {
 | 
						|
			p.ErrorBuckets = append(p.ErrorBuckets, internal.ErrorBucketSummary{
 | 
						|
				ErrorCode: code,
 | 
						|
				Size:      b.size(),
 | 
						|
			})
 | 
						|
		}
 | 
						|
		for i, b := range s.latency {
 | 
						|
			min, max := latencyBucketBounds(i)
 | 
						|
			p.LatencyBuckets = append(p.LatencyBuckets, internal.LatencyBucketSummary{
 | 
						|
				MinLatency: min,
 | 
						|
				MaxLatency: max,
 | 
						|
				Size:       b.size(),
 | 
						|
			})
 | 
						|
		}
 | 
						|
		s.mu.Unlock()
 | 
						|
		out[name] = p
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// ReportSpansByLatency returns a sample of successful spans.
 | 
						|
//
 | 
						|
// minLatency is the minimum latency of spans to be returned.
 | 
						|
// maxLatency, if nonzero, is the maximum latency of spans to be returned.
 | 
						|
func (i internalOnly) ReportSpansByLatency(name string, minLatency, maxLatency time.Duration) []*SpanData {
 | 
						|
	s := spanStoreForName(name)
 | 
						|
	if s == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	var out []*SpanData
 | 
						|
	s.mu.Lock()
 | 
						|
	defer s.mu.Unlock()
 | 
						|
	for i, b := range s.latency {
 | 
						|
		min, max := latencyBucketBounds(i)
 | 
						|
		if i+1 != len(s.latency) && max <= minLatency {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if maxLatency != 0 && maxLatency < min {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		for _, sd := range b.buffer {
 | 
						|
			if sd == nil {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if minLatency != 0 || maxLatency != 0 {
 | 
						|
				d := sd.EndTime.Sub(sd.StartTime)
 | 
						|
				if d < minLatency {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if maxLatency != 0 && d > maxLatency {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
			out = append(out, sd)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// spanStore keeps track of spans stored for a particular span name.
 | 
						|
//
 | 
						|
// It contains all active spans; a sample of spans for failed requests,
 | 
						|
// categorized by error code; and a sample of spans for successful requests,
 | 
						|
// bucketed by latency.
 | 
						|
type spanStore struct {
 | 
						|
	mu                     sync.Mutex // protects everything below.
 | 
						|
	active                 map[*Span]struct{}
 | 
						|
	errors                 map[int32]*bucket
 | 
						|
	latency                []bucket
 | 
						|
	maxSpansPerErrorBucket int
 | 
						|
}
 | 
						|
 | 
						|
// newSpanStore creates a span store.
 | 
						|
func newSpanStore(name string, latencyBucketSize int, errorBucketSize int) *spanStore {
 | 
						|
	s := &spanStore{
 | 
						|
		active:                 make(map[*Span]struct{}),
 | 
						|
		latency:                make([]bucket, len(defaultLatencies)+1),
 | 
						|
		maxSpansPerErrorBucket: errorBucketSize,
 | 
						|
	}
 | 
						|
	for i := range s.latency {
 | 
						|
		s.latency[i] = makeBucket(latencyBucketSize)
 | 
						|
	}
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// spanStoreForName returns the spanStore for the given name.
 | 
						|
//
 | 
						|
// It returns nil if it doesn't exist.
 | 
						|
func spanStoreForName(name string) *spanStore {
 | 
						|
	var s *spanStore
 | 
						|
	ssmu.RLock()
 | 
						|
	s, _ = spanStores[name]
 | 
						|
	ssmu.RUnlock()
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// spanStoreForNameCreateIfNew returns the spanStore for the given name.
 | 
						|
//
 | 
						|
// It creates it if it didn't exist.
 | 
						|
func spanStoreForNameCreateIfNew(name string) *spanStore {
 | 
						|
	ssmu.RLock()
 | 
						|
	s, ok := spanStores[name]
 | 
						|
	ssmu.RUnlock()
 | 
						|
	if ok {
 | 
						|
		return s
 | 
						|
	}
 | 
						|
	ssmu.Lock()
 | 
						|
	defer ssmu.Unlock()
 | 
						|
	s, ok = spanStores[name]
 | 
						|
	if ok {
 | 
						|
		return s
 | 
						|
	}
 | 
						|
	s = newSpanStore(name, defaultBucketSize, defaultBucketSize)
 | 
						|
	spanStores[name] = s
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// spanStoreSetSize resizes the spanStore for the given name.
 | 
						|
//
 | 
						|
// It creates it if it didn't exist.
 | 
						|
func spanStoreSetSize(name string, latencyBucketSize int, errorBucketSize int) {
 | 
						|
	ssmu.RLock()
 | 
						|
	s, ok := spanStores[name]
 | 
						|
	ssmu.RUnlock()
 | 
						|
	if ok {
 | 
						|
		s.resize(latencyBucketSize, errorBucketSize)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	ssmu.Lock()
 | 
						|
	defer ssmu.Unlock()
 | 
						|
	s, ok = spanStores[name]
 | 
						|
	if ok {
 | 
						|
		s.resize(latencyBucketSize, errorBucketSize)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	s = newSpanStore(name, latencyBucketSize, errorBucketSize)
 | 
						|
	spanStores[name] = s
 | 
						|
}
 | 
						|
 | 
						|
func (s *spanStore) resize(latencyBucketSize int, errorBucketSize int) {
 | 
						|
	s.mu.Lock()
 | 
						|
	for i := range s.latency {
 | 
						|
		s.latency[i].resize(latencyBucketSize)
 | 
						|
	}
 | 
						|
	for _, b := range s.errors {
 | 
						|
		b.resize(errorBucketSize)
 | 
						|
	}
 | 
						|
	s.maxSpansPerErrorBucket = errorBucketSize
 | 
						|
	s.mu.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// add adds a span to the active bucket of the spanStore.
 | 
						|
func (s *spanStore) add(span *Span) {
 | 
						|
	s.mu.Lock()
 | 
						|
	s.active[span] = struct{}{}
 | 
						|
	s.mu.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// finished removes a span from the active set, and adds a corresponding
 | 
						|
// SpanData to a latency or error bucket.
 | 
						|
func (s *spanStore) finished(span *Span, sd *SpanData) {
 | 
						|
	latency := sd.EndTime.Sub(sd.StartTime)
 | 
						|
	if latency < 0 {
 | 
						|
		latency = 0
 | 
						|
	}
 | 
						|
	code := sd.Status.Code
 | 
						|
 | 
						|
	s.mu.Lock()
 | 
						|
	delete(s.active, span)
 | 
						|
	if code == 0 {
 | 
						|
		s.latency[latencyBucket(latency)].add(sd)
 | 
						|
	} else {
 | 
						|
		if s.errors == nil {
 | 
						|
			s.errors = make(map[int32]*bucket)
 | 
						|
		}
 | 
						|
		if b := s.errors[code]; b != nil {
 | 
						|
			b.add(sd)
 | 
						|
		} else {
 | 
						|
			b := makeBucket(s.maxSpansPerErrorBucket)
 | 
						|
			s.errors[code] = &b
 | 
						|
			b.add(sd)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	s.mu.Unlock()
 | 
						|
}
 |