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