2727#include < sys/types.h>
2828
2929#include < algorithm>
30- #include < limits>
3130#include < memory>
3231#include < tuple>
3332#include < type_traits>
34- #include < variant>
3533
3634#include " absl/base/thread_annotations.h"
3735#include " absl/strings/str_cat.h"
3836#include " absl/strings/string_view.h"
39- #include " src/core/util/dual_ref_counted .h"
37+ #include " src/core/lib/debug/trace_flags .h"
4038#include " src/core/util/json/json.h"
4139#include " src/core/util/memory_usage.h"
4240#include " src/core/util/ref_counted_ptr.h"
43- #include " src/core/util/single_set_ptr.h"
4441#include " src/core/util/sync.h"
4542#include " src/core/util/time.h"
4643
@@ -51,6 +48,100 @@ namespace testing {
5148size_t GetSizeofTraceEvent (void );
5249}
5350
51+ namespace detail {
52+
53+ class Renderer {
54+ public:
55+ virtual ~Renderer () = default ;
56+ virtual std::string Render () const = 0;
57+ virtual size_t MemoryUsage () const = 0;
58+ };
59+
60+ struct StrCatFn {
61+ template <typename ... Arg>
62+ std::string operator ()(const Arg&... args) {
63+ return absl::StrCat (args...);
64+ }
65+ };
66+
67+ template <typename A>
68+ auto AdaptForStorage (A&& a) {
69+ using RawA = std::remove_reference_t <A>;
70+ if constexpr (std::is_same_v<std::decay_t <RawA>, const char *>) {
71+ return absl::string_view (a);
72+ } else {
73+ return RawA (std::forward<A>(a));
74+ }
75+ }
76+
77+ template <typename ... Args>
78+ std::unique_ptr<Renderer> RendererFromConcatenationInner (Args&&... args) {
79+ class R final : public Renderer {
80+ public:
81+ explicit R (Args&&... args) : args_(std::forward<Args>(args)...) {}
82+
83+ std::string Render () const override {
84+ return std::apply (StrCatFn (), args_);
85+ }
86+ size_t MemoryUsage () const override {
87+ return MemoryUsageOf (args_) + sizeof (Renderer);
88+ }
89+
90+ private:
91+ std::tuple<Args...> args_;
92+ };
93+ return std::make_unique<R>(std::forward<Args>(args)...);
94+ }
95+
96+ template <typename ... Args>
97+ std::unique_ptr<Renderer> RendererFromConcatenation (Args&&... args) {
98+ return RendererFromConcatenationInner (
99+ AdaptForStorage<Args>(std::forward<Args>(args))...);
100+ }
101+
102+ struct RendererFromConcatenationFn {
103+ template <typename ... Args>
104+ auto operator ()(Args&&... args) {
105+ return RendererFromConcatenation (std::forward<Args>(args)...);
106+ }
107+ };
108+
109+ template <typename N>
110+ void OutputLogFromLogExpr (N* out, std::unique_ptr<Renderer> renderer) {
111+ out->NewNode (std::move (renderer)).Commit ();
112+ }
113+
114+ template <typename N, typename ... T>
115+ class LogExpr {
116+ public:
117+ explicit LogExpr (N* node, T&&... values)
118+ : out_(node), values_(std::forward<T>(values)...) {}
119+
120+ ~LogExpr () {
121+ if (out_ != nullptr ) {
122+ OutputLogFromLogExpr (out_,
123+ std::apply (detail::RendererFromConcatenationFn (),
124+ std::move (values_)));
125+ }
126+ }
127+
128+ template <typename U>
129+ friend auto operator <<(LogExpr<N, T...>&& x, U&& u) {
130+ auto mk = [out = std::exchange (x.out_ , nullptr ),
131+ u = AdaptForStorage (std::forward<U>(u))](
132+ T&&... existing_values) mutable {
133+ return LogExpr<N, T..., decltype (u)>(
134+ out, std::forward<T>(existing_values)..., std::move (u));
135+ };
136+ return std::apply (mk, std::move (x.values_ ));
137+ }
138+
139+ private:
140+ N* out_;
141+ std::tuple<T...> values_;
142+ };
143+ } // namespace detail
144+
54145class BaseNode ;
55146
56147// Object used to hold live data for a channel. This data is exposed via the
@@ -67,12 +158,7 @@ class ChannelTrace {
67158 explicit ChannelTrace (size_t max_memory)
68159 : max_memory_(std::min(max_memory, sizeof (Entry) * 32768)) {}
69160
70- class Renderer {
71- public:
72- virtual ~Renderer () = default ;
73- virtual std::string Render () const = 0;
74- virtual size_t MemoryUsage () const = 0;
75- };
161+ using Renderer = detail::Renderer;
76162
77163 enum Severity {
78164 Unset = 0 , // never to be used
@@ -147,7 +233,7 @@ class ChannelTrace {
147233 // Returns a new `Node` object representing the child.
148234 // If this node is invalid (e.g., default-constructed or moved-from),
149235 // an invalid `Node` is returned.
150- [[nodiscard]] Node NewChild (std::unique_ptr<Renderer> renderer) {
236+ [[nodiscard]] Node NewNode (std::unique_ptr<Renderer> renderer) {
151237 if (trace_ == nullptr || ref_.id == kSentinelId ) return Node ();
152238 return Node (trace_, trace_->AppendEntry (ref_, std::move (renderer)));
153239 }
@@ -160,9 +246,10 @@ class ChannelTrace {
160246 // If this node is invalid (e.g., default-constructed or moved-from),
161247 // an invalid `Node` is returned.
162248 template <typename ... Args>
163- [[nodiscard]] Node NewChild (Args&&... args) {
249+ [[nodiscard]] Node NewNode (Args&&... args) {
164250 if (trace_ == nullptr || ref_.id == kSentinelId ) return Node ();
165- return NewChild (RendererFromConcatenation (std::forward<Args>(args)...));
251+ return NewNode (
252+ detail::RendererFromConcatenation (std::forward<Args>(args)...));
166253 }
167254
168255 // Marks the trace entry associated with this `Node` as permanent.
@@ -174,6 +261,8 @@ class ChannelTrace {
174261 committed_ = true ;
175262 }
176263
264+ bool ProducesOutput () const { return ref_.id != kSentinelId ; }
265+
177266 private:
178267 friend class ChannelTrace ;
179268
@@ -203,7 +292,8 @@ class ChannelTrace {
203292
204293 template <typename ... Args>
205294 [[nodiscard]] Node NewNode (Args&&... args) {
206- return NewNode (RendererFromConcatenation (std::forward<Args>(args)...));
295+ return NewNode (
296+ detail::RendererFromConcatenation (std::forward<Args>(args)...));
207297 }
208298
209299 // Creates and returns the raw Json object, so a parent channelz
@@ -215,6 +305,8 @@ class ChannelTrace {
215305 RefCountedPtr<BaseNode>)>
216306 callback) const ABSL_LOCKS_EXCLUDED(mu_);
217307
308+ bool ProducesOutput () const { return max_memory_ > 0 ; }
309+
218310 std::string creation_timestamp () const ;
219311 uint64_t num_events_logged () const {
220312 MutexLock lock (&mu_);
@@ -281,49 +373,6 @@ class ChannelTrace {
281373 void DropEntry (EntryRef entry) ABSL_LOCKS_EXCLUDED(mu_);
282374 void DropEntryId (uint16_t id) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
283375
284- struct StrCatFn {
285- template <typename ... Arg>
286- std::string operator ()(const Arg&... args) {
287- return absl::StrCat (args...);
288- }
289- };
290-
291- template <typename A>
292- static auto AdaptForStorage (A&& a) {
293- using RawA = std::remove_reference_t <A>;
294- if constexpr (std::is_same_v<std::decay_t <RawA>, const char *>) {
295- return absl::string_view (a);
296- } else {
297- return RawA (a);
298- }
299- }
300-
301- template <typename ... Args>
302- static std::unique_ptr<Renderer> RendererFromConcatenationInner (
303- Args&&... args) {
304- class R final : public Renderer {
305- public:
306- explicit R (Args&&... args) : args_(std::forward<Args>(args)...) {}
307-
308- std::string Render () const override {
309- return std::apply (StrCatFn (), args_);
310- }
311- size_t MemoryUsage () const override {
312- return MemoryUsageOf (args_) + sizeof (Renderer);
313- }
314-
315- private:
316- std::tuple<Args...> args_;
317- };
318- return std::make_unique<R>(std::forward<Args>(args)...);
319- }
320-
321- template <typename ... Args>
322- static std::unique_ptr<Renderer> RendererFromConcatenation (Args&&... args) {
323- return RendererFromConcatenationInner (
324- AdaptForStorage<Args>(std::forward<Args>(args))...);
325- }
326-
327376 void RenderEntry (const Entry& entry,
328377 absl::FunctionRef<void (gpr_timespec, Severity, std::string,
329378 RefCountedPtr<BaseNode>)>
@@ -341,7 +390,93 @@ class ChannelTrace {
341390 std::vector<Entry> entries_ ABSL_GUARDED_BY (mu_);
342391};
343392
393+ // A node that GRPC_CHANNELZ_TRACE can output to, that also
394+ // logs to absl LOG(INFO) if a particular TraceFlag is enabled.
395+ // Provides a way to elevate GRPC_TRACE_LOG statements to channelz
396+ // also.
397+ class TraceNode {
398+ public:
399+ TraceNode () = default ;
400+
401+ template <typename F>
402+ TraceNode (ChannelTrace::Node node, TraceFlag& flag, F prefix)
403+ : node_(std::move(node)) {
404+ if (GPR_UNLIKELY (flag.enabled ())) {
405+ log_to_absl_ = std::make_unique<std::string>(prefix ());
406+ }
407+ }
408+
409+ bool ProducesOutput () const {
410+ return node_.ProducesOutput () || log_to_absl_ != nullptr ;
411+ }
412+
413+ void Finish (std::unique_ptr<detail::Renderer> renderer) {
414+ if (GPR_UNLIKELY (log_to_absl_ != nullptr )) {
415+ LOG (INFO) << *log_to_absl_ << renderer->Render ();
416+ }
417+ node_.NewNode (std::move (renderer)).Commit ();
418+ }
419+
420+ void Commit () { node_.Commit (); }
421+
422+ private:
423+ ChannelTrace::Node node_;
424+ std::unique_ptr<std::string> log_to_absl_;
425+ };
426+
427+ namespace detail {
428+ inline ChannelTrace* LogOutputFrom (ChannelTrace& t) {
429+ if (!t.ProducesOutput ()) return nullptr ;
430+ return &t;
431+ }
432+
433+ inline ChannelTrace::Node* LogOutputFrom (ChannelTrace::Node& n) {
434+ if (!n.ProducesOutput ()) return nullptr ;
435+ return &n;
436+ }
437+
438+ inline TraceNode* LogOutputFrom (TraceNode& n) {
439+ if (!n.ProducesOutput ()) return nullptr ;
440+ return &n;
441+ }
442+
443+ inline void OutputLogFromLogExpr (TraceNode* out,
444+ std::unique_ptr<Renderer> renderer) {
445+ out->Finish (std::move (renderer));
446+ }
447+ } // namespace detail
448+
344449} // namespace channelz
345450} // namespace grpc_core
346451
452+ // Log like LOG() to a channelz object (and potentially as part of a GRPC_TRACE
453+ // log with channelz::TraceNode).
454+ //
455+ // `output` is one of a ChannelTrace, ChannelTrace::Node or a TraceNode.
456+ //
457+ // This trace always commits - and the channelz node is inaccessible as a
458+ // result. Use it for annotation like things, and if commit-ability is
459+ // important, put it under a parent node and use that for `output`.
460+ //
461+ // Notes on this weird macro!
462+ // - We want this to be a statement level thing, such that end of statement ==>
463+ // we can commit the log line.
464+ // - To do that we need to ensure we're not an expression, so we want an if,
465+ // for, while, or do statement enclosing things.
466+ // - But hey! we want to stream after the GRPC_CHANNELZ_LOG(foo), so we want an
467+ // if right - if (we_can_output) output << s1 << s2 << s3;
468+ // - But hey! now if someone copy/pastes this in front of an else then we
469+ // bind with that else, and boom we've got a security hole... so let's not do
470+ // that.
471+ // - So, the for here acts as an if (the output = nullptr part ensures we don't)
472+ // actually loop), ensures we have a statement, and ensures we don't
473+ // accidentally bind with a trailing else.
474+ // - And of course we skip wrapping things in {} because we really like that
475+ // GRPC_CHANNELZ_LOG(foo) << "hello!"; syntax.
476+ #define GRPC_CHANNELZ_LOG (output ) \
477+ for (auto * out = grpc_core::channelz::detail::LogOutputFrom(output); \
478+ out != nullptr ; out = nullptr ) \
479+ grpc_core::channelz::detail::LogExpr< \
480+ std::remove_reference_t <decltype (*out)>>(out)
481+
347482#endif // GRPC_SRC_CORE_CHANNELZ_CHANNEL_TRACE_H
0 commit comments