• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 gRPC 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 
15 #include "python_observability_context.h"
16 
17 #include <string.h>
18 
19 #include <iomanip>
20 #include <iostream>
21 #include <new>
22 
23 #include "absl/numeric/int128.h"
24 #include "absl/random/random.h"
25 #include "absl/strings/escaping.h"
26 #include "rpc_encoding.h"
27 #include "src/core/lib/transport/transport.h"
28 
29 namespace grpc_observability {
30 
EnablePythonCensusStats(bool enable)31 void EnablePythonCensusStats(bool enable) {
32   g_python_census_stats_enabled = enable;
33 }
34 
EnablePythonCensusTracing(bool enable)35 void EnablePythonCensusTracing(bool enable) {
36   g_python_census_tracing_enabled = enable;
37 }
38 
PythonCensusStatsEnabled()39 bool PythonCensusStatsEnabled() {
40   return g_python_census_stats_enabled.load(std::memory_order_relaxed);
41 }
42 
PythonCensusTracingEnabled()43 bool PythonCensusTracingEnabled() {
44   return g_python_census_tracing_enabled.load(std::memory_order_relaxed);
45 }
46 
GenerateClientContext(absl::string_view method,absl::string_view trace_id,absl::string_view parent_span_id,PythonCensusContext * context)47 void GenerateClientContext(absl::string_view method, absl::string_view trace_id,
48                            absl::string_view parent_span_id,
49                            PythonCensusContext* context) {
50   // Destruct the current CensusContext to free the Span memory before
51   // overwriting it below.
52   context->~PythonCensusContext();
53   if (method.empty()) {
54     new (context) PythonCensusContext();
55     return;
56   }
57   if (!parent_span_id.empty()) {
58     // Note that parent_span_id exist also means it was marked as sampled at
59     // Python OC, we'll respect that decision.
60     SpanContext parent_context =
61         SpanContext(std::string(trace_id), std::string(parent_span_id), true);
62     new (context) PythonCensusContext(method, parent_context);
63     return;
64   }
65   // Create span without parent.
66   new (context) PythonCensusContext(method, trace_id);
67 }
68 
GenerateServerContext(absl::string_view header,absl::string_view method,PythonCensusContext * context)69 void GenerateServerContext(absl::string_view header, absl::string_view method,
70                            PythonCensusContext* context) {
71   // Destruct the current CensusContext to free the Span memory before
72   // overwriting it below.
73   context->~PythonCensusContext();
74   if (method.empty()) {
75     new (context) PythonCensusContext();
76     return;
77   }
78   SpanContext parent_ctx = FromGrpcTraceBinHeader(header);
79   if (parent_ctx.IsValid()) {
80     new (context) PythonCensusContext(method, parent_ctx);
81   } else {
82     new (context) PythonCensusContext(method);
83   }
84 }
85 
ToGrpcTraceBinHeader(const PythonCensusContext & ctx,uint8_t * out)86 void ToGrpcTraceBinHeader(const PythonCensusContext& ctx, uint8_t* out) {
87   out[kVersionOfs] = kVersionId;
88   out[kTraceIdOfs] = kTraceIdField;
89   uint8_t trace_options_rep_[kSizeTraceOptions];
90 
91   std::string trace_id =
92       absl::HexStringToBytes(absl::string_view(ctx.GetSpanContext().TraceId()));
93   std::string span_id =
94       absl::HexStringToBytes(absl::string_view(ctx.GetSpanContext().SpanId()));
95   trace_options_rep_[0] = ctx.GetSpanContext().IsSampled() ? 1 : 0;
96 
97   memcpy(reinterpret_cast<uint8_t*>(&out[kTraceIdOfs + 1]), trace_id.c_str(),
98          kSizeTraceID);
99 
100   out[kSpanIdOfs] = kSpanIdField;
101   memcpy(reinterpret_cast<uint8_t*>(&out[kSpanIdOfs + 1]), span_id.c_str(),
102          kSizeSpanID);
103 
104   out[kTraceOptionsOfs] = kTraceOptionsField;
105   memcpy(reinterpret_cast<uint8_t*>(&out[kTraceOptionsOfs + 1]),
106          trace_options_rep_, kSizeTraceOptions);
107 }
108 
FromGrpcTraceBinHeader(absl::string_view header)109 SpanContext FromGrpcTraceBinHeader(absl::string_view header) {
110   if (header.size() < kGrpcTraceBinHeaderLen ||
111       header[kVersionOfs] != kVersionId ||
112       header[kTraceIdOfs] != kTraceIdField ||
113       header[kSpanIdOfs] != kSpanIdField ||
114       header[kTraceOptionsOfs] != kTraceOptionsField) {
115     return SpanContext();  // Invalid.
116   }
117 
118   uint8_t options = header[kTraceOptionsOfs + 1] & 1;
119   constexpr uint8_t kIsSampled = 1;
120 
121   uint8_t trace_id_rep_[kTraceIdSize];
122   memcpy(trace_id_rep_,
123          reinterpret_cast<const uint8_t*>(&header[kTraceIdOfs + 1]),
124          kTraceIdSize);
125 
126   uint8_t span_id_rep_[kSpanIdSize];
127   memcpy(span_id_rep_,
128          reinterpret_cast<const uint8_t*>(&header[kSpanIdOfs + 1]),
129          kSpanIdSize);
130 
131   uint8_t trace_option_rep_[kTraceOptionsLen];
132   memcpy(trace_option_rep_, &options, kTraceOptionsLen);
133 
134   SpanContext context(
135       absl::BytesToHexString(absl::string_view(
136           reinterpret_cast<const char*>(trace_id_rep_), kTraceIdSize)),
137       absl::BytesToHexString(absl::string_view(
138           reinterpret_cast<const char*>(span_id_rep_), kSpanIdSize)),
139       trace_option_rep_[0] & kIsSampled);
140   return context;
141 }
142 
TraceContextSerialize(const PythonCensusContext & context,char * tracing_buf,size_t tracing_buf_size)143 size_t TraceContextSerialize(const PythonCensusContext& context,
144                              char* tracing_buf, size_t tracing_buf_size) {
145   if (tracing_buf_size < kGrpcTraceBinHeaderLen) {
146     return 0;
147   }
148   ToGrpcTraceBinHeader(context, reinterpret_cast<uint8_t*>(tracing_buf));
149   return kGrpcTraceBinHeaderLen;
150 }
151 
StatsContextSerialize(size_t,grpc_slice *)152 size_t StatsContextSerialize(size_t /*max_tags_len*/, grpc_slice* /*tags*/) {
153   return 0;
154 }
155 
ServerStatsDeserialize(const char * buf,size_t buf_size,uint64_t * server_elapsed_time)156 size_t ServerStatsDeserialize(const char* buf, size_t buf_size,
157                               uint64_t* server_elapsed_time) {
158   return RpcServerStatsEncoding::Decode(absl::string_view(buf, buf_size),
159                                         server_elapsed_time);
160 }
161 
ServerStatsSerialize(uint64_t server_elapsed_time,char * buf,size_t buf_size)162 size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf,
163                             size_t buf_size) {
164   return RpcServerStatsEncoding::Encode(server_elapsed_time, buf, buf_size);
165 }
166 
GetIncomingDataSize(const grpc_call_final_info * final_info)167 uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info) {
168   return final_info->stats.transport_stream_stats.incoming.data_bytes;
169 }
170 
GetOutgoingDataSize(const grpc_call_final_info * final_info)171 uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info) {
172   return final_info->stats.transport_stream_stats.outgoing.data_bytes;
173 }
174 
175 namespace {
176 // span_id is a 16-character hexadecimal encoded string.
GenerateSpanId()177 std::string GenerateSpanId() {
178   uint64_t span_id = absl::Uniform<uint64_t>(absl::BitGen());
179   std::stringstream hex_string;
180   hex_string << std::setfill('0') << std::setw(16) << std::hex << span_id;
181   return std::string(hex_string.str());
182 }
183 
184 // trace_id is a 32-character hexadecimal encoded string
GenerateTraceId()185 std::string GenerateTraceId() {
186   absl::uint128 trace_id = absl::Uniform<absl::uint128>(absl::BitGen());
187   std::stringstream hex_string;
188   hex_string << std::setfill('0') << std::setw(32) << std::hex << trace_id;
189   return std::string(hex_string.str());
190 }
191 
192 }  // namespace
193 
194 //
195 // Span
196 //
197 
StartSpan(absl::string_view name,const Span * parent)198 Span Span::StartSpan(absl::string_view name, const Span* parent) {
199   std::string span_id = GenerateSpanId();
200   std::string trace_id;
201   std::string parent_span_id;
202   bool should_sample;
203   auto start_time = absl::Now();
204 
205   if (parent != nullptr) {
206     parent_span_id = parent->Context().SpanId();
207     trace_id = parent->Context().TraceId();
208     should_sample = parent->Context().IsSampled();
209   } else {
210     trace_id = GenerateTraceId();
211     should_sample = ShouldSample(trace_id);
212   }
213 
214   SpanContext context = SpanContext(trace_id, span_id, should_sample);
215   return Span(std::string(name), parent_span_id, start_time, context);
216 }
217 
StartSpan(absl::string_view name,const SpanContext & parent_context)218 Span Span::StartSpan(absl::string_view name,
219                      const SpanContext& parent_context) {
220   std::string trace_id = parent_context.TraceId();
221   std::string parent_span_id = parent_context.SpanId();
222   std::string span_id = GenerateSpanId();
223   bool should_sample = parent_context.IsSampled();
224   if (!should_sample) {
225     // Resampling here so that it's possible to collect trace on server side
226     // if client tracing is not enabled.
227     should_sample = ShouldSample(std::string(trace_id));
228   }
229   auto start_time = absl::Now();
230   SpanContext context(trace_id, span_id, should_sample);
231   return Span(std::string(name), parent_span_id, start_time, context);
232 }
233 
StartSpan(absl::string_view name,absl::string_view trace_id)234 Span Span::StartSpan(absl::string_view name, absl::string_view trace_id) {
235   std::string span_id = GenerateSpanId();
236   auto start_time = absl::Now();
237   bool should_sample = ShouldSample(std::string(trace_id));
238   SpanContext context(std::string(trace_id), span_id, should_sample);
239   return Span(std::string(name), "", start_time, context);
240 }
241 
SetStatus(absl::string_view status)242 void Span::SetStatus(absl::string_view status) {
243   status_ = std::string(status);
244 }
245 
AddAttribute(absl::string_view key,absl::string_view value)246 void Span::AddAttribute(absl::string_view key, absl::string_view value) {
247   span_labels_.emplace_back(std::string(key), std::string(value));
248 }
249 
AddAnnotation(absl::string_view description)250 void Span::AddAnnotation(absl::string_view description) {
251   // Need a string format which can be converted to Python datetime.datetime
252   // class directly.
253   std::string time_stamp =
254       absl::FormatTime("%Y-%m-%d %H:%M:%E3S", absl::Now(), absl::UTCTimeZone());
255   span_annotations_.emplace_back(
256       Annotation{time_stamp, std::string(description)});
257 }
258 
ToCensusData() const259 SpanCensusData Span::ToCensusData() const {
260   SpanCensusData census_data;
261   absl::TimeZone utc = absl::UTCTimeZone();
262   census_data.name = name_;
263   // Need a string format which can be exported to StackDriver directly.
264   // See format details:
265   // https://cloud.google.com/trace/docs/reference/v2/rest/v2/projects.traces/batchWrite
266   census_data.start_time =
267       absl::FormatTime("%Y-%m-%dT%H:%M:%E6SZ", start_time_, utc);
268   census_data.end_time =
269       absl::FormatTime("%Y-%m-%dT%H:%M:%E6SZ", end_time_, utc);
270   census_data.trace_id = Context().TraceId();
271   census_data.span_id = Context().SpanId();
272   census_data.should_sample = Context().IsSampled();
273   census_data.parent_span_id = parent_span_id_;
274   census_data.status = status_;
275   census_data.span_labels = span_labels_;
276   census_data.span_annotations = span_annotations_;
277   census_data.child_span_count = child_span_count_;
278   return census_data;
279 }
280 
281 }  // namespace grpc_observability
282