Skip to content

Commit 1e6ce0b

Browse files
author
Thomas Kiley
authored
Merge pull request #5240 from thk123/logging-structured-data
Logging structured data
2 parents 8db2b8b + 1879a8d commit 1e6ce0b

18 files changed

+750
-23
lines changed

src/goto-checker/symex_bmc_incremental_one_loop.cpp

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -137,27 +137,8 @@ bool symex_bmc_incremental_one_loopt::resume(
137137
}
138138
void symex_bmc_incremental_one_loopt::log_unwinding(unsigned unwind)
139139
{
140-
const std::string unwind_num = std::to_string(unwind);
141-
switch(output_ui)
142-
{
143-
case ui_message_handlert::uit::PLAIN:
144-
{
145-
log.statistics() << "Current unwinding: " << unwind_num << messaget::eom;
146-
break;
147-
}
148-
case ui_message_handlert::uit::XML_UI:
149-
{
150-
xmlt xml("current-unwinding");
151-
xml.data = unwind_num;
152-
log.statistics() << xml << messaget::eom;
153-
break;
154-
}
155-
case ui_message_handlert::uit::JSON_UI:
156-
{
157-
json_objectt json;
158-
json["currentUnwinding"] = json_numbert(unwind_num);
159-
log.statistics() << json << messaget::eom;
160-
break;
161-
}
162-
}
140+
structured_datat data{{{labelt({"current", "unwinding"}),
141+
structured_data_entryt::data_node(
142+
json_numbert{std::to_string(unwind)})}}};
143+
log.statistics() << data;
163144
}

src/util/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ SRC = allocate_objects.cpp \
8787
string_container.cpp \
8888
string_hash.cpp \
8989
string_utils.cpp \
90+
structured_data.cpp \
9091
symbol.cpp \
9192
symbol_table_base.cpp \
9293
symbol_table.cpp \

src/util/json.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Author: Daniel Kroening, [email protected]
88

99
#include "json.h"
1010

11+
#include "structured_data.h"
12+
1113
#include <algorithm>
1214
#include <ostream>
1315

@@ -204,3 +206,31 @@ bool operator==(const jsont &left, const jsont &right)
204206
}
205207
UNREACHABLE;
206208
}
209+
210+
jsont json_node(const structured_data_entryt &entry)
211+
{
212+
if(entry.is_leaf())
213+
return entry.leaf_object();
214+
else
215+
{
216+
json_objectt result;
217+
for(const auto sub_entry : entry.children())
218+
{
219+
result[sub_entry.first.camel_case()] = json_node(sub_entry.second);
220+
}
221+
return std::move(result);
222+
}
223+
}
224+
225+
jsont to_json(const structured_datat &data)
226+
{
227+
if(data.data().size() == 0)
228+
return jsont{};
229+
230+
json_objectt result;
231+
for(const auto sub_entry : data.data())
232+
{
233+
result[sub_entry.first.camel_case()] = json_node(sub_entry.second);
234+
}
235+
return std::move(result);
236+
}

src/util/json.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Author: Daniel Kroening, [email protected]
1818
#include "irep.h"
1919
#include "range.h"
2020

21+
class structured_datat;
22+
2123
class json_objectt;
2224
class json_arrayt;
2325

@@ -465,4 +467,27 @@ inline const json_stringt &to_json_string(const jsont &json)
465467

466468
bool operator==(const jsont &left, const jsont &right);
467469

470+
/// Convert the structured_datat into an json object. For example, the
471+
/// structured data:
472+
/// structured_datat data{
473+
/// {{labelt{{"my", "data"}},
474+
/// structured_data_entryt::entry(
475+
/// {{labelt{{"my", "number"}},
476+
/// structured_data_entryt::data_node(json_numbert("10"))},
477+
/// {labelt{{"my", "string"}},
478+
/// structured_data_entryt::data_node(json_stringt("hi"))}})}}};
479+
///
480+
/// Will produce:
481+
/// ```json
482+
/// {
483+
/// "myData": {
484+
/// "myNumber": 10
485+
/// "myString": "hi"
486+
/// }
487+
/// }
488+
/// ```
489+
/// \param data: The structured data to convert.
490+
/// \return The json object as specified.
491+
jsont to_json(const structured_datat &data);
492+
468493
#endif // CPROVER_UTIL_JSON_H

src/util/message.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ void message_handlert::print(
6464
message_count.resize(level+1, 0);
6565
++message_count[level];
6666
}
67+
void message_handlert::print(unsigned level, const structured_datat &data)
68+
{
69+
// default to just printing out the data in a format
70+
print(level, to_pretty(data));
71+
}
6772

6873
messaget::~messaget()
6974
{

src/util/message.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Author: Daniel Kroening, [email protected]
1818
#include "deprecate.h"
1919
#include "invariant.h"
2020
#include "source_location.h"
21+
#include "structured_data.h"
2122

2223
class json_objectt;
2324
class jsont;
@@ -36,6 +37,8 @@ class message_handlert
3637

3738
virtual void print(unsigned level, const jsont &json) = 0;
3839

40+
virtual void print(unsigned level, const structured_datat &data);
41+
3942
virtual void print(
4043
unsigned level,
4144
const std::string &message,
@@ -256,6 +259,17 @@ class messaget
256259

257260
mstreamt &operator<<(const json_objectt &data);
258261

262+
mstreamt &operator<<(const structured_datat &data)
263+
{
264+
if(this->tellp() > 0)
265+
*this << eom; // force end of previous message
266+
if(message.message_handler)
267+
{
268+
message.message_handler->print(message_level, data);
269+
}
270+
return *this;
271+
}
272+
259273
template <class T>
260274
mstreamt &operator << (const T &x)
261275
{

src/util/string_utils.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,11 @@ std::string escape_non_alnum(const std::string &to_escape)
167167
}
168168
return escaped.str();
169169
}
170+
std::string capitalize(const std::string &str)
171+
{
172+
if(str.empty())
173+
return str;
174+
std::string capitalized = str;
175+
capitalized[0] = toupper(capitalized[0]);
176+
return capitalized;
177+
}

src/util/string_utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Author: Daniel Poetzl
1818

1919
std::string strip_string(const std::string &s);
2020

21+
std::string capitalize(const std::string &str);
22+
2123
/// Given a string s, split into a sequence of substrings when separated by
2224
/// specified delimiter.
2325
/// \param s: The string to split up

src/util/structured_data.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*******************************************************************\
2+
3+
Module: Classes for representing generic structured data
4+
5+
Author: Thomas Kiley
6+
7+
\*******************************************************************/
8+
9+
#include "structured_data.h"
10+
#include "range.h"
11+
#include "string_utils.h"
12+
#include <algorithm>
13+
14+
labelt::labelt(std::vector<std::string> components) : components(components)
15+
{
16+
PRECONDITION(!components.empty());
17+
PRECONDITION(std::all_of(
18+
components.begin(), components.end(), [](const std::string &component) {
19+
return !component.empty() &&
20+
std::none_of(component.begin(), component.end(), ::isupper);
21+
}));
22+
}
23+
24+
std::string labelt::camel_case() const
25+
{
26+
std::ostringstream output;
27+
output << *components.begin();
28+
join_strings(
29+
output, std::next(components.begin()), components.end(), "", capitalize);
30+
return output.str();
31+
}
32+
33+
std::string labelt::snake_case() const
34+
{
35+
std::ostringstream output;
36+
join_strings(output, components.begin(), components.end(), '_');
37+
return output.str();
38+
}
39+
40+
std::string labelt::kebab_case() const
41+
{
42+
std::ostringstream output;
43+
join_strings(output, components.begin(), components.end(), '-');
44+
return output.str();
45+
}
46+
47+
std::string labelt::pretty() const
48+
{
49+
std::ostringstream output;
50+
const auto range =
51+
make_range(components.begin(), std::next(components.begin()))
52+
.map(capitalize)
53+
.concat(make_range(std::next(components.begin()), components.end()));
54+
55+
join_strings(output, range.begin(), range.end(), ' ');
56+
return output.str();
57+
}
58+
59+
bool labelt::operator<(const labelt &other) const
60+
{
61+
return components < other.components;
62+
}
63+
64+
structured_data_entryt structured_data_entryt::data_node(const jsont &data)
65+
{
66+
// Structured data (e.g. arrays and objects) should use an entry
67+
PRECONDITION(!(data.is_array() || data.is_object()));
68+
return structured_data_entryt(data);
69+
}
70+
71+
structured_data_entryt
72+
structured_data_entryt::entry(std::map<labelt, structured_data_entryt> children)
73+
{
74+
return structured_data_entryt(std::move(children));
75+
}
76+
77+
structured_data_entryt::structured_data_entryt(jsont data)
78+
: data(std::move(data))
79+
{
80+
}
81+
82+
structured_data_entryt::structured_data_entryt(
83+
std::map<labelt, structured_data_entryt> children)
84+
: _children(std::move(children))
85+
{
86+
}
87+
88+
bool structured_data_entryt::is_leaf() const
89+
{
90+
return _children.empty();
91+
}
92+
93+
std::string structured_data_entryt::leaf_data() const
94+
{
95+
return data.value;
96+
}
97+
const std::map<labelt, structured_data_entryt> &
98+
structured_data_entryt::children() const
99+
{
100+
return _children;
101+
}
102+
103+
jsont structured_data_entryt::leaf_object() const
104+
{
105+
return data;
106+
}
107+
108+
structured_datat::structured_datat(
109+
std::map<labelt, structured_data_entryt> data)
110+
: _data(std::move(data))
111+
{
112+
}
113+
114+
std::vector<std::string>
115+
pretty_node(const std::pair<labelt, structured_data_entryt> &entry)
116+
{
117+
const labelt &label = entry.first;
118+
const structured_data_entryt &data = entry.second;
119+
if(data.is_leaf())
120+
{
121+
std::ostringstream line;
122+
line << label.pretty() << ": " << data.leaf_data();
123+
return {line.str()};
124+
}
125+
else
126+
{
127+
const auto indent = [](const std::string &line) { return "\t" + line; };
128+
129+
const auto &children = data.children();
130+
std::vector<std::vector<std::string>> lines =
131+
make_range(children)
132+
.map(pretty_node)
133+
.map([&](std::vector<std::string> sub_lines) {
134+
return make_range(sub_lines)
135+
.map(indent)
136+
.collect<std::vector<std::string>>();
137+
})
138+
.collect<std::vector<std::vector<std::string>>>();
139+
140+
std::vector<std::string> result;
141+
for(const auto &sub_lines : lines)
142+
{
143+
result.insert(result.end(), sub_lines.begin(), sub_lines.end());
144+
}
145+
return result;
146+
}
147+
}
148+
149+
std::string to_pretty(const structured_datat &data)
150+
{
151+
if(data.data().empty())
152+
return "";
153+
154+
std::vector<std::vector<std::string>> lines =
155+
make_range(data.data())
156+
.map(pretty_node)
157+
.collect<std::vector<std::vector<std::string>>>();
158+
std::vector<std::string> flattened_lines;
159+
for(const auto &line_section : lines)
160+
{
161+
flattened_lines.insert(
162+
flattened_lines.end(), line_section.begin(), line_section.end());
163+
}
164+
std::ostringstream output;
165+
join_strings(output, flattened_lines.begin(), flattened_lines.end(), "\n");
166+
return output.str();
167+
}
168+
169+
const std::map<labelt, structured_data_entryt> &structured_datat::data() const
170+
{
171+
return _data;
172+
}

0 commit comments

Comments
 (0)