Machine readable progress with -q and --log-format json

This commit is contained in:
Thomas Krijnen
2018-05-12 20:12:25 +02:00
parent 589f4bda2e
commit 7786b89269
3 changed files with 157 additions and 71 deletions
+102 -61
View File
@@ -113,7 +113,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
} }
static std::stringstream log_stream; static std::stringstream log_stream;
void write_log(); void write_log(bool);
/// @todo make the filters non-global /// @todo make the filters non-global
IfcGeom::entity_filter entity_filter; // Entity filter is used always by default. IfcGeom::entity_filter entity_filter; // Entity filter is used always by default.
@@ -152,13 +152,16 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile& ifc_file, b
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
std::string log_format;
po::options_description generic_options("Command line options"); po::options_description generic_options("Command line options");
generic_options.add_options() generic_options.add_options()
("help,h", "display usage information") ("help,h", "display usage information")
("version", "display version information") ("version", "display version information")
("verbose,v", "more verbose output") ("verbose,v", "more verbose log messages")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") ("quiet,q", "less status and progress output")
("no-progress", "Suppress possible progress bar type of prints that use carriage return."); ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
("no-progress", "suppress possible progress bar type of prints that use carriage return")
("log-format", po::value<std::string>(&log_format), "log format: plain or json");
po::options_description fileio_options; po::options_description fileio_options;
fileio_options.add_options() fileio_options.add_options()
@@ -174,7 +177,7 @@ int main(int argc, char** argv)
inclusion_traverse_filter include_traverse_filter; inclusion_traverse_filter include_traverse_filter;
exclusion_filter exclude_filter; exclusion_filter exclude_filter;
exclusion_traverse_filter exclude_traverse_filter; exclusion_traverse_filter exclude_traverse_filter;
std::string filter_filename; std::string filter_filename;
po::options_description geom_options("Geometry options"); po::options_description geom_options("Geometry options");
geom_options.add_options() geom_options.add_options()
@@ -336,7 +339,36 @@ int main(int argc, char** argv)
po::notify(vmap); po::notify(vmap);
print_version(); const bool mmap = vmap.count("mmap") != 0;
const bool verbose = vmap.count("verbose") != 0;
const bool no_progress = vmap.count("no-progress") != 0;
const bool quiet = vmap.count("quiet") != 0;
const bool weld_vertices = vmap.count("weld-vertices") != 0;
const bool use_world_coords = vmap.count("use-world-coords") != 0;
const bool convert_back_units = vmap.count("convert-back-units") != 0;
const bool sew_shells = vmap.count("sew-shells") != 0;
#if OCC_VERSION_HEX < 0x60900
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
#endif
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
const bool include_plan = vmap.count("plan") != 0;
const bool include_model = vmap.count("model") != 0 || (!include_plan);
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
const bool use_element_names = vmap.count("use-element-names") != 0;
const bool use_element_guids = vmap.count("use-element-guids") != 0;
const bool use_material_names = vmap.count("use-material-names") != 0;
const bool use_element_types = vmap.count("use-element-types") != 0;
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
const bool no_normals = vmap.count("no-normals") != 0;
const bool center_model = vmap.count("center-model") != 0;
const bool model_offset = vmap.count("model-offset") != 0;
const bool site_local_placement = vmap.count("site-local-placement") != 0;
const bool building_local_placement = vmap.count("building-local-placement") != 0;
const bool generate_uvs = vmap.count("generate-uvs") != 0;
if (!quiet || vmap.count("version")) {
print_version();
}
if (vmap.count("version")) { if (vmap.count("version")) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
@@ -349,31 +381,6 @@ int main(int argc, char** argv)
print_usage(); print_usage();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
const bool mmap = vmap.count("mmap") != 0;
const bool verbose = vmap.count("verbose") != 0;
const bool no_progress = vmap.count("no-progress") != 0;
const bool weld_vertices = vmap.count("weld-vertices") != 0;
const bool use_world_coords = vmap.count("use-world-coords") != 0;
const bool convert_back_units = vmap.count("convert-back-units") != 0;
const bool sew_shells = vmap.count("sew-shells") != 0;
#if OCC_VERSION_HEX < 0x60900
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
#endif
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
const bool include_plan = vmap.count("plan") != 0;
const bool include_model = vmap.count("model") != 0 || (!include_plan);
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
const bool use_element_names = vmap.count("use-element-names") != 0;
const bool use_element_guids = vmap.count("use-element-guids") != 0 ;
const bool use_material_names = vmap.count("use-material-names") != 0;
const bool use_element_types = vmap.count("use-element-types") != 0;
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
const bool no_normals = vmap.count("no-normals") != 0 ;
const bool center_model = vmap.count("center-model") != 0 ;
const bool model_offset = vmap.count("model-offset") != 0 ;
const bool site_local_placement = vmap.count("site-local-placement") != 0 ;
const bool building_local_placement = vmap.count("building-local-placement") != 0 ;
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
#ifdef HAVE_ICU #ifdef HAVE_ICU
if (!unicode_mode.empty()) { if (!unicode_mode.empty()) {
@@ -438,12 +445,25 @@ int main(int argc, char** argv)
Logger::SetOutput(&std::cout, &log_stream); Logger::SetOutput(&std::cout, &log_stream);
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
if (vmap.count("log-format") == 1) {
boost::to_lower(log_format);
if (log_format == "plain") {
Logger::OutputFormat(Logger::FMT_PLAIN);
} else if (log_format == "json") {
Logger::OutputFormat(Logger::FMT_JSON);
} else {
std::cerr << "[Error] --log-format should be either plain or json" << std::endl;
print_usage();
return EXIT_FAILURE;
}
}
IfcParse::IfcFile ifc_file; IfcParse::IfcFile ifc_file;
if (output_extension == ".xml") { if (output_extension == ".xml") {
int exit_code = EXIT_FAILURE; int exit_code = EXIT_FAILURE;
try { try {
if (init_input_file(input_filename, ifc_file, no_progress, mmap)) { if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
XmlSerializer s(output_temp_filename); XmlSerializer s(output_temp_filename);
s.setFile(&ifc_file); s.setFile(&ifc_file);
Logger::Status("Writing XML output..."); Logger::Status("Writing XML output...");
@@ -455,7 +475,7 @@ int main(int argc, char** argv)
} catch (const std::exception& e) { } catch (const std::exception& e) {
Logger::Error(e); Logger::Error(e);
} }
write_log(); write_log(!quiet);
return exit_code; return exit_code;
} }
@@ -548,7 +568,7 @@ int main(int argc, char** argv)
} }
} else { } else {
std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n"; std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n";
write_log(); write_log(!quiet);
print_usage(); print_usage();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -557,7 +577,7 @@ int main(int argc, char** argv)
if (use_element_hierarchy && output_extension != ".dae") { if (use_element_hierarchy && output_extension != ".dae") {
std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n";
write_log(); write_log(!quiet);
print_usage(); print_usage();
delete serializer; delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
@@ -582,14 +602,14 @@ int main(int argc, char** argv)
if (!serializer->ready()) { if (!serializer->ready()) {
delete serializer; delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
write_log(); write_log(!quiet);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
time_t start,end; time_t start,end;
time(&start); time(&start);
if (!init_input_file(input_filename, ifc_file, no_progress, mmap)) { if (!init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -600,7 +620,7 @@ int main(int argc, char** argv)
Logger::Error("No geometrical entities found"); Logger::Error("No geometrical entities found");
delete serializer; delete serializer;
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */ std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
write_log(); write_log(!quiet);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -614,7 +634,7 @@ int main(int argc, char** argv)
serializer->writeHeader(); serializer->writeHeader();
int old_progress = -1; int old_progress = quiet ? 0 : -1;
if (center_model || model_offset) { if (center_model || model_offset) {
double* offset = serializer->settings().offset; double* offset = serializer->settings().offset;
@@ -643,7 +663,9 @@ int main(int argc, char** argv)
Logger::Notice(msg.str()); Logger::Notice(msg.str());
} }
Logger::Status("Creating geometry..."); if (!quiet) {
Logger::Status("Creating geometry...");
}
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next() // The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
// wrap an iterator of all geometrical products in the Ifc file. // wrap an iterator of all geometrical products in the Ifc file.
@@ -670,14 +692,28 @@ int main(int argc, char** argv)
} }
if (!no_progress) { if (!no_progress) {
const int progress = context_iterator.progress() / 2; if (quiet) {
if (old_progress != progress) Logger::ProgressBar(progress); const int progress = context_iterator.progress();
old_progress = progress; for (; old_progress < progress; ++old_progress) {
std::cout << ".";
}
std::cout << std::flush;
} else {
const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress;
}
} }
} while (++num_created, context_iterator.next()); } while (++num_created, context_iterator.next());
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) + if (!no_progress && quiet) {
" objects) "); for (; old_progress < 100; ++old_progress) {
std::cout << ".";
}
} else {
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
" objects) ");
}
serializer->finalize(); serializer->finalize();
delete serializer; delete serializer;
@@ -690,34 +726,39 @@ int main(int argc, char** argv)
output_temp_filename + "' for the conversion result."); output_temp_filename + "' for the conversion result.");
} }
write_log(); write_log(!quiet);
time(&end); time(&end);
int seconds = (int)difftime(end, start); if (!quiet) {
std::stringstream msg; int seconds = (int)difftime(end, start);
int minutes = seconds / 60; std::stringstream msg;
seconds = seconds % 60; int minutes = seconds / 60;
msg << "\nConversion took"; seconds = seconds % 60;
if (minutes > 0) { msg << "\nConversion took";
msg << " " << minutes << " minute"; if (minutes > 0) {
if (minutes > 1) { msg << " " << minutes << " minute";
if (minutes > 1) {
msg << "s";
}
}
msg << " " << seconds << " second";
if (seconds > 1) {
msg << "s"; msg << "s";
} }
Logger::Status(msg.str());
} }
msg << " " << seconds << " second";
if (seconds > 1) {
msg << "s";
}
Logger::Status(msg.str());
return successful ? EXIT_SUCCESS : EXIT_FAILURE; return successful ? EXIT_SUCCESS : EXIT_FAILURE;
} }
void write_log() { void write_log(bool header) {
std::string log = log_stream.str(); std::string log = log_stream.str();
if (!log.empty()) { if (!log.empty()) {
std::cout << "\nLog:\n" << log << std::endl; if (header) {
std::cout << "\nLog:\n";
}
std::cout << log << std::endl;
} }
} }
+48 -9
View File
@@ -23,13 +23,47 @@
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/version.hpp>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
using boost::property_tree::ptree;
namespace {
static const char* severity_strings[] = {"Notice", "Warning", "Error"};
void plain_text_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
os << "[" << severity_strings[type] << "] ";
if (current_product) {
os << "{" << (*current_product)->GlobalId() << "} ";
}
os << message << std::endl;
if (entity) {
os << entity->toString() << std::endl;
}
}
void json_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
ptree pt;
pt.put("level", severity_strings[type]);
if (current_product) {
pt.put("product", (**current_product).entity->toString());
}
pt.put("message", message);
if (entity) {
pt.put("instance", entity);
}
boost::property_tree::write_json(os, pt, false);
}
}
void Logger::SetProduct(boost::optional<IfcSchema::IfcProduct*> product) { void Logger::SetProduct(boost::optional<IfcSchema::IfcProduct*> product) {
current_product = product; current_product = product;
} }
void Logger::SetOutput(std::ostream* l1, std::ostream* l2) { void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
log1 = l1; log1 = l1;
log2 = l2; log2 = l2;
@@ -39,13 +73,12 @@ void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
} }
void Logger::Message(Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) { void Logger::Message(Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
if ( log2 && type >= verbosity ) { if (log2 && type >= verbosity) {
(*log2) << "[" << severity_strings[type] << "] "; if (format == FMT_PLAIN) {
if ( current_product ) { plain_text_message(*log2, current_product, type, message, entity);
(*log2) << "{" << (*current_product)->GlobalId() << "} "; } else if (format == FMT_JSON) {
json_message(*log2, current_product, type, message, entity);
} }
(*log2) << message << std::endl;
if ( entity ) (*log2) << entity->toString() << std::endl;
} }
} }
@@ -54,26 +87,32 @@ void Logger::Message(Logger::Severity type, const std::exception& exception, Ifc
} }
void Logger::Status(const std::string& message, bool new_line) { void Logger::Status(const std::string& message, bool new_line) {
if ( log1 ) { if (log1) {
(*log1) << message; (*log1) << message;
if ( new_line ) (*log1) << std::endl; if ( new_line ) (*log1) << std::endl;
else (*log1) << std::flush; else (*log1) << std::flush;
} }
} }
void Logger::ProgressBar(int progress) { void Logger::ProgressBar(int progress) {
if ( log1 ) { if (log1) {
Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false); Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false);
} }
} }
std::string Logger::GetLog() { std::string Logger::GetLog() {
return log_stream.str(); return log_stream.str();
} }
void Logger::Verbosity(Logger::Severity v) { verbosity = v; } void Logger::Verbosity(Logger::Severity v) { verbosity = v; }
Logger::Severity Logger::Verbosity() { return verbosity; } Logger::Severity Logger::Verbosity() { return verbosity; }
void Logger::OutputFormat(Format f) { format = f; }
Logger::Format Logger::OutputFormat() { return format; }
std::ostream* Logger::log1 = 0; std::ostream* Logger::log1 = 0;
std::ostream* Logger::log2 = 0; std::ostream* Logger::log2 = 0;
std::stringstream Logger::log_stream; std::stringstream Logger::log_stream;
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE; Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
const char* Logger::severity_strings[] = { "Notice","Warning","Error" }; Logger::Format Logger::format = Logger::FMT_PLAIN;
boost::optional<IfcSchema::IfcProduct*> Logger::current_product; boost::optional<IfcSchema::IfcProduct*> Logger::current_product;
+7 -1
View File
@@ -40,20 +40,26 @@
class IFC_PARSE_API Logger { class IFC_PARSE_API Logger {
public: public:
typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity; typedef enum { LOG_NOTICE, LOG_WARNING, LOG_ERROR } Severity;
typedef enum { FMT_PLAIN, FMT_JSON } Format;
private: private:
static std::ostream* log1; static std::ostream* log1;
static std::ostream* log2; static std::ostream* log2;
static std::stringstream log_stream; static std::stringstream log_stream;
static Severity verbosity; static Severity verbosity;
static const char* severity_strings[]; static Format format;
static boost::optional<IfcSchema::IfcProduct*> current_product; static boost::optional<IfcSchema::IfcProduct*> current_product;
public: public:
static void SetProduct(boost::optional<IfcSchema::IfcProduct*> product); static void SetProduct(boost::optional<IfcSchema::IfcProduct*> product);
/// Determines to what stream respectively progress and errors are logged /// Determines to what stream respectively progress and errors are logged
static void SetOutput(std::ostream* l1, std::ostream* l2); static void SetOutput(std::ostream* l1, std::ostream* l2);
/// Determines the types of log messages to get logged /// Determines the types of log messages to get logged
static void Verbosity(Severity v); static void Verbosity(Severity v);
static Severity Verbosity(); static Severity Verbosity();
/// Determines output format: plain text or sequence of JSON objects
static void OutputFormat(Format f);
static Format OutputFormat();
/// Log a message to the output stream /// Log a message to the output stream
static void Message(Severity type, const std::string& message, IfcEntityInstanceData* entity=0); static void Message(Severity type, const std::string& message, IfcEntityInstanceData* entity=0);