mirror of https://github.com/grpc/grpc.git
Gcp Observabiliy : Add constant labels support for stats and tracing (#32128)
* Gcp Observabiliy : Add constant labels support for stats and tracing * Register GCP Observability labels * sanity * Fix build for CI * TEST_TAG_KEY fix * Remove duplicitous constant label settingpull/32146/head^2
parent
ee1d980832
commit
05491fb2f6
14 changed files with 582 additions and 233 deletions
@ -0,0 +1,139 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <string> |
||||
#include <thread> // NOLINT |
||||
#include <vector> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "opencensus/stats/stats.h" |
||||
#include "opencensus/stats/testing/test_utils.h" |
||||
#include "opencensus/tags/tag_map.h" |
||||
|
||||
#include <grpc++/grpc++.h> |
||||
#include <grpcpp/opencensus.h> |
||||
|
||||
#include "src/cpp/ext/filters/census/context.h" |
||||
#include "src/cpp/ext/filters/census/grpc_plugin.h" |
||||
#include "src/proto/grpc/testing/echo.grpc.pb.h" |
||||
#include "test/core/util/test_config.h" |
||||
#include "test/cpp/ext/filters/census/library.h" |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
namespace { |
||||
|
||||
using ::opencensus::stats::View; |
||||
using ::opencensus::stats::testing::TestUtils; |
||||
|
||||
class ConstantLabelsTest : public StatsPluginEnd2EndTest { |
||||
protected: |
||||
static void SetUpTestSuite() { |
||||
grpc::internal::OpenCensusRegistry::Get().RegisterConstantLabels( |
||||
{{"key", "value"}}); |
||||
StatsPluginEnd2EndTest::SetUpTestSuite(); |
||||
} |
||||
}; |
||||
|
||||
// Check that constant labels registered to OpenCensus are exported.
|
||||
TEST_F(ConstantLabelsTest, ConstantLabelsTest) { |
||||
View client_completed_rpcs_view(ClientCompletedRpcsCumulative()); |
||||
View server_completed_rpcs_view(ServerCompletedRpcsCumulative()); |
||||
|
||||
EchoRequest request; |
||||
request.set_message("foo"); |
||||
EchoResponse response; |
||||
|
||||
{ |
||||
grpc::ClientContext context; |
||||
grpc::Status status = stub_->Echo(&context, request, &response); |
||||
ASSERT_TRUE(status.ok()); |
||||
EXPECT_EQ("foo", response.message()); |
||||
} |
||||
|
||||
absl::SleepFor(absl::Milliseconds(500 * grpc_test_slowdown_factor())); |
||||
TestUtils::Flush(); |
||||
|
||||
EXPECT_THAT( |
||||
client_completed_rpcs_view.GetData().int_data(), |
||||
::testing::UnorderedElementsAre(::testing::Pair( |
||||
::testing::ElementsAre("value", client_method_name_, "OK"), 1))); |
||||
EXPECT_THAT( |
||||
server_completed_rpcs_view.GetData().int_data(), |
||||
::testing::UnorderedElementsAre(::testing::Pair( |
||||
::testing::ElementsAre("value", server_method_name_, "OK"), 1))); |
||||
} |
||||
|
||||
TEST_F(ConstantLabelsTest, ConstantAttributesTest) { |
||||
{ |
||||
// Client spans are ended when the ClientContext's destructor is invoked.
|
||||
auto channel = CreateChannel(server_address_, InsecureChannelCredentials()); |
||||
ResetStub(channel); |
||||
EchoRequest request; |
||||
request.set_message("foo"); |
||||
EchoResponse response; |
||||
|
||||
grpc::ClientContext context; |
||||
::opencensus::trace::AlwaysSampler always_sampler; |
||||
::opencensus::trace::StartSpanOptions options; |
||||
options.sampler = &always_sampler; |
||||
auto sampling_span = |
||||
::opencensus::trace::Span::StartSpan("sampling", nullptr, options); |
||||
grpc::CensusContext app_census_context("root", &sampling_span, |
||||
::opencensus::tags::TagMap{}); |
||||
context.set_census_context( |
||||
reinterpret_cast<census_context*>(&app_census_context)); |
||||
context.AddMetadata(kExpectedTraceIdKey, |
||||
app_census_context.Span().context().trace_id().ToHex()); |
||||
traces_recorder_->StartRecording(); |
||||
grpc::Status status = stub_->Echo(&context, request, &response); |
||||
EXPECT_TRUE(status.ok()); |
||||
} |
||||
absl::SleepFor(absl::Milliseconds(500 * grpc_test_slowdown_factor())); |
||||
TestUtils::Flush(); |
||||
::opencensus::trace::exporter::SpanExporterTestPeer::ExportForTesting(); |
||||
traces_recorder_->StopRecording(); |
||||
auto recorded_spans = traces_recorder_->GetAndClearSpans(); |
||||
// We never ended the two spans created in the scope above, so we don't
|
||||
// expect them to be exported.
|
||||
for (const auto& span : recorded_spans) { |
||||
bool found = false; |
||||
for (const auto& attribute : span.attributes()) { |
||||
if (attribute.first == "key" && |
||||
attribute.second.string_value() == "value") { |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
EXPECT_TRUE(found); |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -0,0 +1,33 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include "test/cpp/ext/filters/census/library.h" |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
const ::opencensus::tags::TagKey TEST_TAG_KEY = |
||||
::opencensus::tags::TagKey::Register("my_key"); |
||||
const char* TEST_TAG_VALUE = "my_value"; |
||||
const char* kExpectedTraceIdKey = "expected_trace_id"; |
||||
|
||||
ExportedTracesRecorder* StatsPluginEnd2EndTest::traces_recorder_ = |
||||
new ExportedTracesRecorder(); |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
@ -0,0 +1,194 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include <string> |
||||
#include <thread> // NOLINT |
||||
#include <vector> |
||||
|
||||
#include "absl/strings/str_cat.h" |
||||
#include "gtest/gtest.h" |
||||
#include "opencensus/stats/stats.h" |
||||
#include "opencensus/trace/exporter/span_exporter.h" |
||||
|
||||
#include <grpc++/grpc++.h> |
||||
#include <grpcpp/opencensus.h> |
||||
|
||||
#include "src/core/lib/config/core_configuration.h" |
||||
#include "src/cpp/ext/filters/census/context.h" |
||||
#include "src/proto/grpc/testing/echo.grpc.pb.h" |
||||
#include "test/core/util/test_lb_policies.h" |
||||
#include "test/cpp/end2end/test_service_impl.h" |
||||
|
||||
namespace opencensus { |
||||
namespace trace { |
||||
namespace exporter { |
||||
class SpanExporterTestPeer { |
||||
public: |
||||
static constexpr auto& ExportForTesting = SpanExporter::ExportForTesting; |
||||
}; |
||||
} // namespace exporter
|
||||
} // namespace trace
|
||||
} // namespace opencensus
|
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
|
||||
extern const ::opencensus::tags::TagKey TEST_TAG_KEY; |
||||
extern const char* TEST_TAG_VALUE; |
||||
extern const char* kExpectedTraceIdKey; |
||||
|
||||
class EchoServer final : public TestServiceImpl { |
||||
Status Echo(ServerContext* context, const EchoRequest* request, |
||||
EchoResponse* response) override { |
||||
CheckMetadata(context); |
||||
return TestServiceImpl::Echo(context, request, response); |
||||
} |
||||
|
||||
Status BidiStream( |
||||
ServerContext* context, |
||||
ServerReaderWriter<EchoResponse, EchoRequest>* stream) override { |
||||
CheckMetadata(context); |
||||
return TestServiceImpl::BidiStream(context, stream); |
||||
} |
||||
|
||||
private: |
||||
void CheckMetadata(ServerContext* context) { |
||||
for (const auto& metadata : context->client_metadata()) { |
||||
if (metadata.first == kExpectedTraceIdKey) { |
||||
EXPECT_EQ(metadata.second, reinterpret_cast<const CensusContext*>( |
||||
context->census_context()) |
||||
->Span() |
||||
.context() |
||||
.trace_id() |
||||
.ToHex()); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// A handler that records exported traces. Traces can later be retrieved and
|
||||
// inspected.
|
||||
class ExportedTracesRecorder |
||||
: public ::opencensus::trace::exporter::SpanExporter::Handler { |
||||
public: |
||||
ExportedTracesRecorder() : is_recording_(false) {} |
||||
void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) |
||||
override { |
||||
absl::MutexLock lock(&mutex_); |
||||
if (is_recording_) { |
||||
for (auto const& span : spans) { |
||||
recorded_spans_.push_back(span); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void StartRecording() { |
||||
absl::MutexLock lock(&mutex_); |
||||
ASSERT_FALSE(is_recording_); |
||||
is_recording_ = true; |
||||
} |
||||
|
||||
void StopRecording() { |
||||
absl::MutexLock lock(&mutex_); |
||||
ASSERT_TRUE(is_recording_); |
||||
is_recording_ = false; |
||||
} |
||||
|
||||
std::vector<::opencensus::trace::exporter::SpanData> GetAndClearSpans() { |
||||
absl::MutexLock lock(&mutex_); |
||||
return std::move(recorded_spans_); |
||||
} |
||||
|
||||
private: |
||||
// This mutex is necessary as the SpanExporter runs a loop on a separate
|
||||
// thread which periodically exports spans.
|
||||
absl::Mutex mutex_; |
||||
bool is_recording_ ABSL_GUARDED_BY(mutex_); |
||||
std::vector<::opencensus::trace::exporter::SpanData> recorded_spans_ |
||||
ABSL_GUARDED_BY(mutex_); |
||||
}; |
||||
|
||||
class StatsPluginEnd2EndTest : public ::testing::Test { |
||||
protected: |
||||
static void SetUpTestSuite() { |
||||
grpc_core::CoreConfiguration::Reset(); |
||||
grpc_core::CoreConfiguration::RegisterBuilder( |
||||
[](grpc_core::CoreConfiguration::Builder* builder) { |
||||
grpc_core::RegisterQueueOnceLoadBalancingPolicy(builder); |
||||
}); |
||||
RegisterOpenCensusPlugin(); |
||||
// OpenCensus C++ has no API to unregister a previously-registered handler,
|
||||
// therefore we register this handler once, and enable/disable recording in
|
||||
// the individual tests.
|
||||
::opencensus::trace::exporter::SpanExporter::RegisterHandler( |
||||
absl::WrapUnique(traces_recorder_)); |
||||
} |
||||
|
||||
static void TearDownTestSuite() { |
||||
grpc_shutdown(); |
||||
grpc_core::CoreConfiguration::Reset(); |
||||
} |
||||
|
||||
void SetUp() override { |
||||
// Set up a synchronous server on a different thread to avoid the asynch
|
||||
// interface.
|
||||
grpc::ServerBuilder builder; |
||||
int port; |
||||
// Use IPv4 here because it's less flaky than IPv6 ("[::]:0") on Travis.
|
||||
builder.AddListeningPort("0.0.0.0:0", grpc::InsecureServerCredentials(), |
||||
&port); |
||||
builder.RegisterService(&service_); |
||||
server_ = builder.BuildAndStart(); |
||||
ASSERT_NE(nullptr, server_); |
||||
ASSERT_NE(0, port); |
||||
server_address_ = absl::StrCat("localhost:", port); |
||||
server_thread_ = std::thread(&StatsPluginEnd2EndTest::RunServerLoop, this); |
||||
|
||||
stub_ = EchoTestService::NewStub(grpc::CreateChannel( |
||||
server_address_, grpc::InsecureChannelCredentials())); |
||||
|
||||
// Clear out any previous spans
|
||||
::opencensus::trace::exporter::SpanExporterTestPeer::ExportForTesting(); |
||||
} |
||||
|
||||
void ResetStub(std::shared_ptr<Channel> channel) { |
||||
stub_ = EchoTestService::NewStub(channel); |
||||
} |
||||
|
||||
void TearDown() override { |
||||
server_->Shutdown(); |
||||
server_thread_.join(); |
||||
} |
||||
|
||||
void RunServerLoop() { server_->Wait(); } |
||||
|
||||
const std::string client_method_name_ = "grpc.testing.EchoTestService/Echo"; |
||||
const std::string server_method_name_ = "grpc.testing.EchoTestService/Echo"; |
||||
|
||||
std::string server_address_; |
||||
EchoServer service_; |
||||
std::unique_ptr<grpc::Server> server_; |
||||
std::thread server_thread_; |
||||
|
||||
std::unique_ptr<EchoTestService::Stub> stub_; |
||||
static ExportedTracesRecorder* traces_recorder_; |
||||
}; |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
Loading…
Reference in new issue