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