From: Patrick Donnelly Date: Sat, 17 Feb 2024 15:22:29 +0000 (-0500) Subject: common: add JSONFormatterFile class X-Git-Tag: testing/wip-root-testing-20240411.174241~89^2~62 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=422ab0a5aa46e7e15e4ff9f228e463a97c6e988c;p=ceph-ci.git common: add JSONFormatterFile class To stream JSON to a file for collection (for testing). The idea here that trying to send a very large JSON result over `ceph tell` is best to be avoided. Signed-off-by: Patrick Donnelly (cherry picked from commit 8928a1266fc6bc492dac34253edb36c045e491e5) --- diff --git a/src/common/Formatter.cc b/src/common/Formatter.cc index f121afa07a3..b43e04a8bd0 100644 --- a/src/common/Formatter.cc +++ b/src/common/Formatter.cc @@ -148,12 +148,6 @@ void Formatter::dump_format_unquoted(std::string_view name, const char *fmt, ... // ----------------------- -JSONFormatter::JSONFormatter(bool p) -: m_pretty(p), m_is_pending_string(false) -{ - reset(); -} - void JSONFormatter::flush(std::ostream& os) { finish_pending_string(); @@ -175,30 +169,33 @@ void JSONFormatter::reset() void JSONFormatter::print_comma(json_formatter_stack_entry_d& entry) { + auto& ss = get_ss(); if (entry.size) { if (m_pretty) { - m_ss << ",\n"; + ss << ",\n"; for (unsigned i = 1; i < m_stack.size(); i++) - m_ss << " "; + ss << " "; } else { - m_ss << ","; + ss << ","; } } else if (m_pretty) { - m_ss << "\n"; + ss << "\n"; for (unsigned i = 1; i < m_stack.size(); i++) - m_ss << " "; + ss << " "; } if (m_pretty && entry.is_array) - m_ss << " "; + ss << " "; } void JSONFormatter::print_quoted_string(std::string_view s) { - m_ss << '\"' << json_stream_escaper(s) << '\"'; + auto& ss = get_ss(); + ss << '\"' << json_stream_escaper(s) << '\"'; } void JSONFormatter::print_name(std::string_view name) { + auto& ss = get_ss(); finish_pending_string(); if (m_stack.empty()) return; @@ -206,19 +203,20 @@ void JSONFormatter::print_name(std::string_view name) print_comma(entry); if (!entry.is_array) { if (m_pretty) { - m_ss << " "; + ss << " "; } - m_ss << "\"" << name << "\""; + ss << "\"" << name << "\""; if (m_pretty) - m_ss << ": "; + ss << ": "; else - m_ss << ':'; + ss << ':'; } ++entry.size; } void JSONFormatter::open_section(std::string_view name, const char *ns, bool is_array) { + auto& ss = get_ss(); if (handle_open_section(name, ns, is_array)) { return; } @@ -230,9 +228,9 @@ void JSONFormatter::open_section(std::string_view name, const char *ns, bool is_ print_name(name); } if (is_array) - m_ss << '['; + ss << '['; else - m_ss << '{'; + ss << '{'; json_formatter_stack_entry_d n; n.is_array = is_array; @@ -261,7 +259,7 @@ void JSONFormatter::open_object_section_in_ns(std::string_view name, const char void JSONFormatter::close_section() { - + auto& ss = get_ss(); if (handle_close_section()) { return; } @@ -270,14 +268,14 @@ void JSONFormatter::close_section() struct json_formatter_stack_entry_d& entry = m_stack.back(); if (m_pretty && entry.size) { - m_ss << "\n"; + ss << "\n"; for (unsigned i = 1; i < m_stack.size(); i++) - m_ss << " "; + ss << " "; } - m_ss << (entry.is_array ? ']' : '}'); + ss << (entry.is_array ? ']' : '}'); m_stack.pop_back(); if (m_pretty && m_stack.empty()) - m_ss << "\n"; + ss << "\n"; } void JSONFormatter::finish_pending_string() @@ -300,12 +298,13 @@ void JSONFormatter::add_value(std::string_view name, T val) void JSONFormatter::add_value(std::string_view name, std::string_view val, bool quoted) { + auto& ss = get_ss(); if (handle_value(name, val, quoted)) { return; } print_name(name); if (!quoted) { - m_ss << val; + ss << val; } else { print_quoted_string(val); } @@ -354,12 +353,12 @@ void JSONFormatter::dump_format_va(std::string_view name, const char *ns, bool q int JSONFormatter::get_len() const { - return m_ss.str().size(); + return m_ss.tellp(); } void JSONFormatter::write_raw_data(const char *data) { - m_ss << data; + get_ss() << data; } const char *XMLFormatter::XML_1_DTD = diff --git a/src/common/Formatter.h b/src/common/Formatter.h index cddfdab0c4f..65ffb0a6855 100644 --- a/src/common/Formatter.h +++ b/src/common/Formatter.h @@ -7,6 +7,7 @@ #include "include/buffer_fwd.h" #include +#include #include #include #include @@ -130,21 +131,52 @@ namespace ceph { virtual void write_bin_data(const char* buff, int buf_len); }; - class copyable_sstream : public std::stringstream { + class JSONFormatter : public Formatter { public: - copyable_sstream() {} - copyable_sstream(const copyable_sstream& rhs) { - str(rhs.str()); + explicit JSONFormatter(bool p = false) : m_pretty(p) {} + JSONFormatter(const JSONFormatter& f) : + m_pretty(f.m_pretty), + m_pending_name(f.m_pending_name), + m_stack(f.m_stack), + m_is_pending_string(f.m_is_pending_string), + m_line_break_enabled(f.m_line_break_enabled) + { + m_ss.str(f.m_ss.str()); + m_pending_string.str(f.m_pending_string.str()); + } + JSONFormatter(JSONFormatter&& f) : + m_pretty(f.m_pretty), + m_ss(std::move(f.m_ss)), + m_pending_string(std::move(f.m_pending_string)), + m_pending_name(f.m_pending_name), + m_stack(std::move(f.m_stack)), + m_is_pending_string(f.m_is_pending_string), + m_line_break_enabled(f.m_line_break_enabled) + { } - copyable_sstream& operator=(const copyable_sstream& rhs) { - str(rhs.str()); + JSONFormatter& operator=(const JSONFormatter& f) + { + m_pretty = f.m_pretty; + m_ss.str(f.m_ss.str()); + m_pending_string.str(f.m_pending_string.str()); + m_pending_name = f.m_pending_name; + m_stack = f.m_stack; + m_is_pending_string = f.m_is_pending_string; + m_line_break_enabled = f.m_line_break_enabled; return *this; } - }; - class JSONFormatter : public Formatter { - public: - explicit JSONFormatter(bool p = false); + JSONFormatter& operator=(JSONFormatter&& f) + { + m_pretty = f.m_pretty; + m_ss = std::move(f.m_ss); + m_pending_string = std::move(f.m_pending_string); + m_pending_name = f.m_pending_name; + m_stack = std::move(f.m_stack); + m_is_pending_string = f.m_is_pending_string; + m_line_break_enabled = f.m_line_break_enabled; + return *this; + } void set_status(int status, const char* status_name) override {}; void output_header() override {}; @@ -183,15 +215,18 @@ namespace ceph { int stack_size() { return m_stack.size(); } + virtual std::ostream& get_ss() { + return m_ss; + } + private: struct json_formatter_stack_entry_d { - int size; - bool is_array; - json_formatter_stack_entry_d() : size(0), is_array(false) { } + int size = 0; + bool is_array = false; }; - bool m_pretty; + bool m_pretty = false; void open_section(std::string_view name, const char *ns, bool is_array); void print_quoted_string(std::string_view s); void print_name(std::string_view name); @@ -202,14 +237,44 @@ namespace ceph { void add_value(std::string_view name, T val); void add_value(std::string_view name, std::string_view val, bool quoted); - copyable_sstream m_ss; - copyable_sstream m_pending_string; + mutable std::stringstream m_ss; // mutable for get_len + std::stringstream m_pending_string; std::string m_pending_name; std::vector m_stack; - bool m_is_pending_string; + bool m_is_pending_string = false; bool m_line_break_enabled = false; }; + class JSONFormatterFile : public JSONFormatter { +public: + JSONFormatterFile(const std::string& path, bool pretty=false) : + JSONFormatter(pretty), + path(path), + file(path, std::ios::out | std::ios::trunc) + { + } + ~JSONFormatterFile() { + file.flush(); + } + + void reset() override { + JSONFormatter::reset(); + file = std::ofstream(path, std::ios::out | std::ios::trunc); + } + int get_len() const override { + return file.tellp(); + } + +protected: + std::ostream& get_ss() override { + return file; + } + +private: + std::string path; + mutable std::ofstream file; // mutable for get_len + }; + template void add_value(std::string_view name, T val);