123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- // 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
- }
|