diff --git a/src/applications/bmqstoragetool/bmqstoragetool.m.cpp b/src/applications/bmqstoragetool/bmqstoragetool.m.cpp index 2fe44db4f..6058a088a 100644 --- a/src/applications/bmqstoragetool/bmqstoragetool.m.cpp +++ b/src/applications/bmqstoragetool/bmqstoragetool.m.cpp @@ -57,6 +57,12 @@ parseArgs(CommandLineArguments& arguments, int argc, const char* argv[]) "path to a .bmq_csl file", balcl::TypeInfo(&arguments.d_cslFile), balcl::OccurrenceInfo::e_OPTIONAL}, + {"print-mode", + "print mode", + "can be one of the following: [HUMAN, JSON_PRETTY, JSON_LINE]. " + "Defailt value is HUMAN", + balcl::TypeInfo(&arguments.d_printMode), + balcl::OccurrenceInfo::e_OPTIONAL}, {"guid", "guid", "message guid", diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.cpp b/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.cpp index 4491f2369..c9857969b 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.cpp +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.cpp @@ -53,59 +53,11 @@ class AppKeyMatcher { } }; -/// Print the specified message record `rec` and QueueInfo pointed by the -/// specified `queueInfo_p` to the specified `stream`, using the specified -/// `allocator` for memory allocation. -void printRecord(bsl::ostream& stream, - const mqbs::MessageRecord& rec, - const bmqp_ctrlmsg::QueueInfo* queueInfo_p, - bslma::Allocator* allocator) -{ - bsl::vector fields(allocator); - fields.push_back("PrimaryLeaseId"); - fields.push_back("SequenceNumber"); - fields.push_back("Timestamp"); - fields.push_back("Epoch"); - fields.push_back("FileKey"); - fields.push_back("QueueKey"); - if (queueInfo_p) - fields.push_back("QueueUri"); - fields.push_back("RefCount"); - fields.push_back("MsgOffsetDwords"); - fields.push_back("GUID"); - fields.push_back("Crc32c"); - - bmqu::AlignedPrinter printer(stream, &fields); - printer << rec.header().primaryLeaseId() << rec.header().sequenceNumber(); - - bsls::Types::Uint64 epochValue = rec.header().timestamp(); - bdlt::Datetime datetime; - const int rc = bdlt::EpochUtil::convertFromTimeT64(&datetime, epochValue); - if (0 != rc) { - printer << 0; - } - else { - printer << datetime; - } - - bmqu::MemOutStream fileKeyStr(allocator), queueKeyStr(allocator); - fileKeyStr << rec.fileKey(); - queueKeyStr << rec.queueKey(); - - printer << epochValue << fileKeyStr.str() << queueKeyStr.str(); - if (queueInfo_p) - printer << queueInfo_p->uri(); - printer << rec.refCount() << rec.messageOffsetDwords() << rec.messageGUID() - << rec.crc32c(); - - stream << "\n"; -} - /// Find AppId in the specified `appIds` by the specified `appKey` and store /// the result in the specified `appId`. Return `true` on success and `false /// otherwise. -bool findQueueAppIdByAppKey( - bsl::string* appId, +static bool findQueueAppIdByAppKey( + bsl::string_view* appId, const bsl::vector& appIds, const mqbu::StorageKey& appKey) { @@ -125,110 +77,6 @@ bool findQueueAppIdByAppKey( return false; } -/// Print the specified confirm record `rec` and QueueInfo pointed by the -/// specified `queueInfo_p` to the specified `stream`, using the specified -/// `allocator` for memory allocation. -void printRecord(bsl::ostream& stream, - const mqbs::ConfirmRecord& rec, - const bmqp_ctrlmsg::QueueInfo* queueInfo_p, - bslma::Allocator* allocator) -{ - bsl::vector fields(allocator); - fields.push_back("PrimaryLeaseId"); - fields.push_back("SequenceNumber"); - fields.push_back("Timestamp"); - fields.push_back("Epoch"); - fields.push_back("QueueKey"); - if (queueInfo_p) - fields.push_back("QueueUri"); - fields.push_back("AppKey"); - if (queueInfo_p) - fields.push_back("AppId"); - fields.push_back("GUID"); - - bmqu::MemOutStream queueKeyStr(allocator), appKeyStr(allocator); - queueKeyStr << rec.queueKey(); - - if (rec.appKey().isNull()) { - appKeyStr << "** NULL **"; - } - else { - appKeyStr << rec.appKey(); - } - - bsl::string appIdStr; - if (queueInfo_p) { - if (!findQueueAppIdByAppKey(&appIdStr, - queueInfo_p->appIds(), - rec.appKey())) { - appIdStr = "** NULL **"; - } - } - - bmqu::AlignedPrinter printer(stream, &fields); - printer << rec.header().primaryLeaseId() << rec.header().sequenceNumber(); - - bsls::Types::Uint64 epochValue = rec.header().timestamp(); - bdlt::Datetime datetime; - const int rc = bdlt::EpochUtil::convertFromTimeT64(&datetime, epochValue); - if (0 != rc) { - printer << 0; - } - else { - printer << datetime; - } - - printer << epochValue << queueKeyStr.str(); - if (queueInfo_p) - printer << queueInfo_p->uri(); - printer << appKeyStr.str(); - if (queueInfo_p) - printer << appIdStr; - printer << rec.messageGUID(); - stream << "\n"; -} - -/// Print the specified delete record `rec` and QueueInfo pointed by the -/// specified `queueInfo_p` to the specified `stream`, using the specified -/// `allocator` for memory allocation. -void printRecord(bsl::ostream& stream, - const mqbs::DeletionRecord& rec, - const bmqp_ctrlmsg::QueueInfo* queueInfo_p, - bslma::Allocator* allocator) -{ - bsl::vector fields(allocator); - fields.push_back("PrimaryLeaseId"); - fields.push_back("SequenceNumber"); - fields.push_back("Timestamp"); - fields.push_back("Epoch"); - fields.push_back("QueueKey"); - if (queueInfo_p) - fields.push_back("QueueUri"); - fields.push_back("DeletionFlag"); - fields.push_back("GUID"); - - bmqu::MemOutStream queueKeyStr(allocator); - queueKeyStr << rec.queueKey(); - - bmqu::AlignedPrinter printer(stream, &fields); - printer << rec.header().primaryLeaseId() << rec.header().sequenceNumber(); - - bsls::Types::Uint64 epochValue = rec.header().timestamp(); - bdlt::Datetime datetime; - const int rc = bdlt::EpochUtil::convertFromTimeT64(&datetime, epochValue); - if (0 != rc) { - printer << 0; - } - else { - printer << datetime; - } - printer << epochValue << queueKeyStr.str(); - if (queueInfo_p) - printer << queueInfo_p->uri(); - printer << rec.deletionRecordFlag() << rec.messageGUID(); - stream << "\n"; -} - } // close unnamed namespace // ===================== @@ -239,13 +87,22 @@ void printRecord(bsl::ostream& stream, MessageDetails::MessageDetails(const mqbs::MessageRecord& record, bsls::Types::Uint64 recordIndex, bsls::Types::Uint64 recordOffset, + const QueueMap& queueMap, bslma::Allocator* allocator) : d_messageRecord( RecordDetails(record, recordIndex, recordOffset)) , d_confirmRecords(allocator) +, d_deleteRecord() +, d_queueInfo_p(0) , d_allocator_p(allocator) { - // NOTHING + // Check if queueInfo is present for queue key + bmqp_ctrlmsg::QueueInfo queueInfo(d_allocator_p); + if (queueMap.findInfoByKey(&queueInfo, + d_messageRecord.d_record.queueKey())) { + d_queueInfo_p = &queueInfo; + d_messageRecord.d_queueUri = d_queueInfo_p->uri(); + } } void MessageDetails::addConfirmRecord(const mqbs::ConfirmRecord& record, @@ -254,62 +111,57 @@ void MessageDetails::addConfirmRecord(const mqbs::ConfirmRecord& record, { d_confirmRecords.push_back( RecordDetails(record, recordIndex, recordOffset)); + if (d_queueInfo_p) { + RecordDetails& details = + *d_confirmRecords.rbegin(); + details.d_queueUri = d_queueInfo_p->uri(); + if (!findQueueAppIdByAppKey(&details.d_appId, + d_queueInfo_p->appIds(), + record.appKey())) { + details.d_appId = "** NULL **"; + } + } } void MessageDetails::addDeleteRecord(const mqbs::DeletionRecord& record, bsls::Types::Uint64 recordIndex, bsls::Types::Uint64 recordOffset) { - d_deleteRecord = RecordDetails(record, - recordIndex, - recordOffset); + d_deleteRecord.makeValueInplace( + RecordDetails(record, + recordIndex, + recordOffset)); + if (d_queueInfo_p) + d_deleteRecord->d_queueUri = d_queueInfo_p->uri(); } -void MessageDetails::print(bsl::ostream& os, const QueueMap& queueMap) const +unsigned int MessageDetails::dataRecordOffset() const { - // Check if queueInfo is present for queue key - bmqp_ctrlmsg::QueueInfo queueInfo(d_allocator_p); - const bool queueInfoPresent = queueMap.findInfoByKey( - &queueInfo, - d_messageRecord.d_record.queueKey()); - bmqp_ctrlmsg::QueueInfo* queueInfo_p = queueInfoPresent ? &queueInfo : 0; - - // Print message record - bmqu::MemOutStream ss(d_allocator_p); - ss << "MESSAGE Record, index: " << d_messageRecord.d_recordIndex - << ", offset: " << d_messageRecord.d_recordOffset; - bsl::string delimiter(ss.length(), '=', d_allocator_p); - os << delimiter << '\n' << ss.str() << '\n'; - - printRecord(os, d_messageRecord.d_record, queueInfo_p, d_allocator_p); + return d_messageRecord.d_record.messageOffsetDwords(); +} - // Print confirmations records - if (!d_confirmRecords.empty()) { - bsl::vector >::const_iterator it = - d_confirmRecords.begin(); - for (; it != d_confirmRecords.end(); ++it) { - os << "CONFIRM Record, index: " << it->d_recordIndex - << ", offset: " << it->d_recordOffset << '\n'; - printRecord(os, it->d_record, queueInfo_p, d_allocator_p); - } - } +bsls::Types::Uint64 MessageDetails::messageRecordIndex() const +{ + return d_messageRecord.d_recordIndex; +} - // Print deletion record - if (d_deleteRecord.d_isValid) { - os << "DELETE Record, index: " << d_deleteRecord.d_recordIndex - << ", offset: " << d_deleteRecord.d_recordOffset << '\n'; - printRecord(os, d_deleteRecord.d_record, queueInfo_p, d_allocator_p); - } +const MessageDetails::RecordDetails& +MessageDetails::messageRecord() const +{ + return d_messageRecord; } -unsigned int MessageDetails::dataRecordOffset() const +const bsl::vector >& +MessageDetails::confirmRecords() const { - return d_messageRecord.d_record.messageOffsetDwords(); + return d_confirmRecords; } -bsls::Types::Uint64 MessageDetails::messageRecordIndex() const +const bdlb::NullableValue< + MessageDetails::RecordDetails >& +MessageDetails::deleteRecord() const { - return d_messageRecord.d_recordIndex; + return d_deleteRecord; } } // close package namespace diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.h b/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.h index b9c717daf..5d9c052d9 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.h +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_messagedetails.h @@ -26,9 +26,16 @@ // bmqstoragetool #include +// BMQ +#include + // MQB #include +// BDE +#include +#include + namespace BloombergLP { namespace m_bmqstoragetool { @@ -37,8 +44,8 @@ namespace m_bmqstoragetool { // ===================== class MessageDetails { - private: - // PRIVATE TYPES + public: + // PUBLIC TYPES // Value-semantic type representing message details. template @@ -50,18 +57,12 @@ class MessageDetails { // Index of the record from journal file. bsls::Types::Uint64 d_recordOffset; // Offset of the record from journal file. - bool d_isValid; - // A flag indicating if the object is in valid state. + bsl::string_view d_queueUri; + // URI of the string + bsl::string_view d_appId; // CREATORS - /// Default constructor - RecordDetails() - : d_isValid(false) - { - // NOTHING - } - /// Constructor with the specified `record`, `recordIndex` and /// `recordOffset`. RecordDetails(RECORD_TYPE record, @@ -70,19 +71,21 @@ class MessageDetails { : d_record(record) , d_recordIndex(recordIndex) , d_recordOffset(recordOffset) - , d_isValid(true) { // NOTHING } }; + private: // DATA RecordDetails d_messageRecord; // Message record bsl::vector > d_confirmRecords; // All the confirm records related to the `d_messageRecord` - RecordDetails d_deleteRecord; + bdlb::NullableValue > d_deleteRecord; // Delete record related to the `d_messageRecord` + bmqp_ctrlmsg::QueueInfo* d_queueInfo_p; + // A pointer to the QueueInfo of the message's queue bslma::Allocator* d_allocator_p; // Allocator used inside te class @@ -93,6 +96,7 @@ class MessageDetails { explicit MessageDetails(const mqbs::MessageRecord& record, bsls::Types::Uint64 recordIndex, bsls::Types::Uint64 recordOffset, + const QueueMap& queueMap, bslma::Allocator* allocator); // MANIPULATORS @@ -109,15 +113,23 @@ class MessageDetails { // ACCESSORS - /// Print this object to the specified `os` stream, using specified - /// 'queueMap'. - void print(bsl::ostream& os, const QueueMap& queueMap) const; - /// Return message's data record offset. unsigned int dataRecordOffset() const; /// Return message record index in Journal file. bsls::Types::Uint64 messageRecordIndex() const; + + /// Return a reference to the non-modifiable message record + const RecordDetails& messageRecord() const; + + /// Return a reference to the non-modifiable vector of all the confirm + /// records related to the `d_messageRecord` + const bsl::vector >& + confirmRecords() const; + + /// Return a reference to the non-modifiable delete record + const bdlb::NullableValue >& + deleteRecord() const; }; } // close package namespace diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.cpp b/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.cpp index 79c7e3028..f40475ee7 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.cpp +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.cpp @@ -163,6 +163,11 @@ bool CommandLineArguments::validate(bsl::string* error) ss << "Can't search by queue name, because csl file is not " "specified\n"; } + if (!d_printMode.empty()) { + if (d_printMode != "HUMAN" && d_printMode != "JSON_PRETTY" && + d_printMode != "JSON_LINE") + ss << "Invalid print mode specified\n"; + } if (d_timestampLt < 0 || d_timestampGt < 0 || (d_timestampLt > 0 && d_timestampGt >= d_timestampLt)) { ss << "Invalid timestamp range specified\n"; @@ -211,6 +216,7 @@ bool CommandLineArguments::validate(bsl::string* error) Parameters::Parameters(bslma::Allocator* allocator) : d_queueMap(allocator) +, d_printMode(e_HUMAN) , d_timestampGt(0) , d_timestampLt(0) , d_guid(allocator) @@ -229,6 +235,7 @@ Parameters::Parameters(bslma::Allocator* allocator) Parameters::Parameters(const CommandLineArguments& arguments, bslma::Allocator* allocator) : d_queueMap(allocator) +, d_printMode(e_HUMAN) , d_timestampGt(arguments.d_timestampGt) , d_timestampLt(arguments.d_timestampLt) , d_guid(arguments.d_guid, allocator) @@ -242,6 +249,12 @@ Parameters::Parameters(const CommandLineArguments& arguments, , d_confirmed(arguments.d_confirmed) , d_partiallyConfirmed(arguments.d_partiallyConfirmed) { + if (arguments.d_printMode == "JSON_PRETTY") { + d_printMode = e_JSON_PRETTY; + } + else if (arguments.d_printMode == "JSON_LINE") { + d_printMode = e_JSON_LINE; + } } void Parameters::validateQueueNames(bslma::Allocator* allocator) const diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.h b/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.h index 25ba703c0..c3d7317d1 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.h +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_parameters.h @@ -65,6 +65,8 @@ struct CommandLineArguments { // Path to read data files from bsl::string d_cslFile; // Path to read CSL files from + bsl::string d_printMode; + // Print mode bsl::vector d_guid; // Filter messages by message guids bsl::vector d_queueKey; @@ -95,9 +97,13 @@ struct CommandLineArguments { }; struct Parameters { + // PUBLIC TYPES + enum PrintMode { e_HUMAN, e_JSON_PRETTY, e_JSON_LINE }; // PUBLIC DATA QueueMap d_queueMap; // Queue map containing uri to key and key to info mappings + PrintMode d_printMode; + // Print mode bsls::Types::Int64 d_timestampGt; // Filter messages by minimum timestamp bsls::Types::Int64 d_timestampLt; diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.cpp b/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.cpp new file mode 100644 index 000000000..6c6e24b20 --- /dev/null +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.cpp @@ -0,0 +1,465 @@ +#include "m_bmqstoragetool_recordprinter.h" + +// MQB +#include +#include + +// BDE +#include + +namespace BloombergLP { +namespace m_bmqstoragetool { + +namespace { + +// ========================== +// class RecordDetailsPrinter +// ========================== + +template +class RecordDetailsPrinter { + private: + bsl::ostream& d_stream; + bsl::vector d_fields; + bslma::ManagedPtr d_printer_mp; + bslma::Allocator* d_allocator_p; + + // PRIVATE METHODS + + /// Print the specified message record `rec` to the specified + /// `printer`. + void + printRecord(const MessageDetails::RecordDetails& rec); + /// Print the specified confirm record `rec` to the specified + /// `printer`. + void + printRecord(const MessageDetails::RecordDetails& rec); + /// Print the specified delete record `rec` to the specified + /// `printer`. + void printRecord( + const MessageDetails::RecordDetails& rec); + + public: + // CREATORS + RecordDetailsPrinter(bsl::ostream& stream, bslma::Allocator* allocator); + + // PUBLIC METHODS + + /// Print this object to the specified `os` stream. + template + void printRecordDetails( + const MessageDetails::RecordDetails& details); +}; + +template +void printMessageDetails(bsl::ostream& os, + const MessageDetails& details, + bslma::Allocator* allocator) +{ + RecordDetailsPrinter printer(os, allocator); + + // Print message record + printer.printRecordDetails(details.messageRecord()); + + // Print confirmation records + const bsl::vector >& + confirmRecords = details.confirmRecords(); + if (!confirmRecords.empty()) { + typename bsl::vector >::const_iterator it = confirmRecords.begin(); + for (; it != confirmRecords.end(); ++it) { + printer.printRecordDetails(*it); + } + } + + // Print deletion record + if (!details.deleteRecord().isNull()) { + printer.printRecordDetails(details.deleteRecord().value()); + } +} + +// ================= +// class JsonPrinter +// ================= + +template +class JsonPrinter { + private: + // DATA + bsl::ostream& d_ostream; + const bsl::vector* d_fields_p; + unsigned int d_counter; + + // NOT IMPLEMENTED + JsonPrinter(const JsonPrinter&); + JsonPrinter& operator=(const JsonPrinter&); + + public: + // CREATORS + + /// Create an instance that will print to the specified `stream` a JSON + /// object with the specified `fields` with the optionally specified + /// `indent`. Behavior is undefined unless `indent` >= 0 and at least one + /// field is present in the `fields`. + JsonPrinter(bsl::ostream& stream, const bsl::vector* fields); + + ~JsonPrinter(); + + // MANIPULATORS + + /// Print the specified `value` to the stream held by this printer + /// instance. Behavior is undefined unless there exists a field + /// corresponding to the `value`. + template + JsonPrinter& operator<<(const TYPE& value); +}; + +typedef m_bmqstoragetool::JsonPrinter JsonLinePrinter; +typedef m_bmqstoragetool::JsonPrinter JsonPrettyPrinter; +typedef bmqu::AlignedPrinter HumanReadablePrinter; + +// ============================================================================ +// INLINE DEFINITIONS +// ============================================================================ + +// -------------------- +// RecordDetailsPrinter +// -------------------- + +template +RecordDetailsPrinter::RecordDetailsPrinter( + std::ostream& stream, + bslma::Allocator* allocator) +: d_stream(stream) +, d_fields(allocator) +, d_printer_mp() +, d_allocator_p(allocator) +{ + // NOTHING +} + +template +template +void RecordDetailsPrinter::printRecordDetails( + const MessageDetails::RecordDetails& details) +{ + d_fields.clear(); + d_fields.reserve(16); // max number of fields + d_fields.push_back("RecordType"); + d_fields.push_back("Index"); + d_fields.push_back("Offset"); + d_fields.push_back("GUID"); + d_fields.push_back("PrimaryLeaseId"); + d_fields.push_back("SequenceNumber"); + d_fields.push_back("Timestamp"); + d_fields.push_back("Epoch"); + d_fields.push_back("QueueKey"); + + // It's ok to pass a vector by pointer and push elements after that as + // we've reserved it's capacity in advance. Hense, no reallocations will + // happen and the pointer won't get invalidated. + d_printer_mp.load(new (*d_allocator_p) PRINTER_TYPE(d_stream, &d_fields), + d_allocator_p); + + *d_printer_mp << details.d_record.header().type() << details.d_recordIndex + << details.d_recordOffset << details.d_record.messageGUID() + << details.d_record.header().primaryLeaseId() + << details.d_record.header().sequenceNumber(); + + bsls::Types::Uint64 epochValue = details.d_record.header().timestamp(); + bdlt::Datetime datetime; + const int rc = bdlt::EpochUtil::convertFromTimeT64(&datetime, epochValue); + if (0 != rc) { + *d_printer_mp << 0; + } + else { + *d_printer_mp << datetime; + } + *d_printer_mp << epochValue; + + bmqu::MemOutStream queueKeyStr(d_allocator_p); + queueKeyStr << details.d_record.queueKey(); + *d_printer_mp << queueKeyStr.str(); + if (!details.d_queueUri.empty()) { + d_fields.push_back("QueueUri"); + *d_printer_mp << details.d_queueUri; + } + + printRecord(details); + d_printer_mp.reset(); + d_stream << '\n'; +} + +template +void RecordDetailsPrinter::printRecord( + const MessageDetails::RecordDetails& rec) +{ + d_fields.push_back("FileKey"); + d_fields.push_back("RefCount"); + d_fields.push_back("MsgOffsetDwords"); + d_fields.push_back("Crc32c"); + + bmqu::MemOutStream fileKeyStr(d_allocator_p); + fileKeyStr << rec.d_record.fileKey(); + + *d_printer_mp << fileKeyStr.str() << rec.d_record.refCount() + << rec.d_record.messageOffsetDwords() + << rec.d_record.crc32c(); +} + +template +void RecordDetailsPrinter::printRecord( + const MessageDetails::RecordDetails& rec) +{ + d_fields.push_back("AppKey"); + if (rec.d_record.appKey().isNull()) { + *d_printer_mp << "** NULL **"; + } + else { + *d_printer_mp << rec.d_record.appKey(); + } + + if (!rec.d_appId.empty()) { + d_fields.push_back("AppId"); + *d_printer_mp << rec.d_appId; + } +} + +template +void RecordDetailsPrinter::printRecord( + const MessageDetails::RecordDetails& rec) +{ + d_fields.push_back("DeletionFlag"); + *d_printer_mp << rec.d_record.deletionRecordFlag(); +} + +// ----------- +// JsonPrinter +// ----------- + +template +inline JsonPrinter::JsonPrinter( + bsl::ostream& stream, + const bsl::vector* fields) +: d_ostream(stream) +, d_fields_p(fields) +, d_counter(0) +{ + BSLS_ASSERT_SAFE(0 < d_fields_p->size()); + d_ostream << bsl::setw(braceIndent) << ' ' << '{'; + if (pretty) { + d_ostream << '\n'; + } +} + +template +inline JsonPrinter::~JsonPrinter() +{ + d_ostream << bsl::setw(braceIndent) << ' ' << "},"; +} + +template +template +inline JsonPrinter& +JsonPrinter::operator<<(const TYPE& value) +{ + BSLS_ASSERT_SAFE(d_counter < d_fields_p->size()); + + if (pretty) { + d_ostream << bsl::setw(fieldIndent) << ' '; + } + d_ostream << '\"' << (*d_fields_p)[d_counter] << "\": \"" << value << '\"'; + + ++d_counter; + if (d_counter < d_fields_p->size()) { + d_ostream << ','; + } + d_ostream << (pretty ? '\n' : ' '); + return *this; +} + +} // close anonymous namespace + +class JsonLinePrintManager : public PrintManager { + bsl::ostream& d_stream; + bslma::Allocator* d_allocator_p; + + public: + // CREATORS + JsonLinePrintManager(bsl::ostream& os, bslma::Allocator* allocator); + + ~JsonLinePrintManager() BSLS_KEYWORD_OVERRIDE; + + // PUBLIC METHODS + + void printMessage(const MessageDetails& details) BSLS_KEYWORD_OVERRIDE; + void printFooter(BSLS_ANNOTATION_UNUSED bsl::size_t foundMessagesCount) + BSLS_KEYWORD_OVERRIDE + { + } + void printError(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; + void printGuid(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; +}; + +class JsonPrettyPrintManager : public PrintManager { + bsl::ostream& d_stream; + bslma::Allocator* d_allocator_p; + + public: + // CREATORS + JsonPrettyPrintManager(bsl::ostream& os, bslma::Allocator* allocator); + + ~JsonPrettyPrintManager() BSLS_KEYWORD_OVERRIDE; + + // PUBLIC METHODS + + void printMessage(const MessageDetails& details) BSLS_KEYWORD_OVERRIDE; + void printFooter(BSLS_ANNOTATION_UNUSED bsl::size_t foundMessagesCount) + BSLS_KEYWORD_OVERRIDE + { + } + void printError(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; + void printGuid(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; +}; + +class HumanReadablePrintManager : public PrintManager { + bsl::ostream& d_stream; + bslma::Allocator* d_allocator_p; + + public: + // CREATORS + HumanReadablePrintManager(bsl::ostream& os, bslma::Allocator* allocator); + + ~HumanReadablePrintManager() BSLS_KEYWORD_OVERRIDE; + + // PUBLIC METHODS + + void printMessage(const MessageDetails& details) BSLS_KEYWORD_OVERRIDE; + void printFooter(bsl::size_t foundMessagesCount) BSLS_KEYWORD_OVERRIDE; + void printError(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; + void printGuid(const bmqt::MessageGUID& guid) BSLS_KEYWORD_OVERRIDE; +}; + +JsonLinePrintManager::JsonLinePrintManager(std::ostream& os, + bslma::Allocator* allocator) +: d_stream(os) +, d_allocator_p(allocator) +{ + d_stream << "[\n"; +} + +JsonLinePrintManager::~JsonLinePrintManager() +{ + d_stream << "]\n"; +} + +void JsonLinePrintManager::printMessage(const MessageDetails& details) +{ + printMessageDetails(d_stream, details, d_allocator_p); +} + +void JsonLinePrintManager::printError(const bmqt::MessageGUID& guid) +{ + d_stream << "{\"Logic error\" : \"guid " << guid << " not found\"},\n"; +} + +void JsonLinePrintManager::printGuid(const bmqt::MessageGUID& guid) +{ + d_stream << "\"" << guid << "\",\n"; +} + +JsonPrettyPrintManager::JsonPrettyPrintManager(std::ostream& os, + bslma::Allocator* allocator) +: d_stream(os) +, d_allocator_p(allocator) +{ + d_stream << "[\n"; +} + +void JsonPrettyPrintManager::printMessage(const MessageDetails& details) +{ + printMessageDetails(d_stream, details, d_allocator_p); +} + +void JsonPrettyPrintManager::printError(const bmqt::MessageGUID& guid) +{ + d_stream << "{\n\t\"Logic error\" : \"guid " << guid + << " not found\"\n},\n"; +} + +void JsonPrettyPrintManager::printGuid(const bmqt::MessageGUID& guid) +{ + d_stream << "\"" << guid << "\",\n"; +} + +JsonPrettyPrintManager::~JsonPrettyPrintManager() +{ + d_stream << "]\n"; +} + +HumanReadablePrintManager::HumanReadablePrintManager( + std::ostream& os, + bslma::Allocator* allocator) +: d_stream(os) +, d_allocator_p(allocator) +{ +} + +HumanReadablePrintManager::~HumanReadablePrintManager() +{ + d_stream << "\n"; +} + +void HumanReadablePrintManager::printMessage(const MessageDetails& details) +{ + d_stream << "==============================\n\n"; + printMessageDetails(d_stream, + details, + d_allocator_p); +} + +void HumanReadablePrintManager::printFooter(bsl::size_t foundMessagesCount) +{ + const char* captionForFound = " message GUID(s) found."; + const char* captionForNotFound = "No message GUID found."; + foundMessagesCount > 0 + ? (d_stream << foundMessagesCount << captionForFound) + : d_stream << captionForNotFound; + d_stream << '\n'; +} + +void HumanReadablePrintManager::printError(const bmqt::MessageGUID& guid) +{ + d_stream << "Logic error : guid " << guid << " not found\n"; +} + +void HumanReadablePrintManager::printGuid(const bmqt::MessageGUID& guid) +{ + d_stream << guid << '\n'; +} + +bslma::ManagedPtr createPrintManager(Parameters::PrintMode mode, + std::ostream& stream, + bslma::Allocator* allocator) +{ + bslma::ManagedPtr printManager; + if (mode == Parameters::e_HUMAN) { + printManager.load(new (*allocator) + HumanReadablePrintManager(stream, allocator), + allocator); + } + else if (mode == Parameters::e_JSON_PRETTY) { + printManager.load(new (*allocator) + JsonPrettyPrintManager(stream, allocator), + allocator); + } + else if (mode == Parameters::e_JSON_LINE) { + printManager.load(new (*allocator) + JsonLinePrintManager(stream, allocator), + allocator); + } + return printManager; +} + +} // close package namespace +} // close enterprise namespace diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.h b/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.h new file mode 100644 index 000000000..03c0020a0 --- /dev/null +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_recordprinter.h @@ -0,0 +1,61 @@ +// Copyright 2024 Bloomberg Finance L.P. +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_M_BMQSTORAGETOOL_PRINTMANAGER_H +#define INCLUDED_M_BMQSTORAGETOOL_PRINTMANAGER_H + +//@PURPOSE: Provide PrintManager class for printing records. +// +//@CLASSES: +// PrintManager: provides methods to print journal records. +// +//@DESCRIPTION: Interface class to print journal records. + +// bmqstoragetool +#include +#include + +namespace BloombergLP { + +namespace m_bmqstoragetool { + +// ================== +// class PrintManager +// ================== + +class PrintManager { + public: + // CREATORS + + virtual ~PrintManager() {} + + // PUBLIC METHODS + + virtual void printMessage(const MessageDetails& details) = 0; + virtual void printFooter(bsl::size_t foundMessagesCount) = 0; + virtual void printError(const bmqt::MessageGUID& guid) = 0; + virtual void printGuid(const bmqt::MessageGUID& guid) = 0; +}; + +bslma::ManagedPtr +createPrintManager(Parameters::PrintMode mode, + std::ostream& stream, + bslma::Allocator* allocator); + +} // close package namespace + +} // close enterprise namespace + +#endif // INCLUDED_M_BMQSTORAGETOOL_PRINTMANAGER_H diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.cpp b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.cpp index ce7b98bac..6e4c72cd5 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.cpp +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.cpp @@ -14,6 +14,7 @@ // limitations under the License. // bmqstoragetool +#include #include // MQB @@ -140,16 +141,6 @@ void printJournalFileMeta(bsl::ostream& ostream, } } -// Helper to print message GUID as a string -void outputGuidString(bsl::ostream& ostream, - const bmqt::MessageGUID& messageGUID, - const bool addNewLine = true) -{ - ostream << messageGUID; - if (addNewLine) - ostream << '\n'; -} - // Helper to calculate and print outstanding ratio void outputOutstandingRatio(bsl::ostream& ostream, bsl::size_t totalMessagesCount, @@ -168,16 +159,6 @@ void outputOutstandingRatio(bsl::ostream& ostream, } } -// Helper to print summary of search result -void outputFooter(bsl::ostream& ostream, bsl::size_t foundMessagesCount) -{ - const char* captionForFound = " message GUID(s) found."; - const char* captionForNotFound = "No message GUID found."; - foundMessagesCount > 0 ? (ostream << foundMessagesCount << captionForFound) - : ostream << captionForNotFound; - ostream << '\n'; -} - } // close unnamed namespace // ================== @@ -297,13 +278,13 @@ bool SearchResultTimestampDecorator::processDeletionRecord( // ======================= SearchShortResult::SearchShortResult( - bsl::ostream& ostream, + bslma::ManagedPtr& printManager, bslma::ManagedPtr& payloadDumper, bool printImmediately, bool eraseDeleted, bool printOnDelete, bslma::Allocator* allocator) -: d_ostream(ostream) +: d_printManager(printManager) , d_payloadDumper(payloadDumper) , d_printImmediately(printImmediately) , d_eraseDeleted(eraseDeleted) @@ -373,7 +354,7 @@ void SearchShortResult::outputResult() } } - outputFooter(d_ostream, d_printedMessagesCount); + d_printManager->printFooter(d_printedMessagesCount); } void SearchShortResult::outputResult(const GuidsList& guidFilter) @@ -388,17 +369,17 @@ void SearchShortResult::outputResult(const GuidsList& guidFilter) } else { // this should not happen - d_ostream << "Logic error : guid " << *it << " not found\n"; + d_printManager->printError(*it); } } } - outputFooter(d_ostream, d_printedMessagesCount); + d_printManager->printFooter(d_printedMessagesCount); } void SearchShortResult::outputGuidData(const GuidData& guidData) { - outputGuidString(d_ostream, guidData.first); + d_printManager->printGuid(guidData.first); if (d_payloadDumper) d_payloadDumper->outputPayload(guidData.second); @@ -415,15 +396,15 @@ bool SearchShortResult::hasCache() const // ======================== SearchDetailResult::SearchDetailResult( - bsl::ostream& ostream, const QueueMap& queueMap, + bslma::ManagedPtr& printManager, bslma::ManagedPtr& payloadDumper, bool printImmediately, bool eraseDeleted, bool cleanUnprinted, bslma::Allocator* allocator) -: d_ostream(ostream) -, d_queueMap(queueMap) +: d_queueMap(queueMap) +, d_printManager(printManager) , d_payloadDumper(payloadDumper) , d_printImmediately(printImmediately) , d_eraseDeleted(eraseDeleted) @@ -491,7 +472,7 @@ void SearchDetailResult::outputResult() } } - outputFooter(d_ostream, d_printedMessagesCount); + d_printManager->printFooter(d_printedMessagesCount); } void SearchDetailResult::outputResult(const GuidsList& guidFilter) @@ -506,12 +487,12 @@ void SearchDetailResult::outputResult(const GuidsList& guidFilter) } else { // this should not happen - d_ostream << "Logic error : guid " << *it << " not found\n"; + d_printManager->printError(*it); } } } - outputFooter(d_ostream, d_printedMessagesCount); + d_printManager->printFooter(d_printedMessagesCount); } void SearchDetailResult::addMessageDetails(const mqbs::MessageRecord& record, @@ -520,9 +501,12 @@ void SearchDetailResult::addMessageDetails(const mqbs::MessageRecord& record, { d_messageDetailsMap.emplace( record.messageGUID(), - d_messageDetailsList.insert( - d_messageDetailsList.cend(), - MessageDetails(record, recordIndex, recordOffset, d_allocator_p))); + d_messageDetailsList.insert(d_messageDetailsList.cend(), + MessageDetails(record, + recordIndex, + recordOffset, + d_queueMap, + d_allocator_p))); } void SearchDetailResult::deleteMessageDetails(DetailsMap::iterator iterator) @@ -535,7 +519,7 @@ void SearchDetailResult::deleteMessageDetails(DetailsMap::iterator iterator) void SearchDetailResult::outputMessageDetails( const MessageDetails& messageDetails) { - messageDetails.print(d_ostream, d_queueMap); + d_printManager->printMessage(messageDetails); if (d_payloadDumper) d_payloadDumper->outputPayload(messageDetails.dataRecordOffset()); @@ -775,7 +759,7 @@ void SearchGuidDecorator::outputResult() << " GUID(s) not found:" << '\n'; GuidsList::const_iterator it = d_guids.cbegin(); for (; it != d_guids.cend(); ++it) { - outputGuidString(d_ostream, *it); + d_ostream << *it << '\n'; } } } diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.h b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.h index 123241799..87a8dba8d 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.h +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresult.h @@ -47,6 +47,7 @@ #include #include #include +#include // MQB #include @@ -133,8 +134,8 @@ class SearchShortResult : public SearchResult { // PRIVATE DATA - bsl::ostream& d_ostream; - // Reference to output stream. + const bslma::ManagedPtr d_printManager; + // Pointer to 'PrintManager' instance. const bslma::ManagedPtr d_payloadDumper; // Pointer to 'PayloadDumper' instance. const bool d_printImmediately; @@ -173,7 +174,7 @@ class SearchShortResult : public SearchResult { /// Constructor using the specified `ostream`, `payloadDumper`, /// `printImmediately`, `eraseDeleted`, `printOnDelete` and `allocator`. - explicit SearchShortResult(bsl::ostream& ostream, + explicit SearchShortResult(bslma::ManagedPtr& printManager, bslma::ManagedPtr& payloadDumper, bool printImmediately = true, bool eraseDeleted = false, @@ -211,6 +212,7 @@ class SearchShortResult : public SearchResult { // ======================== /// This class provides logic to handle and output detail result. +// template class SearchDetailResult : public SearchResult { private: // PRIVATE TYPES @@ -223,10 +225,10 @@ class SearchDetailResult : public SearchResult { // PRIVATE DATA - bsl::ostream& d_ostream; - // Reference to output stream. const QueueMap& d_queueMap; // Reference to 'QueueMap' instance. + const bslma::ManagedPtr d_printManager; + // Pointer to 'PrintManager' instance. const bslma::ManagedPtr d_payloadDumper; // Pointer to 'PayloadDumper' instance. const bool d_printImmediately; @@ -275,8 +277,8 @@ class SearchDetailResult : public SearchResult { /// Constructor using the specified `ostream`, `queueMap`, `payloadDumper`, /// `printImmediately`, `eraseDeleted`, `cleanUnprinted` and `allocator`. - SearchDetailResult(bsl::ostream& ostream, - const QueueMap& queueMap, + SearchDetailResult(const QueueMap& queueMap, + bslma::ManagedPtr& printManager, bslma::ManagedPtr& payloadDumper, bool printImmediately = true, bool eraseDeleted = true, diff --git a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresultfactory.cpp b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresultfactory.cpp index 04fe3127e..30e7de66e 100644 --- a/src/applications/bmqstoragetool/m_bmqstoragetool_searchresultfactory.cpp +++ b/src/applications/bmqstoragetool/m_bmqstoragetool_searchresultfactory.cpp @@ -14,6 +14,7 @@ // limitations under the License. // bmqstoragetool +#include #include namespace BloombergLP { @@ -59,11 +60,14 @@ bsl::shared_ptr SearchResultFactory::createSearchResult( // Clean unprinted/unerased data for specific case const bool cleanUnprinted = params->d_confirmed; + bslma::ManagedPtr printManager = + createPrintManager(params->d_printMode, ostream, alloc); + // Create searchResult implementation bsl::shared_ptr searchResult; if (details) { - searchResult.reset(new (*alloc) SearchDetailResult(ostream, - params->d_queueMap, + searchResult.reset(new (*alloc) SearchDetailResult(params->d_queueMap, + printManager, payloadDumper, printImmediately, eraseDeleted, @@ -72,7 +76,7 @@ bsl::shared_ptr SearchResultFactory::createSearchResult( alloc); } else { - searchResult.reset(new (*alloc) SearchShortResult(ostream, + searchResult.reset(new (*alloc) SearchShortResult(printManager, payloadDumper, printImmediately, eraseDeleted, @@ -81,45 +85,48 @@ bsl::shared_ptr SearchResultFactory::createSearchResult( alloc); } - // Create Decorator for specific search - if (!params->d_guid.empty()) { - // Search GUIDs - searchResult.reset(new (*alloc) SearchGuidDecorator(searchResult, - params->d_guid, - ostream, - details, - alloc), - alloc); - } - else if (params->d_summary) { - // Summary - searchResult.reset( - new (*alloc) SummaryProcessor(ostream, - fileManager->journalFileIterator(), - fileManager->dataFileIterator(), - alloc), - alloc); - } - else if (params->d_outstanding || params->d_confirmed) { - // Search outstanding or confirmed - searchResult.reset( - new (*alloc) - SearchOutstandingDecorator(searchResult, ostream, alloc), - alloc); - } - else if (params->d_partiallyConfirmed) { - // Search partially confirmed - searchResult.reset(new (*alloc) - SearchPartiallyConfirmedDecorator(searchResult, - ostream, - alloc), - alloc); - } - else { - // Drefault: search all - searchResult.reset(new (*alloc) - SearchAllDecorator(searchResult, alloc), - alloc); + // TODO: implement printer methods for the decorators + if (params->d_printMode == Parameters::e_HUMAN) { + // Create Decorator for specific search + if (!params->d_guid.empty()) { + // Search GUIDs + searchResult.reset(new (*alloc) SearchGuidDecorator(searchResult, + params->d_guid, + ostream, + details, + alloc), + alloc); + } + else if (params->d_summary) { + // Summary + searchResult.reset(new (*alloc) SummaryProcessor( + ostream, + fileManager->journalFileIterator(), + fileManager->dataFileIterator(), + alloc), + alloc); + } + else if (params->d_outstanding || params->d_confirmed) { + // Search outstanding or confirmed + searchResult.reset( + new (*alloc) + SearchOutstandingDecorator(searchResult, ostream, alloc), + alloc); + } + else if (params->d_partiallyConfirmed) { + // Search partially confirmed + searchResult.reset( + new (*alloc) SearchPartiallyConfirmedDecorator(searchResult, + ostream, + alloc), + alloc); + } + else { + // Drefault: search all + searchResult.reset(new (*alloc) + SearchAllDecorator(searchResult, alloc), + alloc); + } } // Add TimestampDecorator if 'timestampLt' is given. diff --git a/src/applications/bmqstoragetool/package/bmqstoragetool.mem b/src/applications/bmqstoragetool/package/bmqstoragetool.mem index 497ae4f23..6a7e5022e 100644 --- a/src/applications/bmqstoragetool/package/bmqstoragetool.mem +++ b/src/applications/bmqstoragetool/package/bmqstoragetool.mem @@ -9,5 +9,6 @@ m_bmqstoragetool_messagedetails m_bmqstoragetool_parameters m_bmqstoragetool_payloaddumper m_bmqstoragetool_queuemap +m_bmqstoragetool_recordprinter m_bmqstoragetool_searchresult m_bmqstoragetool_searchresultfactory