mirror of https://github.com/grpc/grpc.git
[logging] Add a helper to drop a list of variables out in a LOG statement (#36554)
Not the fastest implementation possible, but it's a log helper so I'm not particularly fussed either -- but a useful utility that we can iterate on later to help debugging.
Closes #36554
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/36554 from ctiller:args 33b4802fcc
PiperOrigin-RevId: 632997320
pull/36592/head^2
parent
989268253e
commit
06b097691e
9 changed files with 280 additions and 3 deletions
@ -0,0 +1,54 @@ |
||||
// Copyright 2024 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 "src/core/lib/gprpp/dump_args.h" |
||||
|
||||
#include "absl/log/check.h" |
||||
#include "absl/strings/ascii.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace dump_args_detail { |
||||
|
||||
std::ostream& operator<<(std::ostream& out, const DumpArgs& args) { |
||||
// Parse the argument string into a vector of keys.
|
||||
// #__VA_ARGS__ produces a stringified version of the arguments passed to the
|
||||
// macro. It's comma separated, and we can use that to split the string into
|
||||
// keys. Those keys might include parenthesis for e.g. argument lists, and so
|
||||
// we need to skip commas that are inside parenthesis.
|
||||
std::vector<absl::string_view> keys; |
||||
int depth = 0; |
||||
const char* start = args.arg_string_; |
||||
for (const char* p = args.arg_string_; *p; ++p) { |
||||
if (*p == '(') { |
||||
++depth; |
||||
} else if (*p == ')') { |
||||
--depth; |
||||
} else if (*p == ',' && depth == 0) { |
||||
keys.push_back(absl::string_view(start, p - start)); |
||||
start = p + 1; |
||||
} |
||||
} |
||||
keys.push_back(start); |
||||
CHECK_EQ(keys.size(), args.arg_dumpers_.size()); |
||||
for (size_t i = 0; i < keys.size(); i++) { |
||||
if (i != 0) out << ", "; |
||||
out << absl::StripAsciiWhitespace(keys[i]) << " = "; |
||||
args.arg_dumpers_[i](out); |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
} // namespace dump_args_detail
|
||||
} // namespace grpc_core
|
@ -0,0 +1,69 @@ |
||||
// Copyright 2024 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_LIB_GPRPP_DUMP_ARGS_H |
||||
#define GRPC_SRC_CORE_LIB_GPRPP_DUMP_ARGS_H |
||||
|
||||
#include <ostream> |
||||
#include <vector> |
||||
|
||||
#include "absl/functional/any_invocable.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace dump_args_detail { |
||||
|
||||
// Helper function... just ignore the initializer list passed into it.
|
||||
// Allows doing 'statements' via parameter pack expansion in C++11 - given
|
||||
// template <typename... Ts>:
|
||||
// do_these_things({foo<Ts>()...});
|
||||
// will execute foo<T>() for each T in Ts.
|
||||
template <typename T> |
||||
void do_these_things(std::initializer_list<T>) {} |
||||
|
||||
class DumpArgs { |
||||
public: |
||||
template <typename... Args> |
||||
explicit DumpArgs(const char* arg_string, const Args&... args) |
||||
: arg_string_(arg_string) { |
||||
do_these_things( |
||||
{AddDumper([a = &args](std::ostream& os) { os << *a; })...}); |
||||
} |
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const DumpArgs& args); |
||||
|
||||
private: |
||||
int AddDumper(absl::AnyInvocable<void(std::ostream&) const> dumper) { |
||||
arg_dumpers_.push_back(std::move(dumper)); |
||||
return 0; |
||||
} |
||||
|
||||
const char* arg_string_; |
||||
std::vector<absl::AnyInvocable<void(std::ostream&) const>> arg_dumpers_; |
||||
}; |
||||
|
||||
} // namespace dump_args_detail
|
||||
} // namespace grpc_core
|
||||
|
||||
// Helper to print a list of variables and their values.
|
||||
// Each type must be streamable to std::ostream.
|
||||
// Usage:
|
||||
// int a = 1;
|
||||
// int b = 2;
|
||||
// LOG(INFO) << GRPC_DUMP_ARGS(a, b)
|
||||
// Output:
|
||||
// a = 1, b = 2
|
||||
#define GRPC_DUMP_ARGS(...) \ |
||||
grpc_core::dump_args_detail::DumpArgs(#__VA_ARGS__, __VA_ARGS__) |
||||
|
||||
#endif // GRPC_SRC_CORE_LIB_GPRPP_DUMP_ARGS_H
|
@ -0,0 +1,44 @@ |
||||
// Copyright 2024 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 "src/core/lib/gprpp/dump_args.h" |
||||
|
||||
#include <sstream> |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
template <typename T> |
||||
std::string Stringify(const T& t) { |
||||
std::ostringstream oss; |
||||
oss << t; |
||||
return oss.str(); |
||||
} |
||||
|
||||
int add(int a, int b) { return a + b; } |
||||
|
||||
TEST(DumpArgsTest, Basic) { |
||||
int a = 1; |
||||
int b = 2; |
||||
int c = 3; |
||||
EXPECT_EQ("a = 1, b = 2, c = 3", Stringify(GRPC_DUMP_ARGS(a, b, c))); |
||||
} |
||||
|
||||
TEST(DumpArgsTest, FunctionCall) { |
||||
EXPECT_EQ("add(1, 2) = 3", Stringify(GRPC_DUMP_ARGS(add(1, 2)))); |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue