1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4 
5 #pragma once
6 
7 #include <cstring>
8 #include <fstream>
9 #include <iostream>
10 #include <sstream>
11 #include <thread>
12 #include <type_traits>
13 #include <typeinfo>
14 #include <unordered_map>
15 
16 #include "caf/abstract_actor.hpp"
17 #include "caf/config.hpp"
18 #include "caf/deep_to_string.hpp"
19 #include "caf/detail/arg_wrapper.hpp"
20 #include "caf/detail/core_export.hpp"
21 #include "caf/detail/log_level.hpp"
22 #include "caf/detail/pretty_type_name.hpp"
23 #include "caf/detail/ringbuffer.hpp"
24 #include "caf/detail/scope_guard.hpp"
25 #include "caf/detail/shared_spinlock.hpp"
26 #include "caf/fwd.hpp"
27 #include "caf/intrusive/drr_queue.hpp"
28 #include "caf/intrusive/fifo_inbox.hpp"
29 #include "caf/intrusive/singly_linked.hpp"
30 #include "caf/ref_counted.hpp"
31 #include "caf/string_view.hpp"
32 #include "caf/timestamp.hpp"
33 #include "caf/unifyn.hpp"
34 
35 /*
36  * To enable logging, you have to define CAF_DEBUG. This enables
37  * CAF_LOG_ERROR messages. To enable more debugging output, you can
38  * define CAF_LOG_LEVEL to:
39  * 1: + warning
40  * 2: + info
41  * 3: + debug
42  * 4: + trace (prints for each logged method entry and exit message)
43  *
44  * Note: this logger emits log4j style output; logs are best viewed
45  *       using a log4j viewer, e.g., http://code.google.com/p/otroslogviewer/
46  *
47  */
48 namespace caf {
49 
50 /// Centrally logs events from all actors in an actor system. To enable
51 /// logging in your application, you need to define `CAF_LOG_LEVEL`. Per
52 /// default, the logger generates log4j compatible output.
53 class CAF_CORE_EXPORT logger : public ref_counted {
54 public:
55   // -- friends ----------------------------------------------------------------
56 
57   friend class actor_system;
58 
59   // -- constants --------------------------------------------------------------
60 
61   /// Configures the size of the circular event queue.
62   static constexpr size_t queue_size = 128;
63 
64   // -- member types -----------------------------------------------------------
65 
66   /// Combines various logging-related flags and parameters into a bitfield.
67   struct config {
68     /// Stores `max(file_verbosity, console_verbosity)`.
69     unsigned verbosity : 4;
70 
71     /// Configures the verbosity for file output.
72     unsigned file_verbosity : 4;
73 
74     /// Configures the verbosity for console output.
75     unsigned console_verbosity : 4;
76 
77     /// Configures whether the logger immediately writes its output in the
78     /// calling thread, bypassing its queue. Use this option only in
79     /// single-threaded test environments.
80     bool inline_output : 1;
81 
82     /// Configures whether the logger generates colored output.
83     bool console_coloring : 1;
84 
85     config();
86   };
87 
88   /// Encapsulates a single logging event.
89   struct CAF_CORE_EXPORT event {
90     // -- constructors, destructors, and assignment operators ------------------
91 
92     event() = default;
93 
94     event(event&&) = default;
95 
96     event(const event&) = default;
97 
98     event& operator=(event&&) = default;
99 
100     event& operator=(const event&) = default;
101 
102     event(unsigned lvl, unsigned line, string_view cat, string_view full_fun,
103           string_view fun, string_view fn, std::string msg, std::thread::id t,
104           actor_id a, timestamp ts);
105 
106     // -- member variables -----------------------------------------------------
107 
108     /// Level/priority of the event.
109     unsigned level;
110 
111     /// Current line in the file.
112     unsigned line_number;
113 
114     /// Name of the category (component) logging the event.
115     string_view category_name;
116 
117     /// Name of the current function as reported by `__PRETTY_FUNCTION__`.
118     string_view pretty_fun;
119 
120     /// Name of the current function as reported by `__func__`.
121     string_view simple_fun;
122 
123     /// Name of the current file.
124     string_view file_name;
125 
126     /// User-provided message.
127     std::string message;
128 
129     /// Thread ID of the caller.
130     std::thread::id tid;
131 
132     /// Actor ID of the caller.
133     actor_id aid;
134 
135     /// Timestamp of the event.
136     timestamp tstamp;
137   };
138 
139   enum field_type {
140     invalid_field,
141     category_field,
142     class_name_field,
143     date_field,
144     file_field,
145     line_field,
146     message_field,
147     method_field,
148     newline_field,
149     priority_field,
150     runtime_field,
151     thread_field,
152     actor_field,
153     percent_sign_field,
154     plain_text_field
155   };
156 
157   /// Represents a single format string field.
158   struct field {
159     field_type kind;
160     std::string text;
161   };
162 
163   /// Stores a parsed format string as list of fields.
164   using line_format = std::vector<field>;
165 
166   /// Utility class for building user-defined log messages with `CAF_ARG`.
167   class CAF_CORE_EXPORT line_builder {
168   public:
169     line_builder();
170 
171     template <class T>
172     detail::enable_if_t<!std::is_pointer<T>::value, line_builder&>
operator <<(const T & x)173     operator<<(const T& x) {
174       if (!str_.empty())
175         str_ += " ";
176       str_ += deep_to_string(x);
177       return *this;
178     }
179 
180     line_builder& operator<<(const local_actor* self);
181 
182     line_builder& operator<<(const std::string& str);
183 
184     line_builder& operator<<(string_view str);
185 
186     line_builder& operator<<(const char* str);
187 
188     line_builder& operator<<(char x);
189 
190     std::string get() const;
191 
192   private:
193     std::string str_;
194   };
195 
196   // -- constructors, destructors, and assignment operators --------------------
197 
198   ~logger() override;
199 
200   // -- logging ----------------------------------------------------------------
201 
202   /// Writes an entry to the event-queue of the logger.
203   /// @thread-safe
204   void log(event&& x);
205 
206   // -- properties -------------------------------------------------------------
207 
208   /// Returns the ID of the actor currently associated to the calling thread.
209   static actor_id thread_local_aid();
210 
211   /// Associates an actor ID to the calling thread and returns the last value.
212   static actor_id thread_local_aid(actor_id aid);
213 
214   /// Returns whether the logger is configured to accept input for given
215   /// component and log level.
216   bool accepts(unsigned level, string_view component_name);
217 
218   /// Returns the output format used for the log file.
file_format() const219   const line_format& file_format() const {
220     return file_format_;
221   }
222 
223   /// Returns the output format used for the console.
console_format() const224   const line_format& console_format() const {
225     return console_format_;
226   }
227 
verbosity() const228   unsigned verbosity() const noexcept {
229     return cfg_.verbosity;
230   }
231 
file_verbosity() const232   unsigned file_verbosity() const noexcept {
233     return cfg_.file_verbosity;
234   }
235 
console_verbosity() const236   unsigned console_verbosity() const noexcept {
237     return cfg_.console_verbosity;
238   }
239 
240   // -- static utility functions -----------------------------------------------
241 
242   /// Renders the prefix (namespace and class) of a fully qualified function.
243   static void render_fun_prefix(std::ostream& out, const event& x);
244 
245   /// Renders the name of a fully qualified function.
246   static void render_fun_name(std::ostream& out, const event& x);
247 
248   /// Renders the date of `x` in ISO 8601 format.
249   static void render_date(std::ostream& out, timestamp x);
250 
251   /// Parses `format_str` into a format description vector.
252   /// @warning The returned vector can have pointers into `format_str`.
253   static line_format parse_format(const std::string& format_str);
254 
255   /// Skips path in `filename`.
256   static string_view skip_path(string_view filename);
257 
258   // -- utility functions ------------------------------------------------------
259 
260   /// Renders `x` using the line format `lf` to `out`.
261   void render(std::ostream& out, const line_format& lf, const event& x) const;
262 
263   /// Returns a string representation of the joined groups of `x` if `x` is an
264   /// actor with the `subscriber` mixin.
265   template <class T>
266   static
267     typename std::enable_if<std::is_base_of<mixin::subscriber_base, T>::value,
268                             std::string>::type
joined_groups_of(const T & x)269     joined_groups_of(const T& x) {
270     return deep_to_string(x.joined_groups());
271   }
272 
273   /// Returns a string representation of an empty list if `x` is not an actor
274   /// with the `subscriber` mixin.
275   template <class T>
276   static
277     typename std::enable_if<!std::is_base_of<mixin::subscriber_base, T>::value,
278                             const char*>::type
joined_groups_of(const T & x)279     joined_groups_of(const T& x) {
280     CAF_IGNORE_UNUSED(x);
281     return "[]";
282   }
283 
284   // -- thread-local properties ------------------------------------------------
285 
286   /// Stores the actor system for the current thread.
287   static void set_current_actor_system(actor_system*);
288 
289   /// Returns the logger for the current thread or `nullptr` if none is
290   /// registered.
291   static logger* current_logger();
292 
293 private:
294   // -- constructors, destructors, and assignment operators --------------------
295 
296   logger(actor_system& sys);
297 
298   // -- initialization ---------------------------------------------------------
299 
300   void init(actor_system_config& cfg);
301 
302   bool open_file();
303 
304   // -- event handling ---------------------------------------------------------
305 
306   void handle_event(const event& x);
307 
308   void handle_file_event(const event& x);
309 
310   void handle_console_event(const event& x);
311 
312   void log_first_line();
313 
314   void log_last_line();
315 
316   // -- thread management ------------------------------------------------------
317 
318   void run();
319 
320   void start();
321 
322   void stop();
323 
324   // -- member variables -------------------------------------------------------
325 
326   // Configures verbosity and output generation.
327   config cfg_;
328 
329   // Filters events by component name before enqueuing a log event. Intersection
330   // of file_filter_ and console_filter_ if both outputs are enabled.
331   std::vector<std::string> global_filter_;
332 
333   // Filters events by component name for file output.
334   std::vector<std::string> file_filter_;
335 
336   // Filters events by component name for console output.
337   std::vector<std::string> console_filter_;
338 
339   // References the parent system.
340   actor_system& system_;
341 
342   // Identifies the thread that called `logger::start`.
343   std::thread::id parent_thread_;
344 
345   // Timestamp of the first log event.
346   timestamp t0_;
347 
348   // Format for generating file output.
349   line_format file_format_;
350 
351   // Format for generating console output.
352   line_format console_format_;
353 
354   // Stream for file output.
355   std::fstream file_;
356 
357   // Filled with log events by other threads.
358   detail::ringbuffer<event, queue_size> queue_;
359 
360   // Stores the assembled name of the log file.
361   std::string file_name_;
362 
363   // Executes `logger::run`.
364   std::thread thread_;
365 };
366 
367 CAF_CORE_EXPORT std::string to_string(logger::field_type x);
368 
369 /// @relates logger::field
370 CAF_CORE_EXPORT std::string to_string(const logger::field& x);
371 
372 /// @relates logger::field
373 CAF_CORE_EXPORT bool operator==(const logger::field& x, const logger::field& y);
374 
375 } // namespace caf
376 
377 // -- macro constants ----------------------------------------------------------
378 
379 /// Expands to a no-op.
380 #define CAF_VOID_STMT static_cast<void>(0)
381 
382 #ifndef CAF_LOG_COMPONENT
383 /// Name of the current component when logging.
384 #  define CAF_LOG_COMPONENT "caf"
385 #endif // CAF_LOG_COMPONENT
386 
387 // -- utility macros -----------------------------------------------------------
388 
389 #ifdef CAF_MSVC
390 /// Expands to a string representation of the current function name that
391 /// includes the full function name and its signature.
392 #  define CAF_PRETTY_FUN __FUNCSIG__
393 #else // CAF_MSVC
394 /// Expands to a string representation of the current function name that
395 /// includes the full function name and its signature.
396 #  define CAF_PRETTY_FUN __PRETTY_FUNCTION__
397 #endif // CAF_MSVC
398 
399 /// Concatenates `a` and `b` to a single preprocessor token.
400 #define CAF_CAT(a, b) a##b
401 
402 #define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message)                    \
403   ::caf::logger::event(loglvl, __LINE__, component, CAF_PRETTY_FUN, __func__,  \
404                        caf::logger::skip_path(__FILE__),                       \
405                        (::caf::logger::line_builder{} << message).get(),       \
406                        ::std::this_thread::get_id(), aid,                      \
407                        ::caf::make_timestamp())
408 
409 /// Expands to `argument = <argument>` in log output.
410 #define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
411 
412 /// Expands to `argname = <argval>` in log output.
413 #define CAF_ARG2(argname, argval) caf::detail::make_arg_wrapper(argname, argval)
414 
415 /// Expands to `argname = [argval, last)` in log output.
416 #define CAF_ARG3(argname, first, last)                                         \
417   caf::detail::make_arg_wrapper(argname, first, last)
418 
419 // -- logging macros -----------------------------------------------------------
420 
421 #define CAF_LOG_IMPL(component, loglvl, message)                               \
422   do {                                                                         \
423     auto CAF_UNIFYN(caf_logger) = caf::logger::current_logger();               \
424     if (CAF_UNIFYN(caf_logger) != nullptr                                      \
425         && CAF_UNIFYN(caf_logger)->accepts(loglvl, component))                 \
426       CAF_UNIFYN(caf_logger)                                                   \
427         ->log(CAF_LOG_MAKE_EVENT(caf::logger::thread_local_aid(), component,   \
428                                  loglvl, message));                            \
429   } while (false)
430 
431 #define CAF_PUSH_AID(aarg)                                                     \
432   caf::actor_id CAF_UNIFYN(caf_aid_tmp) = caf::logger::thread_local_aid(aarg); \
433   auto CAF_UNIFYN(caf_aid_tmp_guard) = caf::detail::make_scope_guard(          \
434     [=] { caf::logger::thread_local_aid(CAF_UNIFYN(caf_aid_tmp)); })
435 
436 #define CAF_PUSH_AID_FROM_PTR(some_ptr)                                        \
437   auto CAF_UNIFYN(caf_aid_ptr) = some_ptr;                                     \
438   CAF_PUSH_AID(CAF_UNIFYN(caf_aid_ptr) ? CAF_UNIFYN(caf_aid_ptr)->id() : 0)
439 
440 #define CAF_SET_AID(aid_arg) caf::logger::thread_local_aid(aid_arg)
441 
442 #define CAF_SET_LOGGER_SYS(ptr) caf::logger::set_current_actor_system(ptr)
443 
444 #if CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
445 
446 #  define CAF_LOG_TRACE(unused) CAF_VOID_STMT
447 
448 #else // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
449 
450 #  define CAF_LOG_TRACE(entry_message)                                         \
451     CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_TRACE,                       \
452                  "ENTRY" << entry_message);                                    \
453     auto CAF_UNIFYN(caf_log_trace_guard_) = ::caf::detail::make_scope_guard(   \
454       [=] { CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_TRACE, "EXIT"); })
455 
456 #endif // CAF_LOG_LEVEL < CAF_LOG_LEVEL_TRACE
457 
458 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
459 #  define CAF_LOG_DEBUG(output)                                                \
460     CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
461 #endif
462 
463 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_INFO
464 #  define CAF_LOG_INFO(output)                                                 \
465     CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output)
466 #endif
467 
468 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_WARNING
469 #  define CAF_LOG_WARNING(output)                                              \
470     CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output)
471 #endif
472 
473 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_ERROR
474 #  define CAF_LOG_ERROR(output)                                                \
475     CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output)
476 #endif
477 
478 #ifndef CAF_LOG_INFO
479 #  define CAF_LOG_INFO(output) CAF_VOID_STMT
480 #  define CAF_LOG_INFO_IF(cond, output) CAF_VOID_STMT
481 #else // CAF_LOG_INFO
482 #  define CAF_LOG_INFO_IF(cond, output)                                        \
483     if (cond)                                                                  \
484       CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output);             \
485     CAF_VOID_STMT
486 #endif // CAF_LOG_INFO
487 
488 #ifndef CAF_LOG_DEBUG
489 #  define CAF_LOG_DEBUG(output) CAF_VOID_STMT
490 #  define CAF_LOG_DEBUG_IF(cond, output) CAF_VOID_STMT
491 #else // CAF_LOG_DEBUG
492 #  define CAF_LOG_DEBUG_IF(cond, output)                                       \
493     if (cond)                                                                  \
494       CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output);            \
495     CAF_VOID_STMT
496 #endif // CAF_LOG_DEBUG
497 
498 #ifndef CAF_LOG_WARNING
499 #  define CAF_LOG_WARNING(output) CAF_VOID_STMT
500 #  define CAF_LOG_WARNING_IF(cond, output) CAF_VOID_STMT
501 #else // CAF_LOG_WARNING
502 #  define CAF_LOG_WARNING_IF(cond, output)                                     \
503     if (cond)                                                                  \
504       CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output);          \
505     CAF_VOID_STMT
506 #endif // CAF_LOG_WARNING
507 
508 #ifndef CAF_LOG_ERROR
509 #  define CAF_LOG_ERROR(output) CAF_VOID_STMT
510 #  define CAF_LOG_ERROR_IF(cond, output) CAF_VOID_STMT
511 #else // CAF_LOG_ERROR
512 #  define CAF_LOG_ERROR_IF(cond, output)                                       \
513     if (cond)                                                                  \
514       CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output);            \
515     CAF_VOID_STMT
516 #endif // CAF_LOG_ERROR
517 
518 // -- macros for logging CE-0001 events ----------------------------------------
519 
520 /// The log component responsible for logging control flow events that are
521 /// crucial for understanding happens-before relations. See RFC SE-0001.
522 #define CAF_LOG_FLOW_COMPONENT "caf_flow"
523 
524 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
525 
526 #  define CAF_LOG_SPAWN_EVENT(ref, ctor_data)                                  \
527     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG,                  \
528                  "SPAWN ; ID ="                                                \
529                    << ref.id() << "; NAME =" << ref.name() << "; TYPE ="       \
530                    << ::caf::detail::pretty_type_name(typeid(ref))             \
531                    << "; ARGS =" << ctor_data.c_str()                          \
532                    << "; NODE =" << ref.node()                                 \
533                    << "; GROUPS =" << ::caf::logger::joined_groups_of(ref))
534 
535 #  define CAF_LOG_SEND_EVENT(ptr)                                              \
536     CAF_LOG_IMPL(                                                              \
537       CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG,                             \
538       "SEND ; TO ="                                                            \
539         << ::caf::deep_to_string(::caf::strong_actor_ptr{this->ctrl()})        \
540              .c_str()                                                          \
541         << "; FROM =" << ::caf::deep_to_string(ptr->sender).c_str()            \
542         << "; STAGES =" << ::caf::deep_to_string(ptr->stages).c_str()          \
543         << "; CONTENT =" << ::caf::deep_to_string(ptr->content()).c_str())
544 
545 #  define CAF_LOG_RECEIVE_EVENT(ptr)                                           \
546     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG,                  \
547                  "RECEIVE ; FROM ="                                            \
548                    << ::caf::deep_to_string(ptr->sender).c_str()               \
549                    << "; STAGES ="                                             \
550                    << ::caf::deep_to_string(ptr->stages).c_str()               \
551                    << "; CONTENT ="                                            \
552                    << ::caf::deep_to_string(ptr->content()).c_str())
553 
554 #  define CAF_LOG_REJECT_EVENT()                                               \
555     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "REJECT")
556 
557 #  define CAF_LOG_ACCEPT_EVENT(unblocked)                                      \
558     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG,                  \
559                  "ACCEPT ; UNBLOCKED =" << unblocked)
560 
561 #  define CAF_LOG_DROP_EVENT()                                                 \
562     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "DROP")
563 
564 #  define CAF_LOG_SKIP_EVENT()                                                 \
565     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "SKIP")
566 
567 #  define CAF_LOG_FINALIZE_EVENT()                                             \
568     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG, "FINALIZE")
569 
570 #  define CAF_LOG_SKIP_OR_FINALIZE_EVENT(invoke_result)                        \
571     do {                                                                       \
572       if (invoke_result == caf::invoke_message_result::skipped)                \
573         CAF_LOG_SKIP_EVENT();                                                  \
574       else                                                                     \
575         CAF_LOG_FINALIZE_EVENT();                                              \
576     } while (false)
577 
578 #  define CAF_LOG_TERMINATE_EVENT(thisptr, rsn)                                \
579     CAF_LOG_IMPL(CAF_LOG_FLOW_COMPONENT, CAF_LOG_LEVEL_DEBUG,                  \
580                  "TERMINATE ; ID =" << thisptr->id() << "; REASON ="           \
581                                     << deep_to_string(rsn).c_str()             \
582                                     << "; NODE =" << thisptr->node())
583 
584 #else // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
585 
586 #  define CAF_LOG_SPAWN_EVENT(ref, ctor_data) CAF_VOID_STMT
587 
588 #  define CAF_LOG_SEND_EVENT(ptr) CAF_VOID_STMT
589 
590 #  define CAF_LOG_RECEIVE_EVENT(ptr) CAF_VOID_STMT
591 
592 #  define CAF_LOG_REJECT_EVENT() CAF_VOID_STMT
593 
594 #  define CAF_LOG_ACCEPT_EVENT(unblocked) CAF_VOID_STMT
595 
596 #  define CAF_LOG_DROP_EVENT() CAF_VOID_STMT
597 
598 #  define CAF_LOG_SKIP_EVENT() CAF_VOID_STMT
599 
600 #  define CAF_LOG_SKIP_OR_FINALIZE_EVENT(unused) CAF_VOID_STMT
601 
602 #  define CAF_LOG_FINALIZE_EVENT() CAF_VOID_STMT
603 
604 #  define CAF_LOG_TERMINATE_EVENT(thisptr, rsn) CAF_VOID_STMT
605 
606 #endif // CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
607 
608 // -- macros for logging streaming-related events ------------------------------
609 
610 /// The log component for logging streaming-related events that are crucial for
611 /// understanding handshaking, credit decisions, etc.
612 #define CAF_LOG_STREAM_COMPONENT "caf_stream"
613 
614 #if CAF_LOG_LEVEL >= CAF_LOG_LEVEL_DEBUG
615 #  define CAF_STREAM_LOG_DEBUG(output)                                         \
616     CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
617 #  define CAF_STREAM_LOG_DEBUG_IF(condition, output)                           \
618     if (condition)                                                             \
619     CAF_LOG_IMPL(CAF_LOG_STREAM_COMPONENT, CAF_LOG_LEVEL_DEBUG, output)
620 #else
621 #  define CAF_STREAM_LOG_DEBUG(unused) CAF_VOID_STMT
622 #  define CAF_STREAM_LOG_DEBUG_IF(unused1, unused2) CAF_VOID_STMT
623 #endif
624