151 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright The OpenTelemetry 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 attribute // import "go.opentelemetry.io/otel/attribute"
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"sync"
 | |
| 	"sync/atomic"
 | |
| )
 | |
| 
 | |
| type (
 | |
| 	// Encoder is a mechanism for serializing a label set into a
 | |
| 	// specific string representation that supports caching, to
 | |
| 	// avoid repeated serialization. An example could be an
 | |
| 	// exporter encoding the label set into a wire representation.
 | |
| 	Encoder interface {
 | |
| 		// Encode returns the serialized encoding of the label
 | |
| 		// set using its Iterator.  This result may be cached
 | |
| 		// by a attribute.Set.
 | |
| 		Encode(iterator Iterator) string
 | |
| 
 | |
| 		// ID returns a value that is unique for each class of
 | |
| 		// label encoder.  Label encoders allocate these using
 | |
| 		// `NewEncoderID`.
 | |
| 		ID() EncoderID
 | |
| 	}
 | |
| 
 | |
| 	// EncoderID is used to identify distinct Encoder
 | |
| 	// implementations, for caching encoded results.
 | |
| 	EncoderID struct {
 | |
| 		value uint64
 | |
| 	}
 | |
| 
 | |
| 	// defaultLabelEncoder uses a sync.Pool of buffers to reduce
 | |
| 	// the number of allocations used in encoding labels.  This
 | |
| 	// implementation encodes a comma-separated list of key=value,
 | |
| 	// with '/'-escaping of '=', ',', and '\'.
 | |
| 	defaultLabelEncoder struct {
 | |
| 		// pool is a pool of labelset builders.  The buffers in this
 | |
| 		// pool grow to a size that most label encodings will not
 | |
| 		// allocate new memory.
 | |
| 		pool sync.Pool // *bytes.Buffer
 | |
| 	}
 | |
| )
 | |
| 
 | |
| // escapeChar is used to ensure uniqueness of the label encoding where
 | |
| // keys or values contain either '=' or ','.  Since there is no parser
 | |
| // needed for this encoding and its only requirement is to be unique,
 | |
| // this choice is arbitrary.  Users will see these in some exporters
 | |
| // (e.g., stdout), so the backslash ('\') is used as a conventional choice.
 | |
| const escapeChar = '\\'
 | |
| 
 | |
| var (
 | |
| 	_ Encoder = &defaultLabelEncoder{}
 | |
| 
 | |
| 	// encoderIDCounter is for generating IDs for other label
 | |
| 	// encoders.
 | |
| 	encoderIDCounter uint64
 | |
| 
 | |
| 	defaultEncoderOnce     sync.Once
 | |
| 	defaultEncoderID       = NewEncoderID()
 | |
| 	defaultEncoderInstance *defaultLabelEncoder
 | |
| )
 | |
| 
 | |
| // NewEncoderID returns a unique label encoder ID. It should be
 | |
| // called once per each type of label encoder. Preferably in init() or
 | |
| // in var definition.
 | |
| func NewEncoderID() EncoderID {
 | |
| 	return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
 | |
| }
 | |
| 
 | |
| // DefaultEncoder returns a label encoder that encodes labels
 | |
| // in such a way that each escaped label's key is followed by an equal
 | |
| // sign and then by an escaped label's value. All key-value pairs are
 | |
| // separated by a comma.
 | |
| //
 | |
| // Escaping is done by prepending a backslash before either a
 | |
| // backslash, equal sign or a comma.
 | |
| func DefaultEncoder() Encoder {
 | |
| 	defaultEncoderOnce.Do(func() {
 | |
| 		defaultEncoderInstance = &defaultLabelEncoder{
 | |
| 			pool: sync.Pool{
 | |
| 				New: func() interface{} {
 | |
| 					return &bytes.Buffer{}
 | |
| 				},
 | |
| 			},
 | |
| 		}
 | |
| 	})
 | |
| 	return defaultEncoderInstance
 | |
| }
 | |
| 
 | |
| // Encode is a part of an implementation of the LabelEncoder
 | |
| // interface.
 | |
| func (d *defaultLabelEncoder) Encode(iter Iterator) string {
 | |
| 	buf := d.pool.Get().(*bytes.Buffer)
 | |
| 	defer d.pool.Put(buf)
 | |
| 	buf.Reset()
 | |
| 
 | |
| 	for iter.Next() {
 | |
| 		i, keyValue := iter.IndexedLabel()
 | |
| 		if i > 0 {
 | |
| 			_, _ = buf.WriteRune(',')
 | |
| 		}
 | |
| 		copyAndEscape(buf, string(keyValue.Key))
 | |
| 
 | |
| 		_, _ = buf.WriteRune('=')
 | |
| 
 | |
| 		if keyValue.Value.Type() == STRING {
 | |
| 			copyAndEscape(buf, keyValue.Value.AsString())
 | |
| 		} else {
 | |
| 			_, _ = buf.WriteString(keyValue.Value.Emit())
 | |
| 		}
 | |
| 	}
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| // ID is a part of an implementation of the LabelEncoder interface.
 | |
| func (*defaultLabelEncoder) ID() EncoderID {
 | |
| 	return defaultEncoderID
 | |
| }
 | |
| 
 | |
| // copyAndEscape escapes `=`, `,` and its own escape character (`\`),
 | |
| // making the default encoding unique.
 | |
| func copyAndEscape(buf *bytes.Buffer, val string) {
 | |
| 	for _, ch := range val {
 | |
| 		switch ch {
 | |
| 		case '=', ',', escapeChar:
 | |
| 			buf.WriteRune(escapeChar)
 | |
| 		}
 | |
| 		buf.WriteRune(ch)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Valid returns true if this encoder ID was allocated by
 | |
| // `NewEncoderID`.  Invalid encoder IDs will not be cached.
 | |
| func (id EncoderID) Valid() bool {
 | |
| 	return id.value != 0
 | |
| }
 |