From 965eff9a9c92e45879dc65785725d383f3dcb2a7 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Wed, 31 Jul 2024 19:34:21 +0000 Subject: [PATCH 01/18] Move spdlog::logger to gz-utils Signed-off-by: Michael Carroll --- CMakeLists.txt | 4 +- cli/src/CMakeLists.txt | 1 + logger/include/CMakeLists.txt | 0 logger/include/gz/utils/CMakeLists.txt | 1 + logger/include/gz/utils/logger/LogMessage.hh | 58 +++++++ logger/include/gz/utils/logger/SplitSink.hh | 168 +++++++++++++++++++ logger/src/CMakeLists.txt | 14 ++ logger/src/LogMessage.cc | 50 ++++++ logger/src/SplitSink_TEST.cc | 69 ++++++++ 9 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 logger/include/CMakeLists.txt create mode 100644 logger/include/gz/utils/CMakeLists.txt create mode 100644 logger/include/gz/utils/logger/LogMessage.hh create mode 100644 logger/include/gz/utils/logger/SplitSink.hh create mode 100644 logger/src/CMakeLists.txt create mode 100644 logger/src/LogMessage.cc create mode 100644 logger/src/SplitSink_TEST.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 271a48e..8b06706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,11 +37,13 @@ if(NOT GZ_UTILS_VENDOR_CLI11) gz_find_package(CLI11 REQUIRED_BY cli PKGCONFIG_IGNORE) endif() +gz_find_package(spdlog REQUIRED_BY logger) + #============================================================================ # Configure the build #============================================================================ gz_configure_build(QUIT_IF_BUILD_ERRORS - COMPONENTS cli) + COMPONENTS cli logger) #============================================================================ # Create package information diff --git a/cli/src/CMakeLists.txt b/cli/src/CMakeLists.txt index 1d3ef22..3213d75 100644 --- a/cli/src/CMakeLists.txt +++ b/cli/src/CMakeLists.txt @@ -2,6 +2,7 @@ gz_add_component( cli INTERFACE INDEPENDENT_FROM_PROJECT_LIB + LIB_DEPS spdlog::spdlog GET_TARGET_NAME gz_utils_cli_target_name) # Make sure that the name is visible also in cli/include/CMakeLists.txt diff --git a/logger/include/CMakeLists.txt b/logger/include/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/logger/include/gz/utils/CMakeLists.txt b/logger/include/gz/utils/CMakeLists.txt new file mode 100644 index 0000000..646d386 --- /dev/null +++ b/logger/include/gz/utils/CMakeLists.txt @@ -0,0 +1 @@ +gz_install_all_headers(COMPONENT logger) diff --git a/logger/include/gz/utils/logger/LogMessage.hh b/logger/include/gz/utils/logger/LogMessage.hh new file mode 100644 index 0000000..016ddf5 --- /dev/null +++ b/logger/include/gz/utils/logger/LogMessage.hh @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GZ_UTILS_LOGGER_LOGMESSAGE_HH_ +#define GZ_UTILS_LOGGER_LOGMESSAGE_HH_ + +#include + +#include +#include + +namespace gz::utils::logger +{ + +class LogMessage +{ + /// \brief Constructor. + /// \param[in] _logLevel Log level. + public: explicit LogMessage(spdlog::level::level_enum _logLevel, + const char *_logger = nullptr, + const char *_file = nullptr, + int _line = -1); + + /// \brief Destructor. + public: ~LogMessage(); + + /// \brief Get access to the underlying stream. + /// \return The underlying stream. + public: std::ostream &stream(); + + /// \brief Log level. + private: spdlog::level::level_enum severity; + + private: std::shared_ptr logger; + + /// \brief Source file location information. + private: spdlog::source_loc sourceLocation; + + /// \brief Underlying stream. + private: std::ostringstream ss; +}; +} // namespace gz::utils::logger + +#endif // GZ_UTILS_LOGGER_LOGMESSAGE_HH_ diff --git a/logger/include/gz/utils/logger/SplitSink.hh b/logger/include/gz/utils/logger/SplitSink.hh new file mode 100644 index 0000000..94ecc9a --- /dev/null +++ b/logger/include/gz/utils/logger/SplitSink.hh @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GZ_UTILS_LOGGER_SPLITSINK_HH_ +#define GZ_UTILS_LOGGER_SPLITSINK_HH_ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace gz::utils::logger +{ +/// \brief Logging sink for spdlog that logs in Gazebo-conventions +/// +/// This will route messages with severity (warn, err, critical) to stderr, +/// and all other levels (info, debug, trace) to stdout +template +class SplitConsoleSink : public spdlog::sinks::base_sink +{ + public: SplitConsoleSink() = default; + public: SplitConsoleSink(const SplitConsoleSink &) = delete; + public: SplitConsoleSink &operator=(const SplitConsoleSink &) = delete; + + protected: void sink_it_(const spdlog::details::log_msg &_msg) override + { + if (!this->should_log(_msg.level)) + { + return; + } + + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || + _msg.level == spdlog::level::critical) + { + this->stderr.log(_msg); + } + else + this->stdout.log(_msg); + } + + protected: void flush_() override + { + this->stdout.flush(); + this->stderr.flush(); + } + + void set_pattern_(const std::string &pattern) override + { + this->set_formatter_(spdlog::details::make_unique(pattern)); + } + + void set_formatter_(std::unique_ptr sink_formatter) override + { + spdlog::sinks::base_sink::formatter_ = std::move(sink_formatter); + this->stdout.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + this->stderr.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + } + + /// \brief Standard output. + private: spdlog::sinks::stdout_color_sink_st stdout; + + /// \brief Standard error. + private: spdlog::sinks::stderr_color_sink_st stderr; +}; + +using SplitConsoleSinkMt = SplitConsoleSink; +using SplitConsoleSinkSt = SplitConsoleSink; + +/// \brief Logging sink for spdlog that logs in Gazebo-conventions +/// +/// This will route messages with severity (warn, err, critical) to stderr, +/// and all other levels (info, debug, trace) to stdout +template +class SplitRingBufferSink: public spdlog::sinks::base_sink +{ + public: SplitRingBufferSink() = default; + public: SplitRingBufferSink(const SplitRingBufferSink &) = delete; + public: SplitRingBufferSink &operator=(const SplitRingBufferSink &) = delete; + + public: std::vector last_raw_stdout(size_t lim = 0) + { + return this->stdout.last_raw(lim); + } + + public: std::vector last_raw_stderr(size_t lim = 0) + { + return this->stderr.last_raw(lim); + } + + public: std::vector last_formatted_stdout(size_t lim = 0) + { + return this->stdout.last_formatted(lim); + } + + public: std::vector last_formatted_stderr(size_t lim = 0) + { + return this->stderr.last_formatted(lim); + } + + protected: void sink_it_(const spdlog::details::log_msg &_msg) override + { + if (!this->should_log(_msg.level)) + { + return; + } + + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || + _msg.level == spdlog::level::critical) + { + this->stderr.log(_msg); + } + else + this->stdout.log(_msg); + } + + protected: void flush_() override + { + this->stdout.flush(); + this->stderr.flush(); + } + + protected: void set_pattern_(const std::string &pattern) override + { + this->set_formatter_(spdlog::details::make_unique(pattern)); + } + + protected: void set_formatter_(std::unique_ptr sink_formatter) override + { + spdlog::sinks::base_sink::formatter_ = std::move(sink_formatter); + this->stdout.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + this->stderr.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + } + + + /// \brief Standard output. + private: spdlog::sinks::ringbuffer_sink_st stdout {n_items}; + + /// \brief Standard error. + private: spdlog::sinks::ringbuffer_sink_st stderr {n_items}; +}; + +template +using SplitRingBufferSinkMt = SplitRingBufferSink; + +} // namespace gz::utils::logger +#endif // GZ_UTILS_LOGGER_SPLITSINK_HH__ diff --git a/logger/src/CMakeLists.txt b/logger/src/CMakeLists.txt new file mode 100644 index 0000000..df5ab44 --- /dev/null +++ b/logger/src/CMakeLists.txt @@ -0,0 +1,14 @@ +gz_get_libsources_and_unittests(sources gtest_sources) + +gz_add_component( + logger + SOURCES ${sources} + INDEPENDENT_FROM_PROJECT_LIB + GET_TARGET_NAME gz_utils_logger_target_name) + +target_link_libraries(${gz_utils_logger_target_name} PUBLIC spdlog::spdlog) + +gz_build_tests(TYPE UNIT + SOURCES ${gtest_sources} + LIB_DEPS ${gz_utils_logger_target_name} +) diff --git a/logger/src/LogMessage.cc b/logger/src/LogMessage.cc new file mode 100644 index 0000000..98d0024 --- /dev/null +++ b/logger/src/LogMessage.cc @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +namespace gz::utils::logger +{ +///////////////////////////////////////////////// +LogMessage::LogMessage(spdlog::level::level_enum _logLevel, + const char *_logger, + const char *_file, + int _line): + severity(_logLevel), + logger(_logger == nullptr ? spdlog::default_logger() : spdlog::get(_logger)), + sourceLocation(_file, _line, "") +{ +} + +///////////////////////////////////////////////// +LogMessage::~LogMessage() +{ + if (sourceLocation.filename != nullptr) + { + logger->log(this->sourceLocation, this->severity, this->ss.str()); + } else { + logger->log(this->severity, this->ss.str()); + } +} +///////////////////////////////////////////////// +std::ostream &LogMessage::stream() +{ + return this->ss; +} + +} // namespace gz::utils::logger diff --git a/logger/src/SplitSink_TEST.cc b/logger/src/SplitSink_TEST.cc new file mode 100644 index 0000000..e004765 --- /dev/null +++ b/logger/src/SplitSink_TEST.cc @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +///////////////////////////////////////////////// +TEST(SplitConsoleSink, foo) +{ + auto split_sink = std::make_shared(); + + spdlog::logger logger("split_sink", {split_sink}); + logger.set_level(spdlog::level::trace); + + logger.trace("trace"); + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + logger.critical("critical"); +} + +///////////////////////////////////////////////// +TEST(SplitRingBufferSink, foo) +{ + auto split_sink = std::make_shared>(); + auto split_sink_console = std::make_shared(); + + spdlog::logger logger("split_sink", {split_sink, split_sink_console}); + logger.set_level(spdlog::level::trace); + + logger.trace("trace"); + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + logger.critical("critical"); + + { + auto logs = split_sink->last_raw_stdout(); + EXPECT_EQ(logs[0].payload, "trace"); + EXPECT_EQ(logs[1].payload, "debug"); + EXPECT_EQ(logs[2].payload, "info"); + } + + { + auto logs = split_sink->last_raw_stderr(); + EXPECT_EQ(logs[0].payload, "warn"); + EXPECT_EQ(logs[1].payload, "error"); + EXPECT_EQ(logs[2].payload, "critical"); + } +} From ac15ba1883650bc74d56f10e7017c72858b770f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 2 Aug 2024 16:31:37 +0200 Subject: [PATCH 02/18] Code compiling and example. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- examples/logger/CMakeLists.txt | 12 ++ examples/logger/main.cc | 43 ++++++ logger/include/CMakeLists.txt | 1 + logger/include/gz/utils/logger/Console.hh | 73 +++++++++ logger/src/CMakeLists.txt | 7 +- logger/src/Console.cc | 179 ++++++++++++++++++++++ 6 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 examples/logger/CMakeLists.txt create mode 100644 examples/logger/main.cc create mode 100644 logger/include/gz/utils/logger/Console.hh create mode 100644 logger/src/Console.cc diff --git a/examples/logger/CMakeLists.txt b/examples/logger/CMakeLists.txt new file mode 100644 index 0000000..4e1c373 --- /dev/null +++ b/examples/logger/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +project(gz-utils-logger-demo) + +# Find the Gazebo Libraries used directly by the example +find_package(gz-utils3 REQUIRED COMPONENTS logger) +set(GZ_UTILS_VER ${gz-utils3_VERSION_MAJOR}) + +add_executable(${PROJECT_NAME} main.cc) +target_link_libraries( + ${PROJECT_NAME} + gz-utils${GZ_UTILS_VER}::logger +) diff --git a/examples/logger/main.cc b/examples/logger/main.cc new file mode 100644 index 0000000..7ef7474 --- /dev/null +++ b/examples/logger/main.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +/* + * At this point, all logger functionality will be available. + */ + +////////////////////////////////////////////////// +int main(int argc, char** argv) +{ + auto splitSink = + std::make_shared>(); + auto splitSinkConsole = + std::make_shared(); + + spdlog::logger logger("split_sink", {splitSink, splitSinkConsole}); + logger.set_level(spdlog::level::trace); + + logger.trace("trace"); + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + logger.critical("critical"); +} diff --git a/logger/include/CMakeLists.txt b/logger/include/CMakeLists.txt index e69de29..87ba4cc 100644 --- a/logger/include/CMakeLists.txt +++ b/logger/include/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(gz/utils) \ No newline at end of file diff --git a/logger/include/gz/utils/logger/Console.hh b/logger/include/gz/utils/logger/Console.hh new file mode 100644 index 0000000..0b8f0cd --- /dev/null +++ b/logger/include/gz/utils/logger/Console.hh @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +#ifndef GZ_UTILS_CONSOLE_HH_ +#define GZ_UTILS_CONSOLE_HH_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace gz::utils::logger +{ + /// \brief Gazebo console and file logging class. + /// This will configure spdlog with a sane set of defaults for logging to the + /// console as well as a file. + class GZ_UTILS_VISIBLE Console + { + /// \brief Class constructor. + /// \param[in] _loggerName Logger name. + public: explicit Console(const std::string &_loggerName); + + /// \brief Set the console output color mode. + ///\param[in] _mode Color mode. + public: void SetColorMode(spdlog::color_mode _mode); + + /// \brief Set the log destination filename. + /// \param[in] _filename Log file name. + public: void SetLogDestination(const std::string &_filename); + + /// \brief Get the log destination filename. + /// \return Log file name. + public: std::string LogDestination() const; + + /// \brief Access the underlying spdlog logger. + /// \return The spdlog logger. + public: [[nodiscard]] spdlog::logger &Logger() const; + + /// \brief Access the underlying spdlog logger, with ownership. + /// \return The spdlog logger. + public: [[nodiscard]] std::shared_ptr LoggerPtr() const; + + /// \brief Access the global gz console logger. + /// \return The gz consoler logger. + public: static Console &Root(); + + /// \brief Implementation Pointer. + GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) + }; +} + +#endif // GZ_UTILS_CONSOLE_HH_ diff --git a/logger/src/CMakeLists.txt b/logger/src/CMakeLists.txt index df5ab44..b6fbdc4 100644 --- a/logger/src/CMakeLists.txt +++ b/logger/src/CMakeLists.txt @@ -1,12 +1,13 @@ gz_get_libsources_and_unittests(sources gtest_sources) -gz_add_component( - logger +gz_add_component(logger SOURCES ${sources} INDEPENDENT_FROM_PROJECT_LIB GET_TARGET_NAME gz_utils_logger_target_name) -target_link_libraries(${gz_utils_logger_target_name} PUBLIC spdlog::spdlog) +target_link_libraries(${gz_utils_logger_target_name} + PUBLIC + spdlog::spdlog) gz_build_tests(TYPE UNIT SOURCES ${gtest_sources} diff --git a/logger/src/Console.cc b/logger/src/Console.cc new file mode 100644 index 0000000..feddb87 --- /dev/null +++ b/logger/src/Console.cc @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace { +/// \brief Custom log sink that routes to stdout/stderr in Gazebo conventions. +class GzSplitSink : public spdlog::sinks::sink +{ + /// \brief Class destructor. + public: ~GzSplitSink() override = default; + + /// \brief Log a message. + /// \param[in] _msg The message to log. + public: void log(const spdlog::details::log_msg &_msg) override + { + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || + _msg.level == spdlog::level::critical) + { + this->stderr.log(_msg); + } + else + this->stdout.log(_msg); + } + + /// \brief Flush messages. + public: void flush() override + { + this->stdout.flush(); + this->stderr.flush(); + } + + /// \brief Set the logging pattern. + /// \param[in] _pattern The logging pattern. + public: void set_pattern(const std::string &_pattern) override + { + this->stdout.set_pattern(_pattern); + this->stderr.set_pattern(_pattern); + } + + /// \brief Set the new formatter. + /// \param[in] _sinkFormatter The formatter. + public: void set_formatter(std::unique_ptr _sinkFormatter) + override + { + this->stdout.set_formatter(_sinkFormatter->clone()); + this->stderr.set_formatter(std::move(_sinkFormatter)); + } + + /// \brief Set the color mode. + /// \param[in] _mode Color mode. + public: void set_color_mode(spdlog::color_mode _mode) + { + this->stdout.set_color_mode(_mode); + this->stderr.set_color_mode(_mode); + } + + /// \brief Standard output. + private: spdlog::sinks::stdout_color_sink_mt stdout; + + /// \brief Standard error. + private: spdlog::sinks::stderr_color_sink_mt stderr; +}; +} // namespace + +namespace gz::utils::logger +{ +/// \brief Private data for the Console class. +class Console::Implementation +{ + /// \brief Constructor. + /// \param[in] _loggerName Logger name. + public: explicit Implementation(const std::string &_loggerName) + : consoleSink(std::make_shared()), + sinks(std::make_shared()), + logger(std::make_shared(_loggerName, sinks)) + { + } + + /// \brief . + std::shared_ptr consoleSink; + + /// \brief . + std::shared_ptr fileSink {nullptr}; + + /// \brief . + std::shared_ptr sinks {nullptr}; + + /// \brief . + std::shared_ptr logger {nullptr}; +}; + +///////////////////////////////////////////////// +Console::Console(const std::string &_loggerName) + : dataPtr(gz::utils::MakeUniqueImpl(_loggerName)) +{ + // Add the console sink as a destination. + this->dataPtr->sinks->add_sink(this->dataPtr->consoleSink); + + // Configure the logger. + this->dataPtr->logger->set_level(spdlog::level::err); + this->dataPtr->logger->flush_on(spdlog::level::err); + + spdlog::flush_every(std::chrono::seconds(5)); + spdlog::register_logger(this->dataPtr->logger); +} + +///////////////////////////////////////////////// +void Console::SetColorMode(spdlog::color_mode _mode) +{ + this->dataPtr->consoleSink->set_color_mode(_mode); +} + +///////////////////////////////////////////////// +void Console::SetLogDestination(const std::string &_filename) +{ + if (this->dataPtr->fileSink) + this->dataPtr->sinks->remove_sink(this->dataPtr->fileSink); + + this->dataPtr->fileSink = + std::make_shared(_filename, true); + this->dataPtr->sinks->add_sink(this->dataPtr->fileSink); +} + +///////////////////////////////////////////////// +std::string Console::LogDestination() const +{ + std::string logPath = ""; + if (this->dataPtr->fileSink) + { + logPath = this->dataPtr->fileSink->filename(); + } + + return logPath; +} + +///////////////////////////////////////////////// +spdlog::logger &Console::Logger() const +{ + return *this->dataPtr->logger; +} + +///////////////////////////////////////////////// +std::shared_ptr Console::LoggerPtr() const +{ + return this->dataPtr->logger; +} + +///////////////////////////////////////////////// +Console &Console::Root() +{ + static gz::utils::NeverDestroyed root{"gz"}; + return root.Access(); +} +} // namespace gz::utils::logger From 4eefd71d081a52ee9b67c4dffb06c2c9803bd67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 19:07:33 +0200 Subject: [PATCH 03/18] Refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- CMakeLists.txt | 4 +- cli/src/CMakeLists.txt | 1 - examples/{logger => log}/CMakeLists.txt | 4 +- examples/{logger => log}/main.cc | 32 ++-- {logger => log}/include/CMakeLists.txt | 0 log/include/gz/utils/CMakeLists.txt | 1 + .../include/gz/utils/log/Logger.hh | 34 +--- .../include/gz/utils/log}/SplitSink.hh | 127 +++++++++---- {logger => log}/src/CMakeLists.txt | 8 +- log/src/Logger.cc | 104 ++++++++++ {logger => log}/src/SplitSink_TEST.cc | 9 +- logger/include/gz/utils/CMakeLists.txt | 1 - logger/include/gz/utils/logger/LogMessage.hh | 58 ------ logger/src/Console.cc | 179 ------------------ logger/src/LogMessage.cc | 50 ----- 15 files changed, 224 insertions(+), 388 deletions(-) rename examples/{logger => log}/CMakeLists.txt (75%) rename examples/{logger => log}/main.cc (53%) rename {logger => log}/include/CMakeLists.txt (100%) create mode 100644 log/include/gz/utils/CMakeLists.txt rename logger/include/gz/utils/logger/Console.hh => log/include/gz/utils/log/Logger.hh (66%) rename {logger/include/gz/utils/logger => log/include/gz/utils/log}/SplitSink.hh (52%) rename {logger => log}/src/CMakeLists.txt (53%) create mode 100644 log/src/Logger.cc rename {logger => log}/src/SplitSink_TEST.cc (85%) delete mode 100644 logger/include/gz/utils/CMakeLists.txt delete mode 100644 logger/include/gz/utils/logger/LogMessage.hh delete mode 100644 logger/src/Console.cc delete mode 100644 logger/src/LogMessage.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index f7b65a1..ceb79a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,13 +37,13 @@ if(NOT GZ_UTILS_VENDOR_CLI11) gz_find_package(CLI11 REQUIRED_BY cli PKGCONFIG_IGNORE) endif() -gz_find_package(spdlog REQUIRED_BY logger) +gz_find_package(spdlog REQUIRED_BY log) #============================================================================ # Configure the build #============================================================================ gz_configure_build(QUIT_IF_BUILD_ERRORS - COMPONENTS cli logger) + COMPONENTS cli log) #============================================================================ # Create package information diff --git a/cli/src/CMakeLists.txt b/cli/src/CMakeLists.txt index 3213d75..1d3ef22 100644 --- a/cli/src/CMakeLists.txt +++ b/cli/src/CMakeLists.txt @@ -2,7 +2,6 @@ gz_add_component( cli INTERFACE INDEPENDENT_FROM_PROJECT_LIB - LIB_DEPS spdlog::spdlog GET_TARGET_NAME gz_utils_cli_target_name) # Make sure that the name is visible also in cli/include/CMakeLists.txt diff --git a/examples/logger/CMakeLists.txt b/examples/log/CMakeLists.txt similarity index 75% rename from examples/logger/CMakeLists.txt rename to examples/log/CMakeLists.txt index 4e1c373..fd511fa 100644 --- a/examples/logger/CMakeLists.txt +++ b/examples/log/CMakeLists.txt @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(gz-utils-logger-demo) # Find the Gazebo Libraries used directly by the example -find_package(gz-utils3 REQUIRED COMPONENTS logger) +find_package(gz-utils3 REQUIRED COMPONENTS log) set(GZ_UTILS_VER ${gz-utils3_VERSION_MAJOR}) add_executable(${PROJECT_NAME} main.cc) target_link_libraries( ${PROJECT_NAME} - gz-utils${GZ_UTILS_VER}::logger + gz-utils${GZ_UTILS_VER}::log ) diff --git a/examples/logger/main.cc b/examples/log/main.cc similarity index 53% rename from examples/logger/main.cc rename to examples/log/main.cc index 7ef7474..1ad207d 100644 --- a/examples/logger/main.cc +++ b/examples/log/main.cc @@ -15,29 +15,23 @@ * */ -#include -#include -#include - -/* - * At this point, all logger functionality will be available. - */ +#include +#include ////////////////////////////////////////////////// int main(int argc, char** argv) { - auto splitSink = - std::make_shared>(); - auto splitSinkConsole = - std::make_shared(); + gz::utils::log::Logger logger("my_logger"); + logger.RawLogger().set_level(spdlog::level::trace); - spdlog::logger logger("split_sink", {splitSink, splitSinkConsole}); - logger.set_level(spdlog::level::trace); + std::filesystem::path logDir = std::filesystem::temp_directory_path(); + std::filesystem::path logFile = "my_log.txt"; + std::filesystem::path logPath = logDir / logFile; - logger.trace("trace"); - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - logger.critical("critical"); + logger.SetLogDestination(logPath); + logger.RawLogger().trace("trace"); + logger.RawLogger().info("info"); + logger.RawLogger().warn("warn"); + logger.RawLogger().error("error"); + logger.RawLogger().critical("critical"); } diff --git a/logger/include/CMakeLists.txt b/log/include/CMakeLists.txt similarity index 100% rename from logger/include/CMakeLists.txt rename to log/include/CMakeLists.txt diff --git a/log/include/gz/utils/CMakeLists.txt b/log/include/gz/utils/CMakeLists.txt new file mode 100644 index 0000000..8733f2d --- /dev/null +++ b/log/include/gz/utils/CMakeLists.txt @@ -0,0 +1 @@ +gz_install_all_headers(COMPONENT log) diff --git a/logger/include/gz/utils/logger/Console.hh b/log/include/gz/utils/log/Logger.hh similarity index 66% rename from logger/include/gz/utils/logger/Console.hh rename to log/include/gz/utils/log/Logger.hh index 0b8f0cd..47298cf 100644 --- a/logger/include/gz/utils/logger/Console.hh +++ b/log/include/gz/utils/log/Logger.hh @@ -14,36 +14,24 @@ * limitations under the License. * */ -#ifndef GZ_UTILS_CONSOLE_HH_ -#define GZ_UTILS_CONSOLE_HH_ +#ifndef GZ_UTILS_LOG_LOGGER_HH_ +#define GZ_UTILS_LOG_LOGGER_HH_ -#include -#include -#include +#include #include -#include #include - #include -#include - -#include -#include -namespace gz::utils::logger +namespace gz::utils::log { /// \brief Gazebo console and file logging class. /// This will configure spdlog with a sane set of defaults for logging to the /// console as well as a file. - class GZ_UTILS_VISIBLE Console + class GZ_UTILS_VISIBLE Logger { /// \brief Class constructor. /// \param[in] _loggerName Logger name. - public: explicit Console(const std::string &_loggerName); - - /// \brief Set the console output color mode. - ///\param[in] _mode Color mode. - public: void SetColorMode(spdlog::color_mode _mode); + public: explicit Logger(const std::string &_loggerName); /// \brief Set the log destination filename. /// \param[in] _filename Log file name. @@ -55,19 +43,15 @@ namespace gz::utils::logger /// \brief Access the underlying spdlog logger. /// \return The spdlog logger. - public: [[nodiscard]] spdlog::logger &Logger() const; + public: [[nodiscard]] spdlog::logger &RawLogger() const; /// \brief Access the underlying spdlog logger, with ownership. /// \return The spdlog logger. - public: [[nodiscard]] std::shared_ptr LoggerPtr() const; - - /// \brief Access the global gz console logger. - /// \return The gz consoler logger. - public: static Console &Root(); + public: [[nodiscard]] std::shared_ptr RawLoggerPtr() const; /// \brief Implementation Pointer. GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) }; } -#endif // GZ_UTILS_CONSOLE_HH_ +#endif // GZ_UTILS_LOG_LOGGER_HH_ diff --git a/logger/include/gz/utils/logger/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh similarity index 52% rename from logger/include/gz/utils/logger/SplitSink.hh rename to log/include/gz/utils/log/SplitSink.hh index 94ecc9a..315ee73 100644 --- a/logger/include/gz/utils/logger/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -15,42 +15,47 @@ * */ -#ifndef GZ_UTILS_LOGGER_SPLITSINK_HH_ -#define GZ_UTILS_LOGGER_SPLITSINK_HH_ +#ifndef GZ_UTILS_LOG_SPLITSINK_HH_ +#define GZ_UTILS_LOG_SPLITSINK_HH_ +#include #include +#include #include #include -#include #include - #include #include #include -namespace gz::utils::logger +namespace gz::utils::log { -/// \brief Logging sink for spdlog that logs in Gazebo-conventions +/// \brief Logging sink for spdlog that logs in Gazebo-conventions. /// /// This will route messages with severity (warn, err, critical) to stderr, -/// and all other levels (info, debug, trace) to stdout +/// and all other levels (info, debug, trace) to stdout. template class SplitConsoleSink : public spdlog::sinks::base_sink { + /// \brief Class constructor. public: SplitConsoleSink() = default; + + /// \brief No copy constructor. public: SplitConsoleSink(const SplitConsoleSink &) = delete; + + /// \brief No asignment operator. public: SplitConsoleSink &operator=(const SplitConsoleSink &) = delete; + /// \brief Try to log a message. + /// \param[in] _msg The message to log. protected: void sink_it_(const spdlog::details::log_msg &_msg) override { if (!this->should_log(_msg.level)) - { return; - } - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || _msg.level == spdlog::level::critical) { this->stderr.log(_msg); @@ -59,22 +64,31 @@ class SplitConsoleSink : public spdlog::sinks::base_sink this->stdout.log(_msg); } + /// \brief Flush messages. protected: void flush_() override { this->stdout.flush(); this->stderr.flush(); } - void set_pattern_(const std::string &pattern) override + /// \brief Set the logging pattern. + /// \param[in] _pattern The logging pattern. + protected: void set_pattern_(const std::string &_pattern) override { - this->set_formatter_(spdlog::details::make_unique(pattern)); + this->set_formatter( + spdlog::details::make_unique(_pattern)); } - void set_formatter_(std::unique_ptr sink_formatter) override + /// \brief Set the new formatter. + /// \param[in] _sinkFormatter The formatter. + protected: void set_formatter_( + std::unique_ptr _sinkFormatter) override { - spdlog::sinks::base_sink::formatter_ = std::move(sink_formatter); - this->stdout.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); - this->stderr.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + spdlog::sinks::base_sink::formatter_ = std::move(_sinkFormatter); + this->stdout.set_formatter( + spdlog::sinks::base_sink::formatter_->clone()); + this->stderr.set_formatter( + spdlog::sinks::base_sink::formatter_->clone()); } /// \brief Standard output. @@ -91,42 +105,62 @@ using SplitConsoleSinkSt = SplitConsoleSink; /// /// This will route messages with severity (warn, err, critical) to stderr, /// and all other levels (info, debug, trace) to stdout -template +template class SplitRingBufferSink: public spdlog::sinks::base_sink { + /// \brief Class constructor. public: SplitRingBufferSink() = default; + + /// \brief No copy constructor. public: SplitRingBufferSink(const SplitRingBufferSink &) = delete; + + /// \brief No asignment operator. public: SplitRingBufferSink &operator=(const SplitRingBufferSink &) = delete; - public: std::vector last_raw_stdout(size_t lim = 0) + /// \brief ToDo. + /// \param[in] _lim + /// \return + public: std::vector last_raw_stdout( + size_t _lim = 0) { - return this->stdout.last_raw(lim); + return this->stdout.last_raw(_lim); } - public: std::vector last_raw_stderr(size_t lim = 0) + /// \brief ToDo. + /// \param[in] _lim + /// \return + public: std::vector last_raw_stderr( + size_t _lim = 0) { - return this->stderr.last_raw(lim); + return this->stderr.last_raw(_lim); } - public: std::vector last_formatted_stdout(size_t lim = 0) + /// \brief ToDo. + /// \param[in] _lim + /// \return + public: std::vector last_formatted_stdout(size_t _lim = 0) { - return this->stdout.last_formatted(lim); + return this->stdout.last_formatted(_lim); } - public: std::vector last_formatted_stderr(size_t lim = 0) + /// \brief ToDo. + /// \param[in] _lim + /// \return + public: std::vector last_formatted_stderr(size_t _lim = 0) { - return this->stderr.last_formatted(lim); + return this->stderr.last_formatted(_lim); } + /// \brief ToDo. + /// \param[in] _lim + /// \return protected: void sink_it_(const spdlog::details::log_msg &_msg) override { if (!this->should_log(_msg.level)) - { return; - } - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || _msg.level == spdlog::level::critical) { this->stderr.log(_msg); @@ -135,34 +169,43 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink this->stdout.log(_msg); } + /// \brief Flush messages. protected: void flush_() override { this->stdout.flush(); this->stderr.flush(); } - protected: void set_pattern_(const std::string &pattern) override + /// \brief Set the logging pattern. + /// \param[in] _pattern The logging pattern. + protected: void set_pattern_(const std::string &_pattern) override { - this->set_formatter_(spdlog::details::make_unique(pattern)); + this->set_formatter_( + spdlog::details::make_unique(_pattern)); } - protected: void set_formatter_(std::unique_ptr sink_formatter) override + /// \brief Set the new formatter. + /// \param[in] _sinkFormatter The formatter. + protected: void set_formatter_( + std::unique_ptr sinkFormatter) override { - spdlog::sinks::base_sink::formatter_ = std::move(sink_formatter); - this->stdout.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); - this->stderr.set_formatter(spdlog::sinks::base_sink::formatter_->clone()); + spdlog::sinks::base_sink::formatter_ = std::move(sinkFormatter); + this->stdout.set_formatter( + spdlog::sinks::base_sink::formatter_->clone()); + this->stderr.set_formatter( + spdlog::sinks::base_sink::formatter_->clone()); } /// \brief Standard output. - private: spdlog::sinks::ringbuffer_sink_st stdout {n_items}; + private: spdlog::sinks::ringbuffer_sink_st stdout {numItems}; /// \brief Standard error. - private: spdlog::sinks::ringbuffer_sink_st stderr {n_items}; + private: spdlog::sinks::ringbuffer_sink_st stderr {numItems}; }; -template -using SplitRingBufferSinkMt = SplitRingBufferSink; +template +using SplitRingBufferSinkMt = SplitRingBufferSink; -} // namespace gz::utils::logger -#endif // GZ_UTILS_LOGGER_SPLITSINK_HH__ +} // namespace gz::utils::log +#endif // GZ_UTILS_LOG_SPLITSINK_HH__ diff --git a/logger/src/CMakeLists.txt b/log/src/CMakeLists.txt similarity index 53% rename from logger/src/CMakeLists.txt rename to log/src/CMakeLists.txt index b6fbdc4..28eadb8 100644 --- a/logger/src/CMakeLists.txt +++ b/log/src/CMakeLists.txt @@ -1,15 +1,15 @@ gz_get_libsources_and_unittests(sources gtest_sources) -gz_add_component(logger +gz_add_component(log SOURCES ${sources} INDEPENDENT_FROM_PROJECT_LIB - GET_TARGET_NAME gz_utils_logger_target_name) + GET_TARGET_NAME gz_utils_log_target_name) -target_link_libraries(${gz_utils_logger_target_name} +target_link_libraries(${gz_utils_log_target_name} PUBLIC spdlog::spdlog) gz_build_tests(TYPE UNIT SOURCES ${gtest_sources} - LIB_DEPS ${gz_utils_logger_target_name} + LIB_DEPS ${gz_utils_log_target_name} ) diff --git a/log/src/Logger.cc b/log/src/Logger.cc new file mode 100644 index 0000000..8362d1f --- /dev/null +++ b/log/src/Logger.cc @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include + +#include +#include +#include +#include +#include + +namespace gz::utils::log +{ +/// \brief Private data for the Logger class. +class Logger::Implementation +{ + /// \brief Constructor. + /// \param[in] _loggerName Logger name. + public: explicit Implementation(const std::string &_loggerName) + : consoleSink(std::make_shared()), + sinks(std::make_shared()), + logger(std::make_shared(_loggerName, sinks)) + { + } + + /// \brief The console sink with stdout and stderr. + std::shared_ptr consoleSink; + + /// \brief The file sink for logging into a file. + std::shared_ptr fileSink {nullptr}; + + /// \brief A sink distribution storing multiple sinks. + std::shared_ptr sinks {nullptr}; + + /// \brief The underlying spdlog logger. + std::shared_ptr logger {nullptr}; +}; + +///////////////////////////////////////////////// +Logger::Logger(const std::string &_loggerName) + : dataPtr(gz::utils::MakeUniqueImpl(_loggerName)) +{ + // Add the console sink by default. + this->dataPtr->sinks->add_sink(this->dataPtr->consoleSink); + + // Configure the logger. + this->dataPtr->logger->set_level(spdlog::level::err); + this->dataPtr->logger->flush_on(spdlog::level::err); + + spdlog::flush_every(std::chrono::seconds(5)); + spdlog::register_logger(this->dataPtr->logger); +} + +///////////////////////////////////////////////// +void Logger::SetLogDestination(const std::string &_filename) +{ + if (this->dataPtr->fileSink) + this->dataPtr->sinks->remove_sink(this->dataPtr->fileSink); + + if (!_filename.empty()) + { + this->dataPtr->fileSink = + std::make_shared(_filename, true); + this->dataPtr->sinks->add_sink(this->dataPtr->fileSink); + } +} + +///////////////////////////////////////////////// +std::string Logger::LogDestination() const +{ + std::string logPath = ""; + if (this->dataPtr->fileSink) + logPath = this->dataPtr->fileSink->filename(); + + return logPath; +} + +///////////////////////////////////////////////// +spdlog::logger &Logger::RawLogger() const +{ + return *this->dataPtr->logger; +} + +///////////////////////////////////////////////// +std::shared_ptr Logger::RawLoggerPtr() const +{ + return this->dataPtr->logger; +} + +} // namespace gz::utils::log diff --git a/logger/src/SplitSink_TEST.cc b/log/src/SplitSink_TEST.cc similarity index 85% rename from logger/src/SplitSink_TEST.cc rename to log/src/SplitSink_TEST.cc index e004765..8371bc1 100644 --- a/logger/src/SplitSink_TEST.cc +++ b/log/src/SplitSink_TEST.cc @@ -18,13 +18,12 @@ #include #include - -#include +#include ///////////////////////////////////////////////// TEST(SplitConsoleSink, foo) { - auto split_sink = std::make_shared(); + auto split_sink = std::make_shared(); spdlog::logger logger("split_sink", {split_sink}); logger.set_level(spdlog::level::trace); @@ -40,8 +39,8 @@ TEST(SplitConsoleSink, foo) ///////////////////////////////////////////////// TEST(SplitRingBufferSink, foo) { - auto split_sink = std::make_shared>(); - auto split_sink_console = std::make_shared(); + auto split_sink = std::make_shared>(); + auto split_sink_console = std::make_shared(); spdlog::logger logger("split_sink", {split_sink, split_sink_console}); logger.set_level(spdlog::level::trace); diff --git a/logger/include/gz/utils/CMakeLists.txt b/logger/include/gz/utils/CMakeLists.txt deleted file mode 100644 index 646d386..0000000 --- a/logger/include/gz/utils/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -gz_install_all_headers(COMPONENT logger) diff --git a/logger/include/gz/utils/logger/LogMessage.hh b/logger/include/gz/utils/logger/LogMessage.hh deleted file mode 100644 index 016ddf5..0000000 --- a/logger/include/gz/utils/logger/LogMessage.hh +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2024 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GZ_UTILS_LOGGER_LOGMESSAGE_HH_ -#define GZ_UTILS_LOGGER_LOGMESSAGE_HH_ - -#include - -#include -#include - -namespace gz::utils::logger -{ - -class LogMessage -{ - /// \brief Constructor. - /// \param[in] _logLevel Log level. - public: explicit LogMessage(spdlog::level::level_enum _logLevel, - const char *_logger = nullptr, - const char *_file = nullptr, - int _line = -1); - - /// \brief Destructor. - public: ~LogMessage(); - - /// \brief Get access to the underlying stream. - /// \return The underlying stream. - public: std::ostream &stream(); - - /// \brief Log level. - private: spdlog::level::level_enum severity; - - private: std::shared_ptr logger; - - /// \brief Source file location information. - private: spdlog::source_loc sourceLocation; - - /// \brief Underlying stream. - private: std::ostringstream ss; -}; -} // namespace gz::utils::logger - -#endif // GZ_UTILS_LOGGER_LOGMESSAGE_HH_ diff --git a/logger/src/Console.cc b/logger/src/Console.cc deleted file mode 100644 index feddb87..0000000 --- a/logger/src/Console.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2024 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace { -/// \brief Custom log sink that routes to stdout/stderr in Gazebo conventions. -class GzSplitSink : public spdlog::sinks::sink -{ - /// \brief Class destructor. - public: ~GzSplitSink() override = default; - - /// \brief Log a message. - /// \param[in] _msg The message to log. - public: void log(const spdlog::details::log_msg &_msg) override - { - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || - _msg.level == spdlog::level::critical) - { - this->stderr.log(_msg); - } - else - this->stdout.log(_msg); - } - - /// \brief Flush messages. - public: void flush() override - { - this->stdout.flush(); - this->stderr.flush(); - } - - /// \brief Set the logging pattern. - /// \param[in] _pattern The logging pattern. - public: void set_pattern(const std::string &_pattern) override - { - this->stdout.set_pattern(_pattern); - this->stderr.set_pattern(_pattern); - } - - /// \brief Set the new formatter. - /// \param[in] _sinkFormatter The formatter. - public: void set_formatter(std::unique_ptr _sinkFormatter) - override - { - this->stdout.set_formatter(_sinkFormatter->clone()); - this->stderr.set_formatter(std::move(_sinkFormatter)); - } - - /// \brief Set the color mode. - /// \param[in] _mode Color mode. - public: void set_color_mode(spdlog::color_mode _mode) - { - this->stdout.set_color_mode(_mode); - this->stderr.set_color_mode(_mode); - } - - /// \brief Standard output. - private: spdlog::sinks::stdout_color_sink_mt stdout; - - /// \brief Standard error. - private: spdlog::sinks::stderr_color_sink_mt stderr; -}; -} // namespace - -namespace gz::utils::logger -{ -/// \brief Private data for the Console class. -class Console::Implementation -{ - /// \brief Constructor. - /// \param[in] _loggerName Logger name. - public: explicit Implementation(const std::string &_loggerName) - : consoleSink(std::make_shared()), - sinks(std::make_shared()), - logger(std::make_shared(_loggerName, sinks)) - { - } - - /// \brief . - std::shared_ptr consoleSink; - - /// \brief . - std::shared_ptr fileSink {nullptr}; - - /// \brief . - std::shared_ptr sinks {nullptr}; - - /// \brief . - std::shared_ptr logger {nullptr}; -}; - -///////////////////////////////////////////////// -Console::Console(const std::string &_loggerName) - : dataPtr(gz::utils::MakeUniqueImpl(_loggerName)) -{ - // Add the console sink as a destination. - this->dataPtr->sinks->add_sink(this->dataPtr->consoleSink); - - // Configure the logger. - this->dataPtr->logger->set_level(spdlog::level::err); - this->dataPtr->logger->flush_on(spdlog::level::err); - - spdlog::flush_every(std::chrono::seconds(5)); - spdlog::register_logger(this->dataPtr->logger); -} - -///////////////////////////////////////////////// -void Console::SetColorMode(spdlog::color_mode _mode) -{ - this->dataPtr->consoleSink->set_color_mode(_mode); -} - -///////////////////////////////////////////////// -void Console::SetLogDestination(const std::string &_filename) -{ - if (this->dataPtr->fileSink) - this->dataPtr->sinks->remove_sink(this->dataPtr->fileSink); - - this->dataPtr->fileSink = - std::make_shared(_filename, true); - this->dataPtr->sinks->add_sink(this->dataPtr->fileSink); -} - -///////////////////////////////////////////////// -std::string Console::LogDestination() const -{ - std::string logPath = ""; - if (this->dataPtr->fileSink) - { - logPath = this->dataPtr->fileSink->filename(); - } - - return logPath; -} - -///////////////////////////////////////////////// -spdlog::logger &Console::Logger() const -{ - return *this->dataPtr->logger; -} - -///////////////////////////////////////////////// -std::shared_ptr Console::LoggerPtr() const -{ - return this->dataPtr->logger; -} - -///////////////////////////////////////////////// -Console &Console::Root() -{ - static gz::utils::NeverDestroyed root{"gz"}; - return root.Access(); -} -} // namespace gz::utils::logger diff --git a/logger/src/LogMessage.cc b/logger/src/LogMessage.cc deleted file mode 100644 index 98d0024..0000000 --- a/logger/src/LogMessage.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2024 Open Source Robotics Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -namespace gz::utils::logger -{ -///////////////////////////////////////////////// -LogMessage::LogMessage(spdlog::level::level_enum _logLevel, - const char *_logger, - const char *_file, - int _line): - severity(_logLevel), - logger(_logger == nullptr ? spdlog::default_logger() : spdlog::get(_logger)), - sourceLocation(_file, _line, "") -{ -} - -///////////////////////////////////////////////// -LogMessage::~LogMessage() -{ - if (sourceLocation.filename != nullptr) - { - logger->log(this->sourceLocation, this->severity, this->ss.str()); - } else { - logger->log(this->severity, this->ss.str()); - } -} -///////////////////////////////////////////////// -std::ostream &LogMessage::stream() -{ - return this->ss; -} - -} // namespace gz::utils::logger From 614460582dca6cda2b989025bea00645681274e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 21:57:58 +0200 Subject: [PATCH 04/18] Adding spdlog dependency to CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- .github/ci/packages.apt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ci/packages.apt b/.github/ci/packages.apt index 805de1f..ebf173c 100644 --- a/.github/ci/packages.apt +++ b/.github/ci/packages.apt @@ -1 +1,2 @@ libgz-cmake4-dev +libspdlog-dev From 55eb7a5ac9429c9686122dc44790466daef56528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 22:10:36 +0200 Subject: [PATCH 05/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/SplitSink.hh | 6 ++++-- log/src/SplitSink_TEST.cc | 16 +++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index 315ee73..9c7be63 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -18,8 +18,10 @@ #ifndef GZ_UTILS_LOG_SPLITSINK_HH_ #define GZ_UTILS_LOG_SPLITSINK_HH_ +#include #include #include +#include #include #include @@ -114,7 +116,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink /// \brief No copy constructor. public: SplitRingBufferSink(const SplitRingBufferSink &) = delete; - /// \brief No asignment operator. + /// \brief No asignment operator. public: SplitRingBufferSink &operator=(const SplitRingBufferSink &) = delete; /// \brief ToDo. @@ -177,7 +179,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink } /// \brief Set the logging pattern. - /// \param[in] _pattern The logging pattern. + /// \param[in] _pattern The logging pattern. protected: void set_pattern_(const std::string &_pattern) override { this->set_formatter_( diff --git a/log/src/SplitSink_TEST.cc b/log/src/SplitSink_TEST.cc index 8371bc1..4b19abe 100644 --- a/log/src/SplitSink_TEST.cc +++ b/log/src/SplitSink_TEST.cc @@ -23,9 +23,9 @@ ///////////////////////////////////////////////// TEST(SplitConsoleSink, foo) { - auto split_sink = std::make_shared(); + auto splitSink = std::make_shared(); - spdlog::logger logger("split_sink", {split_sink}); + spdlog::logger logger("split_sink", {splitSink}); logger.set_level(spdlog::level::trace); logger.trace("trace"); @@ -39,10 +39,12 @@ TEST(SplitConsoleSink, foo) ///////////////////////////////////////////////// TEST(SplitRingBufferSink, foo) { - auto split_sink = std::make_shared>(); - auto split_sink_console = std::make_shared(); + auto splitSink = + std::make_shared>(); + auto splitSinkConsole = + std::make_shared(); - spdlog::logger logger("split_sink", {split_sink, split_sink_console}); + spdlog::logger logger("split_sink", {splitSink, splitSinkConsole}); logger.set_level(spdlog::level::trace); logger.trace("trace"); @@ -53,14 +55,14 @@ TEST(SplitRingBufferSink, foo) logger.critical("critical"); { - auto logs = split_sink->last_raw_stdout(); + auto logs = splitSink->last_raw_stdout(); EXPECT_EQ(logs[0].payload, "trace"); EXPECT_EQ(logs[1].payload, "debug"); EXPECT_EQ(logs[2].payload, "info"); } { - auto logs = split_sink->last_raw_stderr(); + auto logs = splitSink->last_raw_stderr(); EXPECT_EQ(logs[0].payload, "warn"); EXPECT_EQ(logs[1].payload, "error"); EXPECT_EQ(logs[2].payload, "critical"); From a927dad2d2e1cdf237770d20ba127da029804b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 22:20:35 +0200 Subject: [PATCH 06/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/SplitSink.hh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index 9c7be63..d531811 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -154,8 +154,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink } /// \brief ToDo. - /// \param[in] _lim - /// \return + /// \param[in] _msg ToDo. protected: void sink_it_(const spdlog::details::log_msg &_msg) override { if (!this->should_log(_msg.level)) @@ -189,16 +188,15 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink /// \brief Set the new formatter. /// \param[in] _sinkFormatter The formatter. protected: void set_formatter_( - std::unique_ptr sinkFormatter) override + std::unique_ptr _sinkFormatter) override { - spdlog::sinks::base_sink::formatter_ = std::move(sinkFormatter); + spdlog::sinks::base_sink::formatter_ = std::move(_sinkFormatter); this->stdout.set_formatter( spdlog::sinks::base_sink::formatter_->clone()); this->stderr.set_formatter( spdlog::sinks::base_sink::formatter_->clone()); } - /// \brief Standard output. private: spdlog::sinks::ringbuffer_sink_st stdout {numItems}; From 4a893dac4ccde2ee23eaca0797c13ec484e2b161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 22:42:21 +0200 Subject: [PATCH 07/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ceb79a4..74fee08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,11 @@ if(NOT GZ_UTILS_VENDOR_CLI11) gz_find_package(CLI11 REQUIRED_BY cli PKGCONFIG_IGNORE) endif() -gz_find_package(spdlog REQUIRED_BY log) +gz_find_package( + spdlog + PKGCONFIG spdlog + REQUIRED_BY log + PURPOSE "Provide logging") #============================================================================ # Configure the build From 4cf0bcbe593df7295617dcde0ee6cfe249248008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Fri, 16 Aug 2024 22:48:00 +0200 Subject: [PATCH 08/18] Update examples/log/CMakeLists.txt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Addisu Z. Taddese Signed-off-by: Carlos Agüero --- examples/log/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/log/CMakeLists.txt b/examples/log/CMakeLists.txt index fd511fa..4baa2c6 100644 --- a/examples/log/CMakeLists.txt +++ b/examples/log/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-utils-logger-demo) # Find the Gazebo Libraries used directly by the example From be88966ecb3a8721073cbf203e86362ef4209777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Mon, 19 Aug 2024 15:01:00 +0200 Subject: [PATCH 09/18] Namespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/CMakeLists.txt | 2 +- log/include/gz/utils/log/Logger.hh | 71 +++++++++++++++++------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/log/include/CMakeLists.txt b/log/include/CMakeLists.txt index 87ba4cc..6645618 100644 --- a/log/include/CMakeLists.txt +++ b/log/include/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory(gz/utils) \ No newline at end of file +add_subdirectory(gz/utils) diff --git a/log/include/gz/utils/log/Logger.hh b/log/include/gz/utils/log/Logger.hh index 47298cf..54e4958 100644 --- a/log/include/gz/utils/log/Logger.hh +++ b/log/include/gz/utils/log/Logger.hh @@ -20,38 +20,49 @@ #include #include #include +#include +#include #include -namespace gz::utils::log +namespace gz { - /// \brief Gazebo console and file logging class. - /// This will configure spdlog with a sane set of defaults for logging to the - /// console as well as a file. - class GZ_UTILS_VISIBLE Logger - { - /// \brief Class constructor. - /// \param[in] _loggerName Logger name. - public: explicit Logger(const std::string &_loggerName); - - /// \brief Set the log destination filename. - /// \param[in] _filename Log file name. - public: void SetLogDestination(const std::string &_filename); - - /// \brief Get the log destination filename. - /// \return Log file name. - public: std::string LogDestination() const; - - /// \brief Access the underlying spdlog logger. - /// \return The spdlog logger. - public: [[nodiscard]] spdlog::logger &RawLogger() const; - - /// \brief Access the underlying spdlog logger, with ownership. - /// \return The spdlog logger. - public: [[nodiscard]] std::shared_ptr RawLoggerPtr() const; - - /// \brief Implementation Pointer. - GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) - }; -} +namespace utils +{ +namespace log +{ +inline namespace GZ_UTILS_VERSION_NAMESPACE { + +/// \brief Gazebo console and file logging class. +/// This will configure spdlog with a sane set of defaults for logging to the +/// console as well as a file. +class GZ_UTILS_LOG_VISIBLE Logger +{ + /// \brief Class constructor. + /// \param[in] _loggerName Logger name. + public: explicit Logger(const std::string &_loggerName); + + /// \brief Set the log destination filename. + /// \param[in] _filename Log file name. + public: void SetLogDestination(const std::string &_filename); + + /// \brief Get the log destination filename. + /// \return Log file name. + public: std::string LogDestination() const; + + /// \brief Access the underlying spdlog logger. + /// \return The spdlog logger. + public: [[nodiscard]] spdlog::logger &RawLogger() const; + + /// \brief Access the underlying spdlog logger, with ownership. + /// \return The spdlog logger. + public: [[nodiscard]] std::shared_ptr RawLoggerPtr() const; + + /// \brief Implementation Pointer. + GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) +}; +} // namespace GZ_UTILS_VERSION_NAMESPACE +} // namespace log +} // namespace utils +} // namespace gz #endif // GZ_UTILS_LOG_LOGGER_HH_ From 0ac9ddddfbbb69582af2af3e5e7610b178924ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Mon, 19 Aug 2024 15:23:41 +0200 Subject: [PATCH 10/18] Tweaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/Logger.hh | 4 ++-- log/include/gz/utils/log/SplitSink.hh | 17 +++++++++++++++-- log/src/SplitSink_TEST.cc | 1 - 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/log/include/gz/utils/log/Logger.hh b/log/include/gz/utils/log/Logger.hh index 54e4958..3e3a491 100644 --- a/log/include/gz/utils/log/Logger.hh +++ b/log/include/gz/utils/log/Logger.hh @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include namespace gz { @@ -60,7 +60,7 @@ class GZ_UTILS_LOG_VISIBLE Logger /// \brief Implementation Pointer. GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) }; -} // namespace GZ_UTILS_VERSION_NAMESPACE +} // namespace GZ_UTILS_LOG_VERSION_NAMESPACE } // namespace log } // namespace utils } // namespace gz diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index d531811..92b7235 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -24,6 +24,9 @@ #include #include +#include +#include + #include #include #include @@ -31,8 +34,14 @@ #include #include -namespace gz::utils::log +namespace gz +{ +namespace utils +{ +namespace log { +inline namespace GZ_UTILS_LOG_VERSION_NAMESPACE { + /// \brief Logging sink for spdlog that logs in Gazebo-conventions. /// /// This will route messages with severity (warn, err, critical) to stderr, @@ -207,5 +216,9 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink template using SplitRingBufferSinkMt = SplitRingBufferSink; -} // namespace gz::utils::log +} // namespace GZ_UTILS_LOG_VERSION_NAMESPACE +} // namespace log +} // namespace utils +} // namespace gz + #endif // GZ_UTILS_LOG_SPLITSINK_HH__ diff --git a/log/src/SplitSink_TEST.cc b/log/src/SplitSink_TEST.cc index 4b19abe..284fe02 100644 --- a/log/src/SplitSink_TEST.cc +++ b/log/src/SplitSink_TEST.cc @@ -16,7 +16,6 @@ */ #include - #include #include From 49cd5af0594dafde732a2784066fe6ce20354fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Mon, 19 Aug 2024 21:54:04 +0200 Subject: [PATCH 11/18] Constructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/Logger.hh | 2 +- log/include/gz/utils/log/SplitSink.hh | 38 ++++++++++++++++----------- log/src/SplitSink_TEST.cc | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/log/include/gz/utils/log/Logger.hh b/log/include/gz/utils/log/Logger.hh index 3e3a491..01219b1 100644 --- a/log/include/gz/utils/log/Logger.hh +++ b/log/include/gz/utils/log/Logger.hh @@ -30,7 +30,7 @@ namespace utils { namespace log { -inline namespace GZ_UTILS_VERSION_NAMESPACE { +inline namespace GZ_UTILS_LOG_VERSION_NAMESPACE { /// \brief Gazebo console and file logging class. /// This will configure spdlog with a sane set of defaults for logging to the diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index 92b7235..d057cb9 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -116,11 +116,18 @@ using SplitConsoleSinkSt = SplitConsoleSink; /// /// This will route messages with severity (warn, err, critical) to stderr, /// and all other levels (info, debug, trace) to stdout -template +template class SplitRingBufferSink: public spdlog::sinks::base_sink { /// \brief Class constructor. - public: SplitRingBufferSink() = default; + /// \param[in] _numItems + public: explicit SplitRingBufferSink(size_t _numItems) + { + this->stdout = + std::make_shared(_numItems); + this->stderr = + std::make_shared(_numItems); + } /// \brief No copy constructor. public: SplitRingBufferSink(const SplitRingBufferSink &) = delete; @@ -134,7 +141,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink public: std::vector last_raw_stdout( size_t _lim = 0) { - return this->stdout.last_raw(_lim); + return this->stdout->last_raw(_lim); } /// \brief ToDo. @@ -143,7 +150,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink public: std::vector last_raw_stderr( size_t _lim = 0) { - return this->stderr.last_raw(_lim); + return this->stderr->last_raw(_lim); } /// \brief ToDo. @@ -151,7 +158,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink /// \return public: std::vector last_formatted_stdout(size_t _lim = 0) { - return this->stdout.last_formatted(_lim); + return this->stdout->last_formatted(_lim); } /// \brief ToDo. @@ -159,7 +166,7 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink /// \return public: std::vector last_formatted_stderr(size_t _lim = 0) { - return this->stderr.last_formatted(_lim); + return this->stderr->last_formatted(_lim); } /// \brief ToDo. @@ -173,17 +180,17 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink _msg.level == spdlog::level::err || _msg.level == spdlog::level::critical) { - this->stderr.log(_msg); + this->stderr->log(_msg); } else - this->stdout.log(_msg); + this->stdout->log(_msg); } /// \brief Flush messages. protected: void flush_() override { - this->stdout.flush(); - this->stderr.flush(); + this->stdout->flush(); + this->stderr->flush(); } /// \brief Set the logging pattern. @@ -200,21 +207,20 @@ class SplitRingBufferSink: public spdlog::sinks::base_sink std::unique_ptr _sinkFormatter) override { spdlog::sinks::base_sink::formatter_ = std::move(_sinkFormatter); - this->stdout.set_formatter( + this->stdout->set_formatter( spdlog::sinks::base_sink::formatter_->clone()); - this->stderr.set_formatter( + this->stderr->set_formatter( spdlog::sinks::base_sink::formatter_->clone()); } /// \brief Standard output. - private: spdlog::sinks::ringbuffer_sink_st stdout {numItems}; + private: std::shared_ptr stdout; /// \brief Standard error. - private: spdlog::sinks::ringbuffer_sink_st stderr {numItems}; + private: std::shared_ptr stderr; }; -template -using SplitRingBufferSinkMt = SplitRingBufferSink; +using SplitRingBufferSinkMt = SplitRingBufferSink; } // namespace GZ_UTILS_LOG_VERSION_NAMESPACE } // namespace log diff --git a/log/src/SplitSink_TEST.cc b/log/src/SplitSink_TEST.cc index 284fe02..8a6e553 100644 --- a/log/src/SplitSink_TEST.cc +++ b/log/src/SplitSink_TEST.cc @@ -39,7 +39,7 @@ TEST(SplitConsoleSink, foo) TEST(SplitRingBufferSink, foo) { auto splitSink = - std::make_shared>(); + std::make_shared(100); auto splitSinkConsole = std::make_shared(); From d96ec02402f5c948fddf23a6944dc2616ac3c35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 19:37:10 +0200 Subject: [PATCH 12/18] SplitSink tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/SplitSink.hh | 183 +++----------------------- log/src/Logger.cc | 4 +- log/src/SplitSink_TEST.cc | 35 +---- 3 files changed, 20 insertions(+), 202 deletions(-) diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index d057cb9..1faeba3 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -19,19 +19,15 @@ #define GZ_UTILS_LOG_SPLITSINK_HH_ #include -#include #include -#include -#include #include +#include #include #include -#include #include #include -#include #include namespace gz @@ -46,182 +42,37 @@ inline namespace GZ_UTILS_LOG_VERSION_NAMESPACE { /// /// This will route messages with severity (warn, err, critical) to stderr, /// and all other levels (info, debug, trace) to stdout. -template -class SplitConsoleSink : public spdlog::sinks::base_sink +class GZ_UTILS_LOG_VISIBLE SplitConsoleSink : public spdlog::sinks::sink { /// \brief Class constructor. - public: SplitConsoleSink() = default; + public: SplitConsoleSink(); - /// \brief No copy constructor. - public: SplitConsoleSink(const SplitConsoleSink &) = delete; + /// \brief Class destructor. + public: ~SplitConsoleSink() override = default; - /// \brief No asignment operator. - public: SplitConsoleSink &operator=(const SplitConsoleSink &) = delete; - - /// \brief Try to log a message. + /// \brief Log a message. /// \param[in] _msg The message to log. - protected: void sink_it_(const spdlog::details::log_msg &_msg) override - { - if (!this->should_log(_msg.level)) - return; - - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || - _msg.level == spdlog::level::critical) - { - this->stderr.log(_msg); - } - else - this->stdout.log(_msg); - } - - /// \brief Flush messages. - protected: void flush_() override - { - this->stdout.flush(); - this->stderr.flush(); - } - - /// \brief Set the logging pattern. - /// \param[in] _pattern The logging pattern. - protected: void set_pattern_(const std::string &_pattern) override - { - this->set_formatter( - spdlog::details::make_unique(_pattern)); - } - - /// \brief Set the new formatter. - /// \param[in] _sinkFormatter The formatter. - protected: void set_formatter_( - std::unique_ptr _sinkFormatter) override - { - spdlog::sinks::base_sink::formatter_ = std::move(_sinkFormatter); - this->stdout.set_formatter( - spdlog::sinks::base_sink::formatter_->clone()); - this->stderr.set_formatter( - spdlog::sinks::base_sink::formatter_->clone()); - } - - /// \brief Standard output. - private: spdlog::sinks::stdout_color_sink_st stdout; - - /// \brief Standard error. - private: spdlog::sinks::stderr_color_sink_st stderr; -}; - -using SplitConsoleSinkMt = SplitConsoleSink; -using SplitConsoleSinkSt = SplitConsoleSink; - -/// \brief Logging sink for spdlog that logs in Gazebo-conventions -/// -/// This will route messages with severity (warn, err, critical) to stderr, -/// and all other levels (info, debug, trace) to stdout -template -class SplitRingBufferSink: public spdlog::sinks::base_sink -{ - /// \brief Class constructor. - /// \param[in] _numItems - public: explicit SplitRingBufferSink(size_t _numItems) - { - this->stdout = - std::make_shared(_numItems); - this->stderr = - std::make_shared(_numItems); - } - - /// \brief No copy constructor. - public: SplitRingBufferSink(const SplitRingBufferSink &) = delete; - - /// \brief No asignment operator. - public: SplitRingBufferSink &operator=(const SplitRingBufferSink &) = delete; - - /// \brief ToDo. - /// \param[in] _lim - /// \return - public: std::vector last_raw_stdout( - size_t _lim = 0) - { - return this->stdout->last_raw(_lim); - } - - /// \brief ToDo. - /// \param[in] _lim - /// \return - public: std::vector last_raw_stderr( - size_t _lim = 0) - { - return this->stderr->last_raw(_lim); - } - - /// \brief ToDo. - /// \param[in] _lim - /// \return - public: std::vector last_formatted_stdout(size_t _lim = 0) - { - return this->stdout->last_formatted(_lim); - } - - /// \brief ToDo. - /// \param[in] _lim - /// \return - public: std::vector last_formatted_stderr(size_t _lim = 0) - { - return this->stderr->last_formatted(_lim); - } - - /// \brief ToDo. - /// \param[in] _msg ToDo. - protected: void sink_it_(const spdlog::details::log_msg &_msg) override - { - if (!this->should_log(_msg.level)) - return; - - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || - _msg.level == spdlog::level::critical) - { - this->stderr->log(_msg); - } - else - this->stdout->log(_msg); - } + public: void log(const spdlog::details::log_msg &_msg) override; /// \brief Flush messages. - protected: void flush_() override - { - this->stdout->flush(); - this->stderr->flush(); - } + public: void flush() override; /// \brief Set the logging pattern. - /// \param[in] _pattern The logging pattern. - protected: void set_pattern_(const std::string &_pattern) override - { - this->set_formatter_( - spdlog::details::make_unique(_pattern)); - } + /// \param[in] _pattern The logging pattern. + public: void set_pattern(const std::string &_pattern) override; /// \brief Set the new formatter. /// \param[in] _sinkFormatter The formatter. - protected: void set_formatter_( - std::unique_ptr _sinkFormatter) override - { - spdlog::sinks::base_sink::formatter_ = std::move(_sinkFormatter); - this->stdout->set_formatter( - spdlog::sinks::base_sink::formatter_->clone()); - this->stderr->set_formatter( - spdlog::sinks::base_sink::formatter_->clone()); - } + public: void set_formatter(std::unique_ptr _sinkFormatter) + override; - /// \brief Standard output. - private: std::shared_ptr stdout; + /// \brief Set the color mode. + /// \param[in] _mode Color mode. + public: void set_color_mode(spdlog::color_mode _mode); - /// \brief Standard error. - private: std::shared_ptr stderr; + /// \brief Implementation Pointer. + GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr) }; - -using SplitRingBufferSinkMt = SplitRingBufferSink; - } // namespace GZ_UTILS_LOG_VERSION_NAMESPACE } // namespace log } // namespace utils diff --git a/log/src/Logger.cc b/log/src/Logger.cc index 8362d1f..340693a 100644 --- a/log/src/Logger.cc +++ b/log/src/Logger.cc @@ -31,14 +31,14 @@ class Logger::Implementation /// \brief Constructor. /// \param[in] _loggerName Logger name. public: explicit Implementation(const std::string &_loggerName) - : consoleSink(std::make_shared()), + : consoleSink(std::make_shared()), sinks(std::make_shared()), logger(std::make_shared(_loggerName, sinks)) { } /// \brief The console sink with stdout and stderr. - std::shared_ptr consoleSink; + std::shared_ptr consoleSink; /// \brief The file sink for logging into a file. std::shared_ptr fileSink {nullptr}; diff --git a/log/src/SplitSink_TEST.cc b/log/src/SplitSink_TEST.cc index 8a6e553..f591302 100644 --- a/log/src/SplitSink_TEST.cc +++ b/log/src/SplitSink_TEST.cc @@ -22,7 +22,7 @@ ///////////////////////////////////////////////// TEST(SplitConsoleSink, foo) { - auto splitSink = std::make_shared(); + auto splitSink = std::make_shared(); spdlog::logger logger("split_sink", {splitSink}); logger.set_level(spdlog::level::trace); @@ -34,36 +34,3 @@ TEST(SplitConsoleSink, foo) logger.error("error"); logger.critical("critical"); } - -///////////////////////////////////////////////// -TEST(SplitRingBufferSink, foo) -{ - auto splitSink = - std::make_shared(100); - auto splitSinkConsole = - std::make_shared(); - - spdlog::logger logger("split_sink", {splitSink, splitSinkConsole}); - logger.set_level(spdlog::level::trace); - - logger.trace("trace"); - logger.debug("debug"); - logger.info("info"); - logger.warn("warn"); - logger.error("error"); - logger.critical("critical"); - - { - auto logs = splitSink->last_raw_stdout(); - EXPECT_EQ(logs[0].payload, "trace"); - EXPECT_EQ(logs[1].payload, "debug"); - EXPECT_EQ(logs[2].payload, "info"); - } - - { - auto logs = splitSink->last_raw_stderr(); - EXPECT_EQ(logs[0].payload, "warn"); - EXPECT_EQ(logs[1].payload, "error"); - EXPECT_EQ(logs[2].payload, "critical"); - } -} From d1402ec1c99b0e1f523c28f7fb4d6dd2484c2bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 19:40:42 +0200 Subject: [PATCH 13/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/SplitSink.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index 1faeba3..cf0ad02 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -58,7 +58,7 @@ class GZ_UTILS_LOG_VISIBLE SplitConsoleSink : public spdlog::sinks::sink public: void flush() override; /// \brief Set the logging pattern. - /// \param[in] _pattern The logging pattern. + /// \param[in] _pattern The logging pattern. public: void set_pattern(const std::string &_pattern) override; /// \brief Set the new formatter. From 85c149e05324fdb29f50cc802e341800b3d90838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 19:58:10 +0200 Subject: [PATCH 14/18] Adding file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/src/SplitSink.cc | 91 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 log/src/SplitSink.cc diff --git a/log/src/SplitSink.cc b/log/src/SplitSink.cc new file mode 100644 index 0000000..4e8e24c --- /dev/null +++ b/log/src/SplitSink.cc @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include + +#include +#include +#include + +namespace gz::utils::log +{ +/// \brief Private data for the SplitConsoleSink class. +class SplitConsoleSink::Implementation +{ + /// \brief Constructor. + /// \param[in] _loggerName Logger name. + public: Implementation() + { + } + + /// \brief Standard output. + public: spdlog::sinks::stdout_color_sink_mt stdout; + + /// \brief Standard error. + public: spdlog::sinks::stderr_color_sink_mt stderr; +}; + +///////////////////////////////////////////////// +SplitConsoleSink::SplitConsoleSink() + : dataPtr(gz::utils::MakeUniqueImpl()) +{ +} + +///////////////////////////////////////////////// +void SplitConsoleSink::log(const spdlog::details::log_msg &_msg) +{ + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || + _msg.level == spdlog::level::critical) + { + this->dataPtr->stderr.log(_msg); + } + else + this->dataPtr->stdout.log(_msg); +} + +///////////////////////////////////////////////// +void SplitConsoleSink::flush() +{ + this->dataPtr->stdout.flush(); + this->dataPtr->stderr.flush(); +} + +///////////////////////////////////////////////// +void SplitConsoleSink::set_pattern(const std::string &_pattern) +{ + this->dataPtr->stdout.set_pattern(_pattern); + this->dataPtr->stderr.set_pattern(_pattern); +} + +///////////////////////////////////////////////// +void SplitConsoleSink::set_formatter( + std::unique_ptr _sinkFormatter) +{ + this->dataPtr->stdout.set_formatter(_sinkFormatter->clone()); + this->dataPtr->stderr.set_formatter(std::move(_sinkFormatter)); +} + +///////////////////////////////////////////////// +void SplitConsoleSink::set_color_mode(spdlog::color_mode _mode) +{ + this->dataPtr->stdout.set_color_mode(_mode); + this->dataPtr->stderr.set_color_mode(_mode); +} + + +} // namespace gz::utils::log From b1ea607b434dc798866f067578bf0a71d7b1891f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 19:58:49 +0200 Subject: [PATCH 15/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/src/SplitSink.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/log/src/SplitSink.cc b/log/src/SplitSink.cc index 4e8e24c..e0f378e 100644 --- a/log/src/SplitSink.cc +++ b/log/src/SplitSink.cc @@ -87,5 +87,4 @@ void SplitConsoleSink::set_color_mode(spdlog::color_mode _mode) this->dataPtr->stderr.set_color_mode(_mode); } - } // namespace gz::utils::log From 66451d11133d66f4e3fcf884518b7b6e0ddccb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 21:21:15 +0200 Subject: [PATCH 16/18] Win MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/src/SplitSink.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/log/src/SplitSink.cc b/log/src/SplitSink.cc index e0f378e..c70c7bb 100644 --- a/log/src/SplitSink.cc +++ b/log/src/SplitSink.cc @@ -33,10 +33,10 @@ class SplitConsoleSink::Implementation } /// \brief Standard output. - public: spdlog::sinks::stdout_color_sink_mt stdout; + public: spdlog::sinks::stdout_color_sink_mt stdout_; /// \brief Standard error. - public: spdlog::sinks::stderr_color_sink_mt stderr; + public: spdlog::sinks::stderr_color_sink_mt stderr_; }; ///////////////////////////////////////////////// @@ -48,43 +48,43 @@ SplitConsoleSink::SplitConsoleSink() ///////////////////////////////////////////////// void SplitConsoleSink::log(const spdlog::details::log_msg &_msg) { - if (_msg.level == spdlog::level::warn || - _msg.level == spdlog::level::err || - _msg.level == spdlog::level::critical) + if (_msg.level == spdlog::level::warn || + _msg.level == spdlog::level::err || + _msg.level == spdlog::level::critical) { - this->dataPtr->stderr.log(_msg); + this->dataPtr->stderr_.log(_msg); } else - this->dataPtr->stdout.log(_msg); + this->dataPtr->stdout_.log(_msg); } ///////////////////////////////////////////////// void SplitConsoleSink::flush() { - this->dataPtr->stdout.flush(); - this->dataPtr->stderr.flush(); + this->dataPtr->stdout_.flush(); + this->dataPtr->stderr_.flush(); } ///////////////////////////////////////////////// void SplitConsoleSink::set_pattern(const std::string &_pattern) { - this->dataPtr->stdout.set_pattern(_pattern); - this->dataPtr->stderr.set_pattern(_pattern); + this->dataPtr->stdout_.set_pattern(_pattern); + this->dataPtr->stderr_.set_pattern(_pattern); } ///////////////////////////////////////////////// void SplitConsoleSink::set_formatter( std::unique_ptr _sinkFormatter) { - this->dataPtr->stdout.set_formatter(_sinkFormatter->clone()); - this->dataPtr->stderr.set_formatter(std::move(_sinkFormatter)); + this->dataPtr->stdout_.set_formatter(_sinkFormatter->clone()); + this->dataPtr->stderr_.set_formatter(std::move(_sinkFormatter)); } ///////////////////////////////////////////////// void SplitConsoleSink::set_color_mode(spdlog::color_mode _mode) { - this->dataPtr->stdout.set_color_mode(_mode); - this->dataPtr->stderr.set_color_mode(_mode); + this->dataPtr->stdout_.set_color_mode(_mode); + this->dataPtr->stderr_.set_color_mode(_mode); } } // namespace gz::utils::log From 7638bbb58210f06748749cd69e957546a50281ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 21:30:42 +0200 Subject: [PATCH 17/18] package.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- package.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.xml b/package.xml index 5764bbf..52ae53d 100644 --- a/package.xml +++ b/package.xml @@ -13,6 +13,8 @@ gz-cmake4 + spdlog + cmake From f33b37925f1611270b01e4f779ad514122cfeaef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ag=C3=BCero?= Date: Tue, 20 Aug 2024 22:10:56 +0200 Subject: [PATCH 18/18] Tweak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Agüero --- log/include/gz/utils/log/Logger.hh | 2 +- log/include/gz/utils/log/SplitSink.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/log/include/gz/utils/log/Logger.hh b/log/include/gz/utils/log/Logger.hh index 01219b1..3e3a491 100644 --- a/log/include/gz/utils/log/Logger.hh +++ b/log/include/gz/utils/log/Logger.hh @@ -30,7 +30,7 @@ namespace utils { namespace log { -inline namespace GZ_UTILS_LOG_VERSION_NAMESPACE { +inline namespace GZ_UTILS_VERSION_NAMESPACE { /// \brief Gazebo console and file logging class. /// This will configure spdlog with a sane set of defaults for logging to the diff --git a/log/include/gz/utils/log/SplitSink.hh b/log/include/gz/utils/log/SplitSink.hh index cf0ad02..fc0af5e 100644 --- a/log/include/gz/utils/log/SplitSink.hh +++ b/log/include/gz/utils/log/SplitSink.hh @@ -36,7 +36,7 @@ namespace utils { namespace log { -inline namespace GZ_UTILS_LOG_VERSION_NAMESPACE { +inline namespace GZ_UTILS_VERSION_NAMESPACE { /// \brief Logging sink for spdlog that logs in Gazebo-conventions. ///