diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 464253b97..0adf596f3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -198,6 +198,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/LayerRegionFill.cpp ${LIBDIR}/libslic3r/LayerHeightSpline.cpp ${LIBDIR}/libslic3r/Line.cpp + ${LIBDIR}/libslic3r/Log.cpp ${LIBDIR}/libslic3r/Model.cpp ${LIBDIR}/libslic3r/MotionPlanner.cpp ${LIBDIR}/libslic3r/MultiPoint.cpp @@ -284,18 +285,19 @@ set(UI_TEST_SOURCES set(SLIC3R_TEST_SOURCES ${TESTDIR}/test_harness.cpp ${TESTDIR}/test_data.cpp - ${TESTDIR}/libslic3r/test_trianglemesh.cpp ${TESTDIR}/libslic3r/test_config.cpp - ${TESTDIR}/libslic3r/test_support_material.cpp ${TESTDIR}/libslic3r/test_fill.cpp ${TESTDIR}/libslic3r/test_flow.cpp - ${TESTDIR}/libslic3r/test_model.cpp - ${TESTDIR}/libslic3r/test_printgcode.cpp - ${TESTDIR}/libslic3r/test_print.cpp - ${TESTDIR}/libslic3r/test_skirt_brim.cpp - ${TESTDIR}/libslic3r/test_test_data.cpp - ${TESTDIR}/libslic3r/test_geometry.cpp ${TESTDIR}/libslic3r/test_gcodewriter.cpp + ${TESTDIR}/libslic3r/test_geometry.cpp + ${TESTDIR}/libslic3r/test_log.cpp + ${TESTDIR}/libslic3r/test_model.cpp + ${TESTDIR}/libslic3r/test_print.cpp + ${TESTDIR}/libslic3r/test_printgcode.cpp + ${TESTDIR}/libslic3r/test_skirt_brim.cpp + ${TESTDIR}/libslic3r/test_support_material.cpp + ${TESTDIR}/libslic3r/test_test_data.cpp + ${TESTDIR}/libslic3r/test_trianglemesh.cpp ) diff --git a/src/test/libslic3r/test_log.cpp b/src/test/libslic3r/test_log.cpp new file mode 100644 index 000000000..83764059e --- /dev/null +++ b/src/test/libslic3r/test_log.cpp @@ -0,0 +1,119 @@ +#include +#include +#include "Log.hpp" + +using namespace std::literals::string_literals; +using namespace Slic3r; + +SCENARIO( "_Log output with std::string methods" ) { + GIVEN("A log stream and a _Log object") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic", "This"); + THEN("Output string is Topic FERR: This\\n") { + REQUIRE(log.str() == "Topic FERR: This\n"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic", "This"); + THEN("Output string is Topic ERR: This\\n") { + REQUIRE(log.str() == "Topic ERR: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic", "This"); + THEN("Output string is Topic WARN: This\\n") { + REQUIRE(log.str() == "Topic WARN: This\n"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic", "This"); + THEN("Output string is Topic INFO: This\\n") { + REQUIRE(log.str() == "Topic INFO: This\n"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic", "This"); + THEN("Output string is Topic DEBUG: This\\n") { + REQUIRE(log.str() == "Topic DEBUG: This\n"); + } + } + WHEN("msg is called with text \"This\"") { + log.clear(); + cut->raw("This"); + THEN("Output string is This\\n") { + REQUIRE(log.str() == "This\n"); + } + } + } +} +SCENARIO( "_Log output with << methods" ) { + GIVEN("A log stream and a _Log object") { + std::stringstream log; + std::unique_ptr<_Log> cut { _Log::make_log(log) }; + WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->fatal_error("Topic") << "This"; + THEN("Output string is Topic FERR: This") { + REQUIRE(log.str() == "Topic FERR: This"); + } + } + WHEN("error is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->error("Topic") << "This"; + THEN("Output string is Topic ERR: This") { + REQUIRE(log.str() == "Topic ERR: This"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic") << "This"; + THEN("Output string is Topic INFO: This") { + REQUIRE(log.str() == "Topic INFO: This"); + } + } + WHEN("warn is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->warn("Topic") << "This"; + THEN("Output string is Topic WARN: This") { + REQUIRE(log.str() == "Topic WARN: This"); + } + } + WHEN("info is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->info("Topic") << "This"; + THEN("Output string is Topic INFO: This") { + REQUIRE(log.str() == "Topic INFO: This"); + } + } + WHEN("debug is called with topic \"Topic\" and text \"This\"") { + log.clear(); + cut->debug("Topic") << "This"; + THEN("Output string is Topic DEBUG: This") { + REQUIRE(log.str() == "Topic DEBUG: This"); + } + } + WHEN("msg is called with text \"This\"") { + log.clear(); + cut->raw() << "This"; + THEN("Output string is This") { + REQUIRE(log.str() == "This"); + } + } + + } +} + diff --git a/xs/src/libslic3r/Log.cpp b/xs/src/libslic3r/Log.cpp new file mode 100644 index 000000000..f9d763447 --- /dev/null +++ b/xs/src/libslic3r/Log.cpp @@ -0,0 +1,76 @@ +#include "Log.hpp" +#include +#include +#include +#include + +namespace Slic3r { + +std::unique_ptr<_Log> slic3r_log {_Log::make_log()}; + +_Log::_Log() : _out(std::clog), _wout(std::wclog) { +} + +_Log::_Log(std::ostream& out) : _out(out), _wout(std::wclog) { +} +_Log::_Log(std::wostream& out) : _out(std::clog), _wout(out) { +} + +void _Log::fatal_error(const std::string& topic, const std::wstring& message) { +// _wout << this->converter.from_bytes(topic); + _wout << std::setw(6) << "FERR" << ": "; + _wout << message << std::endl; +} +void _Log::fatal_error(const std::string& topic, const std::string& message) { + this->fatal_error(topic) << message << std::endl; +} +std::ostream& _Log::fatal_error(const std::string& topic) { + _out << topic << std::setfill(' ') << std::setw(6) << "FERR" << ": "; + return _out; +} + +void _Log::error(const std::string& topic, const std::string& message) { + this->error(topic) << message << std::endl; +} +std::ostream& _Log::error(const std::string& topic) { + _out << topic << std::setfill(' ') << std::setw(6) << "ERR" << ": "; + return _out; +} + +void _Log::info(const std::string& topic, const std::string& message) { + this->info(topic) << message << std::endl; +} + +std::ostream& _Log::info(const std::string& topic) { + _out << topic << std::setfill(' ') << std::setw(6) << "INFO" << ": "; + return _out; +} + +void _Log::warn(const std::string& topic, const std::string& message) { + this->warn(topic) << message << std::endl; +} + +std::ostream& _Log::warn(const std::string& topic) { + _out << topic << std::setfill(' ') << std::setw(6) << "WARN" << ": "; + return _out; +} + +void _Log::debug(const std::string& topic, const std::string& message) { + this->debug(topic) << message << std::endl; +} + +std::ostream& _Log::debug(const std::string& topic) { + _out << topic << std::setfill(' ') << std::setw(6) << "DEBUG" << ": "; + return _out; +} + +void _Log::raw(const std::string& message) { + this->raw() << message << std::endl; +} +std::ostream& _Log::raw() { + return _out; +} + + + +} // Slic3r diff --git a/xs/src/libslic3r/Log.hpp b/xs/src/libslic3r/Log.hpp index 4246d84a8..bf9ab5128 100644 --- a/xs/src/libslic3r/Log.hpp +++ b/xs/src/libslic3r/Log.hpp @@ -5,14 +5,68 @@ #include #include #include +#include +#include +#include // good until c++17 namespace Slic3r { +/// Singleton instance implementing logging functionality in Slic3r +/// Basic functionality is stubbed in currently, may pass through to Boost::Log +/// eventually. +class _Log { +public: + static std::unique_ptr<_Log> make_log() { + std::unique_ptr<_Log> tmp {new _Log()}; + return tmp; + } + static std::unique_ptr<_Log> make_log(std::ostream& out) { + std::unique_ptr<_Log> tmp {new _Log(out)}; + return tmp; + } + static std::unique_ptr<_Log> make_log(std::wostream& out) { + std::unique_ptr<_Log> tmp {new _Log(out)}; + return tmp; + } + void fatal_error(const std::string& topic, const std::wstring& message); + void fatal_error(const std::string& topic, const std::string& message); + std::ostream& fatal_error(const std::string& topic); + + void error(const std::string& topic, const std::wstring& message); + void error(const std::string& topic, const std::string& message); + std::ostream& error(const std::string& topic); + + void info(const std::string& topic, const std::string& message); + std::ostream& info(const std::string& topic); + void debug(const std::string& topic, const std::string& message); + std::ostream& debug(const std::string& topic); + void warn(const std::string& topic, const std::string& message); + std::ostream& warn(const std::string& topic); + void raw(const std::string& message); + std::ostream& raw(); + +// _Log(_Log const&) = delete; +// void operator=(_Log const&) = delete; +private: + std::ostream& _out; + std::wostream& _wout; + _Log(); + _Log(std::ostream& out); + _Log(std::wostream& out); + + std::wstring_convert> converter; + +}; + +/// Global log reference; initialized in Log.cpp +extern std::unique_ptr<_Log> slic3r_log; + +/// Static class for referencing the various logging functions. Refers to +/// class Log { public: static void fatal_error(std::string topic, std::wstring message) { - std::cerr << topic << " FERR" << ": "; - std::wcerr << message << std::endl; + slic3r_log->fatal_error(topic, message); } static void error(std::string topic, std::wstring message) { std::cerr << topic << " ERR" << ": "; @@ -67,13 +121,18 @@ public: std::cerr << topic << " INFO" << ": "; return std::cerr; } + + /// Unadorned ostream output for multiline constructions. + static std::ostream& raw() { + return std::cerr; + } }; -/// Utility debug function to transform a std::vector of anything that +/// Utility debug function to transform a std::vector of anything that /// supports ostream& operator<<() into a std::string. template -std::string -log_string(const std::vector& in) +std::string +log_string(const std::vector& in) { std::stringstream ss; bool first {true};