Replace logging API

Allow clients to provide a callback function that the LOOT API will use
when logging. This provides more flexibility than the previous choice of
outputting to an exclusive file or the console.
This commit is contained in:
Oliver Hamlet
2017-10-15 14:04:11 +01:00
parent 5b0c1d1448
commit 65a8509d8d
5 changed files with 91 additions and 51 deletions
+1 -1
View File
@@ -187,7 +187,7 @@ set (LOOT_API_HEADERS "${CMAKE_SOURCE_DIR}/include/loot/api.h"
"${CMAKE_SOURCE_DIR}/include/loot/exception/file_access_error.h"
"${CMAKE_SOURCE_DIR}/include/loot/exception/git_state_error.h"
"${CMAKE_SOURCE_DIR}/include/loot/enum/game_type.h"
"${CMAKE_SOURCE_DIR}/include/loot/enum/log_verbosity.h"
"${CMAKE_SOURCE_DIR}/include/loot/enum/log_level.h"
"${CMAKE_SOURCE_DIR}/include/loot/enum/message_type.h"
"${CMAKE_SOURCE_DIR}/include/loot/game_interface.h"
"${CMAKE_SOURCE_DIR}/include/loot/loot_version.h"
+9 -15
View File
@@ -25,6 +25,7 @@
#ifndef LOOT_API_H
#define LOOT_API_H
#include <functional>
#include <string>
#include <memory>
@@ -36,7 +37,7 @@
#include "loot/exception/file_access_error.h"
#include "loot/exception/git_state_error.h"
#include "loot/enum/game_type.h"
#include "loot/enum/log_verbosity.h"
#include "loot/enum/log_level.h"
#include "loot/loot_version.h"
namespace loot {
@@ -47,21 +48,14 @@ namespace loot {
/**@{*/
/**
* @brief Set the API's logging verbosity.
* @details The default is ``LogVerbosity::off``.
* @param verbosity
* The logging verbosity to set.
* @brief Set the callback function that is called when logging.
* @details If this function is not called, the default behaviour is to
* print messages to the console.
* @param callback
* The function called when logging. The first parameter is the
* level of the message being logged, and the second is the message.
*/
LOOT_API void SetLoggingVerbosity(LogVerbosity verbosity);
/**
* @brief Set the file path that logging statements are written to.
* @details If no file is set the default behaviour is to print logging
* statements to the console.
* @param path
* The log file path.
*/
LOOT_API void SetLogFile(const std::string& path);
LOOT_API void SetLoggingCallback(std::function<void(LogLevel, const char*)> callback);
/**@}*/
/**********************************************************************//**
@@ -22,20 +22,23 @@ along with LOOT. If not, see
<https://www.gnu.org/licenses/>.
*/
#ifndef LOOT_LOG_VERBOSITY
#define LOOT_LOG_VERBOSITY
#ifndef LOOT_LOG_LEVEL
#define LOOT_LOG_LEVEL
/**
* The namespace used by the LOOT API.
*/
namespace loot {
/**
* @brief Codes used to specify different levels of API logging verbosity.
* @brief Codes used to specify different levels of API logging.
*/
enum struct LogVerbosity : unsigned int {
off,
warning,
enum struct LogLevel : unsigned int {
trace,
debug,
info,
warning,
error,
fatal
};
}
+45 -25
View File
@@ -46,34 +46,54 @@ std::string ResolvePath(const std::string& path) {
return fs::read_symlink(path).string();
}
LOOT_API void SetLoggingVerbosity(LogVerbosity verbosity) {
switch (verbosity) {
case LogVerbosity::off:
boost::log::core::get()->set_logging_enabled(false);
break;
case LogVerbosity::warning:
boost::log::core::get()->set_filter(boost::log::trivial::severity > boost::log::trivial::warning);
boost::log::core::get()->set_logging_enabled(true);
break;
case LogVerbosity::trace:
boost::log::core::get()->reset_filter();
boost::log::core::get()->set_logging_enabled(true);
break;
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;
}
}
LOOT_API void SetLogFile(const std::string& path) {
boost::log::add_file_log(
boost::log::keywords::file_name = path,
boost::log::keywords::auto_flush = true,
boost::log::keywords::format = (
boost::log::expressions::stream
<< "[" << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%H:%M:%S.%f") << "]"
<< " [" << boost::log::trivial::severity << "]: "
<< boost::log::expressions::smessage
)
);
boost::log::add_common_attributes();
class LoggingSink : public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::concurrent_feeding> {
public:
LoggingSink(std::function<void(LogLevel, const char*)> 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<severity_level>();
if (!severity) {
return;
}
callback(mapFromBoostLog(*severity), str.c_str());
}
private:
std::function<void(LogLevel, const char*)> callback;
};
LOOT_API void SetLoggingCallback(std::function<void(LogLevel, const char*)> callback) {
typedef boost::log::sinks::synchronous_sink<LoggingSink> sink_t;
auto sink_backend = boost::make_shared<LoggingSink>(callback);
boost::shared_ptr<sink_t> sink(new sink_t(sink_backend));
boost::log::core::get()->remove_all_sinks();
boost::log::core::get()->add_sink(sink);
}
LOOT_API bool IsCompatible(const unsigned int versionMajor, const unsigned int versionMinor, const unsigned int versionPatch) {
+27 -4
View File
@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
#include "loot/api.h"
#include "tests/api/interface/create_game_handle_test.h"
#include "tests/api/interface/database_interface_test.h"
#include "tests/api/interface/game_interface_test.h"
@@ -33,15 +34,37 @@
#include <boost/locale.hpp>
int main(int argc, char **argv) {
//Set the locale to get encoding conversions working correctly.
//Set the locale to get encoding conversions working correctly.
std::locale::global(boost::locale::generator().generate(""));
boost::filesystem::path::imbue(std::locale());
loot::InitialiseLocale("");
//Disable logging or else stdout will get overrun.
boost::log::core::get()->set_logging_enabled(false);
loot::SetLoggingVerbosity(loot::LogVerbosity::off);
//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();
}
namespace loot {
namespace test {
TEST(SetLoggingCallback, shouldWriteMessagesToGivenCallback) {
std::string loggedMessages;
SetLoggingCallback([&](LogLevel level, const char * string) {
loggedMessages += std::string(string);
});
try {
CreateGameHandle(GameType::tes4, "", "");
}
catch (...) {
EXPECT_EQ("Initialising load order data for game of type 0 at: \"\"", loggedMessages);
SetLoggingCallback([](LogLevel, const char *) {});
return;
}
FAIL();
}
}
}