1
#include "source/extensions/tracers/zipkin/tracer.h"
2

            
3
#include <chrono>
4

            
5
#include "source/common/common/utility.h"
6
#include "source/common/tracing/http_tracer_impl.h"
7
#include "source/extensions/tracers/zipkin/util.h"
8
#include "source/extensions/tracers/zipkin/zipkin_core_constants.h"
9
#include "source/extensions/tracers/zipkin/zipkin_json_field_names.h"
10

            
11
namespace Envoy {
12
namespace Extensions {
13
namespace Tracers {
14
namespace Zipkin {
15

            
16
3
uint64_t Tracer::generateTraceId() {
17
3
  if (timestamp_trace_ids_) {
18
    // Generate timestamp-prefixed 64-bit value:
19
    // [32-bit epoch seconds][32-bit random]
20
2
    const uint32_t epoch_seconds =
21
2
        static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(
22
2
                                  time_source_.monotonicTime().time_since_epoch())
23
2
                                  .count());
24
2
    const uint32_t random_part = static_cast<uint32_t>(random_generator_.random());
25

            
26
    // Combine: timestamp in upper 32 bits, random in lower 32 bits
27
2
    return (static_cast<uint64_t>(epoch_seconds) << 32) | random_part;
28
2
  } else {
29
    // Use fully random trace ID (existing behavior)
30
1
    return random_generator_.random();
31
1
  }
32
3
}
33

            
34
/**
35
 * @param spawn_child_span whether the Envoy will spawn a child span for the request. This
36
 * means that the Envoy will be treated as an independent hop in the trace chain.
37
 * See
38
 * https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/observability/tracing#different-modes-of-envoy
39
 * for more details.
40
 * @param upstream whether the span is span for an upstream request.
41
 * @param direction the direction of the traffic that the span is for. Egress means
42
 * the span is for an outgoing request, and Ingress means the span is for an incoming request.
43
 */
44
65
Annotation getAnnotation(bool spawn_child_span, bool upstream, Tracing::OperationName direction) {
45
65
  Annotation annotation;
46
65
  if (spawn_child_span) {
47
    // Spawn child span is set to true and Envoy should be treated as an independent hop in the
48
    // trace chain. Determine the span type based on the request type.
49

            
50
    // Create server span for downstream request and client span for upstream request.
51
6
    annotation.setValue(upstream ? CLIENT_SEND : SERVER_RECV);
52
59
  } else {
53
    // Spawn child span is set to false and Envoy should not be treated as an independent hop in the
54
    // trace chain. Determine the span type based on the traffic direction.
55

            
56
    // Create server span for inbound sidecar and client span for outbound sidecar.
57
59
    annotation.setValue(direction == Tracing::OperationName::Egress ? CLIENT_SEND : SERVER_RECV);
58
59
  }
59

            
60
65
  return annotation;
61
65
}
62

            
63
SpanPtr Tracer::startSpan(const Tracing::Config& config, const std::string& span_name,
64
44
                          SystemTime timestamp) {
65
  // Build the endpoint
66
44
  Endpoint ep(service_name_, address_);
67

            
68
  // Build the CS annotation.
69
  // No previous context then this must be span created for downstream request for now.
70
44
  Annotation cs = getAnnotation(split_spans_for_request_ || config.spawnUpstreamSpan(), false,
71
44
                                config.operationName());
72
44
  cs.setEndpoint(std::move(ep));
73

            
74
  // Create an all-new span, with no parent id
75
44
  SpanPtr span_ptr = std::make_unique<Span>(time_source_, *this);
76
44
  span_ptr->setName(span_name);
77
44
  uint64_t random_number = random_generator_.random();
78
44
  span_ptr->setId(random_number);
79

            
80
  // Set trace id(s)
81
44
  if (trace_id_128bit_) {
82
2
    span_ptr->setTraceId(random_number);
83
2
    span_ptr->setTraceIdHigh(generateTraceId());
84
42
  } else {
85
42
    if (timestamp_trace_ids_) {
86
      // 64-bit: timestamp-prefixed
87
1
      span_ptr->setTraceId(generateTraceId());
88
41
    } else {
89
      // Legacy behavior: trace id equals span id
90
41
      span_ptr->setTraceId(random_number);
91
41
    }
92
42
  }
93
44
  int64_t start_time_micro = std::chrono::duration_cast<std::chrono::microseconds>(
94
44
                                 time_source_.monotonicTime().time_since_epoch())
95
44
                                 .count();
96
44
  span_ptr->setStartTime(start_time_micro);
97

            
98
  // Set the timestamp globally for the span and also for the CS annotation
99
44
  uint64_t timestamp_micro =
100
44
      std::chrono::duration_cast<std::chrono::microseconds>(timestamp.time_since_epoch()).count();
101
44
  cs.setTimestamp(timestamp_micro);
102
44
  span_ptr->setTimestamp(timestamp_micro);
103

            
104
  // Add CS annotation to the span
105
44
  span_ptr->addAnnotation(std::move(cs));
106

            
107
44
  return span_ptr;
108
44
}
109

            
110
SpanPtr Tracer::startSpan(const Tracing::Config& config, const std::string& span_name,
111
21
                          SystemTime timestamp, const SpanContext& previous_context) {
112
21
  SpanPtr span_ptr = std::make_unique<Span>(time_source_, *this);
113
  // If the previous context is inner context then this span is span for upstream request.
114
21
  Annotation annotation = getAnnotation(split_spans_for_request_ || config.spawnUpstreamSpan(),
115
21
                                        previous_context.innerContext(), config.operationName());
116
21
  uint64_t timestamp_micro;
117

            
118
21
  timestamp_micro =
119
21
      std::chrono::duration_cast<std::chrono::microseconds>(timestamp.time_since_epoch()).count();
120

            
121
21
  span_ptr->setName(span_name);
122

            
123
  // Set the span's id and parent id
124
21
  if (annotation.value() == CLIENT_SEND || !shared_span_context_) {
125
    // We need to create a new span that is a child of the previous span; no shared context
126

            
127
    // Create a new span id
128
6
    uint64_t random_number = random_generator_.random();
129
6
    span_ptr->setId(random_number);
130

            
131
    // Set the parent id to the id of the previous span
132
6
    span_ptr->setParentId(previous_context.id());
133

            
134
    // Set the timestamp globally for the span
135
6
    span_ptr->setTimestamp(timestamp_micro);
136
15
  } else if (annotation.value() == SERVER_RECV) {
137
    // We need to create a new span that will share context with the previous span
138

            
139
    // Initialize the shared context for the new span
140
15
    span_ptr->setId(previous_context.id());
141
15
    if (previous_context.parentId()) {
142
6
      span_ptr->setParentId(previous_context.parentId());
143
6
    }
144
15
  } else {
145
    return span_ptr; // return an empty span
146
  }
147

            
148
  // Build the endpoint
149
21
  Endpoint ep(service_name_, address_);
150

            
151
  // Add the newly-created annotation to the span
152
21
  annotation.setEndpoint(std::move(ep));
153
21
  annotation.setTimestamp(timestamp_micro);
154
21
  span_ptr->addAnnotation(std::move(annotation));
155

            
156
  // Keep the same trace id
157
21
  span_ptr->setTraceId(previous_context.traceId());
158
21
  if (previous_context.is128BitTraceId()) {
159
3
    span_ptr->setTraceIdHigh(previous_context.traceIdHigh());
160
3
  }
161

            
162
  // Keep the same sampled flag
163
21
  span_ptr->setSampled(previous_context.sampled());
164

            
165
21
  int64_t start_time_micro = std::chrono::duration_cast<std::chrono::microseconds>(
166
21
                                 time_source_.monotonicTime().time_since_epoch())
167
21
                                 .count();
168
21
  span_ptr->setStartTime(start_time_micro);
169

            
170
21
  return span_ptr;
171
21
}
172

            
173
20
void Tracer::reportSpan(Span&& span) {
174
20
  if (reporter_ && span.sampled()) {
175
18
    reporter_->reportSpan(std::move(span));
176
18
  }
177
20
}
178

            
179
45
void Tracer::setReporter(ReporterPtr reporter) { reporter_ = std::move(reporter); }
180

            
181
} // namespace Zipkin
182
} // namespace Tracers
183
} // namespace Extensions
184
} // namespace Envoy