identity.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package tracing
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. // 16 bytes for tracing ID, 8 bytes for span ID and 1 byte for flags
  11. IdentityLength = 16 + 8 + 1
  12. )
  13. type Identity struct {
  14. // Based on https://www.jaegertracing.io/docs/1.36/client-libraries/#value
  15. // parent span ID is always 0 for our case
  16. traceIDUpper uint64
  17. traceIDLower uint64
  18. spanID uint64
  19. flags uint8
  20. }
  21. // TODO: TUN-6604 Remove this. To reconstruct into Jaeger propagation format, convert tracingContext to tracing.Identity
  22. func (tc *Identity) String() string {
  23. return fmt.Sprintf("%016x%016x:%x:0:%x", tc.traceIDUpper, tc.traceIDLower, tc.spanID, tc.flags)
  24. }
  25. func (tc *Identity) MarshalBinary() ([]byte, error) {
  26. buf := bytes.NewBuffer(make([]byte, 0, IdentityLength))
  27. for _, field := range []interface{}{
  28. tc.traceIDUpper,
  29. tc.traceIDLower,
  30. tc.spanID,
  31. tc.flags,
  32. } {
  33. if err := binary.Write(buf, binary.BigEndian, field); err != nil {
  34. return nil, err
  35. }
  36. }
  37. return buf.Bytes(), nil
  38. }
  39. func (tc *Identity) UnmarshalBinary(data []byte) error {
  40. if len(data) < IdentityLength {
  41. return fmt.Errorf("expect tracingContext to have at least %d bytes, got %d", IdentityLength, len(data))
  42. }
  43. buf := bytes.NewBuffer(data)
  44. for _, field := range []interface{}{
  45. &tc.traceIDUpper,
  46. &tc.traceIDLower,
  47. &tc.spanID,
  48. &tc.flags,
  49. } {
  50. if err := binary.Read(buf, binary.BigEndian, field); err != nil {
  51. return err
  52. }
  53. }
  54. return nil
  55. }
  56. func NewIdentity(trace string) (*Identity, error) {
  57. parts := strings.Split(trace, separator)
  58. if len(parts) != 4 {
  59. return nil, fmt.Errorf("trace '%s' doesn't have exactly 4 parts separated by %s", trace, separator)
  60. }
  61. const base = 16
  62. tracingID, err := padTracingID(parts[0])
  63. if err != nil {
  64. return nil, err
  65. }
  66. traceIDUpper, err := strconv.ParseUint(tracingID[:16], base, 64)
  67. if err != nil {
  68. return nil, fmt.Errorf("failed to parse first 16 bytes of tracing ID as uint64, err: %w", err)
  69. }
  70. traceIDLower, err := strconv.ParseUint(tracingID[16:], base, 64)
  71. if err != nil {
  72. return nil, fmt.Errorf("failed to parse last 16 bytes of tracing ID as uint64, err: %w", err)
  73. }
  74. spanID, err := strconv.ParseUint(parts[1], base, 64)
  75. if err != nil {
  76. return nil, fmt.Errorf("failed to parse span ID as uint64, err: %w", err)
  77. }
  78. flags, err := strconv.ParseUint(parts[3], base, 8)
  79. if err != nil {
  80. return nil, fmt.Errorf("failed to parse flag as uint8, err: %w", err)
  81. }
  82. return &Identity{
  83. traceIDUpper: traceIDUpper,
  84. traceIDLower: traceIDLower,
  85. spanID: spanID,
  86. flags: uint8(flags),
  87. }, nil
  88. }
  89. func padTracingID(tracingID string) (string, error) {
  90. if len(tracingID) == 0 {
  91. return "", fmt.Errorf("missing tracing ID")
  92. }
  93. if len(tracingID) == traceID128bitsWidth {
  94. return tracingID, nil
  95. }
  96. // Correctly left pad the trace to a length of 32
  97. left := traceID128bitsWidth - len(tracingID)
  98. paddedTracingID := strings.Repeat("0", left) + tracingID
  99. return paddedTracingID, nil
  100. }