Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.4 KiB
150 lines
4.4 KiB
// 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 label // import "go.opentelemetry.io/otel/label" |
|
|
|
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 label.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 |
|
}
|
|
|