Revert "Revert "Expanding Slic3r::Log""

This reverts commit cf933c62d575d3d147d6bc869c9e3e354b81df17.
This commit is contained in:
Joseph Lenox 2018-11-23 20:27:36 -06:00 committed by Joseph Lenox
parent 1e17979e05
commit 02fd5b9de7
4 changed files with 962 additions and 42 deletions

View File

@ -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
)

View File

@ -0,0 +1,637 @@
#include <catch.hpp>
#include <test_options.hpp>
#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) };
cut->set_level(log_t::DEBUG);
cut->set_inclusive(true);
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) };
cut->set_level(log_t::DEBUG);
cut->set_inclusive(true);
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");
}
}
}
}
SCENARIO( "_Log output inclusive filtering with std::string methods" ) {
GIVEN("Single, inclusive log level of FERR (highest)") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->clear_level(log_t::FERR);
cut->set_inclusive(true);
cut->set_level(log_t::FERR);
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 blank") {
REQUIRE(log.str() == "");
}
}
WHEN("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("info is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->info("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("debug is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->debug("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("Single, inclusive log level of ERR (second-highest)") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(true);
cut->set_level(log_t::ERR);
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("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("info is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->info("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("debug is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->debug("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("Single, inclusive log level of WARN (third-highest)") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(true);
cut->set_level(log_t::WARN);
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("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 blank") {
REQUIRE(log.str() == "");
}
}
WHEN("debug is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->debug("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("Single, inclusive log level of INFO (fourth-highest)") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(true);
cut->set_level(log_t::INFO);
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("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 blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("Single, inclusive log level of DEBUG (fifth-highest)") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(true);
cut->set_level(log_t::DEBUG);
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("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");
}
}
}
}
SCENARIO( "_Log output set filtering with std::string methods" ) {
GIVEN("log level of DEBUG only") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::DEBUG);
WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->fatal_error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("info is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->info("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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");
}
}
}
GIVEN("log level of INFO only") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::INFO);
WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->fatal_error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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 blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("log level of WARN only") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::WARN);
WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->fatal_error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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 blank") {
REQUIRE(log.str() == "");
}
}
WHEN("debug is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->debug("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("log level of FERR only") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::FERR);
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 blank") {
REQUIRE(log.str() == "");
}
}
WHEN("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("info is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->info("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("debug is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->debug("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
}
GIVEN("log level of DEBUG and ERR") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::DEBUG);
cut->set_level(log_t::ERR);
WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->fatal_error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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("warn is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->warn("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("info is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->info("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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");
}
}
}
GIVEN("log level of INFO and WARN") {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(false);
cut->clear_level(log_t::ALL);
cut->set_level(log_t::INFO);
cut->set_level(log_t::WARN);
cut->set_inclusive(false);
WHEN("fatal_error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->fatal_error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
WHEN("error is called with topic \"Topic\" and text \"This\"") {
log.clear();
cut->error("Topic", "This");
THEN("Output string is blank") {
REQUIRE(log.str() == "");
}
}
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 blank") {
REQUIRE(log.str() == "");
}
}
}
}
SCENARIO( "_Log output filtering on topic name" ) {
std::stringstream log;
std::unique_ptr<_Log> cut { _Log::make_log(log) };
cut->set_inclusive(true);
cut->set_level(log_t::ALL);
WHEN("Topic is \"t1\"") {
cut->add_topic("t1");
cut->debug("t1") << "TEXT FOR T1 ";
cut->debug("t2") << "TEXT FOR T2 ";
cut->debug("t3") << "TEXT FOR T3";
THEN("Log text is \"TEXT FOR T1 \"") {
REQUIRE(log.str() == "t1 DEBUG: TEXT FOR T1 ");
}
}
WHEN("Topic is \"t2\"") {
cut->add_topic("t2");
cut->debug("t1") << "TEXT FOR T1 ";
cut->debug("t2") << "TEXT FOR T2 ";
cut->debug("t3") << "TEXT FOR T3";
THEN("Log text is \"TEXT FOR T2 \"") {
REQUIRE(log.str() == "t2 DEBUG: TEXT FOR T2 ");
}
}
WHEN("Topic is \"t3\"") {
cut->add_topic("t3");
cut->debug("t1") << "TEXT FOR T1 ";
cut->debug("t2") << "TEXT FOR T2 ";
cut->debug("t3") << "TEXT FOR T3";
THEN("Log text is \"TEXT FOR T3\"") {
REQUIRE(log.str() == "t3 DEBUG: TEXT FOR T3");
}
}
WHEN("Topic is \"t3\" and \"t2\"") {
cut->add_topic("t2");
cut->add_topic("t3");
cut->debug("t1") << "TEXT FOR T1 ";
cut->debug("t2") << "TEXT FOR T2 ";
cut->debug("t3") << "TEXT FOR T3";
THEN("Log text is \"TEXT FOR T2 TEXT FOR T3\"") {
REQUIRE(log.str() == "t2 DEBUG: TEXT FOR T2 t3 DEBUG: TEXT FOR T3");
}
}
}

147
xs/src/libslic3r/Log.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>
#include <algorithm>
#include "Log.hpp"
/// Local class to suppress output
class NullStream : public std::streambuf
{
public:
int overflow(int c) { return c; }
};
namespace Slic3r {
static NullStream log_null;
static std::ostream null_log(&log_null);
std::unique_ptr<_Log> slic3r_log {_Log::make_log()};
_Log::_Log() : _out(std::clog) {
}
_Log::_Log(std::ostream& out) : _out(out) {
}
bool _Log::_has_log_level(log_t lvl) {
if (!this->_inclusive_levels && this->_log_level.find(lvl) != this->_log_level.end()) {
return true;
} else if (this->_inclusive_levels && *(std::max_element(this->_log_level.cbegin(), this->_log_level.cend())) >= lvl) {
return true;
}
return false;
}
bool _Log::_has_topic(const std::string& topic) {
return this->_topics.find(topic) != this->_topics.end() || this->_topics.size() == 0;
}
void _Log::fatal_error(const std::string& topic, const std::wstring& message) { this->fatal_error(topic, this->converter.to_bytes(message)); }
void _Log::error(const std::string& topic, const std::wstring& message) { this->error(topic, this->converter.to_bytes(message)); }
void _Log::warn(const std::string& topic, const std::wstring& message) { this->warn(topic, this->converter.to_bytes(message)); }
void _Log::info(const std::string& topic, const std::wstring& message) { this->info(topic, this->converter.to_bytes(message)); }
void _Log::debug(const std::string& topic, const std::wstring& message) { this->debug(topic, this->converter.to_bytes(message)); }
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) {
if (this->_has_log_level(log_t::FERR) && this->_has_topic(topic)) {
_out << topic << std::setfill(' ') << std::setw(6) << "FERR" << ": ";
return _out;
}
return null_log;
}
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) {
if (this->_has_log_level(log_t::ERR) && this->_has_topic(topic)) {
_out << topic << std::setfill(' ') << std::setw(6) << "ERR" << ": ";
return _out;
}
return null_log;
}
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) {
if (this->_has_log_level(log_t::INFO) && this->_has_topic(topic)) {
_out << topic << std::setfill(' ') << std::setw(6) << "INFO" << ": ";
return _out;
}
return null_log;
}
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) {
if (this->_has_log_level(log_t::WARN) && this->_has_topic(topic)) {
_out << topic << std::setfill(' ') << std::setw(6) << "WARN" << ": ";
return _out;
}
return null_log;
}
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) {
if (this->_has_log_level(log_t::DEBUG) && this->_has_topic(topic)) {
_out << topic << std::setfill(' ') << std::setw(6) << "DEBUG" << ": ";
return _out;
}
return null_log;
}
void _Log::raw(const std::string& message) {
this->raw() << message << std::endl;
}
void _Log::raw(const std::wstring& message) { this->raw(this->converter.to_bytes(message)); }
std::ostream& _Log::raw() {
return _out;
}
void _Log::set_level(log_t level) {
if (this->_inclusive_levels) {
this->_log_level.clear();
this->_log_level.insert(level);
} else if (level == log_t::ALL) {
this->_log_level.insert(log_t::FERR);
this->_log_level.insert(log_t::ERR);
this->_log_level.insert(log_t::WARN);
this->_log_level.insert(log_t::INFO);
this->_log_level.insert(log_t::DEBUG);
} else {
this->_log_level.insert(level);
}
}
void _Log::clear_level(log_t level) {
if (level == log_t::ALL) {
this->_log_level.clear();
} else {
if (this->_log_level.find(level) != this->_log_level.end())
this->_log_level.erase(level);
}
}
void _Log::clear_topic(const std::string& topic) {
if (topic == "") {
this->_topics.clear();
} else {
if (this->_topics.find(topic) != this->_topics.end()) this->_topics.erase(topic);
}
}
} // Slic3r

View File

@ -5,75 +5,209 @@
#include <vector>
#include <sstream>
#include <iostream>
#include <memory>
#include <locale>
#include <set>
#include <codecvt> // good until c++17
namespace Slic3r {
/// All available logging levels.
enum class log_t : uint8_t { FERR = 0, ERR = 4, WARN = 8, INFO = 16, DEBUG = 32, ALL = 255 };
inline bool operator>(const log_t lhs, const log_t rhs) { return static_cast<uint8_t>(lhs) > static_cast<uint8_t>(rhs); }
inline bool operator<(const log_t lhs, const log_t rhs) { return static_cast<uint8_t>(lhs) < static_cast<uint8_t>(rhs); }
inline bool operator>=(const log_t lhs, const log_t rhs) { return static_cast<uint8_t>(lhs) > static_cast<uint8_t>(rhs) || lhs == rhs; }
inline bool operator<=(const log_t lhs, const log_t rhs) { return static_cast<uint8_t>(lhs) < static_cast<uint8_t>(rhs) || lhs == rhs; }
/// 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;
}
void fatal_error(const std::string& topic, const std::string& message);
void fatal_error(const std::string& topic, const std::wstring& message);
std::ostream& fatal_error(const std::string& topic);
void error(const std::string& topic, const std::string& message);
void error(const std::string& topic, const std::wstring& message);
std::ostream& error(const std::string& topic);
void info(const std::string& topic, const std::string& message);
void info(const std::string& topic, const std::wstring& message);
std::ostream& info(const std::string& topic);
void debug(const std::string& topic, const std::string& message);
void debug(const std::string& topic, const std::wstring& message);
std::ostream& debug(const std::string& topic);
void warn(const std::string& topic, const std::string& message);
void warn(const std::string& topic, const std::wstring& message);
std::ostream& warn(const std::string& topic);
void raw(const std::string& message);
void raw(const std::wstring& message);
std::ostream& raw();
template <class T>
void debug_svg(const std::string& topic, const T& path, bool append = true);
template <class T>
void debug_svg(const std::string& topic, const T* path, bool append = true);
void set_level(log_t level);
void clear_level(log_t level);
void set_inclusive(bool v) { this->_inclusive_levels = v; }
void add_topic(const std::string& topic) { this->_topics.insert(topic); }
void clear_topic(const std::string& topic);
// _Log(_Log const&) = delete;
// void operator=(_Log const&) = delete;
private:
std::ostream& _out;
_Log();
_Log(std::ostream& out);
bool _inclusive_levels { true };
std::set<log_t> _log_level { };
std::set<std::string> _topics { };
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
bool _has_log_level(log_t lvl);
bool _has_topic(const std::string& topic);
};
/// 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:
/// Logs a fatal error with Slic3r.
/// \param topic [in] file or heading for error
/// \param message [in] text of the logged error message
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);
}
/// Logs a regular error with Slic3r.
/// \param topic [in] file or heading for error
/// \param message [in] text of the logged error message
static void error(std::string topic, std::wstring message) {
std::cerr << topic << " ERR" << ": ";
std::wcerr << message << std::endl;
slic3r_log->error(topic, message);
}
/// Logs a fatal error with Slic3r.
/// \param topic [in] file or heading for error
/// \param message [in] text of the logged error message
static void error(std::string topic, std::string message) {
std::cerr << topic << " ERR" << ": ";
std::cerr << message << std::endl;
slic3r_log->error(topic, message);
}
/// Logs an informational message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void info(std::string topic, std::wstring message) {
std::clog << topic << " INFO" << ": ";
std::wclog << message << std::endl;
slic3r_log->info(topic, message);
}
/// Logs an informational message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void info(std::string topic, std::string message) {
std::clog << topic << " INFO" << ": ";
std::clog << message << std::endl;
slic3r_log->info(topic, message);
}
/// Logs a warning message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void warn(std::string topic, std::wstring message) {
std::cerr << topic << " WARN" << ": ";
std::wcerr << message << std::endl;
slic3r_log->warn(topic, message);
}
/// Logs a warning message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void warn(std::string topic, std::string message) {
std::cerr << topic << " WARN" << ": ";
std::cerr << message << std::endl;
slic3r_log->warn(topic, message);
}
/// Logs a debugging message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void debug(std::string topic, std::wstring message) {
std::cerr << topic << " DEBUG" << ": ";
std::wcerr << message << std::endl;
slic3r_log->debug(topic, message);
}
/// Logs a debugging message with Slic3r.
/// \param topic [in] file or heading for message
/// \param message [in] text of the logged message
static void debug(std::string topic, std::string message) {
std::cerr << topic << " DEBUG" << ": ";
std::cerr << message << std::endl;
}
static std::ostream& error(std::string topic) {
std::cerr << topic << " ERR" << ": ";
return std::cerr;
}
static std::ostream& debug(std::string topic) {
std::cerr << topic << " DEBUG" << ": ";
return std::cerr;
slic3r_log->debug(topic, message);
}
static std::ostream& warn(std::string topic) {
std::cerr << topic << " WARN" << ": ";
return std::cerr;
/// Logs an error message with Slic3r.
/// \param topic [in] file or heading for message
/// \return reference to output ostream for << chaining.
/// \note Developer is expected to add newlines.
static std::ostream& error(std::string topic) {
return slic3r_log->error(topic);
}
/// Logs a debugging message with Slic3r.
/// \param topic [in] file or heading for message
/// \return reference to output ostream for << chaining.
/// \note Developer is expected to add newlines.
static std::ostream& debug(std::string topic) {
return slic3r_log->debug(topic);
}
/// Logs a warning message with Slic3r.
/// \param topic [in] file or heading for message
/// \return reference to output ostream for << chaining.
/// \note Developer is expected to add newlines.
static std::ostream& warn(std::string topic) {
return slic3r_log->warn(topic);
}
/// Logs an informational message with Slic3r.
/// \param topic [in] file or heading for message
/// \return reference to output ostream for << chaining.
/// \note Developer is expected to add newlines.
static std::ostream& info(std::string topic) {
std::cerr << topic << " INFO" << ": ";
return std::cerr;
return slic3r_log->info(topic);
}
/// Unadorned ostream output for multiline constructions.
static std::ostream& raw() {
return slic3r_log->raw();
}
/// Adds a topic to filter on with Slic3r's debug system.
/// \param topic [in] name of topic to filter on.
/// Only shows registered topics.
static void add_topic(const std::string& topic) {
slic3r_log->add_topic(topic);
}
/// Removes a topic from the filter list with Slic3r's debug system.
/// \param topic [in] name of topic to remove from filter.
/// \note Default option removes all filters.
static void clear_topic(const std::string& topic = "") {
slic3r_log->clear_topic(topic);
}
};
/// 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 <typename T>
std::string
log_string(const std::vector<T>& in)
std::string
log_string(const std::vector<T>& in)
{
std::stringstream ss;
bool first {true};