Skip to content

Commit

Permalink
impl(otel): detach context from EndSpan(..., future<>) (#12902)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbolduc authored Oct 17, 2023
1 parent 56fe3c6 commit 3dddc28
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 4 deletions.
7 changes: 5 additions & 2 deletions google/cloud/internal/grpc_opentelemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,12 @@ future<T> EndSpan(
std::shared_ptr<grpc::ClientContext> context,
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span,
future<T> fut) {
return fut.then([c = std::move(context), s = std::move(span)](auto f) {
return fut.then([oc = opentelemetry::context::RuntimeContext::GetCurrent(),
c = std::move(context), s = std::move(span)](auto f) {
auto t = f.get();
ExtractAttributes(*c, *s);
return EndSpan(*s, f.get());
DetachOTelContext(oc);
return EndSpan(*s, std::move(t));
});
}

Expand Down
27 changes: 27 additions & 0 deletions google/cloud/internal/grpc_opentelemetry_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,33 @@ TEST(OpenTelemetry, EndSpanFuture) {
SpanHasAttributes(OTelAttribute<std::string>("grpc.peer", _)))));
}

TEST(OpenTelemetry, EndSpanFutureDetachesContext) {
auto span_catcher = InstallSpanCatcher();

// Set the `OTelContext` like we do in `AsyncOperation`s
auto c = opentelemetry::context::Context("key", true);
ScopedOTelContext scope({c});
EXPECT_EQ(c, opentelemetry::context::RuntimeContext::GetCurrent());

promise<Status> p;
auto f =
EndSpan(std::make_shared<grpc::ClientContext>(),
MakeSpanGrpc("google.cloud.foo.v1.Foo", "GetBar"), p.get_future())
.then([](auto f) {
auto s = f.get();
// The `OTelContext` should be cleared by the time we exit
// `EndSpan()`.
EXPECT_EQ(opentelemetry::context::Context{},
opentelemetry::context::RuntimeContext::GetCurrent());
return s;
});

p.set_value(Status());
EXPECT_STATUS_OK(f.get());
EXPECT_THAT(span_catcher->GetSpans(),
ElementsAre(SpanNamed("google.cloud.foo.v1.Foo/GetBar")));
}

TEST(OpenTelemetry, TracedAsyncBackoffEnabled) {
auto span_catcher = InstallSpanCatcher();

Expand Down
9 changes: 7 additions & 2 deletions google/cloud/internal/opentelemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OPENTELEMETRY_H

#include "google/cloud/future.h"
#include "google/cloud/internal/opentelemetry_context.h"
#include "google/cloud/options.h"
#include "google/cloud/status.h"
#include "google/cloud/status_or.h"
Expand Down Expand Up @@ -178,8 +179,12 @@ template <typename T>
future<T> EndSpan(
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span,
future<T> fut) {
return fut.then(
[s = std::move(span)](auto f) { return EndSpan(*s, f.get()); });
return fut.then([oc = opentelemetry::context::RuntimeContext::GetCurrent(),
s = std::move(span)](auto f) {
auto t = f.get();
DetachOTelContext(oc);
return EndSpan(*s, std::move(t));
});
}

/**
Expand Down
23 changes: 23 additions & 0 deletions google/cloud/internal/opentelemetry_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "google/cloud/internal/make_status.h"
#include "google/cloud/opentelemetry_options.h"
#include "google/cloud/testing_util/opentelemetry_matchers.h"
#include "google/cloud/testing_util/status_matchers.h"
#include <gmock/gmock.h>
#ifdef GOOGLE_CLOUD_CPP_HAVE_OPENTELEMETRY
#include <opentelemetry/trace/default_span.h>
Expand Down Expand Up @@ -269,6 +270,28 @@ TEST(OpenTelemetry, EndSpanFutureStatusOr) {
SpanWithStatus(opentelemetry::trace::StatusCode::kError)));
}

TEST(OpenTelemetry, EndSpanFutureDetachesContext) {
auto span_catcher = InstallSpanCatcher();

// Set the `OTelContext` like we do in `AsyncOperation`s
auto c = opentelemetry::context::Context("key", true);
ScopedOTelContext scope({c});
EXPECT_EQ(c, opentelemetry::context::RuntimeContext::GetCurrent());

promise<Status> p;
auto f = EndSpan(MakeSpan("span"), p.get_future()).then([](auto f) {
auto s = f.get();
// The `OTelContext` should be cleared by the time we exit `EndSpan()`.
EXPECT_EQ(opentelemetry::context::Context{},
opentelemetry::context::RuntimeContext::GetCurrent());
return s;
});

p.set_value(Status());
EXPECT_STATUS_OK(f.get());
EXPECT_THAT(span_catcher->GetSpans(), ElementsAre(SpanNamed("span")));
}

TEST(OpenTelemetry, EndSpanVoid) {
auto span_catcher = InstallSpanCatcher();

Expand Down

0 comments on commit 3dddc28

Please sign in to comment.