From 8ef3fea3d3706527c0f04ab9231d1674bcd01cb5 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Wed, 1 Nov 2017 12:45:00 +0000 Subject: [PATCH] Replace Boost.Log with spdlog This removes a bunch of Boost dependencies, and doesn't cause LOOT to crash on launch, so fixes loot/loot#835 too. --- .travis.yml | 2 +- CMakeLists.txt | 17 ++- appveyor.yml | 2 +- src/api/api.cpp | 58 +-------- src/api/game/game.cpp | 35 ++++-- src/api/game/game_cache.cpp | 1 - src/api/game/load_order_handler.cpp | 38 ++++-- src/api/helpers/crc.cpp | 11 +- src/api/helpers/git_helper.cpp | 117 +++++++++++++----- src/api/helpers/git_helper.h | 7 +- src/api/helpers/logging.h | 76 ++++++++++++ src/api/masterlist.cpp | 113 ++++++++++++----- src/api/metadata/condition_evaluator.cpp | 22 +++- src/api/metadata/condition_grammar.h | 38 ++++-- src/api/metadata/conditional_metadata.cpp | 13 +- src/api/metadata/message.cpp | 1 - src/api/metadata/plugin_cleaning_data.cpp | 7 +- src/api/metadata/plugin_metadata.cpp | 14 ++- src/api/metadata_list.cpp | 18 ++- src/api/plugin/plugin.cpp | 46 +++++-- src/api/plugin/plugin_sorter.cpp | 143 +++++++++++++++------- src/api/plugin/plugin_sorter.h | 2 + src/tests/api/interface/main.cpp | 6 +- src/tests/api/internals/main.cpp | 5 - 24 files changed, 556 insertions(+), 236 deletions(-) create mode 100644 src/api/helpers/logging.h diff --git a/.travis.yml b/.travis.yml index d96ef23d..4f3d5fde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ install: - export CXX="g++-6" CC="gcc-6" # Build Boost. - wget https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.5.0/install_boost.py - - python install_boost.py --directory ~ --boost-version 1.64.0 -a 64 -t gcc-6 atomic chrono date_time filesystem iostreams locale log regex system thread + - python install_boost.py --directory ~ --boost-version 1.64.0 -a 64 -t gcc-6 filesystem locale system # Install packages for generating documentation - pip install --user -r docs/requirements.txt # Add sphinx-build to PATH diff --git a/CMakeLists.txt b/CMakeLists.txt index b843c497..dd4a122c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ ELSE () set(RUST_TARGET x86_64-unknown-linux-gnu) ENDIF () -find_package(Boost REQUIRED COMPONENTS atomic log log_setup regex locale thread date_time chrono filesystem system iostreams) +find_package(Boost REQUIRED COMPONENTS locale filesystem system) ExternalProject_Add(GTest PREFIX "external" @@ -137,6 +137,15 @@ ExternalProject_Add(testing-plugins BUILD_COMMAND "" INSTALL_COMMAND "") +ExternalProject_Add(spdlog + PREFIX "external" + URL "https://github.com/gabime/spdlog/archive/v0.14.0.tar.gz" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "") +ExternalProject_Get_Property(spdlog SOURCE_DIR) +set(SPDLOG_INCLUDE_DIRS "${SOURCE_DIR}/include") + ExternalProject_Add(yaml-cpp PREFIX "external" URL "https://github.com/WrinklyNinja/yaml-cpp/archive/patched-for-loot.tar.gz" @@ -223,6 +232,7 @@ set (LOOT_API_HEADERS "${CMAKE_SOURCE_DIR}/include/loot/api.h" "${CMAKE_SOURCE_DIR}/src/api/plugin/plugin_sorter.h" "${CMAKE_SOURCE_DIR}/src/api/helpers/git_helper.h" "${CMAKE_SOURCE_DIR}/src/api/helpers/crc.h" + "${CMAKE_SOURCE_DIR}/src/api/helpers/logging.h" "${CMAKE_SOURCE_DIR}/src/api/helpers/version.h" "${CMAKE_SOURCE_DIR}/src/api/helpers/windows_encoding_converters.h") @@ -277,6 +287,7 @@ include_directories ("${CMAKE_SOURCE_DIR}/src" ${LIBGIT2_INCLUDE_DIRS} ${ESPLUGIN_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} + ${SPDLOG_INCLUDE_DIRS} ${YAML_CPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} ${PSEUDOSEM_INCLUDE_DIRS}) @@ -339,12 +350,12 @@ ENDIF () # Build tests. add_executable (loot_api_internals_tests ${LOOT_API_SRC} ${LOOT_API_HEADERS} ${LOOT_TESTS_SRC} ${LOOT_TESTS_HEADERS}) -add_dependencies (loot_api_internals_tests esplugin libgit2 libloadorder pseudosem yaml-cpp GTest testing-metadata testing-plugins) +add_dependencies (loot_api_internals_tests esplugin libgit2 libloadorder pseudosem spdlog yaml-cpp GTest testing-metadata testing-plugins) target_link_libraries(loot_api_internals_tests ${Boost_LIBRARIES} ${LIBGIT2_LIBRARIES} ${ESPLUGIN_LIBRARIES} ${LIBLOADORDER_LIBRARIES} ${LOOT_LIBS} ${YAML_CPP_LIBRARIES} ${GTEST_LIBRARIES}) # Build API. add_library (loot_api ${LOOT_API_SRC} ${LOOT_API_HEADERS}) -add_dependencies (loot_api esplugin libgit2 libloadorder pseudosem yaml-cpp) +add_dependencies (loot_api esplugin libgit2 libloadorder pseudosem spdlog yaml-cpp) target_link_libraries(loot_api ${Boost_LIBRARIES} ${LIBGIT2_LIBRARIES} ${ESPLUGIN_LIBRARIES} ${LIBLOADORDER_LIBRARIES} ${LOOT_LIBS} ${YAML_CPP_LIBRARIES}) # Build API tests. diff --git a/appveyor.yml b/appveyor.yml index 1710f144..37027950 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,7 +32,7 @@ install: - ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.1.0/install_boost.py', "$env:APPVEYOR_BUILD_FOLDER\install_boost.py") - ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/WrinklyNinja/ci-scripts/1.2.0/delete_old_bintray_versions.py', "$env:APPVEYOR_BUILD_FOLDER\delete_old_bintray_versions.py") - ps: if ($env:PLATFORM -eq "Win32") { $env:ADDRESS_MODEL = '32' } else { $env:ADDRESS_MODEL = '64' } - - ps: python $env:APPVEYOR_BUILD_FOLDER\install_boost.py -d C:\projects -b 1.61.0 -a $env:ADDRESS_MODEL atomic chrono date_time filesystem iostreams locale log regex system thread + - ps: python $env:APPVEYOR_BUILD_FOLDER\install_boost.py -d C:\projects -b 1.61.0 -a $env:ADDRESS_MODEL filesystem locale system before_build: - cd %APPVEYOR_BUILD_FOLDER% diff --git a/src/api/api.cpp b/src/api/api.cpp index 71b72103..567babeb 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -25,14 +25,9 @@ #include "loot/api.h" #include -#include -#include -#include -#include -#include -#include #include "api/game/game.h" +#include "api/helpers/logging.h" namespace fs = boost::filesystem; @@ -46,54 +41,13 @@ std::string ResolvePath(const std::string& path) { return fs::read_symlink(path).string(); } -LogLevel mapFromBoostLog(boost::log::trivial::severity_level severity) { - using boost::log::trivial::severity_level; - switch (severity) { - case severity_level::trace: - return LogLevel::trace; - case severity_level::debug: - return LogLevel::debug; - case severity_level::info: - return LogLevel::info; - case severity_level::warning: - return LogLevel::warning; - case severity_level::error: - return LogLevel::error; - case severity_level::fatal: - return LogLevel::fatal; - default: - return LogLevel::trace; - } -} - -class LoggingSink : public boost::log::sinks::basic_formatted_sink_backend { -public: - LoggingSink(std::function callback) { - this->callback = callback; - } - - void consume(const boost::log::record_view& rec, const std::string& str) { - using boost::log::trivial::severity_level; - auto severity = rec.attribute_values()[boost::log::aux::default_attribute_names::severity()].extract(); - if (!severity) { - return; - } - - callback(mapFromBoostLog(*severity), str.c_str()); - } - -private: - std::function callback; -}; - LOOT_API void SetLoggingCallback(std::function callback) { - typedef boost::log::sinks::synchronous_sink sink_t; + auto sink = std::make_shared(callback); + auto logger = std::make_shared(LOGGER_NAME, sink); + logger->set_level(spdlog::level::level_enum::trace); - auto sink_backend = boost::make_shared(callback); - boost::shared_ptr sink(new sink_t(sink_backend)); - - boost::log::core::get()->remove_all_sinks(); - boost::log::core::get()->add_sink(sink); + spdlog::drop(LOGGER_NAME); + spdlog::register_logger(logger); } LOOT_API bool IsCompatible(const unsigned int versionMajor, const unsigned int versionMinor, const unsigned int versionPatch) { diff --git a/src/api/game/game.cpp b/src/api/game/game.cpp index da9e473a..bfda1db0 100644 --- a/src/api/game/game.cpp +++ b/src/api/game/game.cpp @@ -29,11 +29,9 @@ #include #include -#include -#include -#include #include "api/api_database.h" +#include "api/helpers/logging.h" #include "api/plugin/plugin_sorter.h" #include "loot/exception/file_access_error.h" @@ -66,7 +64,10 @@ Game::Game(const GameType gameType, localDataPath_(localDataPath), cache_(std::make_shared()), loadOrderHandler_(std::make_shared()) { - BOOST_LOG_TRIVIAL(info) << "Initialising load order data for game of type " << (int)type_ << " at: " << gamePath_; + auto logger = getLogger(); + if (logger) { + logger->info("Initialising load order data for game of type {} at: {}", (int) type_, gamePath_.string()); + } loadOrderHandler_->Init(type_, gamePath_, localDataPath_); @@ -98,6 +99,7 @@ bool Game::IsValidPlugin(const std::string& plugin) const { } void Game::LoadPlugins(const std::vector& plugins, bool loadHeadersOnly) { + auto logger = getLogger(); uintmax_t meanFileSize = 0; std::multimap sizeMap; @@ -125,15 +127,22 @@ void Game::LoadPlugins(const std::vector& plugins, bool loadHeaders // Divide the plugins up by thread. unsigned int pluginsPerThread = ceil((double)sizeMap.size() / threadsToUse); vector> pluginGroups(threadsToUse); - BOOST_LOG_TRIVIAL(info) << "Loading " << sizeMap.size() << " plugins using " << threadsToUse << " threads, with up to " << pluginsPerThread << " plugins per thread."; + if (logger) { + logger->info("Loading {} plugins using {} threads, with up to {} plugins per thread.", sizeMap.size(), threadsToUse, pluginsPerThread); + } // The plugins should be split between the threads so that the data // load is as evenly spread as possible. size_t currentGroup = 0; for (const auto& plugin : sizeMap) { - if (currentGroup == threadsToUse) + if (currentGroup == threadsToUse) { currentGroup = 0; - BOOST_LOG_TRIVIAL(trace) << "Adding plugin " << plugin.second << " to loading group " << currentGroup; + } + + if (logger) { + logger->trace("Adding plugin {} to loading group {}", plugin.second, currentGroup); + } + pluginGroups[currentGroup].push_back(plugin.second); ++currentGroup; } @@ -143,18 +152,24 @@ void Game::LoadPlugins(const std::vector& plugins, bool loadHeaders loadOrderHandler_->LoadCurrentState(); // Load the plugins. - BOOST_LOG_TRIVIAL(trace) << "Starting plugin loading."; + if (logger) { + logger->trace("Starting plugin loading."); + } vector threads; while (threads.size() < threadsToUse) { vector& pluginGroup = pluginGroups[threads.size()]; threads.push_back(thread([&]() { for (auto pluginName : pluginGroup) { - BOOST_LOG_TRIVIAL(trace) << "Loading " << pluginName; + if (logger) { + logger->trace("Loading {}", pluginName); + } const bool loadHeader = boost::iequals(pluginName, masterFile_) || loadHeadersOnly; try { cache_->AddPlugin(Plugin(Type(), DataPath(), loadOrderHandler_, pluginName, loadHeader)); } catch(std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Caught exception while trying to add " << pluginName << " to the cache: " << e.what(); + if (logger) { + logger->trace("Caught exception while trying to add {} to the cache: {}", pluginName, e.what()); + } } } })); diff --git a/src/api/game/game_cache.cpp b/src/api/game/game_cache.cpp index 80ebce8e..c92fe182 100644 --- a/src/api/game/game_cache.cpp +++ b/src/api/game/game_cache.cpp @@ -28,7 +28,6 @@ #include #include -#include using boost::locale::to_lower; using std::lock_guard; diff --git a/src/api/game/load_order_handler.cpp b/src/api/game/load_order_handler.cpp index 82351d9a..169beef8 100644 --- a/src/api/game/load_order_handler.cpp +++ b/src/api/game/load_order_handler.cpp @@ -26,9 +26,9 @@ #include #include -#include #include "loot/exception/error_categories.h" +#include "api/helpers/logging.h" using boost::format; using std::string; @@ -78,7 +78,10 @@ void LoadOrderHandler::Init(const GameType& gameType, } void LoadOrderHandler::LoadCurrentState() { - BOOST_LOG_TRIVIAL(debug) << "Loading the current load order state."; + auto logger = getLogger(); + if (logger) { + logger->debug("Loading the current load order state."); + } unsigned int ret = lo_load_current_state(gh_); @@ -86,7 +89,10 @@ void LoadOrderHandler::LoadCurrentState() { } bool LoadOrderHandler::IsPluginActive(const std::string& pluginName) const { - BOOST_LOG_TRIVIAL(debug) << "Checking if plugin \"" << pluginName << "\" is active."; + auto logger = getLogger(); + if (logger) { + logger->debug("Checking if plugin \"{}\" is active.", pluginName); + } bool result = false; unsigned int ret = lo_get_plugin_active(gh_, pluginName.c_str(), &result); @@ -97,7 +103,10 @@ bool LoadOrderHandler::IsPluginActive(const std::string& pluginName) const { } std::vector LoadOrderHandler::GetLoadOrder() const { - BOOST_LOG_TRIVIAL(debug) << "Getting load order."; + auto logger = getLogger(); + if (logger) { + logger->debug("Getting load order."); + } char ** pluginArr; size_t pluginArrSize; @@ -113,12 +122,19 @@ std::vector LoadOrderHandler::GetLoadOrder() const { } void LoadOrderHandler::SetLoadOrder(const std::vector& loadOrder) const { - BOOST_LOG_TRIVIAL(info) << "Setting load order."; + auto logger = getLogger(); + if (logger) { + logger->info("Setting load order."); + } + size_t pluginArrSize = loadOrder.size(); char ** pluginArr = new char*[pluginArrSize]; int i = 0; for (const auto &plugin : loadOrder) { - BOOST_LOG_TRIVIAL(info) << '\t' << '\t' << plugin; + if (logger) { + logger->info("\t\t{}", plugin); + } + pluginArr[i] = new char[plugin.length() + 1]; strcpy(pluginArr[i], plugin.c_str()); ++i; @@ -132,7 +148,9 @@ void LoadOrderHandler::SetLoadOrder(const std::vector& loadOrder) c HandleError("set the load order", ret); - BOOST_LOG_TRIVIAL(info) << "Load order set successfully."; + if (logger) { + logger->info("Load order set successfully."); + } } void LoadOrderHandler::HandleError(const std::string& operation, unsigned int returnCode) const { @@ -150,7 +168,11 @@ void LoadOrderHandler::HandleError(const std::string& operation, unsigned int re err = (format("libloadorder failed to " + operation + ". Details: %1%") % e).str(); } - BOOST_LOG_TRIVIAL(error) << err; + auto logger = getLogger(); + if (logger) { + logger->error(err); + } + throw std::system_error(returnCode, libloadorder_category(), err); } } diff --git a/src/api/helpers/crc.cpp b/src/api/helpers/crc.cpp index 663c8ce0..1e596e8f 100644 --- a/src/api/helpers/crc.cpp +++ b/src/api/helpers/crc.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include "api/helpers/logging.h" #include "loot/exception/file_access_error.h" @@ -48,7 +48,10 @@ size_t GetStreamSize(std::istream& stream) { //Calculate the CRC of the given file for comparison purposes. uint32_t GetCrc32(const boost::filesystem::path& filename) { try { - BOOST_LOG_TRIVIAL(trace) << "Calculating CRC for: " << filename.string(); + auto logger = getLogger(); + if (logger) { + logger->trace("Calculating CRC for: {}", filename.string()); + } boost::filesystem::ifstream ifile(filename, std::ios::binary); ifile.exceptions(std::ios_base::badbit | std::ios_base::failbit); @@ -68,7 +71,9 @@ uint32_t GetCrc32(const boost::filesystem::path& filename) { } uint32_t checksum = result.checksum(); - BOOST_LOG_TRIVIAL(debug) << "CRC32(\"" << filename.string() << "\"): " << std::hex << checksum << std::dec; + if (logger) { + logger->debug("CRC32(\"{}\"): {:x}", filename.string(), checksum); + } return checksum; } catch (std::exception& e) { diff --git a/src/api/helpers/git_helper.cpp b/src/api/helpers/git_helper.cpp index b12bb9c4..ae536271 100644 --- a/src/api/helpers/git_helper.cpp +++ b/src/api/helpers/git_helper.cpp @@ -25,16 +25,30 @@ #include "api/helpers/git_helper.h" #include -#include #include "loot/exception/error_categories.h" #include "loot/exception/git_state_error.h" +#include "api/helpers/logging.h" using std::string; namespace fs = boost::filesystem; namespace loot { +GitHelper::GitHelper() : logger_(getLogger()) {} + +GitHelper::~GitHelper() { + if (data_.repo != nullptr) { + std::string path = git_repository_path(data_.repo); + + if (!path.empty()) { + try { + FixRepoPermissions(path); + } catch (std::exception&) {} + } + } +} + GitHelper::GitData::GitData() : repo(nullptr), remote(nullptr), @@ -78,12 +92,6 @@ GitHelper::GitData::~GitData() { delete[] checkout_options.paths.strings[i]; } - if (!path.empty()) { - try { - FixRepoPermissions(path); - } catch (std::exception&) {} - } - git_libgit2_shutdown(); } @@ -110,20 +118,30 @@ bool GitHelper::IsRepository(const boost::filesystem::path& path) { // Removes the read-only flag from some files in git repositories created by libgit2. void GitHelper::FixRepoPermissions(const boost::filesystem::path& path) { - BOOST_LOG_TRIVIAL(trace) << "Recursively setting write permission on directory: " << path; + if (logger_) { + logger_->trace("Recursively setting write permission on directory: {}", path.string()); + } for (fs::recursive_directory_iterator it(path); it != fs::recursive_directory_iterator(); ++it) { if ((it->status().permissions() & (fs::owner_write | fs::group_write | fs::others_write)) == 0) { - BOOST_LOG_TRIVIAL(trace) << "Setting write permission for: " << it->path(); + if (logger_) { + logger_->trace("Setting write permission for: {}", it->path().string()); + } fs::permissions(it->path(), fs::add_perms | fs::owner_write); } } } int GitHelper::DiffFileCallback(const git_diff_delta *delta, float progress, void * payload) { - BOOST_LOG_TRIVIAL(trace) << "Checking diff for: " << delta->old_file.path; + auto logger = getLogger(); + if (logger) { + logger->trace("Checking diff for: {}", delta->old_file.path); + } + DiffPayload * gdp = (DiffPayload*)payload; if (strcmp(delta->old_file.path, gdp->fileToFind) == 0) { - BOOST_LOG_TRIVIAL(warning) << "Edited masterlist found."; + if (logger) { + logger->warn("Edited masterlist found."); + } gdp->fileFound = true; } @@ -136,7 +154,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur throw GitStateError("Cannot clone repository that has already been opened."); // Clone the remote repository. - BOOST_LOG_TRIVIAL(info) << "Repository doesn't exist, cloning the remote repository."; + if (logger_) { + logger_->info("Repository doesn't exist, cloning the remote repository."); + } fs::path tempPath = path.parent_path() / fs::unique_path(); @@ -147,7 +167,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur // Directory is non-empty. Delete the masterlist file and // .git folder, then move any remaining files to a temporary // folder while the repo is cloned, before moving them back. - BOOST_LOG_TRIVIAL(trace) << "Repo path not empty, renaming folder."; + if (logger_) { + logger_->trace("Repo path not empty, renaming folder."); + } // Clear any read-only flags first. FixRepoPermissions(path); @@ -166,7 +188,9 @@ void GitHelper::Clone(const boost::filesystem::path& path, const std::string& ur if (fs::exists(tempPath)) { //Move contents back in. - BOOST_LOG_TRIVIAL(trace) << "Repo path wasn't empty, moving previous files back in."; + if (logger_) { + logger_->trace("Repo path wasn't empty, moving previous files back in."); + } for (fs::directory_iterator it(tempPath); it != fs::directory_iterator(); ++it) { if (!fs::exists(path / it->path().filename())) { //No conflict, OK to move back in. @@ -182,7 +206,9 @@ void GitHelper::Fetch(const std::string& remote) { if (data_.repo == nullptr) throw GitStateError("Cannot fetch updates for repository that has not been opened."); - BOOST_LOG_TRIVIAL(trace) << "Fetching updates from remote."; + if (logger_) { + logger_->trace("Fetching updates from remote."); + } // Get the origin remote. Call(git_remote_lookup(&data_.remote, data_.repo, remote.c_str())); @@ -193,7 +219,10 @@ void GitHelper::Fetch(const std::string& remote) { // Log some stats on what was fetched either during update or clone. const git_transfer_progress * stats = git_remote_stats(data_.remote); - BOOST_LOG_TRIVIAL(info) << "Received " << stats->indexed_objects << " of " << stats->total_objects << " objects in " << stats->received_bytes << " bytes."; + if (logger_) { + logger_->trace("Received {} of {} objects in {} bytes.", + stats->indexed_objects, stats->total_objects, stats->received_bytes); + } git_remote_free(data_.remote); data_.remote = nullptr; @@ -209,26 +238,34 @@ void GitHelper::CheckoutNewBranch(const std::string& remote, const std::string& else if (data_.reference != nullptr) throw GitStateError("Cannot fetch repository updates, reference memory already allocated."); - BOOST_LOG_TRIVIAL(trace) << "Looking up commit referred to by the remote branch \"" << branch << "\"."; + if (logger_) { + logger_->trace("Looking up commit referred to by the remote branch \"{}\".", branch); + } Call(git_revparse_single(&data_.object, data_.repo, (remote + "/" + branch).c_str())); const git_oid * commit_id = git_object_id(data_.object); - // Create a branch. - BOOST_LOG_TRIVIAL(trace) << "Creating the new branch."; + if (logger_) { + logger_->trace("Creating the new branch."); + } Call(git_commit_lookup(&data_.commit, data_.repo, commit_id)); Call(git_branch_create(&data_.reference, data_.repo, branch.c_str(), data_.commit, 1)); - // Set upstream. - BOOST_LOG_TRIVIAL(trace) << "Setting the upstream for the new branch."; + if (logger_) { + logger_->trace("Setting the upstream for the new branch."); + } Call(git_branch_set_upstream(data_.reference, (remote + "/" + branch).c_str())); // Check if HEAD points to the desired branch and set it to if not. if (!git_branch_is_head(data_.reference)) { - BOOST_LOG_TRIVIAL(trace) << "Setting HEAD to follow branch: " << branch; + if (logger_) { + logger_->trace("Setting HEAD to follow branch: {}", branch); + } Call(git_repository_set_head(data_.repo, (string("refs/heads/") + branch).c_str())); } - BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD."; + if (logger_) { + logger_->trace("Performing a Git checkout of HEAD."); + } Call(git_checkout_head(data_.repo, &data_.checkout_options)); // Free tree and commit pointers. Reference pointer is still used below. @@ -254,7 +291,9 @@ void GitHelper::CheckoutRevision(const std::string& revision) { Call(git_repository_set_head_detached(data_.repo, oid)); // Checkout the new HEAD. - BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD."; + if (logger_) { + logger_->trace("Performing a Git checkout of HEAD."); + } Call(git_checkout_head(data_.repo, &data_.checkout_options)); git_object_free(data_.object); @@ -271,11 +310,15 @@ std::string GitHelper::GetHeadShortId() { else if (data_.buffer.ptr != nullptr) throw GitStateError("Cannot fetch repository updates, buffer memory already allocated."); - BOOST_LOG_TRIVIAL(trace) << "Getting the Git object for HEAD."; + if (logger_) { + logger_->trace("Getting the Git object for HEAD."); + } Call(git_repository_head(&data_.reference, data_.repo)); Call(git_reference_peel(&data_.object, data_.reference, GIT_OBJ_COMMIT)); - BOOST_LOG_TRIVIAL(trace) << "Generating hex string for Git object ID."; + if (logger_) { + logger_->trace("Generating hex string for Git object ID."); + } Call(git_object_short_id(&data_.buffer, data_.object)); string revision = data_.buffer.ptr; @@ -294,24 +337,36 @@ GitHelper::GitData& GitHelper::GetData() { } bool GitHelper::IsFileDifferent(const boost::filesystem::path& repoRoot, const std::string& filename) { + auto logger = getLogger(); + if (!IsRepository(repoRoot)) { - BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: Git repository missing."; + if (logger) { + logger->info("Unknown masterlist revision: Git repository missing."); + } throw GitStateError("Cannot check if the \"" + filename + "\" working copy is edited, Git repository missing."); } - BOOST_LOG_TRIVIAL(debug) << "Existing repository found, attempting to open it."; + if (logger) { + logger->trace("Existing repository found, attempting to open it."); + } GitHelper git; git.Call(git_repository_open(&git.data_.repo, repoRoot.string().c_str())); // Perform a git diff, then iterate the deltas to see if one exists for the masterlist. - BOOST_LOG_TRIVIAL(trace) << "Getting the tree for the HEAD revision."; + if (logger) { + logger->trace("Getting the tree for the HEAD revision."); + } git.Call(git_revparse_single(&git.data_.object, git.data_.repo, "HEAD^{tree}")); git.Call(git_tree_lookup(&git.data_.tree, git.data_.repo, git_object_id(git.data_.object))); - BOOST_LOG_TRIVIAL(trace) << "Performing git diff."; + if (logger) { + logger->trace("Performing git diff."); + } git.Call(git_diff_tree_to_workdir_with_index(&git.data_.diff, git.data_.repo, git.data_.tree, NULL)); - BOOST_LOG_TRIVIAL(trace) << "Iterating over git diff deltas."; + if (logger) { + logger->trace("Iterating over git diff deltas."); + } GitHelper::DiffPayload payload; payload.fileFound = false; payload.fileToFind = filename.c_str(); diff --git a/src/api/helpers/git_helper.h b/src/api/helpers/git_helper.h index 0361412c..f66edcf4 100644 --- a/src/api/helpers/git_helper.h +++ b/src/api/helpers/git_helper.h @@ -29,6 +29,7 @@ #include #include +#include namespace loot { class GitHelper { @@ -59,6 +60,9 @@ public: git_clone_options clone_options; }; + GitHelper(); + ~GitHelper(); + void Call(int error_code); static bool IsRepository(const boost::filesystem::path& path); @@ -77,9 +81,10 @@ public: private: // Removes the read-only flag from some files in git repositories // created by libgit2. - static void FixRepoPermissions(const boost::filesystem::path& path); + void FixRepoPermissions(const boost::filesystem::path& path); GitData data_; + std::shared_ptr logger_; }; } #endif diff --git a/src/api/helpers/logging.h b/src/api/helpers/logging.h new file mode 100644 index 00000000..07a444bd --- /dev/null +++ b/src/api/helpers/logging.h @@ -0,0 +1,76 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2012-2016 WrinklyNinja + + This file is part of LOOT. + + LOOT is free software: you can redistribute + it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + LOOT is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LOOT. If not, see + . + */ +#ifndef LOOT_API_HELPERS_LOGGING +#define LOOT_API_HELPERS_LOGGING + +#include + +#include "loot/enum/log_level.h" + +namespace loot { +static const char * LOGGER_NAME = "loot_api_logger"; + +inline std::shared_ptr getLogger() { + return spdlog::get(LOGGER_NAME); +} + +class SpdLoggingSink : public spdlog::sinks::base_sink { +public: + SpdLoggingSink(std::function callback) { + this->callback = callback; + } + +protected: + void _sink_it(const spdlog::details::log_msg& msg) override { + callback(mapFromSpdlog(msg.level), msg.raw.str().c_str()); + } + + void _flush() override {} + +private: + std::function callback; + + static LogLevel mapFromSpdlog(spdlog::level::level_enum severity) { + using spdlog::level::level_enum; + switch (severity) { + case level_enum::trace: + return LogLevel::trace; + case level_enum::debug: + return LogLevel::debug; + case level_enum::info: + return LogLevel::info; + case level_enum::warn: + return LogLevel::warning; + case level_enum::err: + return LogLevel::error; + case level_enum::critical: + return LogLevel::fatal; + default: + return LogLevel::trace; + } + } +}; +} + +#endif diff --git a/src/api/masterlist.cpp b/src/api/masterlist.cpp index d8f88283..f57f262e 100644 --- a/src/api/masterlist.cpp +++ b/src/api/masterlist.cpp @@ -28,12 +28,12 @@ #include #include -#include #include "loot/exception/file_access_error.h" #include "loot/exception/git_state_error.h" #include "api/game/game.h" #include "api/helpers/git_helper.h" +#include "api/helpers/logging.h" using boost::format; using std::string; @@ -46,22 +46,34 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho GitHelper git; MasterlistInfo info; + auto logger = getLogger(); + if (!fs::exists(path)) { - BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: No masterlist present."; + if (logger) { + logger->info("Unknown masterlist revision: No masterlist present."); + } throw FileAccessError(string("N/A: No masterlist present at ") + path.string()); } else if (!git.IsRepository(path.parent_path())) { - BOOST_LOG_TRIVIAL(info) << "Unknown masterlist revision: Git repository missing."; + if (logger) { + logger->info("Unknown masterlist revision: Git repository missing."); + } throw GitStateError(string("Unknown: \"") + path.parent_path().string() + "\" is not a Git repository."); } - BOOST_LOG_TRIVIAL(debug) << "Existing repository found, attempting to open it."; + if (logger) { + logger->debug("Existing repository found, attempting to open it."); + } git.Call(git_repository_open(&git.GetData().repo, path.parent_path().string().c_str())); //Need to get the HEAD object, because the individual file has a different SHA. - BOOST_LOG_TRIVIAL(info) << "Getting the Git object for the tree at HEAD."; + if (logger) { + logger->info("Getting the Git object for the tree at HEAD."); + } git.Call(git_revparse_single(&git.GetData().object, git.GetData().repo, "HEAD")); - BOOST_LOG_TRIVIAL(trace) << "Generating hex string for Git object ID."; + if (logger) { + logger->trace("Generating hex string for Git object ID."); + } if (shortID) { git.Call(git_object_short_id(&git.GetData().buffer, git.GetData().object)); info.revision_id = git.GetData().buffer.ptr; @@ -70,7 +82,9 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho info.revision_id = git_oid_tostr(c_rev, GIT_OID_HEXSZ + 1, git_object_id(git.GetData().object)); } - BOOST_LOG_TRIVIAL(trace) << "Getting date for Git object."; + if (logger) { + logger->trace("Getting date for Git object."); + } const git_oid * oid = git_object_id(git.GetData().object); git.Call(git_commit_lookup(&git.GetData().commit, git.GetData().repo, oid)); git_time_t time = git_commit_time(git.GetData().commit); @@ -79,7 +93,9 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho out << std::put_time(std::gmtime(&time), "%Y-%m-%d"); info.revision_date = out.str(); - BOOST_LOG_TRIVIAL(trace) << "Diffing masterlist HEAD and working copy."; + if (logger) { + logger->trace("Diffing masterlist HEAD and working copy."); + } info.is_modified = GitHelper::IsFileDifferent(path.parent_path(), path.filename().string()); return info; @@ -91,13 +107,18 @@ bool Masterlist::IsLatest(const boost::filesystem::path& path, throw std::invalid_argument("Repository branch must not be empty."); GitHelper git; + auto logger = getLogger(); if (!git.IsRepository(path.parent_path())) { - BOOST_LOG_TRIVIAL(info) << "Cannot get latest masterlist revision: Git repository missing."; + if (logger) { + logger->info("Cannot get latest masterlist revision: Git repository missing."); + } throw GitStateError(string("Unknown: \"") + path.parent_path().string() + "\" is not a Git repository."); } - BOOST_LOG_TRIVIAL(info) << "Attempting to open repository."; + if (logger) { + logger->info("Attempting to open repository."); + } git.Call(git_repository_open(&git.GetData().repo, path.parent_path().string().c_str())); git.Fetch("origin"); @@ -115,6 +136,7 @@ bool Masterlist::IsLatest(const boost::filesystem::path& path, bool Masterlist::Update(const boost::filesystem::path& path, const std::string& repoUrl, const std::string& repoBranch) { GitHelper git; + auto logger = getLogger(); fs::path repoPath = path.parent_path(); string filename = path.filename().string(); @@ -122,7 +144,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& throw std::invalid_argument("Repository URL and branch must not be empty."); // Initialise checkout options. - BOOST_LOG_TRIVIAL(debug) << "Setting up checkout options."; + if (logger) { + logger->debug("Setting up checkout options."); + } char * paths = new char[filename.length() + 1]; strcpy(paths, filename.c_str()); git.GetData().checkout_options.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING; @@ -135,18 +159,24 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& git.GetData().clone_options.checkout_branch = repoBranch.c_str(); // Now try to access the repository if it exists, or clone one if it doesn't. - BOOST_LOG_TRIVIAL(trace) << "Attempting to open the Git repository at: " << repoPath; + if (logger) { + logger->trace("Attempting to open the Git repository at: {}", repoPath.string()); + } if (!git.IsRepository(repoPath)) git.Clone(repoPath, repoUrl); else { // Repository exists: check settings are correct, then pull updates. // Open the repository. - BOOST_LOG_TRIVIAL(info) << "Existing repository found, attempting to open it."; + if (logger) { + logger->info("Existing repository found, attempting to open it."); + } git.Call(git_repository_open(&git.GetData().repo, repoPath.string().c_str())); // Set the remote URL. - BOOST_LOG_TRIVIAL(info) << "Using remote URL: " << repoUrl; + if (logger) { + logger->info("Using remote URL: {}", repoUrl); + } git.Call(git_remote_set_url(git.GetData().repo, "origin", repoUrl.c_str())); // Now fetch updates from the remote. @@ -164,14 +194,18 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& // Check if HEAD points to the desired branch and set it to if not. if (!git_branch_is_head(git.GetData().reference)) { - BOOST_LOG_TRIVIAL(trace) << "Setting HEAD to follow branch: " << repoBranch; + if (logger) { + logger->trace("Setting HEAD to follow branch: {}", repoBranch); + } git.Call(git_repository_set_head(git.GetData().repo, (string("refs/heads/") + repoBranch).c_str())); } // Get remote branch reference. git.Call(git_branch_upstream(&git.GetData().reference2, git.GetData().reference)); - BOOST_LOG_TRIVIAL(trace) << "Checking HEAD and remote branch's mergeability."; + if (logger) { + logger->trace("Checking HEAD and remote branch's mergeability."); + } git_merge_analysis_t analysis; git_merge_preference_t pref; git.Call(git_annotated_commit_from_ref(&git.GetData().annotated_commit, git.GetData().repo, git.GetData().reference2)); @@ -179,9 +213,13 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& if ((analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) == 0 && (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) == 0) { // The local branch can't be easily merged. Best just to delete and recreate it. - BOOST_LOG_TRIVIAL(trace) << "Local branch cannot be easily merged with remote branch."; + if (logger) { + logger->trace("Local branch cannot be easily merged with remote branch."); + } - BOOST_LOG_TRIVIAL(trace) << "Detaching HEAD so that the branch can be recreated."; + if (logger) { + logger->trace("Detaching HEAD so that the branch can be recreated."); + } git.Call(git_repository_detach_head(git.GetData().repo)); // Need to free ref before calling git.CheckoutNewBranch() @@ -206,8 +244,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& // No merge is required, but HEAD might be ahead of the remote branch. Check // to see if that's the case, and move HEAD back to match the remote branch // if so. - BOOST_LOG_TRIVIAL(trace) << "Local branch is up-to-date with remote branch."; - BOOST_LOG_TRIVIAL(trace) << "Checking to see if local and remote branch heads are equal."; + if (logger) { + logger->trace("Local branch is up-to-date with remote branch. Checking to see if local and remote branch heads are equal."); + } // Get local branch commit ID. git.Call(git_reference_peel(&git.GetData().object, git.GetData().reference, GIT_OBJ_COMMIT)); @@ -223,21 +262,29 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& // action needs to be taken. Otherwise, a checkout // must be performed and the checked-out file parsed. if (!updateBranchHead) { - BOOST_LOG_TRIVIAL(trace) << "Local and remote branch heads are equal."; + if (logger) { + logger->trace("Local and remote branch heads are equal."); + } if (!GitHelper::IsFileDifferent(repoPath, filename)) { - BOOST_LOG_TRIVIAL(info) << "Local branch and masterlist file are already up to date."; + if (logger) { + logger->info("Local branch and masterlist file are already up to date."); + } return false; } - } else - BOOST_LOG_TRIVIAL(trace) << "Local branch heads is ahead of remote branch head."; - } else - BOOST_LOG_TRIVIAL(trace) << "Local branch can be fast-forwarded to remote branch."; + } else if (logger) { + logger->trace("Local branch heads is ahead of remote branch head."); + } + } else if (logger) { + logger->trace("Local branch can be fast-forwarded to remote branch."); + } if (updateBranchHead) { // The remote branch reference points to a particular // commit. Update the local branch reference to point // to the same commit. - BOOST_LOG_TRIVIAL(trace) << "Syncing local branch head with remote branch head."; + if (logger) { + logger->trace("Syncing local branch head with remote branch head."); + } git.Call(git_reference_set_target(&git.GetData().reference2, git.GetData().reference, remote_commit_id, "Setting branch reference.")); git_reference_free(git.GetData().reference2); @@ -247,7 +294,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& git_reference_free(git.GetData().reference); git.GetData().reference = nullptr; - BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD."; + if (logger) { + logger->trace("Performing a Git checkout of HEAD."); + } git.Call(git_checkout_head(git.GetData().repo, &git.GetData().checkout_options)); } } @@ -263,7 +312,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& string revision = git.GetHeadShortId(); //Now try parsing the masterlist. - BOOST_LOG_TRIVIAL(debug) << "Testing masterlist parsing."; + if (logger) { + logger->debug("Testing masterlist parsing."); + } try { this->Load(path); @@ -272,7 +323,9 @@ bool Masterlist::Update(const boost::filesystem::path& path, const std::string& parsingFailed = true; //There was an error, roll back one revision. - BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what(); + if (logger) { + logger->error("Masterlist parsing failed. Masterlist revision {}: {}", revision, e.what()); + } git.CheckoutRevision("HEAD^"); } } while (parsingFailed); diff --git a/src/api/metadata/condition_evaluator.cpp b/src/api/metadata/condition_evaluator.cpp index f9083ca6..60b33163 100644 --- a/src/api/metadata/condition_evaluator.cpp +++ b/src/api/metadata/condition_evaluator.cpp @@ -26,9 +26,9 @@ #include #include -#include #include "api/helpers/crc.h" +#include "api/helpers/logging.h" #include "api/metadata/condition_grammar.h" #include "loot/exception/condition_syntax_error.h" @@ -55,7 +55,10 @@ bool ConditionEvaluator::evaluate(const std::string& condition) const { if (condition.empty()) return true; - BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << condition; + auto logger = getLogger(); + if (logger) { + logger->trace("Evaluating condition: {}", condition); + } auto cachedValue = gameCache_->GetCachedCondition(condition); if (cachedValue.second) @@ -272,7 +275,10 @@ bool ConditionEvaluator::compareVersions(const std::string & filePath, const std Version givenVersion = Version(testVersion); Version trueVersion = getVersion(filePath); - BOOST_LOG_TRIVIAL(trace) << "Version extracted: " << trueVersion.AsString(); + auto logger = getLogger(); + if (logger) { + logger->trace("Version extracted: {}", trueVersion.AsString()); + } return ((comparator == "==" && trueVersion == givenVersion) || (comparator == "!=" && trueVersion != givenVersion) @@ -283,7 +289,10 @@ bool ConditionEvaluator::compareVersions(const std::string & filePath, const std } void ConditionEvaluator::validatePath(const boost::filesystem::path& path) { - BOOST_LOG_TRIVIAL(trace) << "Checking to see if the path \"" << path << "\" is safe."; + auto logger = getLogger(); + if (logger) { + logger->trace("Checking to see if the path \"{}\" is safe.", path.string()); + } boost::filesystem::path temp; for (const auto& component : path) { @@ -357,7 +366,10 @@ bool ConditionEvaluator::isRegexMatchInDataDirectory(const std::pairtrace("The path \"{}\" is not a game subdirectory.", pathRegex.first.string()); + } return false; } diff --git a/src/api/metadata/condition_grammar.h b/src/api/metadata/condition_grammar.h index 6ba556be..893b8b2d 100644 --- a/src/api/metadata/condition_grammar.h +++ b/src/api/metadata/condition_grammar.h @@ -36,10 +36,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -47,6 +45,7 @@ #include "loot/exception/condition_syntax_error.h" #include "api/game/game.h" +#include "api/helpers/logging.h" #include "api/helpers/version.h" #include "api/metadata/condition_evaluator.h" #include "api/plugin/plugin.h" @@ -127,6 +126,8 @@ public: qi::on_error(filePath_, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4)); qi::on_error(comparator_, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4)); qi::on_error(invalidPathChars_, phoenix::bind(&ConditionGrammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4)); + + logger_ = getLogger(); } private: @@ -139,7 +140,9 @@ private: //Eval's exact paths. Check for files and ghosted plugins. void CheckFile(bool& result, const std::string& file) const { - BOOST_LOG_TRIVIAL(trace) << "Checking to see if the file \"" << file << "\" exists."; + if (logger_) { + logger_->trace("Checking to see if the file \"{}\" exists.", file); + } result = false; if (IsRegex(file)) @@ -147,30 +150,40 @@ private: else result = evaluator_.fileExists(file); - BOOST_LOG_TRIVIAL(trace) << "File check result: " << result; + if (logger_) { + logger_->trace("File check result: {}", result); + } } void CheckMany(bool& result, const std::string& regexStr) const { - BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist."; + if (logger_) { + logger_->trace("Checking to see if more than one file matching the regex \"{}\" exists.", regexStr); + } result = false; result = evaluator_.regexMatchesExist(regexStr); } void CheckSum(bool& result, const std::string& file, const uint32_t checksum) { - BOOST_LOG_TRIVIAL(trace) << "Checking the CRC of the file \"" << file << "\"."; + if (logger_) { + logger_->trace("Checking the CRC of the file \"{}\".", file); + } result = false; result = evaluator_.checksumMatches(file, checksum); } void CheckVersion(bool& result, const std::string& file, const std::string& version, const std::string& comparator) const { - BOOST_LOG_TRIVIAL(trace) << "Checking version of file \"" << file << "\"."; + if (logger_) { + logger_->trace("Checking the version of the file \"{}\".", file); + } result = false; result = evaluator_.compareVersions(file, version, comparator); - BOOST_LOG_TRIVIAL(trace) << "Version check result: " << result; + if (logger_) { + logger_->trace("Version check result: {}", result); + } } void CheckActive(bool& result, const std::string& file) const { @@ -180,11 +193,15 @@ private: else result = evaluator_.isPluginActive(file); - BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result; + if (logger_) { + logger_->trace("Active check result: {}", result); + } } void CheckManyActive(bool& result, const std::string& regexStr) const { - BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist."; + if (logger_) { + logger_->trace("Checking to see if more than one file matching the regex \"{}\" is active.", regexStr); + } result = false; result = evaluator_.arePluginsActive(regexStr); @@ -203,6 +220,7 @@ private: boost::spirit::qi::rule invalidPathChars_; const ConditionEvaluator& evaluator_; + std::shared_ptr logger_; }; } #endif diff --git a/src/api/metadata/conditional_metadata.cpp b/src/api/metadata/conditional_metadata.cpp index 62d8db66..85737f86 100644 --- a/src/api/metadata/conditional_metadata.cpp +++ b/src/api/metadata/conditional_metadata.cpp @@ -24,10 +24,8 @@ #include "loot/metadata/conditional_metadata.h" -#include -#include - #include "api/game/game.h" +#include "api/helpers/logging.h" #include "api/metadata/condition_evaluator.h" using std::string; @@ -46,7 +44,12 @@ std::string ConditionalMetadata::GetCondition() const { } void ConditionalMetadata::ParseCondition() const { - BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << condition_; - ConditionEvaluator().evaluate(condition_); + if (!condition_.empty()) { + auto logger = getLogger(); + if (logger) { + logger->trace("Testing condition syntax: {}", condition_); + } + ConditionEvaluator().evaluate(condition_); + } } } diff --git a/src/api/metadata/message.cpp b/src/api/metadata/message.cpp index 762e47dc..0f2ec943 100644 --- a/src/api/metadata/message.cpp +++ b/src/api/metadata/message.cpp @@ -25,7 +25,6 @@ #include "loot/metadata/message.h" #include -#include #include "api/game/game.h" diff --git a/src/api/metadata/plugin_cleaning_data.cpp b/src/api/metadata/plugin_cleaning_data.cpp index e279aa06..1405d18b 100644 --- a/src/api/metadata/plugin_cleaning_data.cpp +++ b/src/api/metadata/plugin_cleaning_data.cpp @@ -26,10 +26,10 @@ #include #include -#include #include "api/game/game.h" #include "api/helpers/crc.h" +#include "api/helpers/logging.h" namespace loot { PluginCleaningData::PluginCleaningData() : crc_(0), itm_(0), ref_(0), nav_(0) {} @@ -78,7 +78,10 @@ std::vector PluginCleaningData::GetInfo() const { } MessageContent PluginCleaningData::ChooseInfo(const std::string& language) const { - BOOST_LOG_TRIVIAL(trace) << "Choosing dirty info content."; + auto logger = getLogger(); + if (logger) { + logger->trace("Choosing dirty info content."); + } return MessageContent::Choose(info_, language); } } diff --git a/src/api/metadata/plugin_metadata.cpp b/src/api/metadata/plugin_metadata.cpp index 6ddd90f8..644e5dec 100644 --- a/src/api/metadata/plugin_metadata.cpp +++ b/src/api/metadata/plugin_metadata.cpp @@ -30,9 +30,9 @@ #include #include #include -#include #include "api/game/game.h" +#include "api/helpers/logging.h" using std::inserter; using std::regex; @@ -50,7 +50,11 @@ PluginMetadata::PluginMetadata(const std::string& n) : name_(n), enabled_(true) } void PluginMetadata::MergeMetadata(const PluginMetadata& plugin) { - BOOST_LOG_TRIVIAL(trace) << "Merging metadata for: " << name_; + auto logger = getLogger(); + if (logger) { + logger->trace("Merging metadata for: {}", name_); + } + if (plugin.HasNameOnly()) return; @@ -92,7 +96,11 @@ void PluginMetadata::MergeMetadata(const PluginMetadata& plugin) { PluginMetadata PluginMetadata::NewMetadata(const PluginMetadata& plugin) const { using std::set_difference; - BOOST_LOG_TRIVIAL(trace) << "Comparing new metadata for: " << name_; + auto logger = getLogger(); + if (logger) { + logger->trace("Comparing new metadata for: {}", name_); + } + PluginMetadata p(*this); //Compare this plugin against the given plugin. diff --git a/src/api/metadata_list.cpp b/src/api/metadata_list.cpp index b1168b8f..322213b5 100644 --- a/src/api/metadata_list.cpp +++ b/src/api/metadata_list.cpp @@ -26,18 +26,21 @@ #include #include -#include #include "loot/exception/file_access_error.h" -#include "api/metadata/yaml/plugin_metadata.h" #include "api/game/game.h" +#include "api/helpers/logging.h" #include "api/metadata/condition_evaluator.h" +#include "api/metadata/yaml/plugin_metadata.h" namespace loot { void MetadataList::Load(const boost::filesystem::path& filepath) { Clear(); - BOOST_LOG_TRIVIAL(debug) << "Loading file: " << filepath; + auto logger = getLogger(); + if (logger) { + logger->debug("Loading file: {}", filepath.string()); + } boost::filesystem::ifstream in(filepath); if (!in.good()) @@ -64,11 +67,16 @@ void MetadataList::Load(const boost::filesystem::path& filepath) { if (metadataList["bash_tags"]) bashTags_ = metadataList["bash_tags"].as>(); - BOOST_LOG_TRIVIAL(debug) << "File loaded successfully."; + if (logger) { + logger->debug("File loaded successfully."); + } } void MetadataList::Save(const boost::filesystem::path& filepath) const { - BOOST_LOG_TRIVIAL(trace) << "Saving metadata list to: " << filepath; + auto logger = getLogger(); + if (logger) { + logger->trace("Saving metadata list to: {}", filepath.string()); + } YAML::Emitter emitter; emitter.SetIndent(2); emitter << YAML::BeginMap; diff --git a/src/api/plugin/plugin.cpp b/src/api/plugin/plugin.cpp index 34f15f93..01817f86 100644 --- a/src/api/plugin/plugin.cpp +++ b/src/api/plugin/plugin.cpp @@ -28,12 +28,12 @@ #include #include -#include #include #include #include "api/game/game.h" #include "api/helpers/crc.h" +#include "api/helpers/logging.h" #include "api/helpers/version.h" #include "loot/exception/file_access_error.h" @@ -53,6 +53,8 @@ Plugin::Plugin(const GameType gameType, loadsArchive_(false), crc_(0), numOverrideRecords_(0) { + auto logger = getLogger(); + try { boost::filesystem::path filepath = dataPath / name_; @@ -68,10 +70,14 @@ Plugin::Plugin(const GameType gameType, } if (!headerOnly) { - BOOST_LOG_TRIVIAL(trace) << name_ << ": Caching CRC value."; + if (logger) { + logger->trace("{}: Caching CRC value.", name_); + } crc_ = GetCrc32(filepath); - BOOST_LOG_TRIVIAL(trace) << name_ << ": Counting override FormIDs."; + if (logger) { + logger->trace("{}: Counting override FormIDs.", name_); + } ret = esp_plugin_count_override_records(esPlugin.get(), &numOverrideRecords_); if (ret != ESP_OK) { throw FileAccessError(name + " : Libespm error code: " + std::to_string(ret)); @@ -79,8 +85,11 @@ Plugin::Plugin(const GameType gameType, } //Also read Bash Tags applied and version string in description. + if (logger) { + logger->trace("{}: Attempting to extract Bash Tags from the description.", name_); + } + string text = GetDescription(); - BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Attempting to extract Bash Tags from the description."; size_t pos1 = text.find("{{BASH:"); if (pos1 != string::npos && pos1 + 7 != text.length()) { pos1 += 7; @@ -94,8 +103,11 @@ Plugin::Plugin(const GameType gameType, for (auto &tag : bashTags) { boost::trim(tag); - BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Extracted Bash Tag: " << tag; tags_.insert(Tag(tag)); + + if (logger) { + logger->trace("{}: Extracted Bash Tag: {}", name_, tag); + } } } } @@ -104,11 +116,15 @@ Plugin::Plugin(const GameType gameType, loadsArchive_ = LoadsArchive(name_, gameType, dataPath); } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Cannot read plugin file \"" << name << "\". Details: " << e.what(); + if (logger) { + logger->error("Cannot read plugin file \"{}\". Details: {}", name_, e.what()); + } throw FileAccessError((boost::format("Cannot read \"%1%\". Details: %2%") % name % e.what()).str()); } - BOOST_LOG_TRIVIAL(trace) << name_ << ": " << "Plugin loading complete."; + if (logger) { + logger->trace("{}: Plugin loading complete.", name_); + } } std::string Plugin::GetName() const { @@ -185,7 +201,10 @@ bool Plugin::DoFormIDsOverlap(const PluginInterface& plugin) const { return doPluginsOverlap; } catch (std::bad_cast&) { - BOOST_LOG_TRIVIAL(error) << "Tried to check if FormIDs overlapped with a non-Plugin implementation of PluginInterface."; + auto logger = getLogger(); + if (logger) { + logger->error("Tried to check if FormIDs overlapped with a non-Plugin implementation of PluginInterface."); + } } return false; @@ -196,7 +215,10 @@ size_t Plugin::NumOverrideFormIDs() const { } bool Plugin::IsValid(const std::string& filename, const GameType gameType, const boost::filesystem::path& dataPath) { - BOOST_LOG_TRIVIAL(trace) << "Checking to see if \"" << filename << "\" is a valid plugin."; + auto logger = getLogger(); + if (logger) { + logger->trace("Checking to see if \"{}\" is a valid plugin.", filename); + } //If the filename passed ends in '.ghost', that should be trimmed. std::string name; @@ -214,7 +236,9 @@ bool Plugin::IsValid(const std::string& filename, const GameType gameType, const int ret = esp_plugin_is_valid(GetEspluginGameId(gameType), path.string().c_str(), true, &isValid); if (ret != ESP_OK || !isValid) { - BOOST_LOG_TRIVIAL(warning) << "The .es(p|m) file \"" << filename << "\" is not a valid plugin."; + if (logger) { + logger->warn("The file \"{}\" is not a valid plugin.", filename); + } } return (ret == ESP_OK && isValid) @@ -314,7 +338,7 @@ unsigned int Plugin::GetEspluginGameId(GameType gameType) { bool hasPluginFileExtension(const std::string& filename, GameType gameType) { bool espOrEsm = boost::iends_with(filename, ".esp") || boost::iends_with(filename, ".esm"); - bool lightMaster = (gameType == GameType::fo4 || gameType == GameType::tes5se) + bool lightMaster = (gameType == GameType::fo4 || gameType == GameType::tes5se) && boost::iends_with(filename, ".esl"); return espOrEsm || lightMaster; diff --git a/src/api/plugin/plugin_sorter.cpp b/src/api/plugin/plugin_sorter.cpp index 96ffdeec..b05338d8 100644 --- a/src/api/plugin/plugin_sorter.cpp +++ b/src/api/plugin/plugin_sorter.cpp @@ -32,10 +32,10 @@ #include #include #include -#include #include "loot/exception/cyclic_interaction_error.h" #include "api/game/game.h" +#include "api/helpers/logging.h" #include "api/metadata/condition_evaluator.h" using std::list; @@ -127,6 +127,8 @@ private: }; std::vector PluginSorter::Sort(Game& game) { + logger_ = getLogger(); + // Clear existing data. graph_.clear(); indexMap_.clear(); @@ -139,57 +141,77 @@ std::vector PluginSorter::Sort(Game& game) { if (boost::num_vertices(graph_) == 0) return vector(); -// Get the existing load order. + // Get the existing load order. oldLoadOrder_ = game.GetLoadOrder(); - BOOST_LOG_TRIVIAL(info) << "Fetched existing load order: "; - for (const auto &plugin : oldLoadOrder_) - BOOST_LOG_TRIVIAL(info) << plugin; + if (logger_) { + logger_->info("Fetched existing load order: "); + for (const auto &plugin : oldLoadOrder_) { + logger_->info("\t\t{}", plugin); + } + } -//Now add the interactions between plugins to the graph as edges. - BOOST_LOG_TRIVIAL(info) << "Adding edges to plugin graph."; - BOOST_LOG_TRIVIAL(debug) << "Adding non-overlap edges."; + //Now add the interactions between plugins to the graph as edges. + if (logger_) { + logger_->info("Adding edges to plugin graph."); + logger_->debug("Adding non-overlap edges."); + } AddSpecificEdges(); PropagatePriorities(); - BOOST_LOG_TRIVIAL(debug) << "Adding priority edges."; + if (logger_) { + logger_->debug("Adding priority edges."); + } AddPriorityEdges(); - BOOST_LOG_TRIVIAL(debug) << "Adding overlap edges."; + if (logger_) { + logger_->debug("Adding overlap edges."); + } AddOverlapEdges(); - BOOST_LOG_TRIVIAL(debug) << "Adding tie-break edges."; + if (logger_) { + logger_->debug("Adding tie-break edges."); + } AddTieBreakEdges(); - BOOST_LOG_TRIVIAL(info) << "Checking to see if the graph is cyclic."; + if (logger_) { + logger_->debug("Checking to see if the graph is cyclic."); + } CheckForCycles(); //Now we can sort. - BOOST_LOG_TRIVIAL(info) << "Performing a topological sort."; + if (logger_) { + logger_->debug("Performing a topological sort."); + } list sortedVertices; boost::topological_sort(graph_, std::front_inserter(sortedVertices), boost::vertex_index_map(vertexIndexMap_)); // Check that the sorted path is Hamiltonian (ie. unique). for (auto it = sortedVertices.begin(); it != sortedVertices.end(); ++it) { - if (next(it) != sortedVertices.end() && !boost::edge(*it, *next(it), graph_).second) { - BOOST_LOG_TRIVIAL(error) << "The calculated load order is not unique. No edge exists between" - << graph_[*it].GetName() << " and " << graph_[*next(it)].GetName() << "."; + if (next(it) != sortedVertices.end() && !boost::edge(*it, *next(it), graph_).second && logger_) { + logger_->error("The calculated load order is not unique. No edge exists between {} and {}.", graph_[*it].GetName(), graph_[*next(it)].GetName()); } } // Output a plugin list using the sorted vertices. - BOOST_LOG_TRIVIAL(info) << "Calculated order: "; + if (logger_) { + logger_->info("Calculated order: "); + } vector plugins; for (const auto &vertex : sortedVertices) { plugins.push_back(graph_[vertex].GetName()); - BOOST_LOG_TRIVIAL(info) << '\t' << plugins.back(); + if (logger_) { + logger_->info("\t{}", plugins.back()); + } } return plugins; } void PluginSorter::AddPluginVertices(Game& game) { - BOOST_LOG_TRIVIAL(info) << "Merging masterlist, userlist into plugin list, evaluating conditions and checking for install validity."; + if (logger_) { + logger_->info("Merging masterlist, userlist into plugin list, evaluating conditions and checking for install validity."); + } // The resolution of tie-breaks in the plugin graph may be dependent // on the order in which vertices are iterated over, as an earlier tie @@ -213,11 +235,15 @@ void PluginSorter::AddPluginVertices(Game& game) { // in the unordered map, as it's probably faster than copying the // full plugin objects then sorting them. for (const auto &plugin : game.GetCache()->GetPlugins()) { - BOOST_LOG_TRIVIAL(trace) << "Getting and evaluating metadata for plugin " << plugin->GetName(); + if (logger_) { + logger_->trace("Getting and evaluating metadata for plugin {}", plugin->GetName()); + } auto metadata = game.GetDatabase()->GetPluginMetadata(plugin->GetName(), true, true); - BOOST_LOG_TRIVIAL(trace) << "Adding vertex for plugin \"" << plugin->GetName() << "\""; + if (logger_) { + logger_->trace("Getting and evaluating metadata for plugin \"{}\"", plugin->GetName()); + } vertex_t v = boost::add_vertex(PluginSortingData(*plugin, std::move(metadata)), graph_); } @@ -288,31 +314,38 @@ void PluginSorter::PropagatePriorities() { // search, setting priorities until an equal or larger value is // encountered. for (const vertex_t& vertex : positivePriorityVertices) { - BOOST_LOG_TRIVIAL(trace) << "Doing DFS for " << graph_[vertex].GetName() - << " which has local priority " << graph_[vertex].GetLocalPriority().GetValue() - << " and global priority " << graph_[vertex].GetGlobalPriority().GetValue(); + if (logger_) { + logger_->trace("Doing DFS for {} which has local priority {} and global priority {}", + graph_[vertex].GetName(), + graph_[vertex].GetLocalPriority().GetValue(), + graph_[vertex].GetGlobalPriority().GetValue()); + } boost::dfs_visitor<> visitor; boost::depth_first_visit(graph_, vertex, visitor, colorMap, - [&vertex](const vertex_t& currentVertex, const PluginGraph& graph) { + [&](const vertex_t& currentVertex, const PluginGraph& graph) { // depth_first_search takes a const graph, so cast it if modifying a vertex. if (graph[currentVertex].GetLocalPriority() < graph[vertex].GetLocalPriority()) { - BOOST_LOG_TRIVIAL(trace) << "Overriding local priority for " - << graph[currentVertex].GetName() - << " from " << graph[currentVertex].GetLocalPriority().GetValue() - << " to " << graph[vertex].GetLocalPriority().GetValue(); + if (logger_) { + logger_->trace("Overriding local priority for {} from {} to {}", + graph[currentVertex].GetName(), + graph[currentVertex].GetLocalPriority().GetValue(), + graph[vertex].GetLocalPriority().GetValue()); + } const_cast(graph)[currentVertex].SetLocalPriority(graph[vertex].GetLocalPriority()); return false; } if (graph[currentVertex].GetGlobalPriority() < graph[vertex].GetGlobalPriority()) { - BOOST_LOG_TRIVIAL(trace) << "Overriding global priority for " - << graph[currentVertex].GetName() - << " from " << graph[currentVertex].GetGlobalPriority().GetValue() - << " to " << graph[vertex].GetGlobalPriority().GetValue(); + if (logger_) { + logger_->trace("Overriding global priority for {} from {} to {}", + graph[currentVertex].GetName(), + graph[currentVertex].GetGlobalPriority().GetValue(), + graph[vertex].GetGlobalPriority().GetValue()); + } const_cast(graph)[currentVertex].SetGlobalPriority(graph[vertex].GetGlobalPriority()); return false; @@ -327,7 +360,11 @@ void PluginSorter::PropagatePriorities() { void PluginSorter::AddEdge(const vertex_t& fromVertex, const vertex_t& toVertex) { if (!boost::edge(fromVertex, toVertex, graph_).second) { - BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph_[fromVertex].GetName() << "\" to \"" << graph_[toVertex].GetName() << "\"."; + if (logger_) { + logger_->trace("Adding edge from \"{}\" to \"{}\".", + graph_[fromVertex].GetName(), + graph_[toVertex].GetName()); + } boost::add_edge(fromVertex, toVertex, graph_); } @@ -337,9 +374,12 @@ void PluginSorter::AddSpecificEdges() { //Add edges for all relationships that aren't overlaps or priority differences. vertex_it vit, vitend; for (tie(vit, vitend) = boost::vertices(graph_); vit != vitend; ++vit) { - BOOST_LOG_TRIVIAL(trace) << "Adding specific edges to vertex for \"" << graph_[*vit].GetName() << "\"."; + if (logger_) { + logger_->trace("Adding specific edges to vertex for \"{}\".", + graph_[*vit].GetName()); + logger_->trace("Adding edges for master flag differences."); + } - BOOST_LOG_TRIVIAL(trace) << "Adding edges for master flag differences."; for (vertex_it vit2 = vit; vit2 != vitend; ++vit2) { if (graph_[*vit].IsMaster() == graph_[*vit2].IsMaster()) continue; @@ -357,19 +397,25 @@ void PluginSorter::AddSpecificEdges() { } vertex_t parentVertex; - BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for masters."; + if (logger_) { + logger_->trace("Adding in-edges for masters."); + } for (const auto &master : graph_[*vit].GetMasters()) { if (GetVertexByName(master, parentVertex)) AddEdge(parentVertex, *vit); } - BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for requirements."; + if (logger_) { + logger_->trace("Adding in-edges for requirements."); + } for (const auto &file : graph_[*vit].GetRequirements()) { if (GetVertexByName(file.GetName(), parentVertex)) AddEdge(parentVertex, *vit); } - BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for 'load after's."; + if (logger_) { + logger_->trace("Adding in-edges for 'load after's."); + } for (const auto &file : graph_[*vit].GetLoadAfterFiles()) { if (GetVertexByName(file.GetName(), parentVertex)) AddEdge(parentVertex, *vit); @@ -379,7 +425,10 @@ void PluginSorter::AddSpecificEdges() { void PluginSorter::AddPriorityEdges() { for (const auto& vertex : boost::make_iterator_range(boost::vertices(graph_))) { - BOOST_LOG_TRIVIAL(trace) << "Adding priority difference edges to vertex for \"" << graph_[vertex].GetName() << "\"."; + if (logger_) { + logger_->trace("Adding priority difference edges to vertex for \"{}\".", + graph_[vertex].GetName()); + } // If the plugin has a global priority of zero and doesn't load // an archive and has no override records, skip it. Plugins without // override records can only conflict with plugins that override @@ -420,10 +469,16 @@ void PluginSorter::AddPriorityEdges() { void PluginSorter::AddOverlapEdges() { for (const auto& vertex : boost::make_iterator_range(boost::vertices(graph_))) { - BOOST_LOG_TRIVIAL(trace) << "Adding overlap edges to vertex for \"" << graph_[vertex].GetName() << "\"."; + if (logger_) { + logger_->trace("Adding overlap edges to vertex for \"{}\".", + graph_[vertex].GetName()); + } if (graph_[vertex].NumOverrideFormIDs() == 0) { - BOOST_LOG_TRIVIAL(trace) << "Skipping vertex for \"" << graph_[vertex].GetName() << "\": the plugin contains no override records."; + if (logger_) { + logger_->trace("Skipping vertex for \"{}\": the plugin contains no override records.", + graph_[vertex].GetName()); + } continue; } @@ -495,7 +550,9 @@ void PluginSorter::AddTieBreakEdges() { // This can be enforced by adding edges between all vertices that aren't already linked. // Use existing load order to decide the direction of these edges. for (const auto& vertex : boost::make_iterator_range(boost::vertices(graph_))) { - BOOST_LOG_TRIVIAL(trace) << "Adding tie-break edges to vertex for \"" << graph_[vertex].GetName() << "\"."; + if (logger_) { + logger_->trace("Adding tie-break edges to vertex for \"{}\"", graph_[vertex].GetName()); + } for (const auto& otherVertex : boost::make_iterator_range(boost::vertices(graph_))) { if (vertex == otherVertex || boost::edge(vertex, otherVertex, graph_).second || boost::edge(otherVertex, vertex, graph_).second) diff --git a/src/api/plugin/plugin_sorter.h b/src/api/plugin/plugin_sorter.h index feba931a..2f5337b6 100644 --- a/src/api/plugin/plugin_sorter.h +++ b/src/api/plugin/plugin_sorter.h @@ -29,6 +29,7 @@ #include #include +#include #include "api/game/game.h" #include "api/plugin/plugin.h" @@ -83,6 +84,7 @@ private: std::map indexMap_; vertex_map_t vertexIndexMap_; std::vector oldLoadOrder_; + std::shared_ptr logger_; }; } diff --git a/src/tests/api/interface/main.cpp b/src/tests/api/interface/main.cpp index 4657b68a..9e3647b7 100644 --- a/src/tests/api/interface/main.cpp +++ b/src/tests/api/interface/main.cpp @@ -30,7 +30,6 @@ #include "tests/api/interface/game_interface_test.h" #include "tests/api/interface/is_compatible_test.h" -#include #include int main(int argc, char **argv) { @@ -39,9 +38,6 @@ int main(int argc, char **argv) { boost::filesystem::path::imbue(std::locale()); loot::InitialiseLocale(""); - //Set a null log callback or else stdout will get overrun. - loot::SetLoggingCallback([](loot::LogLevel, const char *) {}); - ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } @@ -58,7 +54,7 @@ TEST(SetLoggingCallback, shouldWriteMessagesToGivenCallback) { CreateGameHandle(GameType::tes4, "", ""); } catch (...) { - EXPECT_EQ("Initialising load order data for game of type 0 at: \"\"", loggedMessages); + EXPECT_EQ("Initialising load order data for game of type 0 at: ", loggedMessages); SetLoggingCallback([](LogLevel, const char *) {}); return; diff --git a/src/tests/api/internals/main.cpp b/src/tests/api/internals/main.cpp index c063a300..e3093491 100644 --- a/src/tests/api/internals/main.cpp +++ b/src/tests/api/internals/main.cpp @@ -22,8 +22,6 @@ . */ -#include - #include "tests/api/internals/game/game_test.h" #include "tests/api/internals/game/game_cache_test.h" #include "tests/api/internals/game/load_order_handler_test.h" @@ -70,9 +68,6 @@ int main(int argc, char **argv) { std::locale::global(boost::locale::generator().generate("")); boost::filesystem::path::imbue(std::locale()); - //Disable logging or else stdout will get overrun. - boost::log::core::get()->set_logging_enabled(false); - ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }