github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.opentelemetry.io/otel/attribute/encoder.go (about)

     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 attribute // import "go.opentelemetry.io/otel/attribute"
    16  
    17  import (
    18  	"bytes"
    19  	"sync"
    20  	"sync/atomic"
    21  )
    22  
    23  type (
    24  	// Encoder is a mechanism for serializing an attribute set into a specific
    25  	// string representation that supports caching, to avoid repeated
    26  	// serialization. An example could be an exporter encoding the attribute
    27  	// set into a wire representation.
    28  	Encoder interface {
    29  		// Encode returns the serialized encoding of the attribute set using
    30  		// its Iterator. This result may be cached by a attribute.Set.
    31  		Encode(iterator Iterator) string
    32  
    33  		// ID returns a value that is unique for each class of attribute
    34  		// encoder. Attribute encoders allocate these using `NewEncoderID`.
    35  		ID() EncoderID
    36  	}
    37  
    38  	// EncoderID is used to identify distinct Encoder
    39  	// implementations, for caching encoded results.
    40  	EncoderID struct {
    41  		value uint64
    42  	}
    43  
    44  	// defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of
    45  	// allocations used in encoding attributes. This implementation encodes a
    46  	// comma-separated list of key=value, with '/'-escaping of '=', ',', and
    47  	// '\'.
    48  	defaultAttrEncoder struct {
    49  		// pool is a pool of attribute set builders. The buffers in this pool
    50  		// grow to a size that most attribute encodings will not allocate new
    51  		// memory.
    52  		pool sync.Pool // *bytes.Buffer
    53  	}
    54  )
    55  
    56  // escapeChar is used to ensure uniqueness of the attribute encoding where
    57  // keys or values contain either '=' or ','.  Since there is no parser needed
    58  // for this encoding and its only requirement is to be unique, this choice is
    59  // arbitrary.  Users will see these in some exporters (e.g., stdout), so the
    60  // backslash ('\') is used as a conventional choice.
    61  const escapeChar = '\\'
    62  
    63  var (
    64  	_ Encoder = &defaultAttrEncoder{}
    65  
    66  	// encoderIDCounter is for generating IDs for other attribute encoders.
    67  	encoderIDCounter uint64
    68  
    69  	defaultEncoderOnce     sync.Once
    70  	defaultEncoderID       = NewEncoderID()
    71  	defaultEncoderInstance *defaultAttrEncoder
    72  )
    73  
    74  // NewEncoderID returns a unique attribute encoder ID. It should be called
    75  // once per each type of attribute encoder. Preferably in init() or in var
    76  // definition.
    77  func NewEncoderID() EncoderID {
    78  	return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
    79  }
    80  
    81  // DefaultEncoder returns an attribute encoder that encodes attributes in such
    82  // a way that each escaped attribute's key is followed by an equal sign and
    83  // then by an escaped attribute's value. All key-value pairs are separated by
    84  // a comma.
    85  //
    86  // Escaping is done by prepending a backslash before either a backslash, equal
    87  // sign or a comma.
    88  func DefaultEncoder() Encoder {
    89  	defaultEncoderOnce.Do(func() {
    90  		defaultEncoderInstance = &defaultAttrEncoder{
    91  			pool: sync.Pool{
    92  				New: func() interface{} {
    93  					return &bytes.Buffer{}
    94  				},
    95  			},
    96  		}
    97  	})
    98  	return defaultEncoderInstance
    99  }
   100  
   101  // Encode is a part of an implementation of the AttributeEncoder interface.
   102  func (d *defaultAttrEncoder) Encode(iter Iterator) string {
   103  	buf := d.pool.Get().(*bytes.Buffer)
   104  	defer d.pool.Put(buf)
   105  	buf.Reset()
   106  
   107  	for iter.Next() {
   108  		i, keyValue := iter.IndexedAttribute()
   109  		if i > 0 {
   110  			_, _ = buf.WriteRune(',')
   111  		}
   112  		copyAndEscape(buf, string(keyValue.Key))
   113  
   114  		_, _ = buf.WriteRune('=')
   115  
   116  		if keyValue.Value.Type() == STRING {
   117  			copyAndEscape(buf, keyValue.Value.AsString())
   118  		} else {
   119  			_, _ = buf.WriteString(keyValue.Value.Emit())
   120  		}
   121  	}
   122  	return buf.String()
   123  }
   124  
   125  // ID is a part of an implementation of the AttributeEncoder interface.
   126  func (*defaultAttrEncoder) ID() EncoderID {
   127  	return defaultEncoderID
   128  }
   129  
   130  // copyAndEscape escapes `=`, `,` and its own escape character (`\`),
   131  // making the default encoding unique.
   132  func copyAndEscape(buf *bytes.Buffer, val string) {
   133  	for _, ch := range val {
   134  		switch ch {
   135  		case '=', ',', escapeChar:
   136  			_, _ = buf.WriteRune(escapeChar)
   137  		}
   138  		_, _ = buf.WriteRune(ch)
   139  	}
   140  }
   141  
   142  // Valid returns true if this encoder ID was allocated by
   143  // `NewEncoderID`.  Invalid encoder IDs will not be cached.
   144  func (id EncoderID) Valid() bool {
   145  	return id.value != 0
   146  }