Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd)
2019-12-20 12:56:11 +00:00
parent 9a30ff517f
commit 5c45242b3d
1379 changed files with 633020 additions and 0 deletions

119
vendor/go.opencensus.io/trace/basetypes.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
// 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 (
"fmt"
"time"
)
type (
// TraceID is a 16-byte identifier for a set of spans.
TraceID [16]byte
// SpanID is an 8-byte identifier for a single span.
SpanID [8]byte
)
func (t TraceID) String() string {
return fmt.Sprintf("%02x", t[:])
}
func (s SpanID) String() string {
return fmt.Sprintf("%02x", s[:])
}
// Annotation represents a text annotation with a set of attributes and a timestamp.
type Annotation struct {
Time time.Time
Message string
Attributes map[string]interface{}
}
// Attribute represents a key-value pair on a span, link or annotation.
// Construct with one of: BoolAttribute, Int64Attribute, or StringAttribute.
type Attribute struct {
key string
value interface{}
}
// BoolAttribute returns a bool-valued attribute.
func BoolAttribute(key string, value bool) Attribute {
return Attribute{key: key, value: value}
}
// Int64Attribute returns an int64-valued attribute.
func Int64Attribute(key string, value int64) Attribute {
return Attribute{key: key, value: value}
}
// Float64Attribute returns a float64-valued attribute.
func Float64Attribute(key string, value float64) Attribute {
return Attribute{key: key, value: value}
}
// StringAttribute returns a string-valued attribute.
func StringAttribute(key string, value string) Attribute {
return Attribute{key: key, value: value}
}
// LinkType specifies the relationship between the span that had the link
// added, and the linked span.
type LinkType int32
// LinkType values.
const (
LinkTypeUnspecified LinkType = iota // The relationship of the two spans is unknown.
LinkTypeChild // The linked span is a child of the current span.
LinkTypeParent // The linked span is the parent of the current span.
)
// Link represents a reference from one span to another span.
type Link struct {
TraceID TraceID
SpanID SpanID
Type LinkType
// Attributes is a set of attributes on the link.
Attributes map[string]interface{}
}
// MessageEventType specifies the type of message event.
type MessageEventType int32
// MessageEventType values.
const (
MessageEventTypeUnspecified MessageEventType = iota // Unknown event type.
MessageEventTypeSent // Indicates a sent RPC message.
MessageEventTypeRecv // Indicates a received RPC message.
)
// MessageEvent represents an event describing a message sent or received on the network.
type MessageEvent struct {
Time time.Time
EventType MessageEventType
MessageID int64
UncompressedByteSize int64
CompressedByteSize int64
}
// Status is the status of a Span.
type Status struct {
// Code is a status code. Zero indicates success.
//
// If Code will be propagated to Google APIs, it ideally should be a value from
// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto .
Code int32
Message string
}

86
vendor/go.opencensus.io/trace/config.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
// Copyright 2018, 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"
"go.opencensus.io/trace/internal"
)
// Config represents the global tracing configuration.
type Config struct {
// DefaultSampler is the default sampler used when creating new spans.
DefaultSampler Sampler
// IDGenerator is for internal use only.
IDGenerator internal.IDGenerator
// MaxAnnotationEventsPerSpan is max number of annotation events per span
MaxAnnotationEventsPerSpan int
// MaxMessageEventsPerSpan is max number of message events per span
MaxMessageEventsPerSpan int
// MaxAnnotationEventsPerSpan is max number of attributes per span
MaxAttributesPerSpan int
// MaxLinksPerSpan is max number of links per span
MaxLinksPerSpan int
}
var configWriteMu sync.Mutex
const (
// DefaultMaxAnnotationEventsPerSpan is default max number of annotation events per span
DefaultMaxAnnotationEventsPerSpan = 32
// DefaultMaxMessageEventsPerSpan is default max number of message events per span
DefaultMaxMessageEventsPerSpan = 128
// DefaultMaxAttributesPerSpan is default max number of attributes per span
DefaultMaxAttributesPerSpan = 32
// DefaultMaxLinksPerSpan is default max number of links per span
DefaultMaxLinksPerSpan = 32
)
// ApplyConfig applies changes to the global tracing configuration.
//
// Fields not provided in the given config are going to be preserved.
func ApplyConfig(cfg Config) {
configWriteMu.Lock()
defer configWriteMu.Unlock()
c := *config.Load().(*Config)
if cfg.DefaultSampler != nil {
c.DefaultSampler = cfg.DefaultSampler
}
if cfg.IDGenerator != nil {
c.IDGenerator = cfg.IDGenerator
}
if cfg.MaxAnnotationEventsPerSpan > 0 {
c.MaxAnnotationEventsPerSpan = cfg.MaxAnnotationEventsPerSpan
}
if cfg.MaxMessageEventsPerSpan > 0 {
c.MaxMessageEventsPerSpan = cfg.MaxMessageEventsPerSpan
}
if cfg.MaxAttributesPerSpan > 0 {
c.MaxAttributesPerSpan = cfg.MaxAttributesPerSpan
}
if cfg.MaxLinksPerSpan > 0 {
c.MaxLinksPerSpan = cfg.MaxLinksPerSpan
}
config.Store(&c)
}

53
vendor/go.opencensus.io/trace/doc.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// 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 contains support for OpenCensus distributed tracing.
The following assumes a basic familiarity with OpenCensus concepts.
See http://opencensus.io
Exporting Traces
To export collected tracing data, register at least one exporter. You can use
one of the provided exporters or write your own.
trace.RegisterExporter(exporter)
By default, traces will be sampled relatively rarely. To change the sampling
frequency for your entire program, call ApplyConfig. Use a ProbabilitySampler
to sample a subset of traces, or use AlwaysSample to collect a trace on every run:
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
Be careful about using trace.AlwaysSample in a production application with
significant traffic: a new trace will be started and exported for every request.
Adding Spans to a Trace
A trace consists of a tree of spans. In Go, the current span is carried in a
context.Context.
It is common to want to capture all the activity of a function call in a span. For
this to work, the function must take a context.Context as a parameter. Add these two
lines to the top of the function:
ctx, span := trace.StartSpan(ctx, "example.com/Run")
defer span.End()
StartSpan will create a new top-level span if the context
doesn't contain another span, otherwise it will create a child span.
*/
package trace // import "go.opencensus.io/trace"

38
vendor/go.opencensus.io/trace/evictedqueue.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2019, 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
type evictedQueue struct {
queue []interface{}
capacity int
droppedCount int
}
func newEvictedQueue(capacity int) *evictedQueue {
eq := &evictedQueue{
capacity: capacity,
queue: make([]interface{}, 0),
}
return eq
}
func (eq *evictedQueue) add(value interface{}) {
if len(eq.queue) == eq.capacity {
eq.queue = eq.queue[1:]
eq.droppedCount++
}
eq.queue = append(eq.queue, value)
}

97
vendor/go.opencensus.io/trace/export.go generated vendored Normal file
View File

@ -0,0 +1,97 @@
// 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"
"sync/atomic"
"time"
)
// Exporter is a type for functions that receive sampled trace spans.
//
// The ExportSpan method should be safe for concurrent use and should return
// quickly; if an Exporter takes a significant amount of time to process a
// SpanData, that work should be done on another goroutine.
//
// The SpanData should not be modified, but a pointer to it can be kept.
type Exporter interface {
ExportSpan(s *SpanData)
}
type exportersMap map[Exporter]struct{}
var (
exporterMu sync.Mutex
exporters atomic.Value
)
// RegisterExporter adds to the list of Exporters that will receive sampled
// trace spans.
//
// Binaries can register exporters, libraries shouldn't register exporters.
func RegisterExporter(e Exporter) {
exporterMu.Lock()
new := make(exportersMap)
if old, ok := exporters.Load().(exportersMap); ok {
for k, v := range old {
new[k] = v
}
}
new[e] = struct{}{}
exporters.Store(new)
exporterMu.Unlock()
}
// UnregisterExporter removes from the list of Exporters the Exporter that was
// registered with the given name.
func UnregisterExporter(e Exporter) {
exporterMu.Lock()
new := make(exportersMap)
if old, ok := exporters.Load().(exportersMap); ok {
for k, v := range old {
new[k] = v
}
}
delete(new, e)
exporters.Store(new)
exporterMu.Unlock()
}
// SpanData contains all the information collected by a Span.
type SpanData struct {
SpanContext
ParentSpanID SpanID
SpanKind int
Name string
StartTime time.Time
// The wall clock time of EndTime will be adjusted to always be offset
// from StartTime by the duration of the span.
EndTime time.Time
// The values of Attributes each have type string, bool, or int64.
Attributes map[string]interface{}
Annotations []Annotation
MessageEvents []MessageEvent
Status
Links []Link
HasRemoteParent bool
DroppedAttributeCount int
DroppedAnnotationCount int
DroppedMessageEventCount int
DroppedLinkCount int
// ChildSpanCount holds the number of child span created for this span.
ChildSpanCount int
}

22
vendor/go.opencensus.io/trace/internal/internal.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018, 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 internal provides trace internals.
package internal
// IDGenerator allows custom generators for TraceId and SpanId.
type IDGenerator interface {
NewTraceID() [16]byte
NewSpanID() [8]byte
}

61
vendor/go.opencensus.io/trace/lrumap.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2019, 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 (
"github.com/golang/groupcache/lru"
)
// A simple lru.Cache wrapper that tracks the keys of the current contents and
// the cumulative number of evicted items.
type lruMap struct {
cacheKeys map[lru.Key]bool
cache *lru.Cache
droppedCount int
}
func newLruMap(size int) *lruMap {
lm := &lruMap{
cacheKeys: make(map[lru.Key]bool),
cache: lru.New(size),
droppedCount: 0,
}
lm.cache.OnEvicted = func(key lru.Key, value interface{}) {
delete(lm.cacheKeys, key)
lm.droppedCount++
}
return lm
}
func (lm lruMap) len() int {
return lm.cache.Len()
}
func (lm lruMap) keys() []interface{} {
keys := []interface{}{}
for k := range lm.cacheKeys {
keys = append(keys, k)
}
return keys
}
func (lm *lruMap) add(key, value interface{}) {
lm.cacheKeys[lru.Key(key)] = true
lm.cache.Add(lru.Key(key), value)
}
func (lm *lruMap) get(key interface{}) (interface{}, bool) {
return lm.cache.Get(key)
}

75
vendor/go.opencensus.io/trace/sampling.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
// 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 (
"encoding/binary"
)
const defaultSamplingProbability = 1e-4
// Sampler decides whether a trace should be sampled and exported.
type Sampler func(SamplingParameters) SamplingDecision
// SamplingParameters contains the values passed to a Sampler.
type SamplingParameters struct {
ParentContext SpanContext
TraceID TraceID
SpanID SpanID
Name string
HasRemoteParent bool
}
// SamplingDecision is the value returned by a Sampler.
type SamplingDecision struct {
Sample bool
}
// ProbabilitySampler returns a Sampler that samples a given fraction of traces.
//
// It also samples spans whose parents are sampled.
func ProbabilitySampler(fraction float64) Sampler {
if !(fraction >= 0) {
fraction = 0
} else if fraction >= 1 {
return AlwaysSample()
}
traceIDUpperBound := uint64(fraction * (1 << 63))
return Sampler(func(p SamplingParameters) SamplingDecision {
if p.ParentContext.IsSampled() {
return SamplingDecision{Sample: true}
}
x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1
return SamplingDecision{Sample: x < traceIDUpperBound}
})
}
// AlwaysSample returns a Sampler that samples every trace.
// Be careful about using this sampler in a production application with
// significant traffic: a new trace will be started and exported for every
// request.
func AlwaysSample() Sampler {
return func(p SamplingParameters) SamplingDecision {
return SamplingDecision{Sample: true}
}
}
// NeverSample returns a Sampler that samples no traces.
func NeverSample() Sampler {
return func(p SamplingParameters) SamplingDecision {
return SamplingDecision{Sample: false}
}
}

130
vendor/go.opencensus.io/trace/spanbucket.go generated vendored Normal file
View File

@ -0,0 +1,130 @@
// 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 (
"time"
)
// samplePeriod is the minimum time between accepting spans in a single bucket.
const samplePeriod = time.Second
// defaultLatencies contains the default latency bucket bounds.
// TODO: consider defaults, make configurable
var defaultLatencies = [...]time.Duration{
10 * time.Microsecond,
100 * time.Microsecond,
time.Millisecond,
10 * time.Millisecond,
100 * time.Millisecond,
time.Second,
10 * time.Second,
time.Minute,
}
// bucket is a container for a set of spans for a particular error code or latency range.
type bucket struct {
nextTime time.Time // next time we can accept a span
buffer []*SpanData // circular buffer of spans
nextIndex int // location next SpanData should be placed in buffer
overflow bool // whether the circular buffer has wrapped around
}
func makeBucket(bufferSize int) bucket {
return bucket{
buffer: make([]*SpanData, bufferSize),
}
}
// add adds a span to the bucket, if nextTime has been reached.
func (b *bucket) add(s *SpanData) {
if s.EndTime.Before(b.nextTime) {
return
}
if len(b.buffer) == 0 {
return
}
b.nextTime = s.EndTime.Add(samplePeriod)
b.buffer[b.nextIndex] = s
b.nextIndex++
if b.nextIndex == len(b.buffer) {
b.nextIndex = 0
b.overflow = true
}
}
// size returns the number of spans in the bucket.
func (b *bucket) size() int {
if b.overflow {
return len(b.buffer)
}
return b.nextIndex
}
// span returns the ith span in the bucket.
func (b *bucket) span(i int) *SpanData {
if !b.overflow {
return b.buffer[i]
}
if i < len(b.buffer)-b.nextIndex {
return b.buffer[b.nextIndex+i]
}
return b.buffer[b.nextIndex+i-len(b.buffer)]
}
// resize changes the size of the bucket to n, keeping up to n existing spans.
func (b *bucket) resize(n int) {
cur := b.size()
newBuffer := make([]*SpanData, n)
if cur < n {
for i := 0; i < cur; i++ {
newBuffer[i] = b.span(i)
}
b.buffer = newBuffer
b.nextIndex = cur
b.overflow = false
return
}
for i := 0; i < n; i++ {
newBuffer[i] = b.span(i + cur - n)
}
b.buffer = newBuffer
b.nextIndex = 0
b.overflow = true
}
// latencyBucket returns the appropriate bucket number for a given latency.
func latencyBucket(latency time.Duration) int {
i := 0
for i < len(defaultLatencies) && latency >= defaultLatencies[i] {
i++
}
return i
}
// latencyBucketBounds returns the lower and upper bounds for a latency bucket
// number.
//
// The lower bound is inclusive, the upper bound is exclusive (except for the
// last bucket.)
func latencyBucketBounds(index int) (lower time.Duration, upper time.Duration) {
if index == 0 {
return 0, defaultLatencies[index]
}
if index == len(defaultLatencies) {
return defaultLatencies[index-1], 1<<63 - 1
}
return defaultLatencies[index-1], defaultLatencies[index]
}

306
vendor/go.opencensus.io/trace/spanstore.go generated vendored Normal file
View File

@ -0,0 +1,306 @@
// 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()
}

37
vendor/go.opencensus.io/trace/status_codes.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2018, 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
// Status codes for use with Span.SetStatus. These correspond to the status
// codes used by gRPC defined here: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
const (
StatusCodeOK = 0
StatusCodeCancelled = 1
StatusCodeUnknown = 2
StatusCodeInvalidArgument = 3
StatusCodeDeadlineExceeded = 4
StatusCodeNotFound = 5
StatusCodeAlreadyExists = 6
StatusCodePermissionDenied = 7
StatusCodeResourceExhausted = 8
StatusCodeFailedPrecondition = 9
StatusCodeAborted = 10
StatusCodeOutOfRange = 11
StatusCodeUnimplemented = 12
StatusCodeInternal = 13
StatusCodeUnavailable = 14
StatusCodeDataLoss = 15
StatusCodeUnauthenticated = 16
)

598
vendor/go.opencensus.io/trace/trace.go generated vendored Normal file
View File

@ -0,0 +1,598 @@
// 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 (
"context"
crand "crypto/rand"
"encoding/binary"
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
"go.opencensus.io/internal"
"go.opencensus.io/trace/tracestate"
)
// Span represents a span of a trace. It has an associated SpanContext, and
// stores data accumulated while the span is active.
//
// Ideally users should interact with Spans by calling the functions in this
// package that take a Context parameter.
type Span struct {
// data contains information recorded about the span.
//
// It will be non-nil if we are exporting the span or recording events for it.
// Otherwise, data is nil, and the Span is simply a carrier for the
// SpanContext, so that the trace ID is propagated.
data *SpanData
mu sync.Mutex // protects the contents of *data (but not the pointer value.)
spanContext SpanContext
// lruAttributes are capped at configured limit. When the capacity is reached an oldest entry
// is removed to create room for a new entry.
lruAttributes *lruMap
// annotations are stored in FIFO queue capped by configured limit.
annotations *evictedQueue
// messageEvents are stored in FIFO queue capped by configured limit.
messageEvents *evictedQueue
// links are stored in FIFO queue capped by configured limit.
links *evictedQueue
// spanStore is the spanStore this span belongs to, if any, otherwise it is nil.
*spanStore
endOnce sync.Once
executionTracerTaskEnd func() // ends the execution tracer span
}
// IsRecordingEvents returns true if events are being recorded for this span.
// Use this check to avoid computing expensive annotations when they will never
// be used.
func (s *Span) IsRecordingEvents() bool {
if s == nil {
return false
}
return s.data != nil
}
// TraceOptions contains options associated with a trace span.
type TraceOptions uint32
// IsSampled returns true if the span will be exported.
func (sc SpanContext) IsSampled() bool {
return sc.TraceOptions.IsSampled()
}
// setIsSampled sets the TraceOptions bit that determines whether the span will be exported.
func (sc *SpanContext) setIsSampled(sampled bool) {
if sampled {
sc.TraceOptions |= 1
} else {
sc.TraceOptions &= ^TraceOptions(1)
}
}
// IsSampled returns true if the span will be exported.
func (t TraceOptions) IsSampled() bool {
return t&1 == 1
}
// SpanContext contains the state that must propagate across process boundaries.
//
// SpanContext is not an implementation of context.Context.
// TODO: add reference to external Census docs for SpanContext.
type SpanContext struct {
TraceID TraceID
SpanID SpanID
TraceOptions TraceOptions
Tracestate *tracestate.Tracestate
}
type contextKey struct{}
// FromContext returns the Span stored in a context, or nil if there isn't one.
func FromContext(ctx context.Context) *Span {
s, _ := ctx.Value(contextKey{}).(*Span)
return s
}
// NewContext returns a new context with the given Span attached.
func NewContext(parent context.Context, s *Span) context.Context {
return context.WithValue(parent, contextKey{}, s)
}
// All available span kinds. Span kind must be either one of these values.
const (
SpanKindUnspecified = iota
SpanKindServer
SpanKindClient
)
// StartOptions contains options concerning how a span is started.
type StartOptions struct {
// Sampler to consult for this Span. If provided, it is always consulted.
//
// If not provided, then the behavior differs based on whether
// the parent of this Span is remote, local, or there is no parent.
// In the case of a remote parent or no parent, the
// default sampler (see Config) will be consulted. Otherwise,
// when there is a non-remote parent, no new sampling decision will be made:
// we will preserve the sampling of the parent.
Sampler Sampler
// SpanKind represents the kind of a span. If none is set,
// SpanKindUnspecified is used.
SpanKind int
}
// StartOption apply changes to StartOptions.
type StartOption func(*StartOptions)
// WithSpanKind makes new spans to be created with the given kind.
func WithSpanKind(spanKind int) StartOption {
return func(o *StartOptions) {
o.SpanKind = spanKind
}
}
// WithSampler makes new spans to be be created with a custom sampler.
// Otherwise, the global sampler is used.
func WithSampler(sampler Sampler) StartOption {
return func(o *StartOptions) {
o.Sampler = sampler
}
}
// StartSpan starts a new child span of the current span in the context. If
// there is no span in the context, creates a new trace and span.
//
// Returned context contains the newly created span. You can use it to
// propagate the returned span in process.
func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, *Span) {
var opts StartOptions
var parent SpanContext
if p := FromContext(ctx); p != nil {
p.addChild()
parent = p.spanContext
}
for _, op := range o {
op(&opts)
}
span := startSpanInternal(name, parent != SpanContext{}, parent, false, opts)
ctx, end := startExecutionTracerTask(ctx, name)
span.executionTracerTaskEnd = end
return NewContext(ctx, span), span
}
// StartSpanWithRemoteParent starts a new child span of the span from the given parent.
//
// If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is
// preferred for cases where the parent is propagated via an incoming request.
//
// Returned context contains the newly created span. You can use it to
// propagate the returned span in process.
func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, *Span) {
var opts StartOptions
for _, op := range o {
op(&opts)
}
span := startSpanInternal(name, parent != SpanContext{}, parent, true, opts)
ctx, end := startExecutionTracerTask(ctx, name)
span.executionTracerTaskEnd = end
return NewContext(ctx, span), span
}
func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *Span {
span := &Span{}
span.spanContext = parent
cfg := config.Load().(*Config)
if !hasParent {
span.spanContext.TraceID = cfg.IDGenerator.NewTraceID()
}
span.spanContext.SpanID = cfg.IDGenerator.NewSpanID()
sampler := cfg.DefaultSampler
if !hasParent || remoteParent || o.Sampler != nil {
// If this span is the child of a local span and no Sampler is set in the
// options, keep the parent's TraceOptions.
//
// Otherwise, consult the Sampler in the options if it is non-nil, otherwise
// the default sampler.
if o.Sampler != nil {
sampler = o.Sampler
}
span.spanContext.setIsSampled(sampler(SamplingParameters{
ParentContext: parent,
TraceID: span.spanContext.TraceID,
SpanID: span.spanContext.SpanID,
Name: name,
HasRemoteParent: remoteParent}).Sample)
}
if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() {
return span
}
span.data = &SpanData{
SpanContext: span.spanContext,
StartTime: time.Now(),
SpanKind: o.SpanKind,
Name: name,
HasRemoteParent: remoteParent,
}
span.lruAttributes = newLruMap(cfg.MaxAttributesPerSpan)
span.annotations = newEvictedQueue(cfg.MaxAnnotationEventsPerSpan)
span.messageEvents = newEvictedQueue(cfg.MaxMessageEventsPerSpan)
span.links = newEvictedQueue(cfg.MaxLinksPerSpan)
if hasParent {
span.data.ParentSpanID = parent.SpanID
}
if internal.LocalSpanStoreEnabled {
var ss *spanStore
ss = spanStoreForNameCreateIfNew(name)
if ss != nil {
span.spanStore = ss
ss.add(span)
}
}
return span
}
// End ends the span.
func (s *Span) End() {
if s == nil {
return
}
if s.executionTracerTaskEnd != nil {
s.executionTracerTaskEnd()
}
if !s.IsRecordingEvents() {
return
}
s.endOnce.Do(func() {
exp, _ := exporters.Load().(exportersMap)
mustExport := s.spanContext.IsSampled() && len(exp) > 0
if s.spanStore != nil || mustExport {
sd := s.makeSpanData()
sd.EndTime = internal.MonotonicEndTime(sd.StartTime)
if s.spanStore != nil {
s.spanStore.finished(s, sd)
}
if mustExport {
for e := range exp {
e.ExportSpan(sd)
}
}
}
})
}
// makeSpanData produces a SpanData representing the current state of the Span.
// It requires that s.data is non-nil.
func (s *Span) makeSpanData() *SpanData {
var sd SpanData
s.mu.Lock()
sd = *s.data
if s.lruAttributes.len() > 0 {
sd.Attributes = s.lruAttributesToAttributeMap()
sd.DroppedAttributeCount = s.lruAttributes.droppedCount
}
if len(s.annotations.queue) > 0 {
sd.Annotations = s.interfaceArrayToAnnotationArray()
sd.DroppedAnnotationCount = s.annotations.droppedCount
}
if len(s.messageEvents.queue) > 0 {
sd.MessageEvents = s.interfaceArrayToMessageEventArray()
sd.DroppedMessageEventCount = s.messageEvents.droppedCount
}
if len(s.links.queue) > 0 {
sd.Links = s.interfaceArrayToLinksArray()
sd.DroppedLinkCount = s.links.droppedCount
}
s.mu.Unlock()
return &sd
}
// SpanContext returns the SpanContext of the span.
func (s *Span) SpanContext() SpanContext {
if s == nil {
return SpanContext{}
}
return s.spanContext
}
// SetName sets the name of the span, if it is recording events.
func (s *Span) SetName(name string) {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.data.Name = name
s.mu.Unlock()
}
// SetStatus sets the status of the span, if it is recording events.
func (s *Span) SetStatus(status Status) {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.data.Status = status
s.mu.Unlock()
}
func (s *Span) interfaceArrayToLinksArray() []Link {
linksArr := make([]Link, 0)
for _, value := range s.links.queue {
linksArr = append(linksArr, value.(Link))
}
return linksArr
}
func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent {
messageEventArr := make([]MessageEvent, 0)
for _, value := range s.messageEvents.queue {
messageEventArr = append(messageEventArr, value.(MessageEvent))
}
return messageEventArr
}
func (s *Span) interfaceArrayToAnnotationArray() []Annotation {
annotationArr := make([]Annotation, 0)
for _, value := range s.annotations.queue {
annotationArr = append(annotationArr, value.(Annotation))
}
return annotationArr
}
func (s *Span) lruAttributesToAttributeMap() map[string]interface{} {
attributes := make(map[string]interface{})
for _, key := range s.lruAttributes.keys() {
value, ok := s.lruAttributes.get(key)
if ok {
keyStr := key.(string)
attributes[keyStr] = value
}
}
return attributes
}
func (s *Span) copyToCappedAttributes(attributes []Attribute) {
for _, a := range attributes {
s.lruAttributes.add(a.key, a.value)
}
}
func (s *Span) addChild() {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.data.ChildSpanCount++
s.mu.Unlock()
}
// AddAttributes sets attributes in the span.
//
// Existing attributes whose keys appear in the attributes parameter are overwritten.
func (s *Span) AddAttributes(attributes ...Attribute) {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.copyToCappedAttributes(attributes)
s.mu.Unlock()
}
// copyAttributes copies a slice of Attributes into a map.
func copyAttributes(m map[string]interface{}, attributes []Attribute) {
for _, a := range attributes {
m[a.key] = a.value
}
}
func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) {
now := time.Now()
msg := fmt.Sprintf(format, a...)
var m map[string]interface{}
s.mu.Lock()
if len(attributes) != 0 {
m = make(map[string]interface{})
copyAttributes(m, attributes)
}
s.annotations.add(Annotation{
Time: now,
Message: msg,
Attributes: m,
})
s.mu.Unlock()
}
func (s *Span) printStringInternal(attributes []Attribute, str string) {
now := time.Now()
var a map[string]interface{}
s.mu.Lock()
if len(attributes) != 0 {
a = make(map[string]interface{})
copyAttributes(a, attributes)
}
s.annotations.add(Annotation{
Time: now,
Message: str,
Attributes: a,
})
s.mu.Unlock()
}
// Annotate adds an annotation with attributes.
// Attributes can be nil.
func (s *Span) Annotate(attributes []Attribute, str string) {
if !s.IsRecordingEvents() {
return
}
s.printStringInternal(attributes, str)
}
// Annotatef adds an annotation with attributes.
func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{}) {
if !s.IsRecordingEvents() {
return
}
s.lazyPrintfInternal(attributes, format, a...)
}
// AddMessageSendEvent adds a message send event to the span.
//
// messageID is an identifier for the message, which is recommended to be
// unique in this span and the same between the send event and the receive
// event (this allows to identify a message between the sender and receiver).
// For example, this could be a sequence id.
func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
if !s.IsRecordingEvents() {
return
}
now := time.Now()
s.mu.Lock()
s.messageEvents.add(MessageEvent{
Time: now,
EventType: MessageEventTypeSent,
MessageID: messageID,
UncompressedByteSize: uncompressedByteSize,
CompressedByteSize: compressedByteSize,
})
s.mu.Unlock()
}
// AddMessageReceiveEvent adds a message receive event to the span.
//
// messageID is an identifier for the message, which is recommended to be
// unique in this span and the same between the send event and the receive
// event (this allows to identify a message between the sender and receiver).
// For example, this could be a sequence id.
func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
if !s.IsRecordingEvents() {
return
}
now := time.Now()
s.mu.Lock()
s.messageEvents.add(MessageEvent{
Time: now,
EventType: MessageEventTypeRecv,
MessageID: messageID,
UncompressedByteSize: uncompressedByteSize,
CompressedByteSize: compressedByteSize,
})
s.mu.Unlock()
}
// AddLink adds a link to the span.
func (s *Span) AddLink(l Link) {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.links.add(l)
s.mu.Unlock()
}
func (s *Span) String() string {
if s == nil {
return "<nil>"
}
if s.data == nil {
return fmt.Sprintf("span %s", s.spanContext.SpanID)
}
s.mu.Lock()
str := fmt.Sprintf("span %s %q", s.spanContext.SpanID, s.data.Name)
s.mu.Unlock()
return str
}
var config atomic.Value // access atomically
func init() {
gen := &defaultIDGenerator{}
// initialize traceID and spanID generators.
var rngSeed int64
for _, p := range []interface{}{
&rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc,
} {
binary.Read(crand.Reader, binary.LittleEndian, p)
}
gen.traceIDRand = rand.New(rand.NewSource(rngSeed))
gen.spanIDInc |= 1
config.Store(&Config{
DefaultSampler: ProbabilitySampler(defaultSamplingProbability),
IDGenerator: gen,
MaxAttributesPerSpan: DefaultMaxAttributesPerSpan,
MaxAnnotationEventsPerSpan: DefaultMaxAnnotationEventsPerSpan,
MaxMessageEventsPerSpan: DefaultMaxMessageEventsPerSpan,
MaxLinksPerSpan: DefaultMaxLinksPerSpan,
})
}
type defaultIDGenerator struct {
sync.Mutex
// Please keep these as the first fields
// so that these 8 byte fields will be aligned on addresses
// divisible by 8, on both 32-bit and 64-bit machines when
// performing atomic increments and accesses.
// See:
// * https://github.com/census-instrumentation/opencensus-go/issues/587
// * https://github.com/census-instrumentation/opencensus-go/issues/865
// * https://golang.org/pkg/sync/atomic/#pkg-note-BUG
nextSpanID uint64
spanIDInc uint64
traceIDAdd [2]uint64
traceIDRand *rand.Rand
}
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence.
func (gen *defaultIDGenerator) NewSpanID() [8]byte {
var id uint64
for id == 0 {
id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc)
}
var sid [8]byte
binary.LittleEndian.PutUint64(sid[:], id)
return sid
}
// NewTraceID returns a non-zero trace ID from a randomly-chosen sequence.
// mu should be held while this function is called.
func (gen *defaultIDGenerator) NewTraceID() [16]byte {
var tid [16]byte
// Construct the trace ID from two outputs of traceIDRand, with a constant
// added to each half for additional entropy.
gen.Lock()
binary.LittleEndian.PutUint64(tid[0:8], gen.traceIDRand.Uint64()+gen.traceIDAdd[0])
binary.LittleEndian.PutUint64(tid[8:16], gen.traceIDRand.Uint64()+gen.traceIDAdd[1])
gen.Unlock()
return tid
}

32
vendor/go.opencensus.io/trace/trace_go11.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2018, 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.
// +build go1.11
package trace
import (
"context"
t "runtime/trace"
)
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
if !t.IsEnabled() {
// Avoid additional overhead if
// runtime/trace is not enabled.
return ctx, func() {}
}
nctx, task := t.NewTask(ctx, name)
return nctx, task.End
}

25
vendor/go.opencensus.io/trace/trace_nongo11.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2018, 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.
// +build !go1.11
package trace
import (
"context"
)
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
return ctx, func() {}
}

147
vendor/go.opencensus.io/trace/tracestate/tracestate.go generated vendored Normal file
View File

@ -0,0 +1,147 @@
// Copyright 2018, 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 tracestate implements support for the Tracestate header of the
// W3C TraceContext propagation format.
package tracestate
import (
"fmt"
"regexp"
)
const (
keyMaxSize = 256
valueMaxSize = 256
maxKeyValuePairs = 32
)
const (
keyWithoutVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,255}`
keyWithVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}`
keyFormat = `(` + keyWithoutVendorFormat + `)|(` + keyWithVendorFormat + `)`
valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]`
)
var keyValidationRegExp = regexp.MustCompile(`^(` + keyFormat + `)$`)
var valueValidationRegExp = regexp.MustCompile(`^(` + valueFormat + `)$`)
// Tracestate represents tracing-system specific context in a list of key-value pairs. Tracestate allows different
// vendors propagate additional information and inter-operate with their legacy Id formats.
type Tracestate struct {
entries []Entry
}
// Entry represents one key-value pair in a list of key-value pair of Tracestate.
type Entry struct {
// Key is an opaque string up to 256 characters printable. It MUST begin with a lowercase letter,
// and can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and
// forward slashes /.
Key string
// Value is an opaque string up to 256 characters printable ASCII RFC0020 characters (i.e., the
// range 0x20 to 0x7E) except comma , and =.
Value string
}
// Entries returns a slice of Entry.
func (ts *Tracestate) Entries() []Entry {
if ts == nil {
return nil
}
return ts.entries
}
func (ts *Tracestate) remove(key string) *Entry {
for index, entry := range ts.entries {
if entry.Key == key {
ts.entries = append(ts.entries[:index], ts.entries[index+1:]...)
return &entry
}
}
return nil
}
func (ts *Tracestate) add(entries []Entry) error {
for _, entry := range entries {
ts.remove(entry.Key)
}
if len(ts.entries)+len(entries) > maxKeyValuePairs {
return fmt.Errorf("adding %d key-value pairs to current %d pairs exceeds the limit of %d",
len(entries), len(ts.entries), maxKeyValuePairs)
}
ts.entries = append(entries, ts.entries...)
return nil
}
func isValid(entry Entry) bool {
return keyValidationRegExp.MatchString(entry.Key) &&
valueValidationRegExp.MatchString(entry.Value)
}
func containsDuplicateKey(entries ...Entry) (string, bool) {
keyMap := make(map[string]int)
for _, entry := range entries {
if _, ok := keyMap[entry.Key]; ok {
return entry.Key, true
}
keyMap[entry.Key] = 1
}
return "", false
}
func areEntriesValid(entries ...Entry) (*Entry, bool) {
for _, entry := range entries {
if !isValid(entry) {
return &entry, false
}
}
return nil, true
}
// New creates a Tracestate object from a parent and/or entries (key-value pair).
// Entries from the parent are copied if present. The entries passed to this function
// are inserted in front of those copied from the parent. If an entry copied from the
// parent contains the same key as one of the entry in entries then the entry copied
// from the parent is removed. See add func.
//
// An error is returned with nil Tracestate if
// 1. one or more entry in entries is invalid.
// 2. two or more entries in the input entries have the same key.
// 3. the number of entries combined from the parent and the input entries exceeds maxKeyValuePairs.
// (duplicate entry is counted only once).
func New(parent *Tracestate, entries ...Entry) (*Tracestate, error) {
if parent == nil && len(entries) == 0 {
return nil, nil
}
if entry, ok := areEntriesValid(entries...); !ok {
return nil, fmt.Errorf("key-value pair {%s, %s} is invalid", entry.Key, entry.Value)
}
if key, duplicate := containsDuplicateKey(entries...); duplicate {
return nil, fmt.Errorf("contains duplicate keys (%s)", key)
}
tracestate := Tracestate{}
if parent != nil && len(parent.entries) > 0 {
tracestate.entries = append([]Entry{}, parent.entries...)
}
err := tracestate.add(entries)
if err != nil {
return nil, err
}
return &tracestate, nil
}