Skip to content

Commit

Permalink
Make changeset printer util able to parse sync logs (#6375)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbreams authored Mar 27, 2023
1 parent 4aa1235 commit ea7b5d3
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 77 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
-----------

### Internals
* None.
* PrintChangeset sync CLI utility can now ingest trace-level log files and decode hex/base64-compressed changeset data within the logs ([#6375](https://github.com/realm/realm-core/pull/6375))

----------------------------------------------

Expand Down
182 changes: 106 additions & 76 deletions src/realm/sync/tools/print_changeset.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <string>
#include <iostream>

#include <realm/util/cli_args.hpp>
#include <realm/util/compression.hpp>
#include <realm/util/base64.hpp>
#include <realm/util/load_file.hpp>
#include <realm/sync/changeset.hpp>
#include <realm/sync/changeset_parser.hpp>

using namespace realm;

#if REALM_DEBUG
namespace {

sync::Changeset changeset_binary_to_sync_changeset(const std::string& changeset_binary)
Expand Down Expand Up @@ -70,9 +72,17 @@ std::string changeset_compressed_to_binary(const std::string& changeset_compress
return decompressed;
}

void print_changeset(const std::string& path, bool hex, bool compressed)
void parse_and_print_changeset(const std::string& changeset_binary)
{
sync::Changeset changeset = changeset_binary_to_sync_changeset(changeset_binary);
changeset.print(std::cout);
}

void print_changeset_in_file(std::istream& input_file, bool hex, bool compressed)
{
std::string file_contents = util::load_file(path);
std::stringstream input_file_ss;
input_file_ss << input_file.rdbuf();
const auto& file_contents = input_file_ss.str();
std::string changeset_binary;
if (hex) {
changeset_binary = changeset_hex_to_binary(file_contents);
Expand All @@ -83,92 +93,112 @@ void print_changeset(const std::string& path, bool hex, bool compressed)
else {
changeset_binary = file_contents;
}
sync::Changeset changeset = changeset_binary_to_sync_changeset(changeset_binary);
#if REALM_DEBUG
changeset.print(std::cout);
#else
std::cout << "changeset printing is disabled in Release mode, build in Debug mode to use this tool" << std::endl;
#endif
parse_and_print_changeset(changeset_binary);
}

} // namespace

int main(int argc, char* argv[])
void print_changesets_in_log_file(std::istream& input_file)
{
std::string changeset_path;
bool hex = false;
bool compressed = false;

// Process command line
{
const char* prog = argv[0];
--argc;
++argv;
bool error = false;
bool help = false;
int argc_2 = 0;
int i = 0;
char* arg = nullptr;
auto get_string_value = [&](std::string& var) {
if (i < argc) {
var = argv[i++];
return true;
}
return false;
};
while (i < argc) {
arg = argv[i++];
if (arg[0] != '-') {
argv[argc_2++] = arg;
continue;
int log_line_num = 1;
try {
for (std::string line; std::getline(input_file, line); ++log_line_num) {
const std::string_view changeset_prefix("Changeset: ");
const std::string_view compressed_changeset_prefix("Changeset(comp): ");
std::string changeset_contents;
if (auto pos = line.find(changeset_prefix); pos != std::string::npos) {
changeset_contents = changeset_hex_to_binary(line.substr(pos + changeset_prefix.size()));
}
if (std::strcmp(arg, "-h") == 0 || std::strcmp(arg, "--help") == 0) {
help = true;
continue;
else if (auto pos = line.find(compressed_changeset_prefix); pos != std::string::npos) {
changeset_contents =
changeset_compressed_to_binary(line.substr(pos + compressed_changeset_prefix.size()));
}
else if (std::strcmp(arg, "-H") == 0 || std::strcmp(arg, "--hex") == 0) {
hex = true;
else {
std::cout << line << std::endl;
continue;
}
else if (std::strcmp(arg, "-C") == 0 || std::strcmp(arg, "--compressed") == 0) {
compressed = true;
continue;
}
std::cerr << "ERROR: Unknown option: " << arg << "\n";
error = true;
parse_and_print_changeset(changeset_contents);
}
argc = argc_2;
}
catch (const Exception& e) {
throw RuntimeError(e.code(), util::format("Exception at line number %1: %2", log_line_num, e.to_status()));
}
catch (const std::exception& e) {
throw RuntimeError(ErrorCodes::RuntimeError,
util::format("Exception at line number %1: %2", log_line_num, e.what()));
}
}

void print_help(std::string_view prog_name)
{
std::cerr << "Synopsis: " << prog_name
<< " [changeset file]\n"
"\n"
"Where <changeset file> is the file system path of a file containing a\n"
"changeset encoded in hex/base64 compressed format or sync client trace-level log output.\n"
"If no changeset file is given, input shall be read from stdin.\n"
"\n"
"Options:\n"
" -h, --help Display command-line synopsis followed by the list of\n"
" available options.\n"
" -H, --hex Interpret file contents as hex encoded.\n"
" -C, --compressed Interpret file contents as Base64 encoded and compressed.\n"
" -l, -input-is-logfile Read input from stdin as a trace-level log file\n";
}

i = 0;
if (!get_string_value(changeset_path)) {
error = true;
} // namespace
#endif

int main(int argc, const char** argv)
{
#if !REALM_DEBUG
static_cast<void>(argc);
static_cast<void>(argv);
util::format(std::cerr, "changeset printing is disabled in Release mode, build in Debug mode to use this tool\n");
return EXIT_FAILURE;
#else
util::CliArgumentParser arg_parser;
util::CliFlag hex(arg_parser, "hex", 'H');
util::CliFlag compressed(arg_parser, "compressed", 'C');
util::CliFlag help(arg_parser, "help", 'h');
util::CliFlag as_logs(arg_parser, "input-is-logfile", 'l');

std::fstream changeset_input_file;
std::istream* changeset_input = &std::cin;
try {
auto arg_result = arg_parser.parse(argc, argv);
if (arg_result.unmatched_arguments.size() > 1) {
throw std::runtime_error(
util::format("Expected one input argument, got %1", arg_result.unmatched_arguments.size()));
}
else if (i < argc) {
error = true;
else if (arg_result.unmatched_arguments.size() == 1) {
std::string file_path(arg_result.unmatched_arguments.front());
if (file_path.empty() || file_path.front() == '-') {
throw std::runtime_error(util::format("Expected path to file, got \"%1\"", file_path));
}
changeset_input_file.open(file_path);
changeset_input = &changeset_input_file;
}
}
catch (const std::runtime_error& e) {
util::format(std::cerr, "Error parsing arguments: %1\n", e.what());
return EXIT_FAILURE;
}

if (help) {
std::cerr << "Synopsis: " << prog
<< " <changeset file>\n"
"\n"
"Where <changeset file> is the file system path of a file containing a\n"
"changeset, possibly in hex format.\n"
"\n"
"Options:\n"
" -h, --help Display command-line synopsis followed by the list of\n"
" available options.\n"
" -H, --hex Interpret file contents as hex encoded.\n"
" -C, --compressed Interpret file contents as Base64 encoded and compressed.\n";
return EXIT_SUCCESS;
}
if (help) {
print_help(argv[0]);
return EXIT_SUCCESS;
}

if (error) {
std::cerr << "ERROR: Bad command line.\n"
"Try `"
<< prog << " --help`\n";
return EXIT_FAILURE;
try {
if (as_logs) {
print_changesets_in_log_file(*changeset_input);
}
else {
print_changeset_in_file(*changeset_input, static_cast<bool>(hex), static_cast<bool>(compressed));
}
}

print_changeset(changeset_path, hex, compressed);
catch (const std::exception& e) {
util::format(std::cerr, "Error parsing/printing changesets: %1", e.what());
return EXIT_FAILURE;
}
#endif
}
1 change: 1 addition & 0 deletions src/realm/util/cli_args.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class CliArgument : public CliFlag {
};

class CliParseException : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};

Expand Down

0 comments on commit ea7b5d3

Please sign in to comment.