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
+68 -27
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")
("quiet,q", "less status and progress output")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") ("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."); ("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()
@@ -336,22 +339,10 @@ int main(int argc, char** argv)
po::notify(vmap); po::notify(vmap);
print_version();
if (vmap.count("version")) {
return EXIT_SUCCESS;
} else if (vmap.count("help")) {
print_usage(false);
print_options(generic_options.add(geom_options).add(serializer_options));
return EXIT_SUCCESS;
} else if (!vmap.count("input-file")) {
std::cerr << "[Error] Input file not specified" << std::endl;
print_usage();
return EXIT_FAILURE;
}
const bool mmap = vmap.count("mmap") != 0; const bool mmap = vmap.count("mmap") != 0;
const bool verbose = vmap.count("verbose") != 0; const bool verbose = vmap.count("verbose") != 0;
const bool no_progress = vmap.count("no-progress") != 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 weld_vertices = vmap.count("weld-vertices") != 0;
const bool use_world_coords = vmap.count("use-world-coords") != 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 convert_back_units = vmap.count("convert-back-units") != 0;
@@ -375,6 +366,22 @@ int main(int argc, char** argv)
const bool building_local_placement = vmap.count("building-local-placement") != 0; const bool building_local_placement = vmap.count("building-local-placement") != 0;
const bool generate_uvs = vmap.count("generate-uvs") != 0; const bool generate_uvs = vmap.count("generate-uvs") != 0;
if (!quiet || vmap.count("version")) {
print_version();
}
if (vmap.count("version")) {
return EXIT_SUCCESS;
} else if (vmap.count("help")) {
print_usage(false);
print_options(generic_options.add(geom_options).add(serializer_options));
return EXIT_SUCCESS;
} else if (!vmap.count("input-file")) {
std::cerr << "[Error] Input file not specified" << std::endl;
print_usage();
return EXIT_FAILURE;
}
#ifdef HAVE_ICU #ifdef HAVE_ICU
if (!unicode_mode.empty()) { if (!unicode_mode.empty()) {
if (unicode_mode == "utf8") { if (unicode_mode == "utf8") {
@@ -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());
} }
if (!quiet) {
Logger::Status("Creating geometry..."); 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) {
if (quiet) {
const int progress = context_iterator.progress();
for (; old_progress < progress; ++old_progress) {
std::cout << ".";
}
std::cout << std::flush;
} else {
const int progress = context_iterator.progress() / 2; const int progress = context_iterator.progress() / 2;
if (old_progress != progress) Logger::ProgressBar(progress); if (old_progress != progress) Logger::ProgressBar(progress);
old_progress = progress; old_progress = progress;
} }
}
} while (++num_created, context_iterator.next()); } while (++num_created, context_iterator.next());
if (!no_progress && quiet) {
for (; old_progress < 100; ++old_progress) {
std::cout << ".";
}
} else {
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) + Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
" objects) "); " objects) ");
}
serializer->finalize(); serializer->finalize();
delete serializer; delete serializer;
@@ -690,10 +726,11 @@ 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);
if (!quiet) {
int seconds = (int)difftime(end, start); int seconds = (int)difftime(end, start);
std::stringstream msg; std::stringstream msg;
int minutes = seconds / 60; int minutes = seconds / 60;
@@ -710,14 +747,18 @@ int main(int argc, char** argv)
msg << "s"; msg << "s";
} }
Logger::Status(msg.str()); 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;
} }
} }
+45 -6
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;
@@ -40,12 +74,11 @@ 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;
} }
} }
@@ -60,20 +93,26 @@ void Logger::Status(const std::string& message, bool new_line) {
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,21 +40,27 @@
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);
static void Message(Severity type, const std::exception& message, IfcEntityInstanceData* entity = 0); static void Message(Severity type, const std::exception& message, IfcEntityInstanceData* entity = 0);