From 711765194da3c6dacc756db91d287adb0329dbbd Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 17:48:54 +0200 Subject: [PATCH 1/8] iox-#1755 Reintroduce lazy evaluation for logging --- .../error_reporting/error_logging.hpp | 4 +-- .../reporting/include/iox/logging.hpp | 31 +++++++++++++++---- .../moduletests/test_reporting_logging.cpp | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp index 2e6f13dcd2..f7c8269884 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp @@ -26,13 +26,13 @@ /// @brief Log the location of an error. /// @param location the location of the error #define IOX_ERROR_INTERNAL_LOG(location) \ - IOX_LOG_INTERNAL(location.file, location.line, location.function, iox::log::LogLevel::ERROR) \ + IOX_LOG_INTERNAL_LEGACY(location.file, location.line, location.function, iox::log::LogLevel::ERROR) \ << location.file << " line " << location.line /// @brief Log the location of a fatal error. /// @param location the location of the error #define IOX_ERROR_INTERNAL_LOG_FATAL(location) \ - IOX_LOG_INTERNAL(location.file, location.line, location.function, iox::log::LogLevel::FATAL) \ + IOX_LOG_INTERNAL_LEGACY(location.file, location.line, location.function, iox::log::LogLevel::FATAL) \ << location.file << " line " << location.line << ": " /// @brief Log a panic invocation. diff --git a/iceoryx_hoofs/reporting/include/iox/logging.hpp b/iceoryx_hoofs/reporting/include/iox/logging.hpp index 9e04f3802b..8f4f8a1efd 100644 --- a/iceoryx_hoofs/reporting/include/iox/logging.hpp +++ b/iceoryx_hoofs/reporting/include/iox/logging.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2022 by Apex.AI Inc. All rights reserved. +// Copyright (c) 2023 by Mathias Kraus . All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,15 +44,26 @@ inline bool isLogLevelActive(LogLevel logLevel) noexcept // AXIVION Next Construct AutosarC++19_03-A16.0.1 cannot be realized with templates or constexpr functions due to the // intended lazy evaluation technique with the if statement // NOLINTBEGIN(cppcoreguidelines-macro-usage) -#define IOX_LOG_INTERNAL(file, line, function, level) \ - /* if (iox::log::internal::isLogLevelActive(level)) @todo iox-#1755 lazy evaluation causes issues with Axivion */ \ - iox::log::internal::SelectedLogStream(file, line, function, level, iox::log::internal::isLogLevelActive(level)) \ - .self() +// NOLINTBEGIN(bugprone-macro-parentheses) 'msg_stream' cannot be wrapped in parentheses due to the '<<' operator +#define IOX_LOG_INTERNAL(file, line, function, level, msg_stream) \ + if (iox::log::internal::isLogLevelActive(level)) \ + { \ + iox::log::internal::SelectedLogStream(file, line, function, level).self() << msg_stream; \ + } \ + [] {}() // the empty lambda forces a semicolon on the caller side +// NOLINTEND(bugprone-macro-parentheses) + +/// @todo iox-#1755 remove after porting to IOX_LOG_LAZY +#define IOX_LOG_INTERNAL_LEGACY(file, line, function, level) \ + if (iox::log::internal::isLogLevelActive(level)) \ + iox::log::internal::SelectedLogStream(file, line, function, level).self() + /// @brief Macro for logging /// @param[in] level is the log level to be used for the log message +/// @param[in] msg_stream is the log message stream; multiple items can be logged by using the '<<' operator /// @code -/// IOX_LOG(INFO) << "Hello World"; +/// IOX_LOG(INFO, "Hello" << " World"); /// @endcode // AXIVION Next Construct AutosarC++19_03-A16.0.1 needed for source code location, safely wrapped in macro // AXIVION Next Construct AutosarC++19_03-M16.0.6 brackets around macro parameter would lead to compile time failures in this case @@ -59,8 +71,15 @@ inline bool isLogLevelActive(LogLevel logLevel) noexcept // templates the resulting string is too large; we also get the file name and the line of the invocation which should be // sufficient for debugging // NOLINTBEGIN(bugprone-lambda-function-name) +/// @todo iox-#1755 rename this to IOX_LOG +#define IOX_LOG_LAZY(level, msg_stream) \ + IOX_LOG_INTERNAL(__FILE__, __LINE__, static_cast(__FUNCTION__), iox::log::LogLevel::level, msg_stream) +// NOLINTEND(bugprone-lambda-function-name) + +// NOLINTBEGIN(bugprone-lambda-function-name) +/// @todo iox-#1755 remove after porting to IOX_LOG_LAZY #define IOX_LOG(level) \ - IOX_LOG_INTERNAL(__FILE__, __LINE__, static_cast(__FUNCTION__), iox::log::LogLevel::level) + IOX_LOG_INTERNAL_LEGACY(__FILE__, __LINE__, static_cast(__FUNCTION__), iox::log::LogLevel::level) // NOLINTEND(bugprone-lambda-function-name) // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/iceoryx_hoofs/test/moduletests/test_reporting_logging.cpp b/iceoryx_hoofs/test/moduletests/test_reporting_logging.cpp index 12bfc196e6..609ee666c2 100644 --- a/iceoryx_hoofs/test/moduletests/test_reporting_logging.cpp +++ b/iceoryx_hoofs/test/moduletests/test_reporting_logging.cpp @@ -56,7 +56,7 @@ void testLogLevelThreshold(const iox::log::LogLevel loggerLogLevel) } dynamic_cast(iox::log::Logger::get()).clearLogBuffer(); - IOX_LOG_INTERNAL("", 0, "", logEntryLogLevel.value); + IOX_LOG_INTERNAL("", 0, "", logEntryLogLevel.value, ""); if (logEntryLogLevel.value <= loggerLogLevel) { From 2595126b524418e2c5a4f57e205e3be77a37678a Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 18:28:03 +0200 Subject: [PATCH 2/8] iox-#1755 Remove workarounds --- .../include/iox/detail/log/logstream.inl | 30 ------------- .../reporting/include/iox/log/logstream.hpp | 44 ------------------- .../reporting/include/iox/logging.hpp | 4 +- 3 files changed, 2 insertions(+), 76 deletions(-) diff --git a/iceoryx_hoofs/reporting/include/iox/detail/log/logstream.inl b/iceoryx_hoofs/reporting/include/iox/detail/log/logstream.inl index 628ff6725f..a8d9ef6e3d 100644 --- a/iceoryx_hoofs/reporting/include/iox/detail/log/logstream.inl +++ b/iceoryx_hoofs/reporting/include/iox/detail/log/logstream.inl @@ -79,17 +79,6 @@ inline LogStream::LogStream(const char* file, const int line, const char* functi { } -/// @todo iox-#1755 use something like 'source_location' -// AXIVION Next Construct AutosarC++19_03-A3.9.1 : See at declaration in header -// NOLINTNEXTLINE(readability-function-size) -inline LogStream::LogStream( - const char* file, const int line, const char* function, LogLevel logLevel, bool doFlush) noexcept - : m_logger(Logger::get()) - , m_doFlush(doFlush) -{ - m_logger.createLogMessageHeader(file, line, function, logLevel); -} - inline LogStream::~LogStream() noexcept { flush(); @@ -280,25 +269,6 @@ inline LogStream& LogStream::operator<<(const LogLevel value) noexcept return *this; } -namespace internal -{ -// AXIVION Next Construct AutosarC++19_03-A3.9.1 : See at declaration in header -inline LogStreamOff::LogStreamOff(const char*, const int, const char*, LogLevel, bool) noexcept -{ -} - -inline LogStreamOff& LogStreamOff::self() noexcept -{ - return *this; -} - -template -inline LogStreamOff& LogStreamOff::operator<<(T&&) noexcept -{ - return *this; -} -} // namespace internal - // AXIVION ENABLE STYLE AutosarC++19_03-M5.17.1 } // namespace log diff --git a/iceoryx_hoofs/reporting/include/iox/log/logstream.hpp b/iceoryx_hoofs/reporting/include/iox/log/logstream.hpp index c72c4ed8cf..38a9d62b9b 100644 --- a/iceoryx_hoofs/reporting/include/iox/log/logstream.hpp +++ b/iceoryx_hoofs/reporting/include/iox/log/logstream.hpp @@ -117,19 +117,6 @@ class LogStream // platform agnostic way LogStream(const char* file, const int line, const char* function, LogLevel logLevel) noexcept; - /// @brief Constructor for a LogStream object with the logger from iox::log::Logger::get - /// @note This is not intended for public use! Use the 'IOX_LOG' macro instead - /// @param[in] file the file of the log message. Please use the '__FILE__' compiler intrinsic - /// @param[in] line the line of the log message. Please use the '__LINE__' compiler intrinsic - /// @param[in] function the function of the log message. Please use the '__FUNCTION__' compiler intrinsic - /// @param[in] logLevel is the log level for the log message - /// @todo iox-#1755 temporary workaround due to lazy evaluation issues with Axivion; should be removed when the - /// lazy evaluation can be implemented in an way Axivion does not complain - // AXIVION Next Construct AutosarC++19_03-A3.9.1 : file, line and function are used in conjunction with '__FILE__', - // '__LINE__' and '__FUNCTION__'; these are compiler intrinsic and cannot be changed to fixed width types in a - // platform agnostic way - LogStream(const char* file, const int line, const char* function, LogLevel logLevel, bool doFlush) noexcept; - virtual ~LogStream() noexcept; LogStream(const LogStream&) = delete; @@ -294,37 +281,6 @@ class LogStream bool m_doFlush{true}; }; -namespace internal -{ -/// @brief This is an internal helper struct to fully remove the logger from the compiled binary -/// when 'IOX_MINIMAL_LOG_LEVEL == OFF'. It is not intended for direct usage. -struct LogStreamOff -{ - // AXIVION Next Construct AutosarC++19_03-A3.9.1 : file, line and function are used in conjunction with '__FILE__', - // '__LINE__' and '__FUNCTION__'; these are compiler intrinsic and cannot be changed to fixed width types in a - // platform agnostic way - inline LogStreamOff(const char*, const int, const char*, LogLevel, bool) noexcept; - inline LogStreamOff& self() noexcept; - - template - inline LogStreamOff& operator<<(T&&) noexcept; -}; - -template -struct LogStreamTypeSelector -{ - using type = iox::log::LogStream; -}; - -template <> -struct LogStreamTypeSelector -{ - using type = iox::log::internal::LogStreamOff; -}; - -using SelectedLogStream = typename LogStreamTypeSelector::type; -} // namespace internal - } // namespace log } // namespace iox diff --git a/iceoryx_hoofs/reporting/include/iox/logging.hpp b/iceoryx_hoofs/reporting/include/iox/logging.hpp index 8f4f8a1efd..d1a0833e89 100644 --- a/iceoryx_hoofs/reporting/include/iox/logging.hpp +++ b/iceoryx_hoofs/reporting/include/iox/logging.hpp @@ -48,7 +48,7 @@ inline bool isLogLevelActive(LogLevel logLevel) noexcept #define IOX_LOG_INTERNAL(file, line, function, level, msg_stream) \ if (iox::log::internal::isLogLevelActive(level)) \ { \ - iox::log::internal::SelectedLogStream(file, line, function, level).self() << msg_stream; \ + iox::log::LogStream(file, line, function, level).self() << msg_stream; \ } \ [] {}() // the empty lambda forces a semicolon on the caller side // NOLINTEND(bugprone-macro-parentheses) @@ -56,7 +56,7 @@ inline bool isLogLevelActive(LogLevel logLevel) noexcept /// @todo iox-#1755 remove after porting to IOX_LOG_LAZY #define IOX_LOG_INTERNAL_LEGACY(file, line, function, level) \ if (iox::log::internal::isLogLevelActive(level)) \ - iox::log::internal::SelectedLogStream(file, line, function, level).self() + iox::log::LogStream(file, line, function, level).self() /// @brief Macro for logging From ee9e2c9259dba8ec2dfb3652dede6de896ce61df Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 18:49:44 +0200 Subject: [PATCH 3/8] iox-#1755 Port error reporting the new logging macro --- .../custom/default/error_reporting.inl | 22 ++++++++++--------- .../error_reporting/error_logging.hpp | 20 +++++++++++------ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/custom/default/error_reporting.inl b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/custom/default/error_reporting.inl index de1722535c..7d6bf7afd0 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/custom/default/error_reporting.inl +++ b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/custom/default/error_reporting.inl @@ -53,7 +53,7 @@ namespace er // Custom panic with location [[noreturn]] inline void panic(const SourceLocation& location) { - IOX_ERROR_INTERNAL_LOG_PANIC(location) << "Panic"; + IOX_ERROR_INTERNAL_LOG_PANIC(location, "Panic"); panic(); } @@ -63,7 +63,7 @@ namespace er template [[noreturn]] inline void panic(const SourceLocation& location, Message&& msg) { - IOX_ERROR_INTERNAL_LOG_PANIC(location) << "Panic " << msg; + IOX_ERROR_INTERNAL_LOG_PANIC(location, "Panic " << msg); panic(); } @@ -76,8 +76,9 @@ inline void report(const SourceLocation& location, Kind, const Error& error) auto moduleName = toModuleName(error); auto errorName = toErrorName(error); - IOX_ERROR_INTERNAL_LOG(location) << ": " << errorName << " (code " << code.value << ") in module " << moduleName - << " (id " << module.value << ")"; + IOX_ERROR_INTERNAL_LOG(location, + ": " << errorName << " (code " << code.value << ") in module " << moduleName << " (id " + << module.value << ")"); auto& h = ErrorHandler::get(); h.onReportError(ErrorDescriptor(location, code, module)); } @@ -95,8 +96,9 @@ inline void report(const SourceLocation& location, iox::er::FatalKind kind, cons auto moduleName = toModuleName(error); auto errorName = toErrorName(error); - IOX_ERROR_INTERNAL_LOG_FATAL(location) << ": " << kind.name << " " << errorName << " (code " << code.value - << ") in module " << moduleName << " (id " << module.value << ")"; + IOX_ERROR_INTERNAL_LOG_FATAL(location, + ": " << kind.name << " " << errorName << " (code " << code.value << ") in module " + << moduleName << " (id " << module.value << ")"); auto& h = ErrorHandler::get(); h.onReportError(ErrorDescriptor(location, code, module)); } @@ -106,7 +108,7 @@ inline void report(const SourceLocation& location, iox::er::PreconditionViolatio { auto code = toCode(error); auto module = toModule(error); - IOX_ERROR_INTERNAL_LOG_FATAL(location) << kind.name; + IOX_ERROR_INTERNAL_LOG_FATAL(location, kind.name); auto& h = ErrorHandler::get(); h.onReportViolation(ErrorDescriptor(location, code, module)); } @@ -116,7 +118,7 @@ inline void report(const SourceLocation& location, iox::er::AssumptionViolationK { auto code = toCode(error); auto module = toModule(error); - IOX_ERROR_INTERNAL_LOG_FATAL(location) << kind.name; + IOX_ERROR_INTERNAL_LOG_FATAL(location, kind.name); auto& h = ErrorHandler::get(); h.onReportViolation(ErrorDescriptor(location, code, module)); } @@ -127,7 +129,7 @@ report(const SourceLocation& location, iox::er::PreconditionViolationKind kind, { auto code = toCode(error); auto module = toModule(error); - IOX_ERROR_INTERNAL_LOG_FATAL(location) << kind.name << " " << std::forward(msg); + IOX_ERROR_INTERNAL_LOG_FATAL(location, kind.name << " " << std::forward(msg)); auto& h = ErrorHandler::get(); h.onReportViolation(ErrorDescriptor(location, code, module)); } @@ -138,7 +140,7 @@ report(const SourceLocation& location, iox::er::AssumptionViolationKind kind, co { auto code = toCode(error); auto module = toModule(error); - IOX_ERROR_INTERNAL_LOG_FATAL(location) << kind.name << " " << std::forward(msg); + IOX_ERROR_INTERNAL_LOG_FATAL(location, kind.name << " " << std::forward(msg)); auto& h = ErrorHandler::get(); h.onReportViolation(ErrorDescriptor(location, code, module)); } diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp index f7c8269884..10e3932fc7 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp @@ -25,19 +25,25 @@ /// @brief Log the location of an error. /// @param location the location of the error -#define IOX_ERROR_INTERNAL_LOG(location) \ - IOX_LOG_INTERNAL_LEGACY(location.file, location.line, location.function, iox::log::LogLevel::ERROR) \ - << location.file << " line " << location.line +#define IOX_ERROR_INTERNAL_LOG(location, msg_stream) \ + IOX_LOG_INTERNAL(location.file, \ + location.line, \ + location.function, \ + iox::log::LogLevel::ERROR, \ + location.file << " line " << location.line << ": " << msg_stream) /// @brief Log the location of a fatal error. /// @param location the location of the error -#define IOX_ERROR_INTERNAL_LOG_FATAL(location) \ - IOX_LOG_INTERNAL_LEGACY(location.file, location.line, location.function, iox::log::LogLevel::FATAL) \ - << location.file << " line " << location.line << ": " +#define IOX_ERROR_INTERNAL_LOG_FATAL(location, msg_stream) \ + IOX_LOG_INTERNAL(location.file, \ + location.line, \ + location.function, \ + iox::log::LogLevel::FATAL, \ + location.file << " line " << location.line << ": " << msg_stream) /// @brief Log a panic invocation. /// @param location the location of the panic invocation. -#define IOX_ERROR_INTERNAL_LOG_PANIC(location) IOX_ERROR_INTERNAL_LOG_FATAL(location) +#define IOX_ERROR_INTERNAL_LOG_PANIC(location, msg_stream) IOX_ERROR_INTERNAL_LOG_FATAL(location, msg_stream) // NOLINTEND(cppcoreguidelines-macro-usage, bugprone-macro-parentheses) From f9b3409043d424653ef8881c29063327b848d2fa Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 18:52:40 +0200 Subject: [PATCH 4/8] iox-#1755 Update docs --- doc/design/logging.md | 87 ++++++++++++------- .../release-notes/iceoryx-unreleased.md | 32 +++---- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/doc/design/logging.md b/doc/design/logging.md index 78ae6ea79b..0d9a23f444 100644 --- a/doc/design/logging.md +++ b/doc/design/logging.md @@ -95,22 +95,59 @@ by the `BaseLogger` template parameter and the latter by deriving from the logge ![logging with logstream](../website/images/logging_with_logstream.svg) -#### Logging macro +#### Logging macro with lazy evaluation -The `IOX_LOG` macro is intended for general use. It has one parameter which +The `IOX_LOG` macro is intended for general use. It has two parameter. The first one sets the log level and delegates `file`, `line` and `function` to the `IOX_LOG_INTERNAL` macro. The latter can be used in places like a custom `ASSERT` macro which already -obtained the `file`, `line` and `function` parameter. +obtained the `file`, `line` and `function` parameter. The second parameter contains +the items to log and multiple items can be logged by by using the '<<' operator. -The `IOX_LOG_INTERNAL` uses the `iox::log::internal::SelectedLogStream` struct which is -specialized for `LogLevel::OFF` to compile to a no-op when the logging data does not -cause side effects. `IOX_LOG(INFO) << 42;` won't cause side effects but -`IOX_LOG(INFO) << functionCall();` might have some. +The buildup of the log message is only done when the condition to log the message +is fulfilled. This is accomplished by a macro with an if-statement. + +```cpp +#define LAZY(cond, expr) if (cond) { expr; } + +LAZY(x == 42, expensiveFunctionCall()); +``` + +In the example above `expensiveFunctionCall` is only executed if `cond` is `true`. +If `cond` is a compile time constant set to `false`, the whole statement is compiled +to a no-op and optimized away. + +This is the log macro with lazy evaluation + +```cpp +inline bool isLogLevelActive(LogLevel logLevel) noexcept +{ + return ((logLevel) <= MINIMAL_LOG_LEVEL) + && (IGNORE_ACTIVE_LOG_LEVEL || ((logLevel) <= iox::log::Logger::getLogLevel())); +} + +#define IOX_LOG_INTERNAL(file, line, function, level, msg_stream) \ + if (iox::log::internal::isLogLevelActive(level)) \ + { \ + iox::log::LogStream(file, line, function, level).self() << msg_stream; \ + } \ + [] {} () // the empty lambda forces a semicolon on the caller side + +#define IOX_LOG(level, msg_stream) \ + IOX_LOG_INTERNAL(__FILE__, __LINE__, __FUNCTION__, iox::log::LogLevel::level, msg_stream) +``` + +With `MINIMAL_LOG_LEVEL` and `IGNORE_ACTIVE_LOG_LEVEL` being static `constexpr` +constants the compiler will optimize this either to +`if (false) { iox::log::LogStream(...) ... }` and finally completely away or +`if ((level) <= iox::log::Logger::getLogLevel()) { iox::log::LogStream(...) ... }`. +The minimal log level check is intended to fully optimize away a log statement +and the ignore active log level check to always forward the log message to the +logger, independent of the active log level. The `IOX_LOG_INTERNAL` calls `self()` on the `LogStream` instance to create an lvalue reference to the `LogStream` instance. This eases the implementation of logging -support for custom types since `IOX_LOG(INFO) << myType;` would require to implement -an overload with a rvalue `LogStream` reference but `IOX_LOG(INFO) << "#### " << myType;` +support for custom types since `IOX_LOG(INFO, myType);` would require to implement +an overload with a rvalue `LogStream` reference but `IOX_LOG(INFO, "#### " << myType);` requires a lvalue reference. #### Behavior before calling Logger::init @@ -268,7 +305,7 @@ int main() { iox::log::Logger::init(iox::log::logLevelFromEnvOr(iox::log::LogLevel::DEBUG)); - IOX_LOG(DEBUG) << "Hello World"; + IOX_LOG(DEBUG, "Hello World"); return 0; } @@ -296,7 +333,7 @@ iox::log::LogStream& operator<<(iox::log::LogStream& stream, const MyType& m) int main() { MyType m; - IOX_LOG(INFO) << m; + IOX_LOG(INFO, m); return 0; } @@ -362,12 +399,12 @@ int main() { MyLogger::init(); - IOX_LOG(FATAL) << "Whoops ... look, over there is a dead seagull flying!"; - IOX_LOG(ERROR) << "Oh no!"; - IOX_LOG(WARN) << "It didn't happen!"; - IOX_LOG(INFO) << "All glory to the hypnotoad!"; - IOX_LOG(DEBUG) << "I didn't do it!"; - IOX_LOG(TRACE) << "Row row row your boat!"; + IOX_LOG(FATAL, "Whoops ... look, over there is a dead seagull flying!"); + IOX_LOG(ERROR, "Oh no!"); + IOX_LOG(WARN, "It didn't happen!"); + IOX_LOG(INFO, "All glory to the hypnotoad!"); + IOX_LOG(DEBUG, "I didn't do it!"); + IOX_LOG(TRACE, "Row row row your boat!"); return 0; } @@ -375,24 +412,10 @@ int main() ## Open issues -### Lazy evaluation - -Lazy evaluation via the following macro clashes with static code analyzer which -will generate warnings due to missing braces of the if-statement. - -```cpp -#define LAZY() if (cond) - -LAZY() expensiveFunctionCall(); -``` - -Lazy evaluation with stream based logging seems to be challenging. An alternative -might be a variadic macro like `IOX_LOG(INFO, "Hello World");` - ### Miscellaneous - do we need to change the log level after `Logger::init` -- do we want a `IOX_LOG_IF(cond, level)` macro +- do we want a `IOX_LOG_IF(cond, level, msg_stream)` macro - shall the `TestingLogger` register signals to catch `SIGTERM`, etc. and print the log messages when the signal is raised? It might be necessary to wait for the error handling refactoring before this can be done diff --git a/doc/website/release-notes/iceoryx-unreleased.md b/doc/website/release-notes/iceoryx-unreleased.md index 24e225637e..34bbd0bfcf 100644 --- a/doc/website/release-notes/iceoryx-unreleased.md +++ b/doc/website/release-notes/iceoryx-unreleased.md @@ -691,14 +691,14 @@ auto& logger = iox::log::createLogger("MyComponent", "MyContext", iox::log::LogLevel::kInfo); - logger.LogInfo() << "Hello World"; + logger.LogInfo() << "Hello World " << 42; // after #include "iox/logging.hpp" iox::log::Logger::init(iox::log::LogLevel::INFO); - IOX_LOG(INFO) << "Hello World"; + IOX_LOG(INFO, "Hello World " << 42); ``` 31. Setting the default log level changed @@ -771,7 +771,7 @@ { void myFunc() { - LogInfo() << "Hello World"; + LogInfo() << "Hello World " << 42; } } @@ -784,21 +784,21 @@ { void myFunc() { - IOX_LOG(INFO) << "Hello World"; + IOX_LOG(INFO, "Hello World " << 42); } } ``` 34. Free function logger calls changed - | before | after | - |:--------------:|:----------------:| - | `LogFatal()` | `IOX_LOG(FATAL)` | - | `LogError()` | `IOX_LOG(ERROR)` | - | `LogWarn()` | `IOX_LOG(WARN)` | - | `LogInfo()` | `IOX_LOG(INFO)` | - | `LogDebug()` | `IOX_LOG(DEBUG)` | - | `LogVerbose()` | `IOX_LOG(TRACE)` | + | before | after | + |:---------------------:|:---------------------:| + | `LogFatal() << "x"` | `IOX_LOG(FATAL, "x")` | + | `LogError() << "x"` | `IOX_LOG(ERROR, "x")` | + | `LogWarn() << "x"` | `IOX_LOG(WARN, "x")` | + | `LogInfo() << "x"` | `IOX_LOG(INFO, "x")` | + | `LogDebug() << "x"` | `IOX_LOG(DEBUG, "x")` | + | `LogVerbose() << "x"` | `IOX_LOG(TRACE, "x")` | 35. Logger formatting changed @@ -809,8 +809,8 @@ LogInfo() << iox::log::RawBuffer(buf); // currently not supported // after - IOX_LOG(INFO) << iox::log::hex(42); - IOX_LOG(INFO) << iox::log::oct(42); + IOX_LOG(INFO, iox::log::hex(42)); + IOX_LOG(INFO, iox::log::oct(42)); ``` 36. Creating an instance of `LogStream` does not work anymore @@ -827,7 +827,7 @@ stream.Flush(); // after - IOX_LOG(INFO) << [] (auto& stream) -> auto& { + IOX_LOG(INFO, [] (auto& stream) -> auto& { stream << "fibonacci: " for(auto fib : {1, 1, 2, 3, 5, 8}) { @@ -835,7 +835,7 @@ } stream << "..."; return stream; - }; + }); ``` 37. Testing of `LogStream::operator<<` overload for custom types changed From 8123f38f5c78512a77420595827047a8bbe61c30 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 23:40:50 +0200 Subject: [PATCH 5/8] iox-#1755 Add watchdog to PortUser_IntegrationTest --- .../test_popo_port_user_building_blocks.cpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp b/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp index e91fe2f173..578481666c 100644 --- a/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp +++ b/iceoryx_posh/test/integrationtests/test_popo_port_user_building_blocks.cpp @@ -17,6 +17,7 @@ #include "iceoryx_hoofs/internal/concurrent/smart_lock.hpp" #include "iceoryx_hoofs/testing/timing_test.hpp" +#include "iceoryx_hoofs/testing/watch_dog.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_roudi.hpp" #include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp" @@ -46,17 +47,20 @@ struct DummySample uint64_t m_dummy{42U}; }; -static const ServiceDescription TEST_SERVICE_DESCRIPTION("x", "y", "z"); -static const iox::RuntimeName_t TEST_SUBSCRIBER_RUNTIME_NAME("mySubscriberApp"); -static const iox::RuntimeName_t TEST_PUBLISHER_RUNTIME_NAME("myPublisherApp"); -static constexpr uint32_t NUMBER_OF_PUBLISHERS = 17U; -static constexpr uint32_t ITERATIONS = 1000U; +constexpr iox::units::Duration DEADLOCK_TIMEOUT{15_s}; -static constexpr uint32_t NUM_CHUNKS_IN_POOL = NUMBER_OF_PUBLISHERS * ITERATIONS; -static constexpr uint32_t SMALL_CHUNK = 128U; -static constexpr uint32_t CHUNK_META_INFO_SIZE = 256U; -static constexpr size_t MEMORY_SIZE = NUM_CHUNKS_IN_POOL * (SMALL_CHUNK + CHUNK_META_INFO_SIZE); +const ServiceDescription TEST_SERVICE_DESCRIPTION("x", "y", "z"); +const iox::RuntimeName_t TEST_SUBSCRIBER_RUNTIME_NAME("mySubscriberApp"); +const iox::RuntimeName_t TEST_PUBLISHER_RUNTIME_NAME("myPublisherApp"); + +constexpr uint32_t NUMBER_OF_PUBLISHERS = 17U; +constexpr uint32_t ITERATIONS = 1000U; + +constexpr uint32_t NUM_CHUNKS_IN_POOL = NUMBER_OF_PUBLISHERS * ITERATIONS; +constexpr uint32_t SMALL_CHUNK = 128U; +constexpr uint32_t CHUNK_META_INFO_SIZE = 256U; +constexpr size_t MEMORY_SIZE = NUM_CHUNKS_IN_POOL * (SMALL_CHUNK + CHUNK_META_INFO_SIZE); alignas(64) static uint8_t g_memory[MEMORY_SIZE]; class PortUser_IntegrationTest : public Test @@ -86,6 +90,8 @@ class PortUser_IntegrationTest : public Test m_publisherPortUserVector.emplace_back(&m_publisherPortDataVector.back()); m_publisherPortRouDiVector.emplace_back(&m_publisherPortDataVector.back()); } + + m_deadlockWatchdog.watchAndActOnFailure([] { std::terminate(); }); } void TearDown() @@ -103,6 +109,8 @@ class PortUser_IntegrationTest : public Test static_cast(m_subscriberPortRouDiMultiProducer.tryGetCaProMessage()); } + Watchdog m_deadlockWatchdog{DEADLOCK_TIMEOUT}; + uint64_t m_receiveCounter{0U}; std::atomic m_sendCounter{0U}; std::atomic m_publisherRunFinished{0U}; From 5a1c2986910ee0e87af7987fa8ff02599e24d07d Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 28 Sep 2023 23:41:22 +0200 Subject: [PATCH 6/8] iox-#2011 Fix warning in cache action --- .github/workflows/lint_pull_request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint_pull_request.yml b/.github/workflows/lint_pull_request.yml index 15b92f8926..bf79e736f9 100644 --- a/.github/workflows/lint_pull_request.yml +++ b/.github/workflows/lint_pull_request.yml @@ -43,14 +43,14 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Calculate lychee cache key + run: echo "LYCHEE_CACHE_KEY=cache-lychee-all-branches-$(date +'%G-%V')" >> "$GITHUB_ENV" # %G = year; %V = week number + - name: Restore lychee cache uses: actions/cache@v3 - if: always() with: path: .lycheecache - key: cache-lychee-all-branches - # the cache will not be updated and therefore needs to be recreated after 7 days - retention-days: 7 + key: ${{ env.LYCHEE_CACHE_KEY }} - name: Link Checker uses: lycheeverse/lychee-action@v1.8.0 From 545cacc387c258b2f6dee0e4000ff449d457c131 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Fri, 29 Sep 2023 03:35:58 +0200 Subject: [PATCH 7/8] iox-#2011 Fix nightly build --- .github/workflows/lint_pull_request.yml | 5 +- tools/scripts/clang_tidy_check.sh | 179 ++++++++++++++++-------- 2 files changed, 121 insertions(+), 63 deletions(-) diff --git a/.github/workflows/lint_pull_request.yml b/.github/workflows/lint_pull_request.yml index bf79e736f9..f8e840a120 100644 --- a/.github/workflows/lint_pull_request.yml +++ b/.github/workflows/lint_pull_request.yml @@ -21,10 +21,7 @@ jobs: id: changed_files uses: Ana06/get-changed-files@v2.2.0 - name: diff scan, clang-tidy for all files which are in `.clang-tidy-diff-scans.txt` - run: | - for diff_file in ${{ steps.changed_files.outputs.added_modified }}; do - ./tools/scripts/clang_tidy_check.sh scan_list .clang-tidy-diff-scans.txt ${diff_file} - done + run: ./tools/scripts/clang_tidy_check.sh scan_list .clang-tidy-diff-scans.txt "${{ steps.changed_files.outputs.added_modified }}" check-for-todo: runs-on: ubuntu-latest diff --git a/tools/scripts/clang_tidy_check.sh b/tools/scripts/clang_tidy_check.sh index fe91939761..0489453798 100755 --- a/tools/scripts/clang_tidy_check.sh +++ b/tools/scripts/clang_tidy_check.sh @@ -1,6 +1,7 @@ #!/bin/bash # Copyright (c) 2022 - 2023 by Apex.AI Inc. All rights reserved. +# Copyright (c) 2023 by Mathias Kraus . All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,12 +22,17 @@ set -e +COLOR_OFF='\033[0m' +COLOR_RED='\033[1;31m' +COLOR_GREEN='\033[1;32m' +COLOR_YELLOW='\033[1;33m' + MODE=${1:-full} # Can be either `full` for all files or `hook` for formatting with git hooks FILE_FILTER="\.(h|hpp|inl|c|cpp)$" fail() { - printf "\033[1;31merror: %s: %s\033[0m\n" ${FUNCNAME[1]} "${1:-"Unknown error"}" + printf "${COLOR_RED}error: %s: %s${COLOR_OFF}\n" ${FUNCNAME[1]} "${1:-"Unknown error"}" exit 1 } @@ -60,55 +66,121 @@ echo "Using clang-tidy version: $($CLANG_TIDY_CMD --version | sed -n "s/.*versio noSpaceInSuppressions=$(git ls-files | grep -E "$FILE_FILTER" | xargs -I {} grep -h '// NOLINTNEXTLINE (' {} || true) if [[ -n "$noSpaceInSuppressions" ]]; then - echo -e "\e[1;31mRemove space between NOLINTNEXTLINE and '('!\e[m" + echo -e "${COLOR_RED}Remove space between NOLINTNEXTLINE and '('!${COLOR_OFF}" echo "$noSpaceInSuppressions" exit 1 fi function scanWithFileList() { FILE_WITH_SCAN_LIST=$1 - FILE_TO_SCAN=$2 + FILES_TO_SCAN=$2 if ! test -f "$FILE_WITH_SCAN_LIST" then - echo "Scan list file '${FILE_WITH_SCAN_LIST}' does not exist" + echo -e "${COLOR_RED}Scan list file '${FILE_WITH_SCAN_LIST}' does not exist! Aborting!${COLOR_OFF}" return 1 fi + SEPARATOR='' while IFS= read -r LINE do # add files until the comment section starts if [[ "$(echo $LINE | grep "#" | wc -l)" == "1" ]]; then break fi - FILE_LIST="${FILE_LIST} $LINE" + SCAN_LIST_ENTRIES+="${SEPARATOR}${LINE}" + SEPARATOR=$'\n' done < "$FILE_WITH_SCAN_LIST" - if [[ -n $FILE_TO_SCAN ]] + if [[ -z $SCAN_LIST_ENTRIES ]] then - if ! test -f "$FILE_TO_SCAN" - then - echo "The file which should be scanned '${FILE_TO_SCAN}' does not exist" - return 1 - fi + echo -e "${COLOR_YELLOW}'${FILE_WITH_SCAN_LIST}' is empty skipping scan!${COLOR_OFF}" + return 0 + fi - if [[ $(find ${FILE_LIST} -type f | grep -E ${FILE_FILTER} | grep ${FILE_TO_SCAN} | wc -l) == "0" ]] - then - echo "Skipping file '${FILE_TO_SCAN}' since it is not part of '${FILE_WITH_SCAN_LIST}'" - return 0 + ALL_FILES_FROM_SCAN_LIST=$(find ${SCAN_LIST_ENTRIES} -type f | grep -E ${FILE_FILTER} | sort | uniq) + + FILES_TO_SCAN_ARRAY=(${FILES_TO_SCAN}) + NUMBER_OF_FILES=${#FILES_TO_SCAN_ARRAY[@]} + if [[ ${NUMBER_OF_FILES} -gt 0 ]] + then + FILES="" + SEPARATOR='' + SKIP_MESSAGE_PRINTED=0 + for FILE in ${FILES_TO_SCAN}; do + if $(echo ${ALL_FILES_FROM_SCAN_LIST} | grep -q ${FILE}) + then + FILES+="${SEPARATOR}${FILE}" + SEPARATOR=$'\n' + else + if [[ ${SKIP_MESSAGE_PRINTED} -eq 0 ]]; then + echo -e "${COLOR_YELLOW}Skipping files which are not part of '${FILE_WITH_SCAN_LIST}' ...${COLOR_OFF}" + SKIP_MESSAGE_PRINTED=1 + fi + echo -e "${COLOR_YELLOW}[#]${COLOR_OFF} ${FILE}" + fi + done + + if [[ ${SKIP_MESSAGE_PRINTED} -eq 1 ]]; then + echo -e "${COLOR_YELLOW}... end of list with skipped files!${COLOR_OFF}" fi + scan "error" "$FILES" + else + echo "Performing full scan of all files in '${FILE_WITH_SCAN_LIST}'" + scan "error" "${ALL_FILES_FROM_SCAN_LIST}" + fi +} - echo "Scanning file: ${FILE_TO_SCAN}" - $CLANG_TIDY_CMD --warnings-as-errors=* -p build $FILE_TO_SCAN +function scan() { + WARN_MODE=$1 + FILES=$2 + FILES_ARRAY=(${FILES}) + NUMBER_OF_FILES=${#FILES_ARRAY[@]} + + if [[ $WARN_MODE == "warn" ]]; then + WARN_MODE_PARAM="" + elif [[ $WARN_MODE == "error" ]]; then + WARN_MODE_PARAM="--warnings-as-errors=*" else - if [[ -z $FILE_LIST ]] - then - echo "'${FILE_WITH_SCAN_LIST}' is empty skipping folder scan." - return 0 - fi - echo "Performing full scan of all folders in '${FILE_WITH_SCAN_LIST}'" - $CLANG_TIDY_CMD --warnings-as-errors=* -p build "$(find "${FILE_LIST}" -type f | grep -E ${FILE_FILTER})" + echo "Invalid parameter! Must be either 'warn' or 'error' but got '${WARN_MODE}'" + return 1 + fi + + if [[ ${NUMBER_OF_FILES} -eq 0 ]]; then + echo -e "${COLOR_YELLOW}-> nothing to do${COLOR_OFF}" + return 0 fi + + echo -e "${COLOR_GREEN}Processing files ...${COLOR_OFF}" + MAX_CONCURRENT_EXECUTIONS=$(nproc) + CURRENT_CONCURRENT_EXECUTIONS=0 + echo "Concurrency set to '${MAX_CONCURRENT_EXECUTIONS}'" + FILE_COUNTER=1 + for FILE in $FILES; do + # run multiple clang-tidy instances concurrently + if [[ ${CURRENT_CONCURRENT_EXECUTIONS} -ge ${MAX_CONCURRENT_EXECUTIONS} ]]; then + wait -n # wait for one of the background processes to finish + CURRENT_CONCURRENT_EXECUTIONS=$((CURRENT_CONCURRENT_EXECUTIONS - 1)) + fi + + echo -e "${COLOR_GREEN}[${FILE_COUNTER}/${NUMBER_OF_FILES}]${COLOR_OFF} ${FILE}" + FILE_COUNTER=$((FILE_COUNTER + 1)) + + if test -f "$FILE"; then + ${CLANG_TIDY_CMD} ${WARN_MODE_PARAM} --quiet -p build ${FILE} & + CURRENT_CONCURRENT_EXECUTIONS=$((CURRENT_CONCURRENT_EXECUTIONS + 1)) + else + echo -e "${COLOR_RED}File does not exist! Aborting!${COLOR_OFF}" + return 1 + fi + done + # wait on each background process individually to abort script when a process exits with an error + while [[ ${CURRENT_CONCURRENT_EXECUTIONS} -ne 0 ]]; do + wait -n # wait for one of the background processes to finish + CURRENT_CONCURRENT_EXECUTIONS=$((CURRENT_CONCURRENT_EXECUTIONS - 1)) + done + + echo -e "${COLOR_GREEN}... done!${COLOR_OFF}" } if [[ "$MODE" == "hook"* ]]; then @@ -116,35 +188,23 @@ if [[ "$MODE" == "hook"* ]]; then FILE_WITH_SCAN_LIST=$2 fi - FILES=$(git diff --cached --name-only --diff-filter=CMRT | grep -E "$FILE_FILTER" | cat) - # List only added files - ADDED_FILES=$(git diff --cached --name-only --diff-filter=A | grep -E "$FILE_FILTER" | cat) - echo "Checking files with Clang-Tidy" - echo " Number of modified files: $(echo "${FILES}" | grep -v "^$" | wc -l)" - if [ -z "$FILES" ]; then - echo " -> nothing to do" + MODIFIED_FILES=$(git diff --cached --name-only --diff-filter=CMRT | grep -E "$FILE_FILTER" | cat) + MODIFIED_FILES_ARRAY=($MODIFIED_FILES) + NUMBER_OF_MODIFIED_FILES=${#MODIFIED_FILES_ARRAY[@]} + echo "" + echo "Checking modified files with Clang-Tidy" + if [[ $FILE_WITH_SCAN_LIST && ${NUMBER_OF_MODIFIED_FILES} -gt 0 ]]; then + scanWithFileList "${FILE_WITH_SCAN_LIST}" "${MODIFIED_FILES}" else - echo " processing ..." - if [[ $FILE_WITH_SCAN_LIST ]]; then - for FILE_TO_SCAN in $FILES; do - echo " ${FILE_TO_SCAN}" - scanWithFileList $FILE_WITH_SCAN_LIST $FILE_TO_SCAN - done - else - $CLANG_TIDY_CMD -p build $FILES - fi - echo " ... done" + scan "warn" "${MODIFIED_FILES}" fi - echo " " - echo " Number of added files: $(echo "${ADDED_FILES}" | grep -v "^$" | wc -l)" - if [ -z "$ADDED_FILES" ]; then - echo " -> nothing to do" - else - echo " processing ..." - $CLANG_TIDY_CMD --warnings-as-errors=* -p build $ADDED_FILES - echo " ... done" - fi + # List only added files + ADDED_FILES=$(git diff --cached --name-only --diff-filter=A | grep -E "$FILE_FILTER" | cat) + echo "" + echo "Checking added files with Clang-Tidy" + scan "error" "${ADDED_FILES}" + exit elif [[ "$MODE" == "full"* ]]; then DIRECTORY_TO_SCAN=$2 @@ -153,26 +213,27 @@ elif [[ "$MODE" == "full"* ]]; then then if ! test -d "$DIRECTORY_TO_SCAN" then - echo "The directory which should be scanned '${DIRECTORY_TO_SCAN}' does not exist" + echo "${COLOR_RED}The directory which should be scanned '${DIRECTORY_TO_SCAN}' does not exist! Aborting!${COLOR_OFF}" exit 1 fi - echo "scanning all files in '${DIRECTORY_TO_SCAN}'" - $CLANG_TIDY_CMD -p build $(find $DIRECTORY_TO_SCAN -type f | grep -E $FILE_FILTER ) + echo "" + echo "Scanning all files in '${DIRECTORY_TO_SCAN}'" + FILES=$(find $DIRECTORY_TO_SCAN -type f | grep -E $FILE_FILTER ) + scan "warn" "${FILES}" exit $? else - FILES=$(git ls-files | grep -E "$FILE_FILTER") + FILES=$(find iceoryx_* tools/introspection -type f | grep -E "$FILE_FILTER") + echo "" echo "Checking all files with Clang-Tidy" - echo " " - echo $FILES - $CLANG_TIDY_CMD -p build $FILES + scan "warn" "${FILES}" exit $? fi elif [[ "$MODE" == "scan_list"* ]]; then FILE_WITH_SCAN_LIST=$2 - FILE_TO_SCAN=$3 + FILES_TO_SCAN=$3 # if there is more than one file, they must be enclosed in quotes -> "file1 file2 file3" - scanWithFileList $FILE_WITH_SCAN_LIST $FILE_TO_SCAN + scanWithFileList "${FILE_WITH_SCAN_LIST}" "${FILES_TO_SCAN}" exit $? else From 2a5da9ac666e3154a0b228c8f4be6a7746471145 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Wed, 4 Oct 2023 16:29:11 +0200 Subject: [PATCH 8/8] iox-#1755 Fix typos and update documentation --- doc/design/logging.md | 4 ++-- .../include/iceoryx_hoofs/error_reporting/error_logging.hpp | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/design/logging.md b/doc/design/logging.md index 0d9a23f444..d9c0c9bdbc 100644 --- a/doc/design/logging.md +++ b/doc/design/logging.md @@ -97,11 +97,11 @@ by the `BaseLogger` template parameter and the latter by deriving from the logge #### Logging macro with lazy evaluation -The `IOX_LOG` macro is intended for general use. It has two parameter. The first one +The `IOX_LOG` macro is intended for general use. It has two parameters. The first one sets the log level and delegates `file`, `line` and `function` to the `IOX_LOG_INTERNAL` macro. The latter can be used in places like a custom `ASSERT` macro which already obtained the `file`, `line` and `function` parameter. The second parameter contains -the items to log and multiple items can be logged by by using the '<<' operator. +the items to log and multiple items can be logged by using the '<<' operator. The buildup of the log message is only done when the condition to log the message is fulfilled. This is accomplished by a macro with an if-statement. diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp index 10e3932fc7..7795846db0 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/error_reporting/error_logging.hpp @@ -25,6 +25,7 @@ /// @brief Log the location of an error. /// @param location the location of the error +/// @param msg_stream is the log message stream; multiple items can be logged by using the '<<' operator #define IOX_ERROR_INTERNAL_LOG(location, msg_stream) \ IOX_LOG_INTERNAL(location.file, \ location.line, \ @@ -34,6 +35,7 @@ /// @brief Log the location of a fatal error. /// @param location the location of the error +/// @param msg_stream is the log message stream; multiple items can be logged by using the '<<' operator #define IOX_ERROR_INTERNAL_LOG_FATAL(location, msg_stream) \ IOX_LOG_INTERNAL(location.file, \ location.line, \ @@ -43,6 +45,7 @@ /// @brief Log a panic invocation. /// @param location the location of the panic invocation. +/// @param msg_stream is the log message stream; multiple items can be logged by using the '<<' operator #define IOX_ERROR_INTERNAL_LOG_PANIC(location, msg_stream) IOX_ERROR_INTERNAL_LOG_FATAL(location, msg_stream) // NOLINTEND(cppcoreguidelines-macro-usage, bugprone-macro-parentheses)