Skip to content

Commit

Permalink
Merge pull request #2 from yashykt/ot-tracing
Browse files Browse the repository at this point in the history
Fill up C++ sections
  • Loading branch information
YifeiZhuang committed Jan 18, 2024
2 parents 7b48df4 + 528dd82 commit cf24140
Showing 1 changed file with 118 additions and 45 deletions.
163 changes: 118 additions & 45 deletions A72-open-telemetry-tracing.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
A72: OpenTelemetry Tracing
----
* Author(s): [Yifei Zhuang](https://github.com/YifeiZhuang)
* Author(s): [Yifei Zhuang](https://github.com/YifeiZhuang), [Yash Tibrewal](https://github.com/yashykt)
* Approver: [Eric Anderson](https://github.com/ejona86)
* Reviewers: [Mark Roth](https://github.com/markdroth), [Doug Fawley](https://github.com/dfawley),
[Feng Li](https://github.com/fengli79)
* Status: Ready for Implementation
* Implemented in: <language, ...>
* Last updated: 2023-07
* Last updated: 2024-01
* Discussion at: https://groups.google.com/g/grpc-io/c/e_ByaRmtJak

## Abstract
Expand Down Expand Up @@ -274,47 +274,122 @@ optimized path for the same reason. Similarly, the normal `get()` method handles
both binary headers and TextMap propagators.

#### Context Propagation APIs in C++
C++ will also support propagator APIs to provides API
uniformity among languages, as well as getting all the benefits of propagator APIs
mentioned before. Due to the language restriction, C++ can not take the optimization
path to workaround lacking the binary propagator API. That means using propagators
API with C++ needs base64 encoding and therefore is slower compared with just
using metadata API.

TODO: add pseudocode here in C++ for `GrpcTraceBinPropagator`,`GrpcTextMapCarrier`.
C++ will also support propagator APIs to provides API uniformity among
languages. Since gRPC C++ avoids RTTI, it can not use the same optimization path
as Java/Go. This will result in an extra base64 encoding/decoding step to
satisfy `TextMapPropagator` requirement that the key/value pair be a valid HTTP
field. There are possible optimizations C++ might pursue in the future, for
example, providing an explicit knob on `GrpcTraceBinTextMapPropagator` that
assumes that this propagator is being used with gRPC and can hence skirt `TextMapPropagator` compatibility requirements.

```C++
class GrpcTraceBinPropagator : public TextMapPropagator {
public:
void Inject(TextMapCarrier &carrier, const context::Context &context) {
// Slow path for C++. gRPC C++ does not have runtime type inspection, so we
// encode bytes to String to comply with the TextMapSetter API. This code
// path is also used in the situation where GrpcTraceBinPropagator
// is used with a TextMapSetter externally.
// TODO: add implementation
}
context::Context Extract(const TextMapCarrier &carrier, context::Context &context) {
// TODO: add implementaiton
}
}
std::unique_ptr<opentelemetry::context::TextMapCarrier>
MakeGrpcTraceBinTextMapPropagator();
```

The following shows a sketch on what the internal implementation details of this API would look within gRPC C++/Core.

```C++
class GrpcTextMapCarrier : public TextMapCarrier {
public:

namespace grpc {
namespace internal {

class GrpcTraceBinTextMapPropagator
: public opentelemetry::context::TextMapPropagator {
public:
void Inject(opentelemetry::context::TextMapCarrier& carrier,
const opentelemetry::context::Context& context) {
auto span_context = opentelemetry::trace::GetSpan(context)->GetContext();
if (!span_context.IsValid()) {
return;
}
carrier.Set(
"grpc-trace-bin",
absl::Base64Escape(
absl::string_view(SpanContextToGrpcTraceBinHeader(span_context))
.data()),
kGrpcTraceBinHeaderLen);
}

context::Context Extract(const context::propagation::TextMapCarrier& carrier,
opentelemetry::context::Context& context) {
return trace::SetSpan(
context, nostd::shared_ptr<Span> sp(new DefaultSpan(
GrpcTraceBinHeaderToSpanContext(absl::Base64Unescape(
carrier.Get("grpc-trace-bin"))))));
}

private:
constexpr int kGrpcTraceBinHeaderLen = 29;

std::array<uint8_t, kGrpcTraceBinHeaderLen> SpanContextToGrpcTraceBinHeader(
const opentelemetry::trace::SpanContext& ctx) {
std::array<uint8_t, kGrpcTraceBinHeaderLen> header;
header[0] = 0;
header[1] = 0;
ctx.trace_id().CopyBytesTo(&header[2], 16);
header[18] = 1;
ctx.span_id().CopyBytesTo(&header[19], 8);
header[27] = 2;
header[28] = ctx.trace_flags().flags();
return header;
}

opentelemetry::trace::SpanContext GrpcTraceBinHeaderToSpanContext(
nostd::string_view header) {
if (header.size() != kGrpcTraceBinHeaderLen || header[0] != 0 ||
header[1] != 0 || header[18] != 1 || header[27] != 2) {
return SpanContext::GetInvalid();
}
return SpanContext(TraceId(&header[2], 16), SpanId(&header[19], 8),
TraceFlags(header[28]), /*is_remote*/ true);
}
};

class GrpcTextMapCarrier : public opentelemetry::context::TextMapCarrier {
public:
GrpcTextMapCarrier(grpc_metadata_batch* metadata) : metadata_(metadata) {}

nostd::string_view Get(nostd::string_view key) {
// TODO: add implementation
if (key == "grpc-trace-bin") {
return absl::Base64Escape(metadata_->GetStringValue(key).value_or(""));
} else if (absl::EndsWith(key, "-bin")) {
// It's unclear how we want to handle other headers that end with "-bin".
// Return an empty string for now.
return "";
}
return metadata_->GetStringValue(key);
}

void Set(nostd::string_view key, nostd::string_view value) {
// TODO: add implementation
if (key == "grpc-trace-bin") {
metadata_->Set(
grpc_core::GrpcTraceBinMetadata(),
grpc_core::Slice::FromCopiedString(absl::Base64Unescape(value)));
} else if (absl::EndsWith(key, "-bin")) {
// It's unclear how we want to handle other headers that end with "-bin".
// Do nothing for now.
return;
} else {
// A propagator other than GrpcTraceBinTextMapPropagator was used.
metadata_->Append(key, grpc_core::Slice::FromCopiedString(value));
}
}
}
```

The `GrpcTextMapCarrier.set()` and `GrpcTextMapCarrier.get()` method in C++
should handle both binary (`-bin`) header
(e.g. `grpc-trace-bin`) and ASCII header from other TextMap
propagators that users configure into gRPC OpenTelemetry, e.g. W3C.
private:
grpc_metadata_batch* metadata_;
};

} // namespace internal

std::unique_ptr<opentelemetry::context::TextMapCarrier>
MakeGrpcTraceBinTextMapPropagator() {
return std::make_unique<internal::GrpcTraceBinTextMapPropagator>();
}

} // namespace grpc

```
### gRPC OpenTelemetry Tracing API
This section talks about enabling and configuring OpenTelemetry tracing.
Expand Down Expand Up @@ -364,22 +439,20 @@ GlobalInterceptors.setInterceptors(
```

#### C++
In C++, we will add a new method in `OpenTelemetryPluginBuilder`, see [gRPC A66][A66].
The following new methods will be added in `OpenTelemetryPluginBuilder`.

```C++
class OpenTelemetryPluginBuilder {
public:
OpenTelemetryPluginBuilder();
// If `SetMeterProvider()` is not called, no metrics are collected.
OpenTelemetryPluginBuilder& SetMeterProvider(
std::shared_ptr<opentelemetry::metrics::MeterProvider> meter_provider);
+ // Set one or multiple propagators for span context propagation, e.g.
+ // GrpcTraceBinPropagator or community standard ones like W3C, etc.
+ OpenTelemetryPluginBuilder& SetPropagator(
+ std::shared_ptr<opentelemetry::context::propagation::TextMapPropagator>
+ new grpc::GrpcTraceBinPropagator());
...
}
// If `SetTracerProvider()` is not called, no traces are collected.
OpenTelemetryPluginBuilder& SetTracerProvider(
std::shared_ptr<opentelemetry::metrics::TracerProvider> tracer_provider);
// Set one or multiple text map propagators for span context propagation, e.g.
// GrpcTraceBinTextMapPropagator or community standard ones like W3C, etc.
OpenTelemetryPluginBuilder& SetTextMapPropagator(
std::unique_ptr<opentelemetry::context::propagation::TextMapPropagator>
text_map_propagator);
};
```
#### Go
Expand Down

0 comments on commit cf24140

Please sign in to comment.