• 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 #ifndef GRPC_PYTHON_OBSERVABILITY_H
16 #define GRPC_PYTHON_OBSERVABILITY_H
17 
18 #include <grpc/slice.h>
19 #include <stddef.h>
20 #include <stdint.h>
21 
22 #include <algorithm>
23 #include <atomic>
24 #include <string>
25 #include <vector>
26 
27 #include "absl/strings/string_view.h"
28 #include "absl/strings/strip.h"
29 #include "absl/time/clock.h"
30 #include "absl/time/time.h"
31 #include "constants.h"
32 #include "sampler.h"
33 #include "src/core/lib/channel/channel_stack.h"
34 
35 namespace grpc_observability {
36 
37 namespace {
38 std::atomic<bool> g_python_census_stats_enabled(false);
39 std::atomic<bool> g_python_census_tracing_enabled(false);
40 }  // namespace
41 
42 // Enables/Disables Python census stats/tracing. It's only safe to do at the
43 // start of a program, before any channels/servers are built.
44 void EnablePythonCensusStats(bool enable);
45 void EnablePythonCensusTracing(bool enable);
46 // Gets the current status of Python OpenCensus stats/tracing
47 bool PythonCensusStatsEnabled();
48 bool PythonCensusTracingEnabled();
49 
50 static constexpr size_t kTraceIdSize = 16;
51 static constexpr size_t kSpanIdSize = 8;
52 
53 constexpr uint8_t kVersionId = 0;
54 constexpr uint8_t kTraceIdField = 0;
55 constexpr uint8_t kSpanIdField = 1;
56 constexpr uint8_t kTraceOptionsField = 2;
57 
58 constexpr int kVersionLen = 1;
59 constexpr int kTraceIdLen = 16;
60 constexpr int kSpanIdLen = 8;
61 constexpr int kTraceOptionsLen = 1;
62 
63 constexpr int kVersionOfs = 0;
64 constexpr int kTraceIdOfs = 1;
65 constexpr int kSpanIdOfs = kTraceIdOfs + 1 + kTraceIdLen;
66 constexpr int kTraceOptionsOfs = kSpanIdOfs + 1 + kSpanIdLen;
67 
68 static constexpr size_t kSizeTraceID = 16;
69 static constexpr size_t kSizeSpanID = 8;
70 static constexpr size_t kSizeTraceOptions = 1;
71 
72 // The length of the grpc-trace-bin value:
73 //      1 (version)
74 //   +  1 (trace_id field)
75 //   + 16 (length of trace_id)
76 //   +  1 (span_id field)
77 //   +  8 (span_id length)
78 //   +  1 (trace_options field)
79 //   +  1 (trace_options length)
80 //   ----
81 //     29
82 constexpr int kGrpcTraceBinHeaderLen =
83     kVersionLen + 1 + kTraceIdLen + 1 + kSpanIdLen + 1 + kTraceOptionsLen;
84 
85 struct Tag {
86   std::string key;
87   std::string value;
88 };
89 
90 struct Label {
LabelLabel91   Label() {}
LabelLabel92   Label(std::string k, std::string v) : key(k), value(v) {}
93   std::string key;
94   std::string value;
95 };
96 
97 union MeasurementValue {
98   double value_double;
99   int64_t value_int;
100 };
101 
102 struct Measurement {
103   MetricsName name;
104   MeasurementType type;
105   MeasurementValue value;
106   bool registered_method;
107   bool include_exchange_labels;
108 };
109 
110 struct Annotation {
111   std::string time_stamp;
112   std::string description;
113 };
114 
115 struct SpanCensusData {
116   std::string name;
117   std::string start_time;
118   std::string end_time;
119   std::string trace_id;
120   std::string span_id;
121   std::string parent_span_id;
122   std::string status;
123   std::vector<Label> span_labels;
124   std::vector<Annotation> span_annotations;
125   int64_t child_span_count;
126   bool should_sample;
127 };
128 
129 // SpanContext is associated with span to help manage the current context of a
130 // span. It's created when creating a new Span and will be destroyed together
131 // with associated Span.
132 class SpanContext final {
133  public:
SpanContext()134   SpanContext() : is_valid_(false) {}
135 
SpanContext(const std::string & trace_id,const std::string & span_id,bool should_sample)136   SpanContext(const std::string& trace_id, const std::string& span_id,
137               bool should_sample)
138       : trace_id_(trace_id),
139         span_id_(span_id),
140         should_sample_(should_sample),
141         is_valid_(true) {}
142 
143   // Returns the TraceId associated with this SpanContext.
TraceId()144   std::string TraceId() const { return trace_id_; }
145 
146   // Returns the SpanId associated with this SpanContext.
SpanId()147   std::string SpanId() const { return span_id_; }
148 
IsSampled()149   bool IsSampled() const { return should_sample_; }
150 
IsValid()151   bool IsValid() const { return is_valid_; }
152 
153  private:
154   std::string trace_id_;
155   std::string span_id_;
156   bool should_sample_;
157   bool is_valid_;
158 };
159 
160 // Span is associated with PythonCensusContext to help manage tracing related
161 // data. It's created by calling StartSpan and will be destroyed together with
162 // associated PythonCensusContext.
163 class Span final {
164  public:
Span(const std::string & name,const std::string & parent_span_id,absl::Time start_time,const SpanContext & context)165   explicit Span(const std::string& name, const std::string& parent_span_id,
166                 absl::Time start_time, const SpanContext& context)
167       : name_(name),
168         parent_span_id_(parent_span_id),
169         start_time_(start_time),
170         context_(context) {}
171 
End()172   void End() { end_time_ = absl::Now(); }
173 
IncreaseChildSpanCount()174   void IncreaseChildSpanCount() { ++child_span_count_; }
175 
176   static Span StartSpan(absl::string_view name, const Span* parent);
177 
178   static Span StartSpan(absl::string_view name,
179                         const SpanContext& parent_context);
180 
181   static Span StartSpan(absl::string_view name, absl::string_view trace_id);
182 
BlankSpan()183   static Span BlankSpan() { return StartSpan("", ""); }
184 
Context()185   const SpanContext& Context() const { return context_; }
186 
187   void SetStatus(absl::string_view status);
188 
189   void AddAttribute(absl::string_view key, absl::string_view value);
190 
191   void AddAnnotation(absl::string_view description);
192 
193   SpanCensusData ToCensusData() const;
194 
195  private:
ShouldSample(const std::string & trace_id)196   static bool ShouldSample(const std::string& trace_id) {
197     return ProbabilitySampler::Get().ShouldSample(trace_id);
198   }
199 
200   std::string name_;
201   std::string parent_span_id_;
202   absl::Time start_time_;
203   absl::Time end_time_;
204   std::string status_;
205   std::vector<Label> span_labels_;
206   std::vector<Annotation> span_annotations_;
207   SpanContext context_;
208   uint64_t child_span_count_ = 0;
209 };
210 
211 // PythonCensusContext is associated with each clientCallTracer,
212 // clientCallAttemptTracer and ServerCallTracer to help manage the span,
213 // spanContext and labels for each tracer. Create a new PythonCensusContext will
214 // always result in creating a new span (and a new SpanContext for that span).
215 // It's created during callTracer initialization and will be destroyed after
216 // the destruction of each callTracer.
217 class PythonCensusContext {
218  public:
PythonCensusContext()219   PythonCensusContext() : span_(Span::BlankSpan()), labels_({}) {}
220 
PythonCensusContext(absl::string_view name)221   explicit PythonCensusContext(absl::string_view name)
222       : span_(Span::StartSpan(name, nullptr)), labels_({}) {}
223 
PythonCensusContext(absl::string_view name,absl::string_view trace_id)224   PythonCensusContext(absl::string_view name, absl::string_view trace_id)
225       : span_(Span::StartSpan(name, trace_id)), labels_({}) {}
226 
PythonCensusContext(absl::string_view name,const SpanContext & parent_context)227   PythonCensusContext(absl::string_view name, const SpanContext& parent_context)
228       : span_(Span::StartSpan(name, parent_context)), labels_({}) {}
229 
PythonCensusContext(absl::string_view name,const Span * parent,const std::vector<Label> & labels)230   PythonCensusContext(absl::string_view name, const Span* parent,
231                       const std::vector<Label>& labels)
232       : span_(Span::StartSpan(name, parent)), labels_(labels) {}
233 
234   // For attempt Spans only
PythonCensusContext(absl::string_view name,const Span * parent)235   PythonCensusContext(absl::string_view name, const Span* parent)
236       : span_(Span::StartSpan(name, parent)), labels_({}) {}
237 
GetSpan()238   Span& GetSpan() { return span_; }
Labels()239   std::vector<Label>& Labels() { return labels_; }  // Only used for metrics
GetSpanContext()240   const SpanContext& GetSpanContext() const { return span_.Context(); }
241 
AddSpanAttribute(absl::string_view key,absl::string_view attribute)242   void AddSpanAttribute(absl::string_view key, absl::string_view attribute) {
243     span_.AddAttribute(key, attribute);
244   }
245 
AddSpanAnnotation(absl::string_view description)246   void AddSpanAnnotation(absl::string_view description) {
247     span_.AddAnnotation(description);
248   }
249 
IncreaseChildSpanCount()250   void IncreaseChildSpanCount() { span_.IncreaseChildSpanCount(); }
251 
EndSpan()252   void EndSpan() { GetSpan().End(); }
253 
254  private:
255   grpc_observability::Span span_;
256   std::vector<Label> labels_;
257 };
258 
259 // Creates a new client context that is by default a new root context.
260 // If the current context is the default context then the newly created
261 // span automatically becomes a root span. This should only be called with a
262 // blank CensusContext as it overwrites it.
263 void GenerateClientContext(absl::string_view method, absl::string_view trace_id,
264                            absl::string_view parent_span_id,
265                            PythonCensusContext* context);
266 
267 // Deserialize the incoming SpanContext and generate a new server context based
268 // on that. This new span will never be a root span. This should only be called
269 // with a blank CensusContext as it overwrites it.
270 void GenerateServerContext(absl::string_view header, absl::string_view method,
271                            PythonCensusContext* context);
272 
GetMethod(const char * method)273 inline std::string GetMethod(const char* method) {
274   if (std::string(method).empty()) {
275     return "";
276   }
277   // Check for leading '/' and trim it if present.
278   return std::string(absl::StripPrefix(method, "/"));
279 }
280 
GetTarget(const char * target)281 inline std::string GetTarget(const char* target) { return std::string(target); }
282 
283 // Fills a pre-allocated buffer with the value for the grpc-trace-bin header.
284 // The buffer must be at least kGrpcTraceBinHeaderLen bytes long.
285 void ToGrpcTraceBinHeader(const PythonCensusContext& ctx, uint8_t* out);
286 
287 // Parses the value of the binary grpc-trace-bin header, returning a
288 // SpanContext. If parsing fails, IsValid will be false.
289 //
290 // Example value, hex encoded:
291 //   00                               (version)
292 //   00                               (trace_id field)
293 //   12345678901234567890123456789012 (trace_id)
294 //   01                               (span_id field)
295 //   0000000000003039                 (span_id)
296 //   02                               (trace_options field)
297 //   01                               (options: enabled)
298 //
299 // See also:
300 // https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md
301 SpanContext FromGrpcTraceBinHeader(absl::string_view header);
302 
303 // Serializes the outgoing trace context. tracing_buf must be
304 // opencensus::trace::propagation::kGrpcTraceBinHeaderLen bytes long.
305 size_t TraceContextSerialize(const PythonCensusContext& context,
306                              char* tracing_buf, size_t tracing_buf_size);
307 
308 // Serializes the outgoing stats context.  Field IDs are 1 byte followed by
309 // field data. A 1 byte version ID is always encoded first. Tags are directly
310 // serialized into the given grpc_slice.
311 size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags);
312 
313 // Deserialize incoming server stats. Returns the number of bytes deserialized.
314 size_t ServerStatsDeserialize(const char* buf, size_t buf_size,
315                               uint64_t* server_elapsed_time);
316 
317 // Serialize outgoing server stats. Returns the number of bytes serialized.
318 size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf,
319                             size_t buf_size);
320 
321 // Returns the incoming data size from the grpc call final info.
322 uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info);
323 
324 // Returns the outgoing data size from the grpc call final info.
325 uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info);
326 
327 }  // namespace grpc_observability
328 
329 #endif  // GRPC_PYTHON_OBSERVABILITY_H
330