Skip to content

Commit 7786b89

Browse files
committed
Machine readable progress with -q and --log-format json
1 parent 589f4bd commit 7786b89

3 files changed

Lines changed: 153 additions & 67 deletions

File tree

src/ifcconvert/IfcConvert.cpp

Lines changed: 98 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
113113
}
114114

115115
static std::stringstream log_stream;
116-
void write_log();
116+
void write_log(bool);
117117

118118
/// @todo make the filters non-global
119119
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
152152

153153
int main(int argc, char** argv)
154154
{
155+
std::string log_format;
155156
po::options_description generic_options("Command line options");
156157
generic_options.add_options()
157158
("help,h", "display usage information")
158159
("version", "display version information")
159-
("verbose,v", "more verbose output")
160-
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
161-
("no-progress", "Suppress possible progress bar type of prints that use carriage return.");
160+
("verbose,v", "more verbose log messages")
161+
("quiet,q", "less status and progress output")
162+
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)")
163+
("no-progress", "suppress possible progress bar type of prints that use carriage return")
164+
("log-format", po::value<std::string>(&log_format), "log format: plain or json");
162165

163166
po::options_description fileio_options;
164167
fileio_options.add_options()
@@ -174,7 +177,7 @@ int main(int argc, char** argv)
174177
inclusion_traverse_filter include_traverse_filter;
175178
exclusion_filter exclude_filter;
176179
exclusion_traverse_filter exclude_traverse_filter;
177-
std::string filter_filename;
180+
std::string filter_filename;
178181

179182
po::options_description geom_options("Geometry options");
180183
geom_options.add_options()
@@ -336,22 +339,10 @@ int main(int argc, char** argv)
336339

337340
po::notify(vmap);
338341

339-
print_version();
340-
341-
if (vmap.count("version")) {
342-
return EXIT_SUCCESS;
343-
} else if (vmap.count("help")) {
344-
print_usage(false);
345-
print_options(generic_options.add(geom_options).add(serializer_options));
346-
return EXIT_SUCCESS;
347-
} else if (!vmap.count("input-file")) {
348-
std::cerr << "[Error] Input file not specified" << std::endl;
349-
print_usage();
350-
return EXIT_FAILURE;
351-
}
352342
const bool mmap = vmap.count("mmap") != 0;
353343
const bool verbose = vmap.count("verbose") != 0;
354344
const bool no_progress = vmap.count("no-progress") != 0;
345+
const bool quiet = vmap.count("quiet") != 0;
355346
const bool weld_vertices = vmap.count("weld-vertices") != 0;
356347
const bool use_world_coords = vmap.count("use-world-coords") != 0;
357348
const bool convert_back_units = vmap.count("convert-back-units") != 0;
@@ -363,17 +354,33 @@ int main(int argc, char** argv)
363354
const bool include_plan = vmap.count("plan") != 0;
364355
const bool include_model = vmap.count("model") != 0 || (!include_plan);
365356
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
366-
const bool use_element_names = vmap.count("use-element-names") != 0;
367-
const bool use_element_guids = vmap.count("use-element-guids") != 0 ;
368-
const bool use_material_names = vmap.count("use-material-names") != 0;
357+
const bool use_element_names = vmap.count("use-element-names") != 0;
358+
const bool use_element_guids = vmap.count("use-element-guids") != 0;
359+
const bool use_material_names = vmap.count("use-material-names") != 0;
369360
const bool use_element_types = vmap.count("use-element-types") != 0;
370361
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
371-
const bool no_normals = vmap.count("no-normals") != 0 ;
372-
const bool center_model = vmap.count("center-model") != 0 ;
373-
const bool model_offset = vmap.count("model-offset") != 0 ;
374-
const bool site_local_placement = vmap.count("site-local-placement") != 0 ;
375-
const bool building_local_placement = vmap.count("building-local-placement") != 0 ;
376-
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
362+
const bool no_normals = vmap.count("no-normals") != 0;
363+
const bool center_model = vmap.count("center-model") != 0;
364+
const bool model_offset = vmap.count("model-offset") != 0;
365+
const bool site_local_placement = vmap.count("site-local-placement") != 0;
366+
const bool building_local_placement = vmap.count("building-local-placement") != 0;
367+
const bool generate_uvs = vmap.count("generate-uvs") != 0;
368+
369+
if (!quiet || vmap.count("version")) {
370+
print_version();
371+
}
372+
373+
if (vmap.count("version")) {
374+
return EXIT_SUCCESS;
375+
} else if (vmap.count("help")) {
376+
print_usage(false);
377+
print_options(generic_options.add(geom_options).add(serializer_options));
378+
return EXIT_SUCCESS;
379+
} else if (!vmap.count("input-file")) {
380+
std::cerr << "[Error] Input file not specified" << std::endl;
381+
print_usage();
382+
return EXIT_FAILURE;
383+
}
377384

378385
#ifdef HAVE_ICU
379386
if (!unicode_mode.empty()) {
@@ -438,12 +445,25 @@ int main(int argc, char** argv)
438445
Logger::SetOutput(&std::cout, &log_stream);
439446
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
440447

448+
if (vmap.count("log-format") == 1) {
449+
boost::to_lower(log_format);
450+
if (log_format == "plain") {
451+
Logger::OutputFormat(Logger::FMT_PLAIN);
452+
} else if (log_format == "json") {
453+
Logger::OutputFormat(Logger::FMT_JSON);
454+
} else {
455+
std::cerr << "[Error] --log-format should be either plain or json" << std::endl;
456+
print_usage();
457+
return EXIT_FAILURE;
458+
}
459+
}
460+
441461
IfcParse::IfcFile ifc_file;
442462

443463
if (output_extension == ".xml") {
444464
int exit_code = EXIT_FAILURE;
445465
try {
446-
if (init_input_file(input_filename, ifc_file, no_progress, mmap)) {
466+
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
447467
XmlSerializer s(output_temp_filename);
448468
s.setFile(&ifc_file);
449469
Logger::Status("Writing XML output...");
@@ -455,7 +475,7 @@ int main(int argc, char** argv)
455475
} catch (const std::exception& e) {
456476
Logger::Error(e);
457477
}
458-
write_log();
478+
write_log(!quiet);
459479
return exit_code;
460480
}
461481

@@ -548,7 +568,7 @@ int main(int argc, char** argv)
548568
}
549569
} else {
550570
std::cerr << "[Error] Unknown output filename extension '" + output_extension + "'\n";
551-
write_log();
571+
write_log(!quiet);
552572
print_usage();
553573
return EXIT_FAILURE;
554574
}
@@ -557,7 +577,7 @@ int main(int argc, char** argv)
557577

558578
if (use_element_hierarchy && output_extension != ".dae") {
559579
std::cerr << "[Error] --use-element-hierarchy can be used only with .dae output.\n";
560-
write_log();
580+
write_log(!quiet);
561581
print_usage();
562582
delete serializer;
563583
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
@@ -582,14 +602,14 @@ int main(int argc, char** argv)
582602
if (!serializer->ready()) {
583603
delete serializer;
584604
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
585-
write_log();
605+
write_log(!quiet);
586606
return EXIT_FAILURE;
587607
}
588608

589609
time_t start,end;
590610
time(&start);
591611

592-
if (!init_input_file(input_filename, ifc_file, no_progress, mmap)) {
612+
if (!init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
593613
return EXIT_FAILURE;
594614
}
595615

@@ -600,7 +620,7 @@ int main(int argc, char** argv)
600620
Logger::Error("No geometrical entities found");
601621
delete serializer;
602622
std::remove(output_temp_filename.c_str()); /**< @todo Windows Unicode support */
603-
write_log();
623+
write_log(!quiet);
604624
return EXIT_FAILURE;
605625
}
606626

@@ -614,7 +634,7 @@ int main(int argc, char** argv)
614634

615635
serializer->writeHeader();
616636

617-
int old_progress = -1;
637+
int old_progress = quiet ? 0 : -1;
618638

619639
if (center_model || model_offset) {
620640
double* offset = serializer->settings().offset;
@@ -643,7 +663,9 @@ int main(int argc, char** argv)
643663
Logger::Notice(msg.str());
644664
}
645665

646-
Logger::Status("Creating geometry...");
666+
if (!quiet) {
667+
Logger::Status("Creating geometry...");
668+
}
647669

648670
// The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next()
649671
// wrap an iterator of all geometrical products in the Ifc file.
@@ -670,14 +692,28 @@ int main(int argc, char** argv)
670692
}
671693

672694
if (!no_progress) {
673-
const int progress = context_iterator.progress() / 2;
674-
if (old_progress != progress) Logger::ProgressBar(progress);
675-
old_progress = progress;
695+
if (quiet) {
696+
const int progress = context_iterator.progress();
697+
for (; old_progress < progress; ++old_progress) {
698+
std::cout << ".";
699+
}
700+
std::cout << std::flush;
701+
} else {
702+
const int progress = context_iterator.progress() / 2;
703+
if (old_progress != progress) Logger::ProgressBar(progress);
704+
old_progress = progress;
705+
}
676706
}
677707
} while (++num_created, context_iterator.next());
678708

679-
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
680-
" objects) ");
709+
if (!no_progress && quiet) {
710+
for (; old_progress < 100; ++old_progress) {
711+
std::cout << ".";
712+
}
713+
} else {
714+
Logger::Status("\rDone creating geometry (" + boost::lexical_cast<std::string>(num_created) +
715+
" objects) ");
716+
}
681717

682718
serializer->finalize();
683719
delete serializer;
@@ -690,34 +726,39 @@ int main(int argc, char** argv)
690726
output_temp_filename + "' for the conversion result.");
691727
}
692728

693-
write_log();
729+
write_log(!quiet);
694730

695731
time(&end);
696732

697-
int seconds = (int)difftime(end, start);
698-
std::stringstream msg;
699-
int minutes = seconds / 60;
700-
seconds = seconds % 60;
701-
msg << "\nConversion took";
702-
if (minutes > 0) {
703-
msg << " " << minutes << " minute";
704-
if (minutes > 1) {
733+
if (!quiet) {
734+
int seconds = (int)difftime(end, start);
735+
std::stringstream msg;
736+
int minutes = seconds / 60;
737+
seconds = seconds % 60;
738+
msg << "\nConversion took";
739+
if (minutes > 0) {
740+
msg << " " << minutes << " minute";
741+
if (minutes > 1) {
742+
msg << "s";
743+
}
744+
}
745+
msg << " " << seconds << " second";
746+
if (seconds > 1) {
705747
msg << "s";
706748
}
749+
Logger::Status(msg.str());
707750
}
708-
msg << " " << seconds << " second";
709-
if (seconds > 1) {
710-
msg << "s";
711-
}
712-
Logger::Status(msg.str());
713751

714752
return successful ? EXIT_SUCCESS : EXIT_FAILURE;
715753
}
716754

717-
void write_log() {
755+
void write_log(bool header) {
718756
std::string log = log_stream.str();
719757
if (!log.empty()) {
720-
std::cout << "\nLog:\n" << log << std::endl;
758+
if (header) {
759+
std::cout << "\nLog:\n";
760+
}
761+
std::cout << log << std::endl;
721762
}
722763
}
723764

src/ifcparse/IfcLogger.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,47 @@
2323
#include <boost/algorithm/string/replace.hpp>
2424
#include <boost/optional.hpp>
2525

26+
#include <boost/property_tree/ptree.hpp>
27+
#include <boost/property_tree/json_parser.hpp>
28+
#include <boost/version.hpp>
29+
2630
#include <iostream>
2731
#include <algorithm>
2832

33+
using boost::property_tree::ptree;
34+
35+
namespace {
36+
static const char* severity_strings[] = {"Notice", "Warning", "Error"};
37+
38+
void plain_text_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
39+
os << "[" << severity_strings[type] << "] ";
40+
if (current_product) {
41+
os << "{" << (*current_product)->GlobalId() << "} ";
42+
}
43+
os << message << std::endl;
44+
if (entity) {
45+
os << entity->toString() << std::endl;
46+
}
47+
}
48+
49+
void json_message(std::ostream& os, const boost::optional<IfcSchema::IfcProduct*>& current_product, Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
50+
ptree pt;
51+
pt.put("level", severity_strings[type]);
52+
if (current_product) {
53+
pt.put("product", (**current_product).entity->toString());
54+
}
55+
pt.put("message", message);
56+
if (entity) {
57+
pt.put("instance", entity);
58+
}
59+
boost::property_tree::write_json(os, pt, false);
60+
}
61+
}
2962

3063
void Logger::SetProduct(boost::optional<IfcSchema::IfcProduct*> product) {
3164
current_product = product;
3265
}
66+
3367
void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
3468
log1 = l1;
3569
log2 = l2;
@@ -39,13 +73,12 @@ void Logger::SetOutput(std::ostream* l1, std::ostream* l2) {
3973
}
4074

4175
void Logger::Message(Logger::Severity type, const std::string& message, IfcEntityInstanceData* entity) {
42-
if ( log2 && type >= verbosity ) {
43-
(*log2) << "[" << severity_strings[type] << "] ";
44-
if ( current_product ) {
45-
(*log2) << "{" << (*current_product)->GlobalId() << "} ";
76+
if (log2 && type >= verbosity) {
77+
if (format == FMT_PLAIN) {
78+
plain_text_message(*log2, current_product, type, message, entity);
79+
} else if (format == FMT_JSON) {
80+
json_message(*log2, current_product, type, message, entity);
4681
}
47-
(*log2) << message << std::endl;
48-
if ( entity ) (*log2) << entity->toString() << std::endl;
4982
}
5083
}
5184

@@ -54,26 +87,32 @@ void Logger::Message(Logger::Severity type, const std::exception& exception, Ifc
5487
}
5588

5689
void Logger::Status(const std::string& message, bool new_line) {
57-
if ( log1 ) {
90+
if (log1) {
5891
(*log1) << message;
5992
if ( new_line ) (*log1) << std::endl;
6093
else (*log1) << std::flush;
6194
}
6295
}
96+
6397
void Logger::ProgressBar(int progress) {
64-
if ( log1 ) {
98+
if (log1) {
6599
Status("\r[" + std::string(progress,'#') + std::string(50 - progress,' ') + "]", false);
66100
}
67101
}
102+
68103
std::string Logger::GetLog() {
69104
return log_stream.str();
70105
}
106+
71107
void Logger::Verbosity(Logger::Severity v) { verbosity = v; }
72108
Logger::Severity Logger::Verbosity() { return verbosity; }
73109

110+
void Logger::OutputFormat(Format f) { format = f; }
111+
Logger::Format Logger::OutputFormat() { return format; }
112+
74113
std::ostream* Logger::log1 = 0;
75114
std::ostream* Logger::log2 = 0;
76115
std::stringstream Logger::log_stream;
77116
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
78-
const char* Logger::severity_strings[] = { "Notice","Warning","Error" };
117+
Logger::Format Logger::format = Logger::FMT_PLAIN;
79118
boost::optional<IfcSchema::IfcProduct*> Logger::current_product;

0 commit comments

Comments
 (0)