encoder.go 4.0 KB

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