diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d021c2c..9639b0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,15 +13,6 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Restore Conan Cache - id: conan-cache-restore - uses: actions/cache/restore@v4 - with: - path: | - /home/runner/.conan2 - /home/runner/work/spectator-cpp/spectator-cpp/cmake-build - key: ${{ runner.os }}-conan - - name: Install System Dependencies run: | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test @@ -32,12 +23,3 @@ jobs: ./setup-venv.sh source venv/bin/activate ./build.sh - - - name: Save Conan Cache - id: conan-cache-save - uses: actions/cache/save@v4 - with: - path: | - /home/runner/.conan2 - /home/runner/work/spectator-cpp/spectator-cpp/cmake-build - key: ${{ steps.conan-cache-restore.outputs.cache-primary-key }} \ No newline at end of file diff --git a/Dockerfiles/Ubuntu.Dockerfile b/Dockerfiles/Ubuntu.Dockerfile index 97662a8..cfb440f 100644 --- a/Dockerfiles/Ubuntu.Dockerfile +++ b/Dockerfiles/Ubuntu.Dockerfile @@ -12,6 +12,10 @@ RUN apt-get update && apt-get install -y \ cmake \ build-essential +# Set up alternatives to make gcc-13 and g++-13 the default +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 && \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100 + # Create a default working directory WORKDIR /home/ubuntu/spectator-cpp diff --git a/build.sh b/build.sh index d8564f5..bab4527 100755 --- a/build.sh +++ b/build.sh @@ -42,6 +42,8 @@ echo "CXX=$CXX" if [[ ! -f "$HOME/.conan2/profiles/default" ]]; then echo -e "${BLUE}==== create default profile ====${NC}" conan profile detect + # Set C++ standard to 20 so spdlog uses std_format + sed -i 's/compiler\.cppstd=.*/compiler.cppstd=20/' "$HOME/.conan2/profiles/default" fi if [[ ! -d $BUILD_DIR ]]; then diff --git a/conanfile.py b/conanfile.py index a6604bd..3931fcd 100644 --- a/conanfile.py +++ b/conanfile.py @@ -9,3 +9,9 @@ class SpectatorCppConan(ConanFile): ) tool_requires = () generators = "CMakeDeps", "CMakeToolchain" + + def configure(self): + # Configure spdlog to be header-only + self.options["spdlog"].header_only = True + self.options["spdlog"].use_std_fmt = True + diff --git a/libs/logger/CMakeLists.txt b/libs/logger/CMakeLists.txt index 4d73d98..3b9a52f 100644 --- a/libs/logger/CMakeLists.txt +++ b/libs/logger/CMakeLists.txt @@ -1,12 +1,13 @@ -add_library(spectator-logger INTERFACE) +add_library(spectator-logger logger.cpp) target_include_directories(spectator-logger - INTERFACE + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(spectator-logger - INTERFACE + PUBLIC spectator-utils - spdlog::spdlog + PRIVATE + spdlog::spdlog_header_only ) \ No newline at end of file diff --git a/libs/logger/logger.cpp b/libs/logger/logger.cpp new file mode 100644 index 0000000..c00890d --- /dev/null +++ b/libs/logger/logger.cpp @@ -0,0 +1,68 @@ +#include "logger.h" + +#include +#include +#include +#include +#include + +constexpr const char* kMainLogger = "spectator"; + +class Logger::LoggerImpl +{ + public: + std::shared_ptr m_logger; + + LoggerImpl() + { + try + { + m_logger = spdlog::create_async_nb(kMainLogger); + if (m_logger == nullptr) + { + throw std::runtime_error("Failed to create logger: spdlog returned null"); + } + } + catch (const spdlog::spdlog_ex& ex) + { + throw std::runtime_error("Log initialization failed: " + std::string(ex.what())); + } + } + + ~LoggerImpl() = default; + + spdlog::logger* GetLogger() const + { + return m_logger.get(); + } +}; + +Logger::Logger() : m_impl(std::make_unique()) +{ +} + +Logger::~Logger() = default; + +void Logger::debug(const std::string& msg) +{ + auto* logger = GetInstance().m_impl->GetLogger(); + logger->debug(msg); +} + +void Logger::info(const std::string& msg) +{ + auto* logger = GetInstance().m_impl->GetLogger(); + logger->info(msg); +} + +void Logger::warn(const std::string& msg) +{ + auto* logger = GetInstance().m_impl->GetLogger(); + logger->warn(msg); +} + +void Logger::error(const std::string& msg) +{ + auto* logger = GetInstance().m_impl->GetLogger(); + logger->error(msg); +} \ No newline at end of file diff --git a/libs/logger/logger.h b/libs/logger/logger.h index 503193d..1168cc8 100644 --- a/libs/logger/logger.h +++ b/libs/logger/logger.h @@ -2,87 +2,53 @@ #include -#include #include #include - -#include -#include -#include -#include - -constexpr const char* kMainLogger = "spectator"; +#include class Logger final : public Singleton { private: - std::shared_ptr m_logger; + // Forward declaration for pimpl + class LoggerImpl; + std::unique_ptr m_impl; friend class Singleton; - Logger() - { - try - { - m_logger = spdlog::create_async_nb(kMainLogger); - } - catch (const spdlog::spdlog_ex& ex) - { - std::cerr << "Log initialization failed: " << ex.what() << "\n"; - m_logger = nullptr; - } - } - - ~Logger() = default; + Logger(); + ~Logger(); Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; Logger(Logger&&) = delete; Logger& operator=(Logger&&) = delete; public: - static spdlog::logger* GetLogger() { return GetInstance().m_logger.get(); } - - static void debug(const std::string& msg) - { - GetLogger()->debug(msg); - } - - static void info(const std::string& msg) - { - GetLogger()->info(msg); - } - - static void warn(const std::string& msg) - { - GetLogger()->warn(msg); - } - - static void error(const std::string& msg) - { - GetLogger()->error(msg); - } + static void debug(const std::string& msg); + static void info(const std::string& msg); + static void warn(const std::string& msg); + static void error(const std::string& msg); template - static void debug(fmt::format_string fmt, Args&&... args) + static void debug(std::format_string fmt, Args&&... args) { - GetLogger()->debug(fmt, std::forward(args)...); + debug(std::format(fmt, std::forward(args)...)); } template - static void info(fmt::format_string fmt, Args&&... args) + static void info(std::format_string fmt, Args&&... args) { - GetLogger()->info(fmt, std::forward(args)...); + info(std::format(fmt, std::forward(args)...)); } template - static void warn(fmt::format_string fmt, Args&&... args) + static void warn(std::format_string fmt, Args&&... args) { - GetLogger()->warn(fmt, std::forward(args)...); + warn(std::format(fmt, std::forward(args)...)); } template - static void error(fmt::format_string fmt, Args&&... args) + static void error(std::format_string fmt, Args&&... args) { - GetLogger()->error(fmt, std::forward(args)...); + error(std::format(fmt, std::forward(args)...)); } -}; +}; \ No newline at end of file diff --git a/libs/writer/writer_types/include/udp_writer.h b/libs/writer/writer_types/include/udp_writer.h index ce64bbd..7a621c8 100644 --- a/libs/writer/writer_types/include/udp_writer.h +++ b/libs/writer/writer_types/include/udp_writer.h @@ -4,7 +4,6 @@ #include #include -#include class UDPWriter final : public BaseWriter { @@ -15,13 +14,6 @@ class UDPWriter final : public BaseWriter void Close() override; private: - std::string m_host; - int m_port; - std::unique_ptr m_io_context; - std::unique_ptr m_socket; - boost::asio::ip::udp::endpoint m_endpoint; - bool m_socketEstablished; - - bool CreateSocket(); - bool TryToSend(const std::string& message); + class Impl; + std::unique_ptr m_pImpl; }; diff --git a/libs/writer/writer_types/include/uds_writer.h b/libs/writer/writer_types/include/uds_writer.h index e2ea37b..f8666f9 100644 --- a/libs/writer/writer_types/include/uds_writer.h +++ b/libs/writer/writer_types/include/uds_writer.h @@ -3,7 +3,6 @@ #include #include -#include #include class UDSWriter final : public BaseWriter @@ -15,12 +14,6 @@ class UDSWriter final : public BaseWriter void Close() override; private: - std::string m_socketPath; - std::unique_ptr m_ioContext; - std::unique_ptr m_socket; - boost::asio::local::datagram_protocol::endpoint m_endpoint; - bool m_socketEstablished; - - bool CreateSocket(); - bool TryToSend(const std::string& message); + class Impl; + std::unique_ptr m_pImpl; }; diff --git a/libs/writer/writer_types/src/udp_writer.cpp b/libs/writer/writer_types/src/udp_writer.cpp index b3ad9ee..caad76f 100644 --- a/libs/writer/writer_types/src/udp_writer.cpp +++ b/libs/writer/writer_types/src/udp_writer.cpp @@ -1,23 +1,47 @@ #include #include +#include -UDPWriter::UDPWriter(const std::string& host, int port) : +class UDPWriter::Impl +{ +public: + Impl(const std::string& host, int port); + ~Impl() = default; + + bool CreateSocket(); + bool TryToSend(const std::string& message); + void Close(); + + std::string m_host; + int m_port; + std::unique_ptr m_io_context; + std::unique_ptr m_socket; + boost::asio::ip::udp::endpoint m_endpoint; + bool m_socketEstablished; +}; + +UDPWriter::Impl::Impl(const std::string& host, int port) : m_host(host), m_port(port), m_io_context(std::make_unique()), m_socket(nullptr), m_socketEstablished(false) { - if (false == CreateSocket()) +} + +UDPWriter::UDPWriter(const std::string& host, int port) : + m_pImpl(std::make_unique(host, port)) +{ + if (false == m_pImpl->CreateSocket()) { - Logger::error("UDPWriter: Failed to create socket for {}:{} during construction", m_host, m_port); + Logger::error("UDPWriter: Failed to create socket for {}:{} during construction", m_pImpl->m_host, m_pImpl->m_port); } } UDPWriter::~UDPWriter() { Close(); } -bool UDPWriter::CreateSocket() try +bool UDPWriter::Impl::CreateSocket() try { if (m_socketEstablished) { @@ -44,7 +68,7 @@ catch (const boost::system::system_error& ex) return false; } -bool UDPWriter::TryToSend(const std::string& message) try +bool UDPWriter::Impl::TryToSend(const std::string& message) try { boost::system::error_code ec; for (int i = 0; i < 3; i++) @@ -67,20 +91,20 @@ catch (const boost::system::system_error& ex) void UDPWriter::Write(const std::string& message) { - if (false == this->m_socketEstablished && false == this->CreateSocket()) + if (false == m_pImpl->m_socketEstablished && false == m_pImpl->CreateSocket()) { - Logger::error("UDPWriter: Failed to write message, socket not established {}:{}", m_host, m_port); + Logger::error("UDPWriter: Failed to write message, socket not established {}:{}", m_pImpl->m_host, m_pImpl->m_port); return; } - if (TryToSend(message) == false) + if (m_pImpl->TryToSend(message) == false) { Logger::error("UDP Writer: Failed to send message: {}", message); - this->Close(); + m_pImpl->Close(); } } -void UDPWriter::Close() try +void UDPWriter::Impl::Close() try { this->m_socketEstablished = false; if(m_socket && m_socket->is_open()) @@ -96,4 +120,12 @@ void UDPWriter::Close() try catch (const boost::system::system_error& ex) { Logger::error("UDP Writer: Boost exception: {}", ex.what()); +} + +void UDPWriter::Close() +{ + if (m_pImpl) + { + m_pImpl->Close(); + } } \ No newline at end of file diff --git a/libs/writer/writer_types/src/uds_writer.cpp b/libs/writer/writer_types/src/uds_writer.cpp index 8126f06..512ccf7 100644 --- a/libs/writer/writer_types/src/uds_writer.cpp +++ b/libs/writer/writer_types/src/uds_writer.cpp @@ -1,22 +1,45 @@ #include #include +#include -UDSWriter::UDSWriter(const std::string& socketPath) +class UDSWriter::Impl +{ +public: + Impl(const std::string& socketPath); + ~Impl() = default; + + bool CreateSocket(); + bool TryToSend(const std::string& message); + void Close(); + + std::string m_socketPath; + std::unique_ptr m_ioContext; + std::unique_ptr m_socket; + boost::asio::local::datagram_protocol::endpoint m_endpoint; + bool m_socketEstablished; +}; + +UDSWriter::Impl::Impl(const std::string& socketPath) : m_socketPath(socketPath), m_ioContext(std::make_unique()), m_socket(nullptr), m_socketEstablished(false) { - if (false == CreateSocket()) +} + +UDSWriter::UDSWriter(const std::string& socketPath) + : m_pImpl(std::make_unique(socketPath)) +{ + if (false == m_pImpl->CreateSocket()) { - Logger::error("UDS Writer: Failed to create socket for {} during construction", m_socketPath); + Logger::error("UDS Writer: Failed to create socket for {} during construction", m_pImpl->m_socketPath); } } UDSWriter::~UDSWriter() { Close(); } -bool UDSWriter::CreateSocket() try +bool UDSWriter::Impl::CreateSocket() try { if (m_socketEstablished) { @@ -43,7 +66,7 @@ catch (const boost::system::system_error& ex) return false; } -bool UDSWriter::TryToSend(const std::string& message) try +bool UDSWriter::Impl::TryToSend(const std::string& message) try { boost::system::error_code ec; for (int i = 0; i < 3; i++) @@ -66,20 +89,20 @@ catch (const boost::system::system_error& ex) void UDSWriter::Write(const std::string& message) { - if (false == this->m_socketEstablished && false == this->CreateSocket()) + if (false == m_pImpl->m_socketEstablished && false == m_pImpl->CreateSocket()) { - Logger::error("UDS Writer: Failed to write message, socket not established {}", m_socketPath); + Logger::error("UDS Writer: Failed to write message, socket not established {}", m_pImpl->m_socketPath); return; } - if (false == this->TryToSend(message)) + if (false == m_pImpl->TryToSend(message)) { Logger::error("UDS Writer: Failed to send message: {}", message); - this->Close(); + m_pImpl->Close(); } } -void UDSWriter::Close() try +void UDSWriter::Impl::Close() try { this->m_socketEstablished = false; if (m_socket != nullptr && m_socket->is_open()) @@ -96,3 +119,11 @@ catch (const boost::system::system_error& ex) { Logger::error("UDS Writer: Boost exception: {}", ex.what()); } + +void UDSWriter::Close() +{ + if (m_pImpl) + { + m_pImpl->Close(); + } +} diff --git a/libs/writer/writer_wrapper/writer.h b/libs/writer/writer_wrapper/writer.h index 6502d32..0bc2f77 100644 --- a/libs/writer/writer_wrapper/writer.h +++ b/libs/writer/writer_wrapper/writer.h @@ -5,6 +5,10 @@ #include #include +#include +#include +#include +#include class Writer final : public Singleton { diff --git a/spectator/CMakeLists.txt b/spectator/CMakeLists.txt index af8cfde..5317366 100644 --- a/spectator/CMakeLists.txt +++ b/spectator/CMakeLists.txt @@ -1,8 +1,9 @@ # Create a monolithic registry library with all required sources -add_library(spectator-registry +add_library(spectator-registry STATIC registry.cpp # Include all required source files directly ${CMAKE_CURRENT_SOURCE_DIR}/../libs/config/config.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../libs/logger/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libs/meter/meter_id/meter_id.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libs/utils/src/util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libs/writer/writer_config/writer_config.cpp @@ -16,6 +17,7 @@ target_include_directories(spectator-registry PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../libs/config + ${CMAKE_CURRENT_SOURCE_DIR}/../libs/logger/logger ${CMAKE_CURRENT_SOURCE_DIR}/../libs/meter/meter_id ${CMAKE_CURRENT_SOURCE_DIR}/../libs/meter/meter_types/include ${CMAKE_CURRENT_SOURCE_DIR}/../libs/utils/include @@ -27,11 +29,11 @@ target_include_directories(spectator-registry # Link only external dependencies, not internal spectator libraries target_link_libraries(spectator-registry - PUBLIC - spdlog::spdlog + PRIVATE + spdlog::spdlog_header_only Boost::boost - Boost::system ) + add_executable(registry-test test_registry.cpp) target_link_libraries(registry-test PRIVATE GTest::gtest @@ -39,4 +41,4 @@ target_link_libraries(registry-test PRIVATE spectator-registry spectator-utils ) -add_test(NAME registry-test COMMAND registry-test) \ No newline at end of file +add_test(NAME registry-test COMMAND registry-test)