encoder.go 4.5 KB

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