diff --git a/google/cloud/storage/internal/async/object_descriptor_connection_tracing.cc b/google/cloud/storage/internal/async/object_descriptor_connection_tracing.cc index 8f5e71fcddfb5..78595dfda6bca 100644 --- a/google/cloud/storage/internal/async/object_descriptor_connection_tracing.cc +++ b/google/cloud/storage/internal/async/object_descriptor_connection_tracing.cc @@ -14,6 +14,7 @@ #include "google/cloud/storage/internal/async/object_descriptor_connection_tracing.h" #include "google/cloud/storage/async/reader_connection.h" +#include "google/cloud/storage/internal/async/reader_connection_tracing.h" #include "google/cloud/internal/opentelemetry.h" #include "google/cloud/version.h" #ifdef GOOGLE_CLOUD_CPP_HAVE_OPENTELEMETRY @@ -58,7 +59,7 @@ class AsyncObjectDescriptorConnectionTracing {{sc::kThreadId, internal::CurrentThreadId()}, {"read-start", p.start}, {"read-length", p.length}}); - return result; + return MakeTracingReaderConnection(span_, std::move(result)); } private: diff --git a/google/cloud/storage/internal/async/object_descriptor_connection_tracing_test.cc b/google/cloud/storage/internal/async/object_descriptor_connection_tracing_test.cc index fbecb948f7dc3..46c1e049c2208 100644 --- a/google/cloud/storage/internal/async/object_descriptor_connection_tracing_test.cc +++ b/google/cloud/storage/internal/async/object_descriptor_connection_tracing_test.cc @@ -34,18 +34,41 @@ namespace { using ReadResponse = ::google::cloud::storage_experimental::AsyncReaderConnection::ReadResponse; using ::google::cloud::storage_experimental::ObjectDescriptorConnection; +using ::google::cloud::storage_experimental::ReadPayload; using ::google::cloud::storage_mocks::MockAsyncObjectDescriptorConnection; using ::google::cloud::storage_mocks::MockAsyncReaderConnection; using ::google::cloud::testing_util::EventNamed; using ::google::cloud::testing_util::InstallSpanCatcher; using ::google::cloud::testing_util::OTelAttribute; +using ::google::cloud::testing_util::OTelContextCaptured; +using ::google::cloud::testing_util::PromiseWithOTelContext; using ::google::cloud::testing_util::SpanEventAttributesAre; using ::google::cloud::testing_util::SpanHasInstrumentationScope; using ::google::cloud::testing_util::SpanKindIsClient; using ::google::cloud::testing_util::SpanNamed; using ::google::cloud::testing_util::SpanWithStatus; +using ::google::cloud::testing_util::ThereIsAnActiveSpan; using ::testing::_; +// A helper to set expectations on a mock async reader. It captures the OTel +// context and returns a future that can be controlled by the test. +auto expect_context = [](auto& p) { + return [&p] { + EXPECT_TRUE(ThereIsAnActiveSpan()); + EXPECT_TRUE(OTelContextCaptured()); + return p.get_future(); + }; +}; + +// A helper to be used in a `.then()` clause. It verifies the OTel context +// has been detached before the user receives the result. +auto expect_no_context = [](auto f) { + auto t = f.get(); + EXPECT_FALSE(ThereIsAnActiveSpan()); + EXPECT_FALSE(OTelContextCaptured()); + return t; +}; + TEST(ObjectDescriptorConnectionTracing, Read) { namespace sc = ::opentelemetry::trace::SemanticConventions; auto span_catcher = InstallSpanCatcher(); @@ -76,6 +99,55 @@ TEST(ObjectDescriptorConnectionTracing, Read) { OTelAttribute(sc::kThreadId, _))))))); } +TEST(ObjectDescriptorConnectionTracing, ReadThenRead) { + namespace sc = ::opentelemetry::trace::SemanticConventions; + auto span_catcher = InstallSpanCatcher(); + + auto mock_connection = + std::make_shared(); + auto* mock_reader_ptr = new MockAsyncReaderConnection; + PromiseWithOTelContext p; + EXPECT_CALL(*mock_reader_ptr, Read).WillOnce(expect_context(p)); + + EXPECT_CALL(*mock_connection, Read) + .WillOnce([&](ObjectDescriptorConnection::ReadParams) { + return std::unique_ptr( + mock_reader_ptr); + }); + + auto connection = MakeTracingObjectDescriptorConnection( + internal::MakeSpan("test-span"), std::move(mock_connection)); + + auto reader = connection->Read({}); + auto f = reader->Read().then(expect_no_context); + p.set_value(ReadPayload("test-payload").set_offset(123)); + (void)f.get(); + + connection.reset(); // End the span + + auto spans = span_catcher->GetSpans(); + EXPECT_THAT( + spans, ElementsAre(AllOf( + SpanNamed("test-span"), + SpanWithStatus(opentelemetry::trace::StatusCode::kOk), + SpanHasInstrumentationScope(), SpanKindIsClient(), + SpanEventsAre( + AllOf(EventNamed("gl-cpp.open.read"), + SpanEventAttributesAre( + OTelAttribute("read-length", 0), + OTelAttribute("read-start", 0), + OTelAttribute(sc::kThreadId, _))), + AllOf(EventNamed("gl-cpp.read"), + SpanEventAttributesAre( + OTelAttribute( + "message.starting_offset", 123), + OTelAttribute(sc::kThreadId, _), + OTelAttribute("rpc.message.id", 1), + // THIS WAS THE MISSING ATTRIBUTE: + OTelAttribute("rpc.message.type", + "RECEIVED"))))))); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_internal