id.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package telemetry
   5  
   6  import (
   7  	"encoding/hex"
   8  	"errors"
   9  	"fmt"
  10  )
  11  
  12  const (
  13  	traceIDSize = 16
  14  	spanIDSize  = 8
  15  )
  16  
  17  // TraceID is a custom data type that is used for all trace IDs.
  18  type TraceID [traceIDSize]byte
  19  
  20  // String returns the hex string representation form of a TraceID.
  21  func (tid TraceID) String() string {
  22  	return hex.EncodeToString(tid[:])
  23  }
  24  
  25  // IsEmpty returns false if id contains at least one non-zero byte.
  26  func (tid TraceID) IsEmpty() bool {
  27  	return tid == [traceIDSize]byte{}
  28  }
  29  
  30  // MarshalJSON converts the trace ID into a hex string enclosed in quotes.
  31  func (tid TraceID) MarshalJSON() ([]byte, error) {
  32  	if tid.IsEmpty() {
  33  		return []byte(`""`), nil
  34  	}
  35  	return marshalJSON(tid[:])
  36  }
  37  
  38  // UnmarshalJSON inflates the trace ID from hex string, possibly enclosed in
  39  // quotes.
  40  func (tid *TraceID) UnmarshalJSON(data []byte) error {
  41  	*tid = [traceIDSize]byte{}
  42  	return unmarshalJSON(tid[:], data)
  43  }
  44  
  45  // SpanID is a custom data type that is used for all span IDs.
  46  type SpanID [spanIDSize]byte
  47  
  48  // String returns the hex string representation form of a SpanID.
  49  func (sid SpanID) String() string {
  50  	return hex.EncodeToString(sid[:])
  51  }
  52  
  53  // IsEmpty returns true if the span ID contains at least one non-zero byte.
  54  func (sid SpanID) IsEmpty() bool {
  55  	return sid == [spanIDSize]byte{}
  56  }
  57  
  58  // MarshalJSON converts span ID into a hex string enclosed in quotes.
  59  func (sid SpanID) MarshalJSON() ([]byte, error) {
  60  	if sid.IsEmpty() {
  61  		return []byte(`""`), nil
  62  	}
  63  	return marshalJSON(sid[:])
  64  }
  65  
  66  // UnmarshalJSON decodes span ID from hex string, possibly enclosed in quotes.
  67  func (sid *SpanID) UnmarshalJSON(data []byte) error {
  68  	*sid = [spanIDSize]byte{}
  69  	return unmarshalJSON(sid[:], data)
  70  }
  71  
  72  // marshalJSON converts id into a hex string enclosed in quotes.
  73  func marshalJSON(id []byte) ([]byte, error) {
  74  	// Plus 2 quote chars at the start and end.
  75  	hexLen := hex.EncodedLen(len(id)) + 2
  76  
  77  	b := make([]byte, hexLen)
  78  	hex.Encode(b[1:hexLen-1], id)
  79  	b[0], b[hexLen-1] = '"', '"'
  80  
  81  	return b, nil
  82  }
  83  
  84  // unmarshalJSON inflates trace id from hex string, possibly enclosed in quotes.
  85  func unmarshalJSON(dst, src []byte) error {
  86  	if l := len(src); l >= 2 && src[0] == '"' && src[l-1] == '"' {
  87  		src = src[1 : l-1]
  88  	}
  89  	nLen := len(src)
  90  	if nLen == 0 {
  91  		return nil
  92  	}
  93  
  94  	if len(dst) != hex.DecodedLen(nLen) {
  95  		return errors.New("invalid length for ID")
  96  	}
  97  
  98  	_, err := hex.Decode(dst, src)
  99  	if err != nil {
 100  		return fmt.Errorf("cannot unmarshal ID from string '%s': %w", string(src), err)
 101  	}
 102  	return nil
 103  }
 104