Skip to content

Commit

Permalink
impl(common): LogWrapper with explicit Options (#12398)
Browse files Browse the repository at this point in the history
  • Loading branch information
coryan authored Aug 17, 2023
1 parent 58b1cab commit a9174ce
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ class TestStubLogging : public TestStub {
std::shared_ptr<grpc::ClientContext> context,
GetTableRequest const& request) override {
return LogWrapper(
[this](google::cloud::CompletionQueue& cq, auto context,
[this](google::cloud::CompletionQueue& cq,
std::shared_ptr<grpc::ClientContext> context,
GetTableRequest const& request) {
return child_->AsyncGetTable(cq, std::move(context), request);
},
Expand Down
50 changes: 42 additions & 8 deletions google/cloud/internal/log_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "google/cloud/internal/debug_string_status.h"
#include "google/cloud/internal/invoke_result.h"
#include "google/cloud/log.h"
#include "google/cloud/options.h"
#include "google/cloud/status_or.h"
#include "google/cloud/tracing_options.h"
#include "google/cloud/version.h"
Expand Down Expand Up @@ -104,13 +105,29 @@ std::unique_ptr<T> LogResponse(std::unique_ptr<T> response,
return response;
}

template <typename Functor, typename Request, typename Context,
typename Result = google::cloud::internal::invoke_result_t<
Functor, Context, Options const&, Request const&>>
Result LogWrapper(Functor&& functor, Context&& context, Options const& opts,
Request const& request, char const* where,
TracingOptions const& options) {
LogRequest(where, "", DebugString(request, options));
return LogResponse(functor(std::forward<Context>(context), opts, request),
where, "", options);
}

template <typename Functor, typename Request, typename Context,
typename Result = google::cloud::internal::invoke_result_t<
Functor, Context, Request const&>>
Result LogWrapper(Functor&& functor, Context&& context, Request const& request,
char const* where, TracingOptions const& options) {
LogRequest(where, "", DebugString(request, options));
return LogResponse(functor(context, request), where, "", options);
auto wrapper = [functor = std::forward<Functor>(functor)](
Context&& context, Options const&,
Request const& request) {
return functor(std::forward<Context>(context), request);
};
return LogWrapper(std::move(wrapper), std::forward<Context>(context),
Options{}, request, where, options);
}

template <
Expand All @@ -124,21 +141,38 @@ Result LogWrapper(Functor&& functor, grpc::ClientContext& context,
return LogResponse(functor(context, request, cq), where, "", options);
}

template <
typename Functor, typename Request, typename Context,
typename Result = google::cloud::internal::invoke_result_t<
Functor, google::cloud::CompletionQueue&, Context, Request const&>>
template <typename Functor, typename Request, typename Context,
typename Result = google::cloud::internal::invoke_result_t<
Functor, google::cloud::CompletionQueue&, Context, Options const&,
Request const&>>
Result LogWrapper(Functor&& functor, google::cloud::CompletionQueue& cq,
Context&& context, Request const& request, char const* where,
Context&& context, Options const& opts,
Request const& request, char const* where,
TracingOptions const& options) {
// Because this is an asynchronous request we need a unique identifier so
// applications can match the request and response in the log.
auto args = RequestIdForLogging();
LogRequest(where, args, DebugString(request, options));
return LogResponse(functor(cq, std::forward<Context>(context), request),
return LogResponse(functor(cq, std::forward<Context>(context), opts, request),
where, std::move(args), options);
}

template <
typename Functor, typename Request, typename Context,
typename Result = google::cloud::internal::invoke_result_t<
Functor, google::cloud::CompletionQueue&, Context, Request const&>>
Result LogWrapper(Functor&& functor, google::cloud::CompletionQueue& cq,
Context&& context, Request const& request, char const* where,
TracingOptions const& options) {
auto wrapper = [functor = std::forward<Functor>(functor)](
google::cloud::CompletionQueue& cq, Context context,
Options const&, Request const& request) {
return functor(cq, std::move(context), request);
};
return LogWrapper(std::move(wrapper), cq, std::forward<Context>(context),
Options{}, request, where, options);
}

} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace cloud
Expand Down
120 changes: 120 additions & 0 deletions google/cloud/internal/log_wrapper_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ using ::testing::NotNull;
using Request = google::protobuf::Duration;
using Response = google::protobuf::Timestamp;

struct TestOption {
using Type = std::string;
};
struct TestContext {};

template <typename T>
Expand Down Expand Up @@ -267,6 +270,123 @@ TYPED_TEST(LogWrapperTest, AsyncError) {
HasSubstr(" >> future_status="))));
}

TYPED_TEST(LogWrapperTest, BlockingSuccessExplicitOptions) {
using ReturnType = std::tuple_element_t<0, typename TestFixture::TestTypes>;
using ContextPtrType =
std::tuple_element_t<1, typename TestFixture::TestTypes>;
using ContextType = typename ContextPtrType::element_type;

auto functor = [](ContextType&, Options const& opts, Request const&) {
EXPECT_EQ(opts.get<TestOption>(), "test-option");
return SuccessValue(ReturnType{});
};

testing_util::ScopedLog log;

auto context = MakeContext(ContextPtrType{});
auto actual =
LogWrapper(functor, *context, Options{}.set<TestOption>("test-option"),
MakeRequest(), "in-test", TracingOptions{});
EXPECT_THAT(actual, IsSuccess(ReturnType{}));

auto const log_lines = log.ExtractLines();
auto const expected_request = DebugString(MakeRequest(), TracingOptions{});
auto const expected_response = SuccessMarker(ReturnType{});
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" << "),
HasSubstr(expected_request))));
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" >> "),
HasSubstr(expected_response))));
}

TYPED_TEST(LogWrapperTest, BlockingErrorExplicitOptions) {
using ReturnType = std::tuple_element_t<0, typename TestFixture::TestTypes>;
using ContextPtrType =
std::tuple_element_t<1, typename TestFixture::TestTypes>;
using ContextType = typename ContextPtrType::element_type;

auto functor = [](ContextType&, Options const& opts, Request const&) {
EXPECT_EQ(opts.get<TestOption>(), "test-option");
return ErrorValue(ReturnType{});
};

testing_util::ScopedLog log;

google::cloud::CompletionQueue cq;
auto context = MakeContext(ContextPtrType{});
auto actual =
LogWrapper(functor, *context, Options{}.set<TestOption>("test-option"),
MakeRequest(), "in-test", TracingOptions{});
EXPECT_THAT(actual, IsError(ReturnType{}));

auto const log_lines = log.ExtractLines();
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" << "))));
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" >> status="))));
}

TYPED_TEST(LogWrapperTest, AsyncSuccessExplicitOptions) {
using ReturnType = std::tuple_element_t<0, typename TestFixture::TestTypes>;
using ContextType = std::tuple_element_t<1, typename TestFixture::TestTypes>;

auto functor = [](google::cloud::CompletionQueue&, ContextType,
Options const& opts, Request const&) {
EXPECT_EQ(opts.get<TestOption>(), "test-option");
return make_ready_future(SuccessValue(ReturnType{}));
};

testing_util::ScopedLog log;

google::cloud::CompletionQueue cq;
auto context = MakeContext(ContextType{});
auto actual = LogWrapper(functor, cq, std::move(context),
Options{}.set<TestOption>("test-option"),
MakeRequest(), "in-test", TracingOptions{});
EXPECT_THAT(actual.get(), IsSuccess(ReturnType{}));

auto const log_lines = log.ExtractLines();
auto const expected_request = DebugString(MakeRequest(), TracingOptions{});
auto const expected_response = SuccessMarker(ReturnType{});
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" << "),
HasSubstr(expected_request))));
EXPECT_THAT(log_lines, Contains(AllOf(HasSubstr("in-test("),
HasSubstr(" >> future_status="))));
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" >> "),
HasSubstr(expected_response))));
}

TYPED_TEST(LogWrapperTest, AsyncErrorExplicitOptions) {
using ReturnType = std::tuple_element_t<0, typename TestFixture::TestTypes>;
using ContextType = std::tuple_element_t<1, typename TestFixture::TestTypes>;

auto functor = [](google::cloud::CompletionQueue&, ContextType,
Options const& opts, Request const&) {
EXPECT_EQ(opts.get<TestOption>(), "test-option");
return make_ready_future(ErrorValue(ReturnType{}));
};

testing_util::ScopedLog log;

google::cloud::CompletionQueue cq;
auto context = MakeContext(ContextType{});
auto actual = LogWrapper(functor, cq, std::move(context),
Options{}.set<TestOption>("test-option"),
MakeRequest(), "in-test", TracingOptions{});
EXPECT_THAT(actual.get(), IsError(ReturnType{}));

auto const log_lines = log.ExtractLines();
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" << "))));
EXPECT_THAT(log_lines,
Contains(AllOf(HasSubstr("in-test("), HasSubstr(" >> status="))));
EXPECT_THAT(log_lines, Contains(AllOf(HasSubstr("in-test("),
HasSubstr(" >> future_status="))));
}

} // namespace
} // namespace internal
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Expand Down

0 comments on commit a9174ce

Please sign in to comment.