From c9452597f604ae3c1fd926e3aee556d096893a4e Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Tue, 23 Dec 2025 17:04:47 +0100 Subject: [PATCH 1/9] CMake: Improvements for installation. --- CMakeLists.txt | 43 ++++++++++++++++++---- cmake/LoggerConfig.cmake.in | 3 ++ docs/installation.md | 55 ++++++++++++++++++++++++++++ examples/globalConfig/CMakeLists.txt | 2 +- examples/standard/CMakeLists.txt | 2 +- src/CMakeLists.txt | 26 +++++++++++-- tests/CMakeLists.txt | 7 +++- 7 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 cmake/LoggerConfig.cmake.in create mode 100644 docs/installation.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 9576e76..39a3168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,48 @@ cmake_minimum_required(VERSION 3.5) -project(LoggingProject) +project(Logger VERSION 1.0.0 LANGUAGES CXX) + set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(LOGGER_IS_TOP_LEVEL OFF) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(LOGGER_IS_TOP_LEVEL ON) +endif() -option(BUILD_TESTS "Create the unit tests for the project." True) -option(BUILD_EXAMPLES "Create examples for the project." True) +option(LOGGER_BUILD_TESTS "Build Logger unit tests" ${LOGGER_IS_TOP_LEVEL}) +option(LOGGER_BUILD_EXAMPLES "Build Logger examples" ${LOGGER_IS_TOP_LEVEL}) +option(LOGGER_ENABLE_INSTALL "Enable install/export targets for Logger" ${LOGGER_IS_TOP_LEVEL}) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) # Setup project settings add_subdirectory(lib) - add_subdirectory(src) -if(BUILD_TESTS) + +if(LOGGER_BUILD_TESTS) + enable_testing() add_subdirectory(tests) endif() -if(BUILD_EXAMPLES) +if(LOGGER_BUILD_EXAMPLES) add_subdirectory(examples) -endif() \ No newline at end of file +endif() + +if(LOGGER_ENABLE_INSTALL) + configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LoggerConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + ) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + ) +endif() diff --git a/cmake/LoggerConfig.cmake.in b/cmake/LoggerConfig.cmake.in new file mode 100644 index 0000000..59b23ab --- /dev/null +++ b/cmake/LoggerConfig.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/LoggerTargets.cmake") diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..d814571 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,55 @@ +# Logger installation & integration + +## CMake options +- `LOGGER_BUILD_TESTS` (default: `ON` when top level) – build unit tests. +- `LOGGER_BUILD_EXAMPLES` (default: `ON` when top level) – build example apps. +- `LOGGER_ENABLE_INSTALL` (default: `ON` when top level) – emit install/export targets. + +## Build locally +```bash +cmake -S . -B build -DLOGGER_BUILD_TESTS=ON -DLOGGER_BUILD_EXAMPLES=ON +cmake --build build +ctest --test-dir build --output-on-failure +``` + +## Use as a git submodule (pinned, audit-friendly) +1) `git submodule add external/logger` +2) In your root `CMakeLists.txt`: +```cmake +set(LOGGER_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(LOGGER_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +add_subdirectory(external/logger) +target_link_libraries( PRIVATE Logger::Logger) +``` +- Choose this when you want an auditable, locked revision in your tree. + +## Use via FetchContent (easy consumption, automatic download) +```cmake +include(FetchContent) +FetchContent_Declare( + Logger + GIT_REPOSITORY + GIT_TAG +) +set(LOGGER_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(LOGGER_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(Logger) +target_link_libraries( PRIVATE Logger::Logger) +``` +- Prefer this when you want simple, on-demand fetching without VCS plumbing. +- Pin `GIT_TAG` to a commit or release; mirror internally if external fetches are blocked. + +## Use an installed package (reusable across projects/CI) +```bash +cmake --build build --target install --prefix +``` +```cmake +find_package(Logger CONFIG REQUIRED) +target_link_libraries( PRIVATE Logger::Logger) +``` + +## Integration notes +- Public include path is `Logger/` (e.g., `#include `). +- The library is C++20 and exports an ALIAS target `Logger::Logger`. +- Default output dirs: `${binary_dir}/out/bin` and `${binary_dir}/out/lib`. +- Log rotation writes sequential `()` suffixed files in the same directory. diff --git a/examples/globalConfig/CMakeLists.txt b/examples/globalConfig/CMakeLists.txt index 911b33b..2b494e1 100644 --- a/examples/globalConfig/CMakeLists.txt +++ b/examples/globalConfig/CMakeLists.txt @@ -9,7 +9,7 @@ add_executable(${targetName} ) # Link to required libraries -target_link_libraries(${targetName} PUBLIC Logging) +target_link_libraries(${targetName} PUBLIC Logger::Logger) set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderExamples}") # Setup project settings diff --git a/examples/standard/CMakeLists.txt b/examples/standard/CMakeLists.txt index f15faa0..5ac7245 100644 --- a/examples/standard/CMakeLists.txt +++ b/examples/standard/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(${targetName} ) # Link to required libraries -target_link_libraries(${targetName} PUBLIC Logging) +target_link_libraries(${targetName} PUBLIC Logger::Logger) set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderExamples}") # Setup project settings diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e998730..96a3309 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -set(targetName Logging) +set(targetName Logger) # Get files to build file(GLOB_RECURSE headers CONFIGURE_DEPENDS "*.hpp") @@ -9,10 +9,15 @@ add_library(${targetName} STATIC ${headers} ${sources} ) +add_library(Logger::Logger ALIAS ${targetName}) # Link to thread module. target_link_libraries(${targetName}) -target_include_directories(${targetName} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_include_directories(${targetName} + PUBLIC + $ + $ +) set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderSource}") # Setup project settings @@ -29,5 +34,18 @@ target_compile_definitions(${targetName} PUBLIC LOGVERSION_MINOR=0) target_compile_definitions(${targetName} PUBLIC LOGVERSION_PATCH=0) # System installation -# install(TARGETS ${targetName} DESTINATION lib) -# install(FILES ${headers} DESTINATION include/Logger) \ No newline at end of file +if(LOGGER_ENABLE_INSTALL) + install(TARGETS ${targetName} + EXPORT LoggerTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Logger + ) + install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Logger) + install(EXPORT LoggerTargets + FILE LoggerTargets.cmake + NAMESPACE Logger:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + ) +endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 23ddb16..e8e7e27 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,13 +6,16 @@ add_executable(${targetName} ${CMAKE_CURRENT_LIST_DIR}/LogLevel.gtest.cpp ${CMAKE_CURRENT_LIST_DIR}/LogConfig.gtest.cpp ${CMAKE_CURRENT_LIST_DIR}/LogOutput.gtest.cpp + ${CMAKE_CURRENT_LIST_DIR}/Profiler.gtest.cpp ) # Link to required libraries -target_link_libraries(${targetName} PUBLIC Logging gtest_main) -set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderSource}") +target_link_libraries(${targetName} PUBLIC Logger::Logger gtest_main) +set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderTests}") # Setup project settings set_project_warnings(${targetName}) # Which warnings to enable set_compile_options(${targetName}) # Which extra compiler flags to enable set_output_directory(${targetName}) # Set the output directory of the library + +add_test(NAME Logger.UnitTests COMMAND ${targetName}) From 25e4638d48e392736566e2d159e371465ca8399c Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Tue, 23 Dec 2025 17:05:10 +0100 Subject: [PATCH 2/9] General improvements. --- examples/globalConfig/LogModule.cpp | 22 +++--- examples/globalConfig/main.cpp | 34 +++++----- examples/standard/main.cpp | 12 ++-- src/LogOutputConsole.cpp | 7 +- src/LogOutputFile.cpp | 40 ++++++----- src/LogOutputMock.cpp | 5 +- src/Profiler.cpp | 14 ++-- src/include/Profiler.hpp | 3 +- tests/LogConfig.gtest.cpp | 24 +++---- tests/LogEntry.gtest.cpp | 24 +++---- tests/LogLevel.gtest.cpp | 16 ++--- tests/LogOutput.gtest.cpp | 100 +++++++++++++++++----------- tests/Profiler.gtest.cpp | 43 ++++++++++++ 13 files changed, 203 insertions(+), 141 deletions(-) create mode 100644 tests/Profiler.gtest.cpp diff --git a/examples/globalConfig/LogModule.cpp b/examples/globalConfig/LogModule.cpp index 8ae5fe4..6d55bf9 100644 --- a/examples/globalConfig/LogModule.cpp +++ b/examples/globalConfig/LogModule.cpp @@ -1,30 +1,30 @@ #include "LogModule.hpp" #include "LogConfig.hpp" -#include "LogOutputFile.hpp" #include "LogOutputConsole.hpp" +#include "LogOutputFile.hpp" Logging::LogConfig config; //! Enable logging of any entries to an output file + console(for debug builds). static void InitializeLogger() { - config.SetLogEnabled(true); - config.SetMinLogLevel(Logging::LogLevel::Any); + config.SetLogEnabled(true); + config.SetMinLogLevel(Logging::LogLevel::Any); auto logFile = std::make_shared("Logfile.txt"); - config.AddLogOutput(logFile); + config.AddLogOutput(logFile); #ifdef _DEBUG - config.AddLogOutput(std::make_shared()); + config.AddLogOutput(std::make_shared()); #endif } Logging::Logger Logger() { - static bool initialized = false; - if (!initialized) { - InitializeLogger(); - initialized = true; - } + static bool initialized = false; + if (!initialized) { + InitializeLogger(); + initialized = true; + } - return Logging::Logger(config); + return Logging::Logger(config); } diff --git a/examples/globalConfig/main.cpp b/examples/globalConfig/main.cpp index 810001d..eea2c22 100644 --- a/examples/globalConfig/main.cpp +++ b/examples/globalConfig/main.cpp @@ -2,32 +2,32 @@ #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) -#define VERSION_STRING "Version: " STR(LOGVERSION_MAJOR) "." STR(LOGVERSION_MINOR) "." STR(LOGVERSION_PATCH) +#define VERSION_STRING "Version: " STR(LOGVERSION_MAJOR) "." STR(LOGVERSION_MINOR) "." STR(LOGVERSION_PATCH) //! Example for logging. class Calculator { public: - Calculator(const int a, const int b) : m_a(a), m_b(b) { - Logger().Log(Logging::LogLevel::Debug, "Calculator constructor called."); - } + Calculator(const int a, const int b) : m_a(a), m_b(b) { + Logger().Log(Logging::LogLevel::Debug, "Calculator constructor called."); + } - int Add() const { - Logger().Log(Logging::LogLevel::Debug, "Add function called."); - return m_a + m_b; - } + int Add() const { + Logger().Log(Logging::LogLevel::Debug, "Add function called."); + return m_a + m_b; + } private: - int m_a; - int m_b; + int m_a; + int m_b; }; int main() { - auto logger = Logger(); - logger.Log(Logging::LogLevel::Info, "Starting application."); - logger.Log(Logging::LogLevel::Info, VERSION_STRING); - logger.Log(Logging::LogLevel::Info, "Author: Oliver Benz"); - logger.Flush(); + auto logger = Logger(); + logger.Log(Logging::LogLevel::Info, "Starting application."); + logger.Log(Logging::LogLevel::Info, VERSION_STRING); + logger.Log(Logging::LogLevel::Info, "Author: Oliver Benz"); + logger.Flush(); - Calculator calc(9, 10); - calc.Add(); + Calculator calc(9, 10); + calc.Add(); } \ No newline at end of file diff --git a/examples/standard/main.cpp b/examples/standard/main.cpp index a358a90..48feaba 100644 --- a/examples/standard/main.cpp +++ b/examples/standard/main.cpp @@ -1,13 +1,13 @@ -#include -#include "Logger.hpp" #include "LogLevel.hpp" #include "LogOutputConsole.hpp" -#include "LogOutputMock.hpp" #include "LogOutputFile.hpp" +#include "LogOutputMock.hpp" +#include "Logger.hpp" +#include -int main(){ +int main() { std::cout << "Version: " << LOGVERSION_MAJOR << "." << LOGVERSION_MINOR << "." << LOGVERSION_PATCH << std::endl; - + static Logging::LogConfig config; auto mock = std::make_shared(); auto logFile = std::make_shared("Filename.txt"); @@ -24,7 +24,7 @@ int main(){ } std::cout << "\nMockData:\n"; - for (const auto& entry : mock->m_logEntries){ + for (const auto& entry: mock->m_logEntries) { std::cout << LevelToText(entry.m_level) + " " + entry.m_text + "\n"; } diff --git a/src/LogOutputConsole.cpp b/src/LogOutputConsole.cpp index 383ee31..0196ed4 100644 --- a/src/LogOutputConsole.cpp +++ b/src/LogOutputConsole.cpp @@ -3,14 +3,13 @@ namespace Logging { void LogOutputConsole::Write(const std::vector& logEntries) { - // TODO: cout or cerr for (const auto& entry: logEntries) { - std::cout << entry.OutputText() << "\n"; + std::clog << entry.OutputText() << "\n"; } } void LogOutputConsole::Write(const LogEntry& entry) { - std::cout << entry.OutputText() << "\n"; + std::clog << entry.OutputText() << "\n"; } -} // namespace Logging \ No newline at end of file +} // namespace Logging diff --git a/src/LogOutputFile.cpp b/src/LogOutputFile.cpp index 066920d..708b251 100644 --- a/src/LogOutputFile.cpp +++ b/src/LogOutputFile.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace Logging { @@ -10,33 +11,33 @@ LogOutputFile::LogOutputFile(const std::string& filePath, std::size_t maxFileSiz } void LogOutputFile::RotateFile() { - // TODO: Test this function - std::size_t pos = m_filePath.rfind('.'); - const std::string path = m_filePath.substr(0, pos); - const std::string ext = m_filePath.substr(pos + 1); + const std::filesystem::path originalPath(m_filePath); + const auto parent = originalPath.parent_path(); + const auto stem = originalPath.stem().string(); + const auto extension = originalPath.extension().string(); + + const auto baseName = extension.empty() ? originalPath.filename().string() : stem; - // New filename of logFile has postfix [number]. unsigned count = 1; - std::string newFilePath; + std::filesystem::path newFilePath; do { - newFilePath = path + "(" + std::to_string(count) + ")." + ext; + auto rotatedName = baseName + "(" + std::to_string(count) + ")" + extension; + newFilePath = parent.empty() ? std::filesystem::path(rotatedName) : parent / rotatedName; ++count; } while (std::filesystem::exists(newFilePath)); - // The current file is renamed with the postfix. - if (!std::rename(m_filePath.c_str(), newFilePath.c_str()) == 0) { - // throw std::ios_base::failure(std::stderror(-1)); + std::error_code ec; + std::filesystem::rename(originalPath, newFilePath, ec); + if (ec) { + throw std::filesystem::filesystem_error("Failed to rotate log file", originalPath, newFilePath, ec); } } void LogOutputFile::Write(const std::vector& logEntries) { std::unique_lock lock(m_writeLock); - std::ofstream outfile; - outfile.open(m_filePath.c_str(), std::ios::out | std::ios::app); - if (outfile.fail()) { - // throw std::ios_base::failure(std::stderror(-1)); - } + std::ofstream outfile(m_filePath, std::ios::out | std::ios::app); + outfile.exceptions(std::ios::failbit | std::ios::badbit); for (const auto& entry: logEntries) { outfile << entry.OutputText() << "\n"; @@ -53,11 +54,8 @@ void LogOutputFile::Write(const std::vector& logEntries) { void LogOutputFile::Write(const LogEntry& entry) { std::unique_lock lock(m_writeLock); - std::ofstream outfile; - outfile.open(m_filePath.c_str(), std::ios::out | std::ios::app); - if (outfile.fail()) { - // throw std::ios_base::failure(std::stderror(-1)); - } + std::ofstream outfile(m_filePath, std::ios::out | std::ios::app); + outfile.exceptions(std::ios::failbit | std::ios::badbit); outfile << entry.OutputText() << "\n"; @@ -73,4 +71,4 @@ std::string LogOutputFile::FilePath() const { return m_filePath; } -} // namespace Logging \ No newline at end of file +} // namespace Logging diff --git a/src/LogOutputMock.cpp b/src/LogOutputMock.cpp index 1d4dc15..c8ba984 100644 --- a/src/LogOutputMock.cpp +++ b/src/LogOutputMock.cpp @@ -3,10 +3,7 @@ namespace Logging { void LogOutputMock::Write(const std::vector& logEntries) { - for (std::size_t i = 0; i != logEntries.size(); ++i) { - // TODO: Properly copy all at once.. - m_logEntries.emplace_back(logEntries[i]); - } + m_logEntries.insert(m_logEntries.end(), logEntries.begin(), logEntries.end()); } void LogOutputMock::Write(const LogEntry& entry) { diff --git a/src/Profiler.cpp b/src/Profiler.cpp index 7e4eb0d..bd5354e 100644 --- a/src/Profiler.cpp +++ b/src/Profiler.cpp @@ -14,15 +14,17 @@ Profiler::Profiler(Logger logger, std::string identifier) Profiler::~Profiler() { const auto now = std::chrono::steady_clock::now(); const auto timeMs = std::chrono::duration_cast(now - m_startTime).count(); // t in ms - m_logger.Log(Logging::LogLevel::Info, std::format("<-- {} END: ", m_identifier, timeMs)); + m_logger.Log(Logging::LogLevel::Info, std::format("<-- {} END: {}ms", m_identifier, timeMs)); } void Profiler::LogStep(const std::string& stepName) { - const auto timeMs = - std::chrono::duration_cast(m_lastTime - m_startTime).count(); // t in ms - m_logger.Log(Logging::LogLevel::Info, std::format("--- {} STEP {}: ", m_identifier, stepName, timeMs)); - m_lastTime = std::chrono::steady_clock::now(); + const auto now = std::chrono::steady_clock::now(); + const auto stepMs = std::chrono::duration_cast(now - m_lastTime).count(); // t in ms + const auto totalMs = std::chrono::duration_cast(now - m_startTime).count(); // t in ms + m_logger.Log(Logging::LogLevel::Info, + std::format("--- {} STEP {}: +{}ms ({}ms total)", m_identifier, stepName, stepMs, totalMs)); + m_lastTime = now; } -} // namespace Logging \ No newline at end of file +} // namespace Logging diff --git a/src/include/Profiler.hpp b/src/include/Profiler.hpp index 02fa7f8..16814a5 100644 --- a/src/include/Profiler.hpp +++ b/src/include/Profiler.hpp @@ -1,6 +1,7 @@ #pragma once #include "Logger.hpp" +#include namespace Logging { @@ -23,4 +24,4 @@ class Profiler { std::chrono::time_point m_lastTime; }; -} // namespace Logging \ No newline at end of file +} // namespace Logging diff --git a/tests/LogConfig.gtest.cpp b/tests/LogConfig.gtest.cpp index dc1257f..8d8e759 100644 --- a/tests/LogConfig.gtest.cpp +++ b/tests/LogConfig.gtest.cpp @@ -1,10 +1,10 @@ #include "gtest/gtest.h" -#include "Logger.hpp" #include "LogConfig.hpp" +#include "LogOutputConsole.hpp" #include "LogOutputFile.hpp" #include "LogOutputMock.hpp" -#include "LogOutputConsole.hpp" +#include "Logger.hpp" namespace Logging { namespace GTest { @@ -18,7 +18,7 @@ TEST(LogConfig, DisableLogging) { // Disbled logging config.SetLogEnabled(false); - logger.Log(LogLevel::Info, "Testing Entry 1"); + logger.Log(LogLevel::Info, "Testing Entry 1"); logger.Log(LogLevel::Debug, "Testing Entry 2"); logger.Log(LogLevel::Error, "Testing Entry 3"); logger.Flush(); @@ -27,7 +27,7 @@ TEST(LogConfig, DisableLogging) { // Enable logging config.SetLogEnabled(true); - logger.Log(LogLevel::Info, "Testing Entry 1"); + logger.Log(LogLevel::Info, "Testing Entry 1"); logger.Log(LogLevel::Debug, "Testing Entry 2"); logger.Flush(); @@ -35,11 +35,11 @@ TEST(LogConfig, DisableLogging) { // Disbled logging config.SetLogEnabled(false); - logger.Log(LogLevel::Info, "Testing Entry 1"); + logger.Log(LogLevel::Info, "Testing Entry 1"); logger.Log(LogLevel::Debug, "Testing Entry 2"); logger.Flush(); - EXPECT_EQ(mock->m_logEntries.size(), 2u); // Still 2 entries + EXPECT_EQ(mock->m_logEntries.size(), 2u); // Still 2 entries } TEST(LogConfig, MinLogLevel) { @@ -51,11 +51,11 @@ TEST(LogConfig, MinLogLevel) { //! Add 5 log entries and check the mock contains the expected amount of entries. const auto RunTest = [&](const std::size_t expectCount) { - logger.Log(LogLevel::Info, "This is info."); - logger.Log(LogLevel::Debug, "This is debug."); + logger.Log(LogLevel::Info, "This is info."); + logger.Log(LogLevel::Debug, "This is debug."); logger.Log(LogLevel::Warning, "This is warning."); - logger.Log(LogLevel::Error, "This is error."); - logger.Log(LogLevel::Critical,"This is critical."); + logger.Log(LogLevel::Error, "This is error."); + logger.Log(LogLevel::Critical, "This is critical."); logger.Flush(); EXPECT_EQ(mock->m_logEntries.size(), expectCount); @@ -98,5 +98,5 @@ TEST(LogConfig, LogOutputs) { EXPECT_EQ(config.LogOutputs().size(), 5u); } -} -} +} // namespace GTest +} // namespace Logging diff --git a/tests/LogEntry.gtest.cpp b/tests/LogEntry.gtest.cpp index ff8728a..b9fca28 100644 --- a/tests/LogEntry.gtest.cpp +++ b/tests/LogEntry.gtest.cpp @@ -7,21 +7,19 @@ namespace Logging { namespace GTest { TEST(LogEntry, OutputFormat) { - LogEntry logEntry{LogLevel::Info, "This is a test", "2023-02-03 15:47:00"}; - const std::string expected = "2023-02-03 15:47:00 [Info] This is a test"; + LogEntry logEntry{LogLevel::Info, "This is a test", "2023-02-03 15:47:00"}; + const std::string expected = "2023-02-03 15:47:00 [Info] This is a test"; - EXPECT_STREQ(logEntry.OutputText().c_str(), expected.c_str()); + EXPECT_STREQ(logEntry.OutputText().c_str(), expected.c_str()); +} -/* - std::regex r("%d%d.%d%d.%d%d %d%d-%d%d-%d%d%d%d [%s] %s"); +TEST(LogEntry, OutputTextPattern) { + LogEntry logEntry{LogLevel::Warning, "Pattern Check", "01.02.2023 03:04:05"}; + const auto output = logEntry.OutputText(); - std::smatch match; - std::regex_search(logEntry.OutputText(), match, r); -*/ - // TODO: - // - Check the output string has proper format. Should formatting be job of entry? For now ok. - // - Use all functions once so it's covered.. + std::regex pattern(R"(^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2} \[Warning\] Pattern Check$)"); + EXPECT_TRUE(std::regex_match(output, pattern)); } -} -} +} // namespace GTest +} // namespace Logging diff --git a/tests/LogLevel.gtest.cpp b/tests/LogLevel.gtest.cpp index bec0cb4..185d519 100644 --- a/tests/LogLevel.gtest.cpp +++ b/tests/LogLevel.gtest.cpp @@ -6,13 +6,13 @@ namespace Logging { namespace GTest { TEST(LogLevel, LevelToText) { - EXPECT_STREQ(LevelToText(LogLevel::Any).c_str(), "[Any]"); - EXPECT_STREQ(LevelToText(LogLevel::Info).c_str(), "[Info]"); - EXPECT_STREQ(LevelToText(LogLevel::Debug).c_str(), "[Debug]"); - EXPECT_STREQ(LevelToText(LogLevel::Warning).c_str(), "[Warning]"); - EXPECT_STREQ(LevelToText(LogLevel::Error).c_str(), "[Error]"); - EXPECT_STREQ(LevelToText(LogLevel::Critical).c_str(), "[Critical]"); + EXPECT_STREQ(LevelToText(LogLevel::Any).c_str(), "[Any]"); + EXPECT_STREQ(LevelToText(LogLevel::Info).c_str(), "[Info]"); + EXPECT_STREQ(LevelToText(LogLevel::Debug).c_str(), "[Debug]"); + EXPECT_STREQ(LevelToText(LogLevel::Warning).c_str(), "[Warning]"); + EXPECT_STREQ(LevelToText(LogLevel::Error).c_str(), "[Error]"); + EXPECT_STREQ(LevelToText(LogLevel::Critical).c_str(), "[Critical]"); } -} -} +} // namespace GTest +} // namespace Logging diff --git a/tests/LogOutput.gtest.cpp b/tests/LogOutput.gtest.cpp index 8af1aae..69ff28a 100644 --- a/tests/LogOutput.gtest.cpp +++ b/tests/LogOutput.gtest.cpp @@ -1,20 +1,22 @@ #include "gtest/gtest.h" -#include "Logger.hpp" #include "LogLevel.hpp" #include "LogOutputConsole.hpp" -#include "LogOutputMock.hpp" #include "LogOutputFile.hpp" +#include "LogOutputMock.hpp" +#include "Logger.hpp" +#include #include #include +#include namespace Logging { namespace GTest { TEST(LogOutput, Mock) { - auto mock = std::make_shared(); + auto mock = std::make_shared(); - LogConfig config; + LogConfig config; config.AddLogOutput(mock); { @@ -24,31 +26,58 @@ TEST(LogOutput, Mock) { logger.Log(LogLevel::Error, "Error text"); } - EXPECT_EQ(mock->m_logEntries.size(), 3); - + EXPECT_EQ(mock->m_logEntries.size(), 3); + EXPECT_STREQ(mock->m_logEntries[0].m_text.c_str(), "This is a test."); EXPECT_EQ(mock->m_logEntries[0].m_level, LogLevel::Info); - EXPECT_STREQ(mock->m_logEntries[1].m_text.c_str(), "Debug Entry"); + EXPECT_STREQ(mock->m_logEntries[1].m_text.c_str(), "Debug Entry"); EXPECT_EQ(mock->m_logEntries[1].m_level, LogLevel::Debug); - EXPECT_STREQ(mock->m_logEntries[2].m_text.c_str(), "Error text"); + EXPECT_STREQ(mock->m_logEntries[2].m_text.c_str(), "Error text"); EXPECT_EQ(mock->m_logEntries[2].m_level, LogLevel::Error); } +TEST(LogOutput, MockCopiesVectorInOrder) { + LogOutputMock mock; + std::vector entries{ + {LogLevel::Info, "Vector entry 1", "time1"}, + {LogLevel::Debug, "Vector entry 2", "time2"}, + }; + + mock.Write(entries); + mock.Write(LogEntry{LogLevel::Error, "Single entry", "time3"}); + + ASSERT_EQ(mock.m_logEntries.size(), 3u); + EXPECT_EQ(mock.m_logEntries[0].m_text, "Vector entry 1"); + EXPECT_EQ(mock.m_logEntries[1].m_text, "Vector entry 2"); + EXPECT_EQ(mock.m_logEntries[2].m_text, "Single entry"); +} + +TEST(LogOutput, ConsoleUsesClog) { + LogOutputConsole console; + std::ostringstream capture; + auto* originalBuffer = std::clog.rdbuf(capture.rdbuf()); + + console.Write(LogEntry{LogLevel::Info, "Console entry", "2023-02-03 15:47:00"}); + console.Write(std::vector{{LogLevel::Warning, "Vector console entry", "2023-02-03 15:48:00"}}); + + std::clog.rdbuf(originalBuffer); + + const auto output = capture.str(); + EXPECT_NE(output.find("[Info] Console entry"), std::string::npos); + EXPECT_NE(output.find("[Warning] Vector console entry"), std::string::npos); +} + TEST(LogOutput, FileBasic) { - auto logFile1 = std::make_shared("Log1.txt"); + auto logFile1 = std::make_shared("Log1.txt"); auto logFile2 = std::make_shared("Log2.txt"); - LogConfig config; + LogConfig config; config.AddLogOutput(logFile1); config.AddLogOutput(logFile2); - std::array expectOutput { - "[Info] This is a test.", - "[Debug] Debug Entry", - "[Error] Error text" - }; + std::array expectOutput{"[Info] This is a test.", "[Debug] Debug Entry", "[Error] Error text"}; //! Lambda to check a files content is the expectedOutput. auto compareExpect = [&expectOutput](const std::string& fileName) { @@ -56,7 +85,7 @@ TEST(LogOutput, FileBasic) { std::ifstream file(fileName.c_str()); unsigned counter = 0; std::string text; - while(getline(file, text)) { + while (getline(file, text)) { // line contains the timestap so we use .find() EXPECT_TRUE(text.find(expectOutput[counter]) != std::string::npos); ++counter; @@ -67,7 +96,7 @@ TEST(LogOutput, FileBasic) { // Write data to file { Logger logger = Logger(config); - logger.Log(LogLevel::Info,"This is a test."); + logger.Log(LogLevel::Info, "This is a test."); logger.Log(LogLevel::Debug, "Debug Entry"); logger.Log(LogLevel::Error, "Error text"); } @@ -83,35 +112,29 @@ TEST(LogOutput, FileBasic) { TEST(LogOutput, FileMaxSize) { static constexpr std::uintmax_t maxSize = 50; - auto logFile = std::make_shared("Logfile.txt", maxSize); + auto logFile = std::make_shared("Logfile.txt", maxSize); - LogConfig config; + LogConfig config; config.AddLogOutput(logFile); // With timestamp/loglevel, this string produces output > maxSize Bytes const std::string testString('a', maxSize); // Write to output file - { - Logger(config).Log(LogLevel::Info, testString); - } - EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after next write + { Logger(config).Log(LogLevel::Info, testString); } + EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after next write EXPECT_TRUE(std::filesystem::exists("Logfile(1).txt")); // Wrapping happens after a write - EXPECT_FALSE(std::filesystem::exists("Logfile(2).txt")); // Does not exist yet + EXPECT_FALSE(std::filesystem::exists("Logfile(2).txt")); // Does not exist yet // Less than maxSize Bytes - { - Logger(config).Log(LogLevel::Info, "a"); - } - EXPECT_TRUE(std::filesystem::exists("Logfile.txt")); // New write -> Create file again + { Logger(config).Log(LogLevel::Info, "a"); } + EXPECT_TRUE(std::filesystem::exists("Logfile.txt")); // New write -> Create file again EXPECT_TRUE(std::filesystem::exists("Logfile(1).txt")); // Still exists - EXPECT_FALSE(std::filesystem::exists("Logfile(2).txt")); // Does not exist yet + EXPECT_FALSE(std::filesystem::exists("Logfile(2).txt")); // Does not exist yet // Fill the current log file - { - Logger(config).Log(LogLevel::Debug, testString); - } - EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after new write + { Logger(config).Log(LogLevel::Debug, testString); } + EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after new write EXPECT_TRUE(std::filesystem::exists("Logfile(1).txt")); // Still exists EXPECT_TRUE(std::filesystem::exists("Logfile(2).txt")); // Newly created from write EXPECT_FALSE(std::filesystem::exists("Logfile(3).txt")); // Does not exist yet @@ -122,19 +145,20 @@ TEST(LogOutput, FileMaxSize) { logger.Log(LogLevel::Debug, testString); logger.Log(LogLevel::Debug, testString); } - EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after new write + EXPECT_FALSE(std::filesystem::exists("Logfile.txt")); // New file only created after new write EXPECT_TRUE(std::filesystem::exists("Logfile(1).txt")); // Still exists EXPECT_TRUE(std::filesystem::exists("Logfile(2).txt")); // Still exists - EXPECT_TRUE(std::filesystem::exists("Logfile(3).txt")); // Newly created from write -> One log write always to one file + EXPECT_TRUE( + std::filesystem::exists("Logfile(3).txt")); // Newly created from write -> One log write always to one file EXPECT_FALSE(std::filesystem::exists("Logfile(4).txt")); // Does not exist // Cleanup - EXPECT_NE(std::remove("Logfile.txt"), 0); // Does not exist + EXPECT_NE(std::remove("Logfile.txt"), 0); // Does not exist EXPECT_EQ(std::remove("Logfile(1).txt"), 0); EXPECT_EQ(std::remove("Logfile(2).txt"), 0); EXPECT_EQ(std::remove("Logfile(3).txt"), 0); EXPECT_NE(std::remove("Logfile(4).txt"), 0); // Does not exist } -} -} \ No newline at end of file +} // namespace GTest +} // namespace Logging diff --git a/tests/Profiler.gtest.cpp b/tests/Profiler.gtest.cpp new file mode 100644 index 0000000..20c0802 --- /dev/null +++ b/tests/Profiler.gtest.cpp @@ -0,0 +1,43 @@ +#include "gtest/gtest.h" + +#include "LogConfig.hpp" +#include "LogOutputMock.hpp" +#include "Profiler.hpp" + +#include +#include + +namespace Logging { +namespace GTest { + +TEST(Profiler, LogsStartStepsAndEnd) { + LogConfig config; + auto mock = std::make_shared(); + config.AddLogOutput(mock); + + { + Logger logger(config); + Profiler profiler(logger, "ProfileCase"); + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + profiler.LogStep("First"); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + profiler.LogStep("Second"); + } + + ASSERT_EQ(mock->m_logEntries.size(), 4u); + EXPECT_NE(mock->m_logEntries[0].m_text.find("ProfileCase"), std::string::npos); + EXPECT_NE(mock->m_logEntries[0].m_text.find("START"), std::string::npos); + + EXPECT_NE(mock->m_logEntries[1].m_text.find("STEP First"), std::string::npos); + EXPECT_NE(mock->m_logEntries[1].m_text.find("ms"), std::string::npos); + + EXPECT_NE(mock->m_logEntries[2].m_text.find("STEP Second"), std::string::npos); + EXPECT_NE(mock->m_logEntries[2].m_text.find("ms"), std::string::npos); + + EXPECT_NE(mock->m_logEntries[3].m_text.find("END"), std::string::npos); + EXPECT_NE(mock->m_logEntries[3].m_text.find("ProfileCase"), std::string::npos); +} + +} // namespace GTest +} // namespace Logging From 30f689b0cc38ccc0d7ea20f083a9b4ac7c898367 Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 14:00:01 +0100 Subject: [PATCH 3/9] CMake: Minor improvements. --- CMakeLists.txt | 39 ++++++++++++++++++++-------- examples/CMakeLists.txt | 2 +- examples/globalConfig/CMakeLists.txt | 2 +- examples/standard/CMakeLists.txt | 2 +- lib/FetchLibraries.cmake | 5 +++- src/CMakeLists.txt | 11 ++++---- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39a3168..3e108f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(Logger VERSION 1.0.0 LANGUAGES CXX) @@ -10,6 +10,16 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(LOGGER_IS_TOP_LEVEL ON) endif() +# Normalize install prefix/old include paths for typical GNU layouts on Unix-like systems. +if(UNIX AND NOT WIN32) + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR CMAKE_INSTALL_PREFIX MATCHES "^/var/empty") + set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Install path prefix" FORCE) + endif() + if(CMAKE_INSTALL_OLDINCLUDEDIR MATCHES "^/var/empty") + set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "Old-style include path" FORCE) + endif() +endif() + option(LOGGER_BUILD_TESTS "Build Logger unit tests" ${LOGGER_IS_TOP_LEVEL}) option(LOGGER_BUILD_EXAMPLES "Build Logger examples" ${LOGGER_IS_TOP_LEVEL}) option(LOGGER_ENABLE_INSTALL "Enable install/export targets for Logger" ${LOGGER_IS_TOP_LEVEL}) @@ -29,17 +39,24 @@ if(LOGGER_BUILD_EXAMPLES) add_subdirectory(examples) endif() +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LoggerConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) + +export( + EXPORT LoggerTargets + FILE ${CMAKE_CURRENT_BINARY_DIR}/LoggerTargets.cmake + NAMESPACE Logger:: +) + if(LOGGER_ENABLE_INSTALL) - configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LoggerConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger - ) - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion - ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2e6f359..6899553 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) add_subdirectory(standard) add_subdirectory(globalConfig) diff --git a/examples/globalConfig/CMakeLists.txt b/examples/globalConfig/CMakeLists.txt index 2b494e1..229377c 100644 --- a/examples/globalConfig/CMakeLists.txt +++ b/examples/globalConfig/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) set(targetName Example.GlobalConfig) diff --git a/examples/standard/CMakeLists.txt b/examples/standard/CMakeLists.txt index 5ac7245..b98a95c 100644 --- a/examples/standard/CMakeLists.txt +++ b/examples/standard/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) set(targetName Example.Standard) diff --git a/lib/FetchLibraries.cmake b/lib/FetchLibraries.cmake index dfe05b4..5d23479 100644 --- a/lib/FetchLibraries.cmake +++ b/lib/FetchLibraries.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) include(FetchContent) @@ -16,3 +16,6 @@ set_target_properties(gtest PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gtest_main PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gmock PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gmock_main PROPERTIES FOLDER "${ideFolderExternal}") + +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set(INSTALL_GMOCK OFF CACHE BOOL "" FORCE) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96a3309..c4e71aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,12 +11,10 @@ add_library(${targetName} STATIC ) add_library(Logger::Logger ALIAS ${targetName}) -# Link to thread module. -target_link_libraries(${targetName}) target_include_directories(${targetName} PUBLIC $ - $ + $ ) set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderSource}") @@ -40,9 +38,12 @@ if(LOGGER_ENABLE_INSTALL) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Logger + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) - install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/Logger) + install( + DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/Logger + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) install(EXPORT LoggerTargets FILE LoggerTargets.cmake NAMESPACE Logger:: From d3434917b8aed1c29fa16fdaaa18f45ef56607d3 Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 14:32:54 +0100 Subject: [PATCH 4/9] CMake: Update gtest version. --- lib/FetchLibraries.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/FetchLibraries.cmake b/lib/FetchLibraries.cmake index 5d23479..c970ce9 100644 --- a/lib/FetchLibraries.cmake +++ b/lib/FetchLibraries.cmake @@ -3,11 +3,10 @@ cmake_minimum_required(VERSION 3.16) include(FetchContent) # GTest -# Version 1.13.0 FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG b796f7d44681514f58a683a3a71ff17c94edb0c1 + GIT_TAG v1.17.0 ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # For Windows: Prevent overriding the parent project's compiler/linker settings From 1f5d6e7b325c8b8bb586e8acf90d6de826b90f10 Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 15:05:34 +0100 Subject: [PATCH 5/9] Logger: Move headers to subdirectory. --- examples/globalConfig/LogModule.cpp | 6 +++--- examples/globalConfig/LogModule.hpp | 2 +- examples/standard/main.cpp | 11 ++++++----- src/LogConfig.cpp | 2 +- src/LogEntry.cpp | 2 +- src/LogLevel.cpp | 2 +- src/LogOutputConsole.cpp | 4 +++- src/LogOutputFile.cpp | 2 +- src/LogOutputMock.cpp | 2 +- src/Logger.cpp | 2 +- src/Profiler.cpp | 3 ++- src/include/{ => Logger}/ILogOutput.hpp | 3 ++- src/include/{ => Logger}/LogConfig.hpp | 4 ++-- src/include/{ => Logger}/LogEntry.hpp | 3 +-- src/include/{ => Logger}/LogLevel.hpp | 0 src/include/{ => Logger}/LogOutputConsole.hpp | 6 ++++-- src/include/{ => Logger}/LogOutputFile.hpp | 5 ++++- src/include/{ => Logger}/LogOutputMock.hpp | 5 ++++- src/include/{ => Logger}/Logger.hpp | 7 +++---- src/include/{ => Logger}/Profiler.hpp | 4 +++- tests/LogConfig.gtest.cpp | 10 +++++----- tests/LogEntry.gtest.cpp | 3 ++- tests/LogLevel.gtest.cpp | 2 +- tests/LogOutput.gtest.cpp | 11 ++++++----- tests/Profiler.gtest.cpp | 6 +++--- 25 files changed, 61 insertions(+), 46 deletions(-) rename src/include/{ => Logger}/ILogOutput.hpp (95%) rename src/include/{ => Logger}/LogConfig.hpp (93%) rename src/include/{ => Logger}/LogEntry.hpp (88%) rename src/include/{ => Logger}/LogLevel.hpp (100%) rename src/include/{ => Logger}/LogOutputConsole.hpp (72%) rename src/include/{ => Logger}/LogOutputFile.hpp (92%) rename src/include/{ => Logger}/LogOutputMock.hpp (79%) rename src/include/{ => Logger}/Logger.hpp (85%) rename src/include/{ => Logger}/Profiler.hpp (92%) diff --git a/examples/globalConfig/LogModule.cpp b/examples/globalConfig/LogModule.cpp index 6d55bf9..af09ac8 100644 --- a/examples/globalConfig/LogModule.cpp +++ b/examples/globalConfig/LogModule.cpp @@ -1,8 +1,8 @@ #include "LogModule.hpp" -#include "LogConfig.hpp" -#include "LogOutputConsole.hpp" -#include "LogOutputFile.hpp" +#include "Logger/LogConfig.hpp" +#include "Logger/LogOutputConsole.hpp" +#include "Logger/LogOutputFile.hpp" Logging::LogConfig config; diff --git a/examples/globalConfig/LogModule.hpp b/examples/globalConfig/LogModule.hpp index ccd1435..9b8a47b 100644 --- a/examples/globalConfig/LogModule.hpp +++ b/examples/globalConfig/LogModule.hpp @@ -1,6 +1,6 @@ #pragma once -#include "Logger.hpp" +#include "Logger/Logger.hpp" //! Returns the logger instance based on the set up configuration. Logging::Logger Logger(); \ No newline at end of file diff --git a/examples/standard/main.cpp b/examples/standard/main.cpp index 48feaba..92b3e06 100644 --- a/examples/standard/main.cpp +++ b/examples/standard/main.cpp @@ -1,8 +1,9 @@ -#include "LogLevel.hpp" -#include "LogOutputConsole.hpp" -#include "LogOutputFile.hpp" -#include "LogOutputMock.hpp" -#include "Logger.hpp" +#include "Logger/LogLevel.hpp" +#include "Logger/LogOutputConsole.hpp" +#include "Logger/LogOutputFile.hpp" +#include "Logger/LogOutputMock.hpp" +#include "Logger/Logger.hpp" + #include int main() { diff --git a/src/LogConfig.cpp b/src/LogConfig.cpp index b2090a4..b77aeea 100644 --- a/src/LogConfig.cpp +++ b/src/LogConfig.cpp @@ -1,4 +1,4 @@ -#include "LogConfig.hpp" +#include "Logger/LogConfig.hpp" namespace Logging { diff --git a/src/LogEntry.cpp b/src/LogEntry.cpp index e334427..72e2da3 100644 --- a/src/LogEntry.cpp +++ b/src/LogEntry.cpp @@ -1,4 +1,4 @@ -#include "LogEntry.hpp" +#include "Logger/LogEntry.hpp" #include diff --git a/src/LogLevel.cpp b/src/LogLevel.cpp index d7eee1d..bac8a45 100644 --- a/src/LogLevel.cpp +++ b/src/LogLevel.cpp @@ -1,4 +1,4 @@ -#include "LogLevel.hpp" +#include "Logger/LogLevel.hpp" namespace Logging { diff --git a/src/LogOutputConsole.cpp b/src/LogOutputConsole.cpp index 0196ed4..bd324ee 100644 --- a/src/LogOutputConsole.cpp +++ b/src/LogOutputConsole.cpp @@ -1,4 +1,6 @@ -#include "LogOutputConsole.hpp" +#include "Logger/LogOutputConsole.hpp" + +#include namespace Logging { diff --git a/src/LogOutputFile.cpp b/src/LogOutputFile.cpp index 708b251..b8d23e1 100644 --- a/src/LogOutputFile.cpp +++ b/src/LogOutputFile.cpp @@ -1,4 +1,4 @@ -#include "LogOutputFile.hpp" +#include "Logger/LogOutputFile.hpp" #include #include diff --git a/src/LogOutputMock.cpp b/src/LogOutputMock.cpp index c8ba984..11e7ebf 100644 --- a/src/LogOutputMock.cpp +++ b/src/LogOutputMock.cpp @@ -1,4 +1,4 @@ -#include "LogOutputMock.hpp" +#include "Logger/LogOutputMock.hpp" namespace Logging { diff --git a/src/Logger.cpp b/src/Logger.cpp index f2300f7..569fa28 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -1,4 +1,4 @@ -#include "Logger.hpp" +#include "Logger/Logger.hpp" #include #include diff --git a/src/Profiler.cpp b/src/Profiler.cpp index bd5354e..b841967 100644 --- a/src/Profiler.cpp +++ b/src/Profiler.cpp @@ -1,4 +1,5 @@ -#include "Profiler.hpp" +#include "Logger/Profiler.hpp" + #include namespace Logging { diff --git a/src/include/ILogOutput.hpp b/src/include/Logger/ILogOutput.hpp similarity index 95% rename from src/include/ILogOutput.hpp rename to src/include/Logger/ILogOutput.hpp index f641813..267b553 100644 --- a/src/include/ILogOutput.hpp +++ b/src/include/Logger/ILogOutput.hpp @@ -1,6 +1,7 @@ #pragma once -#include "LogEntry.hpp" +#include "Logger/LogEntry.hpp" + #include namespace Logging { diff --git a/src/include/LogConfig.hpp b/src/include/Logger/LogConfig.hpp similarity index 93% rename from src/include/LogConfig.hpp rename to src/include/Logger/LogConfig.hpp index 08a5373..2dfc465 100644 --- a/src/include/LogConfig.hpp +++ b/src/include/Logger/LogConfig.hpp @@ -1,7 +1,7 @@ #pragma once -#include "ILogOutput.hpp" -#include "LogLevel.hpp" +#include "Logger/ILogOutput.hpp" +#include "Logger/LogLevel.hpp" #include #include diff --git a/src/include/LogEntry.hpp b/src/include/Logger/LogEntry.hpp similarity index 88% rename from src/include/LogEntry.hpp rename to src/include/Logger/LogEntry.hpp index 9a6a3cc..7914986 100644 --- a/src/include/LogEntry.hpp +++ b/src/include/Logger/LogEntry.hpp @@ -1,8 +1,7 @@ #pragma once -#include "LogLevel.hpp" +#include "Logger/LogLevel.hpp" -#include #include namespace Logging { diff --git a/src/include/LogLevel.hpp b/src/include/Logger/LogLevel.hpp similarity index 100% rename from src/include/LogLevel.hpp rename to src/include/Logger/LogLevel.hpp diff --git a/src/include/LogOutputConsole.hpp b/src/include/Logger/LogOutputConsole.hpp similarity index 72% rename from src/include/LogOutputConsole.hpp rename to src/include/Logger/LogOutputConsole.hpp index fec5717..0a713be 100644 --- a/src/include/LogOutputConsole.hpp +++ b/src/include/Logger/LogOutputConsole.hpp @@ -1,7 +1,9 @@ #pragma once -#include "ILogOutput.hpp" -#include +#include "Logger/ILogOutput.hpp" +#include "Logger/LogEntry.hpp" + +#include namespace Logging { diff --git a/src/include/LogOutputFile.hpp b/src/include/Logger/LogOutputFile.hpp similarity index 92% rename from src/include/LogOutputFile.hpp rename to src/include/Logger/LogOutputFile.hpp index b3ef7f0..8affb07 100644 --- a/src/include/LogOutputFile.hpp +++ b/src/include/Logger/LogOutputFile.hpp @@ -1,8 +1,11 @@ #pragma once -#include "ILogOutput.hpp" +#include "Logger/ILogOutput.hpp" +#include "Logger/LogEntry.hpp" + #include #include +#include namespace Logging { diff --git a/src/include/LogOutputMock.hpp b/src/include/Logger/LogOutputMock.hpp similarity index 79% rename from src/include/LogOutputMock.hpp rename to src/include/Logger/LogOutputMock.hpp index 02eaaeb..f6deef1 100644 --- a/src/include/LogOutputMock.hpp +++ b/src/include/Logger/LogOutputMock.hpp @@ -1,6 +1,9 @@ #pragma once -#include "ILogOutput.hpp" +#include "Logger/ILogOutput.hpp" +#include "Logger/LogEntry.hpp" + +#include namespace Logging { diff --git a/src/include/Logger.hpp b/src/include/Logger/Logger.hpp similarity index 85% rename from src/include/Logger.hpp rename to src/include/Logger/Logger.hpp index af3622d..5e32714 100644 --- a/src/include/Logger.hpp +++ b/src/include/Logger/Logger.hpp @@ -1,10 +1,9 @@ #pragma once -#include "LogConfig.hpp" -#include "LogEntry.hpp" -#include "LogLevel.hpp" +#include "Logger/LogConfig.hpp" +#include "Logger/LogEntry.hpp" +#include "Logger/LogLevel.hpp" -#include #include namespace Logging { diff --git a/src/include/Profiler.hpp b/src/include/Logger/Profiler.hpp similarity index 92% rename from src/include/Profiler.hpp rename to src/include/Logger/Profiler.hpp index 16814a5..13fb95f 100644 --- a/src/include/Profiler.hpp +++ b/src/include/Logger/Profiler.hpp @@ -1,7 +1,9 @@ #pragma once -#include "Logger.hpp" +#include "Logger/Logger.hpp" + #include +#include namespace Logging { diff --git a/tests/LogConfig.gtest.cpp b/tests/LogConfig.gtest.cpp index 8d8e759..32b72c5 100644 --- a/tests/LogConfig.gtest.cpp +++ b/tests/LogConfig.gtest.cpp @@ -1,10 +1,10 @@ #include "gtest/gtest.h" -#include "LogConfig.hpp" -#include "LogOutputConsole.hpp" -#include "LogOutputFile.hpp" -#include "LogOutputMock.hpp" -#include "Logger.hpp" +#include "Logger/LogConfig.hpp" +#include "Logger/LogOutputConsole.hpp" +#include "Logger/LogOutputFile.hpp" +#include "Logger/LogOutputMock.hpp" +#include "Logger/Logger.hpp" namespace Logging { namespace GTest { diff --git a/tests/LogEntry.gtest.cpp b/tests/LogEntry.gtest.cpp index b9fca28..7604141 100644 --- a/tests/LogEntry.gtest.cpp +++ b/tests/LogEntry.gtest.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" -#include "LogEntry.hpp" +#include "Logger/LogEntry.hpp" + #include namespace Logging { diff --git a/tests/LogLevel.gtest.cpp b/tests/LogLevel.gtest.cpp index 185d519..613962b 100644 --- a/tests/LogLevel.gtest.cpp +++ b/tests/LogLevel.gtest.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" -#include "LogLevel.hpp" +#include "Logger/LogLevel.hpp" namespace Logging { namespace GTest { diff --git a/tests/LogOutput.gtest.cpp b/tests/LogOutput.gtest.cpp index 69ff28a..65d3357 100644 --- a/tests/LogOutput.gtest.cpp +++ b/tests/LogOutput.gtest.cpp @@ -1,10 +1,11 @@ #include "gtest/gtest.h" -#include "LogLevel.hpp" -#include "LogOutputConsole.hpp" -#include "LogOutputFile.hpp" -#include "LogOutputMock.hpp" -#include "Logger.hpp" +#include "Logger/LogLevel.hpp" +#include "Logger/LogOutputConsole.hpp" +#include "Logger/LogOutputFile.hpp" +#include "Logger/LogOutputMock.hpp" +#include "Logger/Logger.hpp" + #include #include #include diff --git a/tests/Profiler.gtest.cpp b/tests/Profiler.gtest.cpp index 20c0802..94e96c1 100644 --- a/tests/Profiler.gtest.cpp +++ b/tests/Profiler.gtest.cpp @@ -1,8 +1,8 @@ #include "gtest/gtest.h" -#include "LogConfig.hpp" -#include "LogOutputMock.hpp" -#include "Profiler.hpp" +#include "Logger/LogConfig.hpp" +#include "Logger/LogOutputMock.hpp" +#include "Logger/Profiler.hpp" #include #include From dcfa55852d86109af6301291d4b5001e6cb7b9cd Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 15:45:41 +0100 Subject: [PATCH 6/9] CMake: Update library --- .gitmodules | 2 +- lib/cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 69503ee..06c3765 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "cmake"] path = lib/cmake - url = https://github.com/OliverBenz/cmake_files.git + url = git@github.com:OliverBenz/cmake_files.git diff --git a/lib/cmake b/lib/cmake index ff32b73..129648a 160000 --- a/lib/cmake +++ b/lib/cmake @@ -1 +1 @@ -Subproject commit ff32b7392b154ceb80a0cc7d79006623811913a7 +Subproject commit 129648aa6dc1d4e5e94c4192d6e5147f9c76c13e From 2a7931aa0aaa2d61c8aba57629bd840695397ab5 Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 15:46:15 +0100 Subject: [PATCH 7/9] CMake: Fix installation path. --- CMakeLists.txt | 26 +++++++++++++++++++------- lib/FetchLibraries.cmake | 3 --- src/CMakeLists.txt | 18 +++++++++--------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e108f2..696c006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,25 +10,37 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(LOGGER_IS_TOP_LEVEL ON) endif() -# Normalize install prefix/old include paths for typical GNU layouts on Unix-like systems. +# Normalize install prefix for Unix-like systems (prefer /opt). if(UNIX AND NOT WIN32) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR CMAKE_INSTALL_PREFIX MATCHES "^/var/empty") - set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Install path prefix" FORCE) - endif() - if(CMAKE_INSTALL_OLDINCLUDEDIR MATCHES "^/var/empty") - set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "Old-style include path" FORCE) + set(CMAKE_INSTALL_PREFIX "/opt" CACHE PATH "Install path prefix" FORCE) endif() endif() +# Project Options option(LOGGER_BUILD_TESTS "Build Logger unit tests" ${LOGGER_IS_TOP_LEVEL}) option(LOGGER_BUILD_EXAMPLES "Build Logger examples" ${LOGGER_IS_TOP_LEVEL}) option(LOGGER_ENABLE_INSTALL "Enable install/export targets for Logger" ${LOGGER_IS_TOP_LEVEL}) +# Dependencies Options +set(FETCHCONTENT_UPDATES_DISCONNECTED ON CACHE BOOL "Use previously downloaded FetchContent sources" FORCE) +set(INSTALL_GTEST OFF CACHE BOOL "Do not install googletest" FORCE) +set(INSTALL_GMOCK OFF CACHE BOOL "Do not install googlemock" FORCE) + +# Installation include(GNUInstallDirs) include(CMakePackageConfigHelpers) # Setup project settings add_subdirectory(lib) + +project_settings_configure_install_layout( + LOGGER_INSTALL_ARCH + LOGGER_INSTALL_CONFIG + LOGGER_INSTALL_BASE + PROJECT_NAME ${PROJECT_NAME} + INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} +) add_subdirectory(src) if(LOGGER_BUILD_TESTS) @@ -42,7 +54,7 @@ endif() configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LoggerConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + INSTALL_DESTINATION ${LOGGER_INSTALL_BASE}/lib/cmake ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake @@ -60,6 +72,6 @@ if(LOGGER_ENABLE_INSTALL) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/LoggerConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + DESTINATION ${LOGGER_INSTALL_BASE}/lib/cmake ) endif() diff --git a/lib/FetchLibraries.cmake b/lib/FetchLibraries.cmake index c970ce9..f6896bb 100644 --- a/lib/FetchLibraries.cmake +++ b/lib/FetchLibraries.cmake @@ -15,6 +15,3 @@ set_target_properties(gtest PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gtest_main PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gmock PROPERTIES FOLDER "${ideFolderExternal}") set_target_properties(gmock_main PROPERTIES FOLDER "${ideFolderExternal}") - -set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) -set(INSTALL_GMOCK OFF CACHE BOOL "" FORCE) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4e71aa..ff12f3b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,12 +11,13 @@ add_library(${targetName} STATIC ) add_library(Logger::Logger ALIAS ${targetName}) +target_compile_features(${targetName} PUBLIC cxx_std_20) target_include_directories(${targetName} PUBLIC $ - $ + $ ) -set_target_properties(${targetName} PROPERTIES FOLDER "${ideFolderSource}") +set_target_properties(${targetName} PROPERTIES FOLDER "${IDE_FOLDER_SOURCE}") # Setup project settings set_project_warnings(${targetName}) # Which warnings to enable @@ -24,7 +25,7 @@ set_compile_options(${targetName}) # Which extra compiler flags to enable set_output_directory(${targetName}) # Set the output directory of the library # Copy header files to output after build -copy_headers_to_output("${targetName}" "${headers}" "") +copy_headers_to_output("${targetName}" "${headers}" "Logger") # Specify version target_compile_definitions(${targetName} PUBLIC LOGVERSION_MAJOR=1) @@ -35,18 +36,17 @@ target_compile_definitions(${targetName} PUBLIC LOGVERSION_PATCH=0) if(LOGGER_ENABLE_INSTALL) install(TARGETS ${targetName} EXPORT LoggerTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${LOGGER_INSTALL_BASE}/lib/${LOGGER_INSTALL_ARCH}/${LOGGER_INSTALL_CONFIG} + LIBRARY DESTINATION ${LOGGER_INSTALL_BASE}/lib/${LOGGER_INSTALL_ARCH}/${LOGGER_INSTALL_CONFIG} + RUNTIME DESTINATION ${LOGGER_INSTALL_BASE}/bin/${LOGGER_INSTALL_ARCH}/${LOGGER_INSTALL_CONFIG} ) install( DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/Logger - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + DESTINATION ${LOGGER_INSTALL_BASE}/include ) install(EXPORT LoggerTargets FILE LoggerTargets.cmake NAMESPACE Logger:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Logger + DESTINATION ${LOGGER_INSTALL_BASE}/lib/cmake ) endif() From 546c33690199cc8261b487e42b0ca7c765b8005d Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 16:57:21 +0100 Subject: [PATCH 8/9] README: Update documentation. --- README.md | 8 +++++++- docs/Documentation.md | 2 ++ docs/{installation.md => Installation.md} | 13 ++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) rename docs/{installation.md => Installation.md} (82%) diff --git a/README.md b/README.md index 630b323..a4233b6 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# Template \ No newline at end of file +# Simple Logger +Just a simple logger. +Check examples for how to use. + +### Documentation +- [General Documentation](docs/Documentation.md) +- [Installation](docs/Installation.md) \ No newline at end of file diff --git a/docs/Documentation.md b/docs/Documentation.md index e69de29..7405b42 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -0,0 +1,2 @@ +Gotta do this. +Just check the examples for how to use. \ No newline at end of file diff --git a/docs/installation.md b/docs/Installation.md similarity index 82% rename from docs/installation.md rename to docs/Installation.md index d814571..81df4fe 100644 --- a/docs/installation.md +++ b/docs/Installation.md @@ -1,6 +1,6 @@ # Logger installation & integration -## CMake options +## CMake Options - `LOGGER_BUILD_TESTS` (default: `ON` when top level) – build unit tests. - `LOGGER_BUILD_EXAMPLES` (default: `ON` when top level) – build example apps. - `LOGGER_ENABLE_INSTALL` (default: `ON` when top level) – emit install/export targets. @@ -8,11 +8,11 @@ ## Build locally ```bash cmake -S . -B build -DLOGGER_BUILD_TESTS=ON -DLOGGER_BUILD_EXAMPLES=ON -cmake --build build +cmake --build build -j ctest --test-dir build --output-on-failure ``` -## Use as a git submodule (pinned, audit-friendly) +## Use as a git submodule 1) `git submodule add external/logger` 2) In your root `CMakeLists.txt`: ```cmake @@ -23,7 +23,7 @@ target_link_libraries( PRIVATE Logger::Logger) ``` - Choose this when you want an auditable, locked revision in your tree. -## Use via FetchContent (easy consumption, automatic download) +## Use via FetchContent ```cmake include(FetchContent) FetchContent_Declare( @@ -39,7 +39,7 @@ target_link_libraries( PRIVATE Logger::Logger) - Prefer this when you want simple, on-demand fetching without VCS plumbing. - Pin `GIT_TAG` to a commit or release; mirror internally if external fetches are blocked. -## Use an installed package (reusable across projects/CI) +## Use an installed package ```bash cmake --build build --target install --prefix ``` @@ -49,7 +49,6 @@ target_link_libraries( PRIVATE Logger::Logger) ``` ## Integration notes -- Public include path is `Logger/` (e.g., `#include `). +- Public include path is `Logger/` (e.g., `#include `). - The library is C++20 and exports an ALIAS target `Logger::Logger`. - Default output dirs: `${binary_dir}/out/bin` and `${binary_dir}/out/lib`. -- Log rotation writes sequential `()` suffixed files in the same directory. From 6385687372fc0d6aa2a5b272ed8d4225fbaf63a6 Mon Sep 17 00:00:00 2001 From: Oliver Benz Date: Fri, 26 Dec 2025 17:27:34 +0100 Subject: [PATCH 9/9] Logger: No throwing. --- src/LogOutputFile.cpp | 61 +++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/src/LogOutputFile.cpp b/src/LogOutputFile.cpp index b8d23e1..a4e637b 100644 --- a/src/LogOutputFile.cpp +++ b/src/LogOutputFile.cpp @@ -18,53 +18,76 @@ void LogOutputFile::RotateFile() { const auto baseName = extension.empty() ? originalPath.filename().string() : stem; - unsigned count = 1; + std::error_code ec; + constexpr unsigned kMaxRotations = 10000; + unsigned count = 1u; std::filesystem::path newFilePath; do { + if (count > kMaxRotations) { + return; + } + auto rotatedName = baseName + "(" + std::to_string(count) + ")" + extension; newFilePath = parent.empty() ? std::filesystem::path(rotatedName) : parent / rotatedName; ++count; - } while (std::filesystem::exists(newFilePath)); + } while (std::filesystem::exists(newFilePath, ec)); + ec.clear(); // // Any errors during probing are ignored by design - std::error_code ec; std::filesystem::rename(originalPath, newFilePath, ec); - if (ec) { - throw std::filesystem::filesystem_error("Failed to rotate log file", originalPath, newFilePath, ec); - } + if (ec == std::errc::cross_device_link) { + // Best-effort fallback + std::filesystem::copy_file( + originalPath, + newFilePath, + std::filesystem::copy_options::overwrite_existing, + ec); + + if (!ec) { + std::filesystem::remove(originalPath, ec); + } + } + + // Any failure beyond this point is intentionally ignored. } void LogOutputFile::Write(const std::vector& logEntries) { std::unique_lock lock(m_writeLock); std::ofstream outfile(m_filePath, std::ios::out | std::ios::app); - outfile.exceptions(std::ios::failbit | std::ios::badbit); + if (!outfile.is_open()) { + return; + } for (const auto& entry: logEntries) { outfile << entry.OutputText() << "\n"; } - - // Check max filesize reached - const auto fileSize = outfile.tellp(); outfile.close(); - if (fileSize != std::ofstream::pos_type(-1) && static_cast(fileSize) >= m_maxFileSize) { - RotateFile(); - } + + // Check max file size reached + std::error_code ec; + const auto fileSize = std::filesystem::file_size(m_filePath, ec); + if (!ec && fileSize >= m_maxFileSize) { + RotateFile(); + } } void LogOutputFile::Write(const LogEntry& entry) { std::unique_lock lock(m_writeLock); std::ofstream outfile(m_filePath, std::ios::out | std::ios::app); - outfile.exceptions(std::ios::failbit | std::ios::badbit); + if (!outfile.is_open()) { + return; + } outfile << entry.OutputText() << "\n"; + outfile.close(); // Check max filesize reached - const auto fileSize = outfile.tellp(); - outfile.close(); - if (fileSize != std::ofstream::pos_type(-1) && static_cast(fileSize) >= m_maxFileSize) { - RotateFile(); - } + std::error_code ec; + const auto fileSize = std::filesystem::file_size(m_filePath, ec); + if (!ec && fileSize >= m_maxFileSize) { + RotateFile(); + } } std::string LogOutputFile::FilePath() const {