]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common: add JSONFormatterFile class
authorPatrick Donnelly <pdonnell@redhat.com>
Sat, 17 Feb 2024 15:22:29 +0000 (10:22 -0500)
committerPatrick Donnelly <pdonnell@redhat.com>
Fri, 22 Mar 2024 15:38:01 +0000 (11:38 -0400)
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 <pdonnell@redhat.com>
(cherry picked from commit 8928a1266fc6bc492dac34253edb36c045e491e5)

src/common/Formatter.cc
src/common/Formatter.h

index f121afa07a3e386ccbabde7a7b5d614d8fe695ba..b43e04a8bd09f2fad9b0a1a478f4bed1dda5edec 100644 (file)
@@ -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 =
index cddfdab0c4f0ffbdddabd850f6db9033f2c8a46e..65ffb0a68551edeed5a783e0fdf524d172e7e805 100644 (file)
@@ -7,6 +7,7 @@
 #include "include/buffer_fwd.h"
 
 #include <deque>
+#include <fstream>
 #include <list>
 #include <memory>
 #include <vector>
@@ -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<json_formatter_stack_entry_d> 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 <class T>
   void add_value(std::string_view name, T val);