github.com/kiali/kiali@v1.84.0/tracing/jaeger/model/ids.go (about)

     1  // Copyright (c) 2019 The Jaeger Authors.
     2  // Copyright (c) 2018 Uber Technologies, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  // Cloned from github.com/jaegertracing/jaeger/
    17  
    18  // nolint
    19  package model
    20  
    21  import (
    22  	"encoding/base64"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"strconv"
    26  )
    27  
    28  const (
    29  	// traceIDShortBytesLen indicates length of 64bit traceID when represented as list of bytes
    30  	traceIDShortBytesLen = 8
    31  	// traceIDLongBytesLen indicates length of 128bit traceID when represented as list of bytes
    32  	traceIDLongBytesLen = 16
    33  )
    34  
    35  // TraceID is a random 128bit identifier for a trace
    36  type TraceID struct {
    37  	Low  uint64 `json:"lo"`
    38  	High uint64 `json:"hi"`
    39  }
    40  
    41  // SpanID is a random 64bit identifier for a span
    42  type SpanID uint64
    43  
    44  // ------- TraceID -------
    45  
    46  // NewTraceID creates a new TraceID from two 64bit unsigned ints.
    47  func NewTraceID(high, low uint64) TraceID {
    48  	return TraceID{High: high, Low: low}
    49  }
    50  
    51  func (t TraceID) String() string {
    52  	if t.High == 0 {
    53  		return fmt.Sprintf("%016x", t.Low)
    54  	}
    55  	return fmt.Sprintf("%016x%016x", t.High, t.Low)
    56  }
    57  
    58  // TraceIDFromString creates a TraceID from a hexadecimal string
    59  func TraceIDFromString(s string) (TraceID, error) {
    60  	var hi, lo uint64
    61  	var err error
    62  	switch {
    63  	case len(s) > 32:
    64  		return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
    65  	case len(s) > 16:
    66  		hiLen := len(s) - 16
    67  		if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
    68  			return TraceID{}, err
    69  		}
    70  		if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
    71  			return TraceID{}, err
    72  		}
    73  	default:
    74  		if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
    75  			return TraceID{}, err
    76  		}
    77  	}
    78  	return TraceID{High: hi, Low: lo}, nil
    79  }
    80  
    81  // TraceIDFromBytes creates a TraceID from list of bytes
    82  func TraceIDFromBytes(data []byte) (TraceID, error) {
    83  	var t TraceID
    84  	switch {
    85  	case len(data) == traceIDLongBytesLen:
    86  		t.High = binary.BigEndian.Uint64(data[:traceIDShortBytesLen])
    87  		t.Low = binary.BigEndian.Uint64(data[traceIDShortBytesLen:])
    88  	case len(data) == traceIDShortBytesLen:
    89  		t.Low = binary.BigEndian.Uint64(data)
    90  	default:
    91  		return TraceID{}, fmt.Errorf("invalid length for TraceID")
    92  	}
    93  	return t, nil
    94  }
    95  
    96  // MarshalText is called by encoding/json, which we do not want people to use.
    97  func (t TraceID) MarshalText() ([]byte, error) {
    98  	return nil, fmt.Errorf("unsupported method TraceID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
    99  }
   100  
   101  // UnmarshalText is called by encoding/json, which we do not want people to use.
   102  func (t *TraceID) UnmarshalText(text []byte) error {
   103  	return fmt.Errorf("unsupported method TraceID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
   104  }
   105  
   106  // Size returns the size of this datum in protobuf. It is always 16 bytes.
   107  func (t *TraceID) Size() int {
   108  	return 16
   109  }
   110  
   111  // MarshalTo converts trace ID into a binary representation. Called by protobuf serialization.
   112  func (t *TraceID) MarshalTo(data []byte) (n int, err error) {
   113  	var b [16]byte
   114  	binary.BigEndian.PutUint64(b[:8], uint64(t.High))
   115  	binary.BigEndian.PutUint64(b[8:], uint64(t.Low))
   116  	return marshalBytes(data, b[:])
   117  }
   118  
   119  // Unmarshal inflates this trace ID from binary representation. Called by protobuf serialization.
   120  func (t *TraceID) Unmarshal(data []byte) error {
   121  	var err error
   122  	*t, err = TraceIDFromBytes(data)
   123  	return err
   124  }
   125  
   126  func marshalBytes(dst []byte, src []byte) (n int, err error) {
   127  	if len(dst) < len(src) {
   128  		return 0, fmt.Errorf("buffer is too short")
   129  	}
   130  	return copy(dst, src), nil
   131  }
   132  
   133  // MarshalJSON converts trace id into a base64 string enclosed in quotes.
   134  // Used by protobuf JSON serialization.
   135  // Example: {high:2, low:1} => "AAAAAAAAAAIAAAAAAAAAAQ==".
   136  func (t TraceID) MarshalJSON() ([]byte, error) {
   137  	var b [16]byte
   138  	t.MarshalTo(b[:]) // can only error on incorrect buffer size
   139  	s := make([]byte, 24+2)
   140  	base64.StdEncoding.Encode(s[1:25], b[:])
   141  	s[0], s[25] = '"', '"'
   142  	return s, nil
   143  }
   144  
   145  // UnmarshalJSON inflates trace id from base64 string, possibly enclosed in quotes.
   146  // User by protobuf JSON serialization.
   147  func (t *TraceID) UnmarshalJSON(data []byte) error {
   148  	s := string(data)
   149  	if l := len(s); l > 2 && s[0] == '"' && s[l-1] == '"' {
   150  		s = s[1 : l-1]
   151  	}
   152  	b, err := base64.StdEncoding.DecodeString(s)
   153  	if err != nil {
   154  		return fmt.Errorf("cannot unmarshal TraceID from string '%s': %v", string(data), err)
   155  	}
   156  	return t.Unmarshal(b)
   157  }
   158  
   159  // ------- SpanID -------
   160  
   161  // NewSpanID creates a new SpanID from a 64bit unsigned int.
   162  func NewSpanID(v uint64) SpanID {
   163  	return SpanID(v)
   164  }
   165  
   166  func (s SpanID) String() string {
   167  	return fmt.Sprintf("%016x", uint64(s))
   168  }
   169  
   170  // SpanIDFromString creates a SpanID from a hexadecimal string
   171  func SpanIDFromString(s string) (SpanID, error) {
   172  	if len(s) > 16 {
   173  		return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
   174  	}
   175  	id, err := strconv.ParseUint(s, 16, 64)
   176  	if err != nil {
   177  		return SpanID(0), err
   178  	}
   179  	return SpanID(id), nil
   180  }
   181  
   182  // SpanIDFromBytes creates a SpandID from list of bytes
   183  func SpanIDFromBytes(data []byte) (SpanID, error) {
   184  	if len(data) != traceIDShortBytesLen {
   185  		return SpanID(0), fmt.Errorf("invalid length for SpanID")
   186  	}
   187  	return NewSpanID(binary.BigEndian.Uint64(data)), nil
   188  }
   189  
   190  // MarshalText is called by encoding/json, which we do not want people to use.
   191  func (s SpanID) MarshalText() ([]byte, error) {
   192  	return nil, fmt.Errorf("unsupported method SpanID.MarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
   193  }
   194  
   195  // UnmarshalText is called by encoding/json, which we do not want people to use.
   196  func (s *SpanID) UnmarshalText(text []byte) error {
   197  	return fmt.Errorf("unsupported method SpanID.UnmarshalText; please use github.com/gogo/protobuf/jsonpb for marshalling")
   198  }
   199  
   200  // Size returns the size of this datum in protobuf. It is always 8 bytes.
   201  func (s *SpanID) Size() int {
   202  	return 8
   203  }
   204  
   205  // MarshalTo converts span ID into a binary representation. Called by protobuf serialization.
   206  func (s *SpanID) MarshalTo(data []byte) (n int, err error) {
   207  	var b [8]byte
   208  	binary.BigEndian.PutUint64(b[:], uint64(*s))
   209  	return marshalBytes(data, b[:])
   210  }
   211  
   212  // Unmarshal inflates span ID from a binary representation. Called by protobuf serialization.
   213  func (s *SpanID) Unmarshal(data []byte) error {
   214  	var err error
   215  	*s, err = SpanIDFromBytes(data)
   216  	return err
   217  }
   218  
   219  // MarshalJSON converts span id into a base64 string enclosed in quotes.
   220  // Used by protobuf JSON serialization.
   221  // Example: {1} => "AAAAAAAAAAE=".
   222  func (s SpanID) MarshalJSON() ([]byte, error) {
   223  	var b [8]byte
   224  	s.MarshalTo(b[:]) // can only error on incorrect buffer size
   225  	v := make([]byte, 12+2)
   226  	base64.StdEncoding.Encode(v[1:13], b[:])
   227  	v[0], v[13] = '"', '"'
   228  	return v, nil
   229  }
   230  
   231  // UnmarshalJSON inflates span id from base64 string, possibly enclosed in quotes.
   232  // User by protobuf JSON serialization.
   233  //
   234  // There appears to be a bug in gogoproto, as this function is only called for numeric values.
   235  // https://github.com/gogo/protobuf/issues/411#issuecomment-393856837
   236  func (s *SpanID) UnmarshalJSON(data []byte) error {
   237  	str := string(data)
   238  	if l := len(str); l > 2 && str[0] == '"' && str[l-1] == '"' {
   239  		str = str[1 : l-1]
   240  	}
   241  	b, err := base64.StdEncoding.DecodeString(str)
   242  	if err != nil {
   243  		return fmt.Errorf("cannot unmarshal SpanID from string '%s': %v", string(data), err)
   244  	}
   245  	return s.Unmarshal(b)
   246  }