encoder.go 4.4 KB

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