span.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright 2022 The OpenZipkin 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 model
  15. import (
  16. "encoding/json"
  17. "errors"
  18. "strings"
  19. "time"
  20. )
  21. // unmarshal errors
  22. var (
  23. ErrValidTraceIDRequired = errors.New("valid traceId required")
  24. ErrValidIDRequired = errors.New("valid span id required")
  25. ErrValidDurationRequired = errors.New("valid duration required")
  26. )
  27. // BaggageFields holds the interface for consumers needing to interact with
  28. // the fields in application logic.
  29. type BaggageFields interface {
  30. // Get returns the values for a field identified by its key.
  31. Get(key string) []string
  32. // Add adds the provided values to a header designated by key. If not
  33. // accepted by the baggage implementation, it will return false.
  34. Add(key string, value ...string) bool
  35. // Set sets the provided values to a header designated by key. If not
  36. // accepted by the baggage implementation, it will return false.
  37. Set(key string, value ...string) bool
  38. // Delete removes the field data designated by key. If not accepted by the
  39. // baggage implementation, it will return false.
  40. Delete(key string) bool
  41. // Iterate will iterate over the available fields and for each one it will
  42. // trigger the callback function.
  43. Iterate(f func(key string, values []string))
  44. }
  45. // SpanContext holds the context of a Span.
  46. type SpanContext struct {
  47. TraceID TraceID `json:"traceId"`
  48. ID ID `json:"id"`
  49. ParentID *ID `json:"parentId,omitempty"`
  50. Debug bool `json:"debug,omitempty"`
  51. Sampled *bool `json:"-"`
  52. Err error `json:"-"`
  53. Baggage BaggageFields `json:"-"`
  54. }
  55. // SpanModel structure.
  56. //
  57. // If using this library to instrument your application you will not need to
  58. // directly access or modify this representation. The SpanModel is exported for
  59. // use cases involving 3rd party Go instrumentation libraries desiring to
  60. // export data to a Zipkin server using the Zipkin V2 Span model.
  61. type SpanModel struct {
  62. SpanContext
  63. Name string `json:"name,omitempty"`
  64. Kind Kind `json:"kind,omitempty"`
  65. Timestamp time.Time `json:"-"`
  66. Duration time.Duration `json:"-"`
  67. Shared bool `json:"shared,omitempty"`
  68. LocalEndpoint *Endpoint `json:"localEndpoint,omitempty"`
  69. RemoteEndpoint *Endpoint `json:"remoteEndpoint,omitempty"`
  70. Annotations []Annotation `json:"annotations,omitempty"`
  71. Tags map[string]string `json:"tags,omitempty"`
  72. }
  73. // MarshalJSON exports our Model into the correct format for the Zipkin V2 API.
  74. func (s SpanModel) MarshalJSON() ([]byte, error) {
  75. type Alias SpanModel
  76. var timestamp int64
  77. if !s.Timestamp.IsZero() {
  78. if s.Timestamp.Unix() < 1 {
  79. // Zipkin does not allow Timestamps before Unix epoch
  80. return nil, ErrValidTimestampRequired
  81. }
  82. timestamp = s.Timestamp.Round(time.Microsecond).UnixNano() / 1e3
  83. }
  84. if s.Duration < time.Microsecond {
  85. if s.Duration < 0 {
  86. // negative duration is not allowed and signals a timing logic error
  87. return nil, ErrValidDurationRequired
  88. } else if s.Duration > 0 {
  89. // sub microsecond durations are reported as 1 microsecond
  90. s.Duration = 1 * time.Microsecond
  91. }
  92. } else {
  93. // Duration will be rounded to nearest microsecond representation.
  94. //
  95. // NOTE: Duration.Round() is not available in Go 1.8 which we still support.
  96. // To handle microsecond resolution rounding we'll add 500 nanoseconds to
  97. // the duration. When truncated to microseconds in the call to marshal, it
  98. // will be naturally rounded. See TestSpanDurationRounding in span_test.go
  99. s.Duration += 500 * time.Nanosecond
  100. }
  101. s.Name = strings.ToLower(s.Name)
  102. if s.LocalEndpoint.Empty() {
  103. s.LocalEndpoint = nil
  104. }
  105. if s.RemoteEndpoint.Empty() {
  106. s.RemoteEndpoint = nil
  107. }
  108. return json.Marshal(&struct {
  109. T int64 `json:"timestamp,omitempty"`
  110. D int64 `json:"duration,omitempty"`
  111. Alias
  112. }{
  113. T: timestamp,
  114. D: s.Duration.Nanoseconds() / 1e3,
  115. Alias: (Alias)(s),
  116. })
  117. }
  118. // UnmarshalJSON imports our Model from a Zipkin V2 API compatible span
  119. // representation.
  120. func (s *SpanModel) UnmarshalJSON(b []byte) error {
  121. type Alias SpanModel
  122. span := &struct {
  123. T uint64 `json:"timestamp,omitempty"`
  124. D uint64 `json:"duration,omitempty"`
  125. *Alias
  126. }{
  127. Alias: (*Alias)(s),
  128. }
  129. if err := json.Unmarshal(b, &span); err != nil {
  130. return err
  131. }
  132. if s.ID < 1 {
  133. return ErrValidIDRequired
  134. }
  135. if span.T > 0 {
  136. s.Timestamp = time.Unix(0, int64(span.T)*1e3)
  137. }
  138. s.Duration = time.Duration(span.D*1e3) * time.Nanosecond
  139. if s.LocalEndpoint.Empty() {
  140. s.LocalEndpoint = nil
  141. }
  142. if s.RemoteEndpoint.Empty() {
  143. s.RemoteEndpoint = nil
  144. }
  145. return nil
  146. }