This is an automated email from the ASF dual-hosted git repository. fgerlits pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
commit 01a5665c98aeae373fc537cc3bb81b8578eed7c6 Author: Martin Zink <[email protected]> AuthorDate: Fri Sep 30 16:45:50 2022 +0200 MINIFICPP-1949 ConsumeWindowsEventLog should have the identifier regex compiled during onSchedule also: MINIFICPP-1956 ConsumeWindowsEventLog plaintext out should contain the EvtFormatMessage errors add checks for malloc readded identifier replacement in plain text refactored GetEventTimestampStr refactored getWindowsErrorAsString to windowsErrorToErrorCode refactored MetadataWalker::regex_ from optional& to raw pointer changed std::format to fmt::format Signed-off-by: Ferenc Gerlits <[email protected]> This closes #1434 --- extensions/windows-event-log/Bookmark.cpp | 67 ++++---- extensions/windows-event-log/Bookmark.h | 15 +- .../windows-event-log/ConsumeWindowsEventLog.cpp | 179 +++++++++++---------- .../windows-event-log/ConsumeWindowsEventLog.h | 77 +++++---- .../windows-event-log/tests/BookmarkTests.cpp | 6 +- extensions/windows-event-log/tests/CWELTestUtils.h | 4 +- .../tests/ConsumeWindowsEventLogTests.cpp | 135 +++++++++------- .../tests/MetadataWalkerTests.cpp | 63 +++++--- .../windows-event-log/wel/MetadataWalker.cpp | 37 +---- extensions/windows-event-log/wel/MetadataWalker.h | 37 ++--- .../windows-event-log/wel/WindowsEventLog.cpp | 134 ++++++++------- extensions/windows-event-log/wel/WindowsEventLog.h | 48 +++--- libminifi/include/utils/OsUtils.h | 3 + libminifi/src/utils/OsUtils.cpp | 4 + 14 files changed, 396 insertions(+), 413 deletions(-) diff --git a/extensions/windows-event-log/Bookmark.cpp b/extensions/windows-event-log/Bookmark.cpp index aa403d3c0..300aef3c4 100644 --- a/extensions/windows-event-log/Bookmark.cpp +++ b/extensions/windows-event-log/Bookmark.cpp @@ -18,7 +18,6 @@ #include "Bookmark.h" -#include <direct.h> #include <vector> #include <unordered_map> #include <utility> @@ -26,12 +25,9 @@ #include "wel/UnicodeConversion.h" #include "utils/file/FileUtils.h" +#include "utils/OsUtils.h" -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace processors { +namespace org::apache::nifi::minifi::processors { static const std::string BOOKMARK_KEY = "bookmark"; Bookmark::Bookmark(const std::wstring& channel, @@ -65,7 +61,8 @@ Bookmark::Bookmark(const std::wstring& channel, } if (!bookmarkXml_.empty()) { - if (hBookmark_ = unique_evt_handle{ EvtCreateBookmark(bookmarkXml_.c_str()) }) { + hBookmark_ = unique_evt_handle{EvtCreateBookmark(bookmarkXml_.c_str())}; + if (hBookmark_) { ok_ = true; return; } @@ -77,18 +74,19 @@ Bookmark::Bookmark(const std::wstring& channel, state_manager_->set(state_map); } - if (!(hBookmark_ = unique_evt_handle{ EvtCreateBookmark(0) })) { + hBookmark_ = unique_evt_handle{EvtCreateBookmark(nullptr)}; + if (!hBookmark_) { LOG_LAST_ERROR(EvtCreateBookmark); return; } - const auto hEventResults = unique_evt_handle{ EvtQuery(0, channel.c_str(), query.c_str(), EvtQueryChannelPath) }; + const auto hEventResults = unique_evt_handle{ EvtQuery(nullptr, channel.c_str(), query.c_str(), EvtQueryChannelPath) }; if (!hEventResults) { LOG_LAST_ERROR(EvtQuery); return; } - if (!EvtSeek(hEventResults.get(), 0, 0, 0, processOldEvents? EvtSeekRelativeToFirst : EvtSeekRelativeToLast)) { + if (!EvtSeek(hEventResults.get(), 0, nullptr, 0, processOldEvents? EvtSeekRelativeToFirst : EvtSeekRelativeToLast)) { LOG_LAST_ERROR(EvtSeek); return; } @@ -129,48 +127,41 @@ bool Bookmark::saveBookmarkXml(const std::wstring& bookmarkXml) { return state_manager_->set(state_map); } -bool Bookmark::saveBookmark(EVT_HANDLE hEvent) { - std::wstring bookmarkXml; - if (!getNewBookmarkXml(hEvent, bookmarkXml)) { +bool Bookmark::saveBookmark(EVT_HANDLE event_handle) { + auto bookmark_xml = getNewBookmarkXml(event_handle); + if (!bookmark_xml) { + logger_->log_error("%s", bookmark_xml.error()); return false; } - return saveBookmarkXml(bookmarkXml); + return saveBookmarkXml(*bookmark_xml); } -bool Bookmark::getNewBookmarkXml(EVT_HANDLE hEvent, std::wstring& bookmarkXml) { +nonstd::expected<std::wstring, std::string> Bookmark::getNewBookmarkXml(EVT_HANDLE hEvent) { if (!EvtUpdateBookmark(hBookmark_.get(), hEvent)) { - LOG_LAST_ERROR(EvtUpdateBookmark); - return false; + return nonstd::make_unexpected(fmt::format("EvtUpdateBookmark failed due to %s", utils::OsUtils::windowsErrorToErrorCode(GetLastError()).message())); } // Render the bookmark as an XML string that can be persisted. logger_->log_trace("Rendering new bookmark"); DWORD bufferSize{}; DWORD bufferUsed{}; DWORD propertyCount{}; - if (!EvtRender(nullptr, hBookmark_.get(), EvtRenderBookmark, bufferSize, nullptr, &bufferUsed, &propertyCount)) { - DWORD status = ERROR_SUCCESS; - if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError())) { - bufferSize = bufferUsed; - - std::vector<wchar_t> buf(bufferSize / 2 + 1); + bool event_render_succeeded_without_buffer = EvtRender(nullptr, hBookmark_.get(), EvtRenderBookmark, bufferSize, nullptr, &bufferUsed, &propertyCount); + if (event_render_succeeded_without_buffer) + return nonstd::make_unexpected("EvtRender failed to determine the required buffer size."); - if (!EvtRender(nullptr, hBookmark_.get(), EvtRenderBookmark, bufferSize, &buf[0], &bufferUsed, &propertyCount)) { - LOG_LAST_ERROR(EvtRender); - return false; - } + auto last_error = GetLastError(); + if (last_error != ERROR_INSUFFICIENT_BUFFER) + return nonstd::make_unexpected(fmt::format("EvtRender failed due to %s", utils::OsUtils::windowsErrorToErrorCode(last_error).message())); - bookmarkXml = buf.data(); + bufferSize = bufferUsed; + std::vector<wchar_t> buf(bufferSize / 2 + 1); - return true; - } - if (ERROR_SUCCESS != (status = GetLastError())) { - LOG_LAST_ERROR(EvtRender); - return false; - } + if (!EvtRender(nullptr, hBookmark_.get(), EvtRenderBookmark, bufferSize, buf.data(), &bufferUsed, &propertyCount)) { + return nonstd::make_unexpected(fmt::format("EvtRender failed due to %s", utils::OsUtils::windowsErrorToErrorCode(GetLastError()).message())); } - return false; + return std::wstring(buf.data()); } bool Bookmark::getBookmarkXmlFromFile(std::wstring& bookmarkXml) { @@ -212,8 +203,4 @@ bool Bookmark::getBookmarkXmlFromFile(std::wstring& bookmarkXml) { return true; } -} /* namespace processors */ -} /* namespace minifi */ -} /* namespace nifi */ -} /* namespace apache */ -} /* namespace org */ +} // namespace org::apache::nifi::minifi::processors diff --git a/extensions/windows-event-log/Bookmark.h b/extensions/windows-event-log/Bookmark.h index 60d6860b8..66937df4f 100644 --- a/extensions/windows-event-log/Bookmark.h +++ b/extensions/windows-event-log/Bookmark.h @@ -28,12 +28,9 @@ #include "core/ProcessSession.h" #include "wel/UniqueEvtHandle.h" #include "logging/Logger.h" +#include "utils/expected.h" -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace processors { +namespace org::apache::nifi::minifi::processors { #define LOG_LAST_ERROR(func) logger_->log_error("!"#func" error %x", GetLastError()) @@ -50,7 +47,7 @@ class Bookmark { explicit operator bool() const noexcept; /* non-owning */ EVT_HANDLE getBookmarkHandleFromXML(); - bool getNewBookmarkXml(EVT_HANDLE hEvent, std::wstring& bookmarkXml); + nonstd::expected<std::wstring, std::string> getNewBookmarkXml(EVT_HANDLE hEvent); bool saveBookmarkXml(const std::wstring& bookmarkXml); private: @@ -67,8 +64,4 @@ class Bookmark { std::wstring bookmarkXml_; }; -} /* namespace processors */ -} /* namespace minifi */ -} /* namespace nifi */ -} /* namespace apache */ -} /* namespace org */ +} // namespace org::apache::nifi::minifi::processors diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp index a787470fc..578da4e9f 100644 --- a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp +++ b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp @@ -19,7 +19,7 @@ */ #include "ConsumeWindowsEventLog.h" -#include <stdio.h> +#include <cstdio> #include <vector> #include <tuple> #include <utility> @@ -44,21 +44,19 @@ #include "core/PropertyBuilder.h" #include "core/Resource.h" #include "Bookmark.h" +#include "wel/UniqueEvtHandle.h" #include "utils/Deleters.h" #include "logging/LoggerConfiguration.h" #include "utils/gsl.h" +#include "utils/OsUtils.h" #pragma comment(lib, "wevtapi.lib") #pragma comment(lib, "ole32.lib") using namespace std::literals::chrono_literals; -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace processors { +namespace org::apache::nifi::minifi::processors { const int EVT_NEXT_TIMEOUT_MS = 500; @@ -214,7 +212,7 @@ void ConsumeWindowsEventLog::initialize() { setSupportedRelationships(relationships()); } -bool ConsumeWindowsEventLog::insertHeaderName(wel::METADATA_NAMES &header, const std::string &key, const std::string & value) const { +bool ConsumeWindowsEventLog::insertHeaderName(wel::METADATA_NAMES &header, const std::string &key, const std::string & value) { wel::METADATA name = wel::WindowsEventLogMetadata::getMetadataFromString(key); if (name != wel::METADATA::UNKNOWN) { @@ -224,16 +222,15 @@ bool ConsumeWindowsEventLog::insertHeaderName(wel::METADATA_NAMES &header, const return false; } -void ConsumeWindowsEventLog::onSchedule(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) { +void ConsumeWindowsEventLog::onSchedule(const std::shared_ptr<core::ProcessContext>& context, const std::shared_ptr<core::ProcessSessionFactory>&) { state_manager_ = context->getStateManager(); if (state_manager_ == nullptr) { throw Exception(PROCESSOR_EXCEPTION, "Failed to get StateManager"); } - context->getProperty(IdentifierMatcher.getName(), regex_); context->getProperty(ResolveAsAttributes.getName(), resolve_as_attributes_); context->getProperty(IdentifierFunction.getName(), apply_identifier_function_); - context->getProperty(EventHeaderDelimiter.getName(), header_delimiter_); + header_delimiter_ = context->getProperty(EventHeaderDelimiter); context->getProperty(BatchCommitSize.getName(), batch_commit_size_); header_names_.clear(); @@ -256,6 +253,11 @@ void ConsumeWindowsEventLog::onSchedule(const std::shared_ptr<core::ProcessConte } } + regex_.reset(); + if (auto identifier_matcher = context->getProperty(IdentifierMatcher); identifier_matcher && !identifier_matcher->empty()) { + regex_.emplace(*identifier_matcher); + } + std::string mode; context->getProperty(OutputFormat.getName(), mode); @@ -319,8 +321,8 @@ void ConsumeWindowsEventLog::onSchedule(const std::shared_ptr<core::ProcessConte } } - context->getProperty(MaxBufferSize.getName(), maxBufferSize_); - logger_->log_debug("ConsumeWindowsEventLog: MaxBufferSize %" PRIu64, maxBufferSize_); + context->getProperty(MaxBufferSize.getName(), max_buffer_size_); + logger_->log_debug("ConsumeWindowsEventLog: MaxBufferSize %" PRIu64, max_buffer_size_); provenanceUri_ = "winlog://" + computerName_ + "/" + channel_ + "?" + query; logger_->log_trace("Successfully configured CWEL"); @@ -346,7 +348,7 @@ bool ConsumeWindowsEventLog::commitAndSaveBookmark(const std::wstring &bookmark_ return true; } -std::tuple<size_t, std::wstring> ConsumeWindowsEventLog::processEventLogs(const std::shared_ptr<core::ProcessContext> &context, +std::tuple<size_t, std::wstring> ConsumeWindowsEventLog::processEventLogs(const std::shared_ptr<core::ProcessContext>&, const std::shared_ptr<core::ProcessSession> &session, const EVT_HANDLE& event_query_results) { size_t processed_event_count = 0; std::wstring bookmark_xml; @@ -367,13 +369,19 @@ std::tuple<size_t, std::wstring> ConsumeWindowsEventLog::processEventLogs(const const auto guard_next_event = gsl::finally([next_event]() { EvtClose(next_event); }); logger_->log_trace("Succesfully got the next event, performing event rendering"); - EventRender event_render; - std::wstring new_bookmark_xml; - if (createEventRender(next_event, event_render) && bookmark_->getNewBookmarkXml(next_event, new_bookmark_xml)) { - bookmark_xml = std::move(new_bookmark_xml); - processed_event_count++; - putEventRenderFlowFileToSession(event_render, *session); + auto event_render = createEventRender(next_event); + if (!event_render) { + logger_->log_error("%s", event_render.error()); + continue; } + auto new_bookmark_xml = bookmark_->getNewBookmarkXml(next_event); + if (!new_bookmark_xml) { + logger_->log_error("%s", new_bookmark_xml.error()); + continue; + } + bookmark_xml = std::move(*new_bookmark_xml); + processed_event_count++; + putEventRenderFlowFileToSession(*event_render, *session); } logger_->log_trace("Finished enumerating events."); return std::make_tuple(processed_event_count, bookmark_xml); @@ -400,13 +408,12 @@ void ConsumeWindowsEventLog::onTrigger(const std::shared_ptr<core::ProcessContex logger_->log_debug("processed %zu Events in %" PRId64 " ms", processed_event_count, time_diff()); }); - const auto event_query_results = EvtQuery(0, wstrChannel_.c_str(), wstrQuery_.c_str(), EvtQueryChannelPath); + wel::unique_evt_handle event_query_results{EvtQuery(nullptr, wstrChannel_.c_str(), wstrQuery_.c_str(), EvtQueryChannelPath)}; if (!event_query_results) { LOG_LAST_ERROR(EvtQuery); context->yield(); return; } - const auto guard_event_query_results = gsl::finally([event_query_results]() { EvtClose(event_query_results); }); logger_->log_trace("Retrieved results in Channel: %ls with Query: %ls", wstrChannel_.c_str(), wstrQuery_.c_str()); @@ -418,7 +425,7 @@ void ConsumeWindowsEventLog::onTrigger(const std::shared_ptr<core::ProcessContex return; } - if (!EvtSeek(event_query_results, 1, bookmark_handle, 0, EvtSeekRelativeToBookmark)) { + if (!EvtSeek(event_query_results.get(), 1, bookmark_handle, 0, EvtSeekRelativeToBookmark)) { LOG_LAST_ERROR(EvtSeek); context->yield(); return; @@ -427,7 +434,7 @@ void ConsumeWindowsEventLog::onTrigger(const std::shared_ptr<core::ProcessContex refreshTimeZoneData(); std::wstring bookmark_xml; - std::tie(processed_event_count, bookmark_xml) = processEventLogs(context, session, event_query_results); + std::tie(processed_event_count, bookmark_xml) = processEventLogs(context, session, event_query_results.get()); if (processed_event_count == 0 || !commitAndSaveBookmark(bookmark_xml, context, session)) { context->yield(); @@ -446,7 +453,10 @@ wel::WindowsEventLogHandler& ConsumeWindowsEventLog::getEventLogHandler(const st std::wstring temp_wstring = std::wstring(name.begin(), name.end()); LPCWSTR widechar = temp_wstring.c_str(); - providers_[name] = wel::WindowsEventLogHandler(EvtOpenPublisherMetadata(NULL, widechar, NULL, 0, 0)); + auto opened_publisher_metadata_provider = EvtOpenPublisherMetadata(nullptr, widechar, nullptr, 0, 0); + if (!opened_publisher_metadata_provider) + logger_->log_warn("EvtOpenPublisherMetadata failed due to %s", utils::OsUtils::windowsErrorToErrorCode(GetLastError()).message()); + providers_[name] = wel::WindowsEventLogHandler(opened_publisher_metadata_provider); logger_->log_info("Handler not found for %s, creating. Number of cached handlers: %zu", name, providers_.size()); return providers_[name]; } @@ -478,11 +488,11 @@ void ConsumeWindowsEventLog::substituteXMLPercentageItems(pugi::xml_document& do for (size_t numberPos = 0; std::string::npos != (numberPos = nodeText.find(percentages, numberPos));) { numberPos += percentages.size(); - auto number = 0u; + uint64_t number{}; try { // Assumption - first character is not '0', otherwise not all digits will be replaced by 'value'. number = std::stoul(&nodeText[numberPos]); - } catch (const std::invalid_argument &) { + } catch (const std::invalid_argument&) { continue; } @@ -497,7 +507,7 @@ void ConsumeWindowsEventLog::substituteXMLPercentageItems(pugi::xml_document& do number, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pBuffer, - 1024, 0)) { + 1024, nullptr)) { value = pBuffer; LocalFree(pBuffer); @@ -537,79 +547,82 @@ void ConsumeWindowsEventLog::substituteXMLPercentageItems(pugi::xml_document& do doc.traverse(treeWalker); } -bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& eventRender) { +nonstd::expected<std::string, std::string> ConsumeWindowsEventLog::renderEventAsXml(EVT_HANDLE event_handle) { logger_->log_trace("Rendering an event"); WCHAR stackBuffer[4096]; DWORD size = sizeof(stackBuffer); using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>; - std::unique_ptr<WCHAR, Deleter> buf{stackBuffer, Deleter{ stackBuffer }}; + std::unique_ptr<WCHAR, Deleter> buf{stackBuffer, Deleter{stackBuffer}}; DWORD used = 0; DWORD propertyCount = 0; - if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) { - if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) { - LOG_LAST_ERROR(EvtRender); - return false; + if (!EvtRender(nullptr, event_handle, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) { + DWORD last_error = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER != last_error) { + std::string error_message = fmt::format("EvtRender failed due to %s", utils::OsUtils::windowsErrorToErrorCode(last_error).message()); + return nonstd::make_unexpected(error_message); } - if (used > maxBufferSize_) { - logger_->log_error("Dropping event because it couldn't be rendered within %" PRIu64 " bytes.", maxBufferSize_); - return false; + if (used > max_buffer_size_) { + std::string error_message = fmt::format("Dropping event because it couldn't be rendered within %" PRIu64 " bytes.", max_buffer_size_); + return nonstd::make_unexpected(error_message); } size = used; - buf.reset((LPWSTR)malloc(size)); - if (!buf) { - return false; - } - if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) { - LOG_LAST_ERROR(EvtRender); - return false; + buf.reset((LPWSTR) malloc(size)); + if (!buf) + return nonstd::make_unexpected("malloc failed"); + if (!EvtRender(nullptr, event_handle, EvtRenderEventXml, size, buf.get(), &used, &propertyCount)) { + std::string error_message = fmt::format("EvtRender failed due to %s", utils::OsUtils::windowsErrorToErrorCode(GetLastError()).message()); + return nonstd::make_unexpected(error_message); } } + logger_->log_trace("Event rendered with size %" PRIu32, used); + return wel::to_string(buf.get()); +} - logger_->log_debug("Event rendered with size %" PRIu32 ". Performing doc traversing...", used); - - std::string xml = wel::to_string(buf.get()); +nonstd::expected<EventRender, std::string> ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent) { + auto event_as_xml = renderEventAsXml(hEvent); + if (!event_as_xml) + return nonstd::make_unexpected(event_as_xml.error()); pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_string(xml.c_str()); + if (!doc.load_string(event_as_xml->c_str())) + return nonstd::make_unexpected("Invalid XML produced"); - if (!result) { - logger_->log_error("Invalid XML produced"); - return false; - } + EventRender result; // this is a well known path. - std::string providerName = doc.child("Event").child("System").child("Provider").attribute("Name").value(); - wel::WindowsEventLogMetadataImpl metadata{getEventLogHandler(providerName).getMetadata(), hEvent}; - wel::MetadataWalker walker{metadata, channel_, !resolve_as_attributes_, apply_identifier_function_, regex_}; + std::string provider_name = doc.child("Event").child("System").child("Provider").attribute("Name").value(); + wel::WindowsEventLogMetadataImpl metadata{getEventLogHandler(provider_name).getMetadata(), hEvent}; + wel::MetadataWalker walker{metadata, channel_, !resolve_as_attributes_, apply_identifier_function_, regex_ ? &*regex_ : nullptr}; // resolve the event metadata doc.traverse(walker); - logger_->log_debug("Finish doc traversing, performing writing..."); + logger_->log_trace("Finish doc traversing, performing writing..."); if (output_.plaintext) { logger_->log_trace("Writing event in plain text"); - auto& handler = getEventLogHandler(providerName); - auto message = handler.getEventMessage(hEvent); + auto& handler = getEventLogHandler(provider_name); + auto event_message = handler.getEventMessage(hEvent); - if (!message.empty()) { - for (const auto &mapEntry : walker.getIdentifiers()) { - // replace the identifiers with their translated strings. - if (mapEntry.first.empty() || mapEntry.second.empty()) { - continue; // This is most probably a result of a failed ID resolution + if (event_message) { + for (const auto& map_entry : walker.getIdentifiers()) { + if (map_entry.first.empty() || map_entry.second.empty()) { + continue; } - utils::StringUtils::replaceAll(message, mapEntry.first, mapEntry.second); + utils::StringUtils::replaceAll(*event_message, map_entry.first, map_entry.second); } - wel::WindowsEventLogHeader log_header(header_names_); - // set the delimiter - log_header.setDelimiter(header_delimiter_); - // render the header. - eventRender.plaintext = log_header.getEventHeader([&walker](wel::METADATA metadata) { return walker.getMetadata(metadata); }); - eventRender.plaintext += "Message" + header_delimiter_ + " "; - eventRender.plaintext += message; } + + std::string_view payload_name = event_message ? "Message" : "Error"; + + wel::WindowsEventLogHeader log_header(header_names_, header_delimiter_, payload_name.size()); + result.plaintext = log_header.getEventHeader([&walker](wel::METADATA metadata) { return walker.getMetadata(metadata); }); + result.plaintext += payload_name; + result.plaintext += log_header.getDelimiterFor(payload_name.size()); + result.plaintext += event_message.has_value() ? *event_message : event_message.error().message(); + logger_->log_trace("Finish writing in plain text"); } @@ -618,7 +631,7 @@ bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& e logger_->log_trace("Finish substituting %% in XML"); if (resolve_as_attributes_) { - eventRender.matched_fields = walker.getFieldValues(); + result.matched_fields = walker.getFieldValues(); } } @@ -627,27 +640,27 @@ bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& e wel::XmlString writer; doc.print(writer, "", pugi::format_raw); // no indentation or formatting - xml = writer.xml_; + event_as_xml = writer.xml_; - eventRender.xml = std::move(xml); + result.xml = std::move(*event_as_xml); logger_->log_trace("Finish writing in XML"); } if (output_.json.type == JSONType::Raw) { logger_->log_trace("Writing event in raw JSON"); - eventRender.json = wel::jsonToString(wel::toRawJSON(doc)); + result.json = wel::jsonToString(wel::toRawJSON(doc)); logger_->log_trace("Finish writing in raw JSON"); } else if (output_.json.type == JSONType::Simple) { logger_->log_trace("Writing event in simple JSON"); - eventRender.json = wel::jsonToString(wel::toSimpleJSON(doc)); + result.json = wel::jsonToString(wel::toSimpleJSON(doc)); logger_->log_trace("Finish writing in simple JSON"); } else if (output_.json.type == JSONType::Flattened) { logger_->log_trace("Writing event in flattened JSON"); - eventRender.json = wel::jsonToString(wel::toFlattenedJSON(doc)); + result.json = wel::jsonToString(wel::toFlattenedJSON(doc)); logger_->log_trace("Finish writing in flattened JSON"); } - return true; + return result; } void ConsumeWindowsEventLog::refreshTimeZoneData() { @@ -725,18 +738,18 @@ void ConsumeWindowsEventLog::putEventRenderFlowFileToSession(const EventRender& } } -void ConsumeWindowsEventLog::LogWindowsError(std::string error) const { +void ConsumeWindowsEventLog::LogWindowsError(const std::string& error) const { auto error_id = GetLastError(); LPVOID lpMsg; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, + nullptr, error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsg, - 0, NULL); + 0, nullptr); logger_->log_error((error + " %x: %s\n").c_str(), static_cast<int>(error_id), reinterpret_cast<char *>(lpMsg)); @@ -745,8 +758,4 @@ void ConsumeWindowsEventLog::LogWindowsError(std::string error) const { REGISTER_RESOURCE(ConsumeWindowsEventLog, Processor); -} // namespace processors -} // namespace minifi -} // namespace nifi -} // namespace apache -} // namespace org +} // namespace org::apache::nifi::minifi::processors diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.h b/extensions/windows-event-log/ConsumeWindowsEventLog.h index 536f0a280..e7530d624 100644 --- a/extensions/windows-event-log/ConsumeWindowsEventLog.h +++ b/extensions/windows-event-log/ConsumeWindowsEventLog.h @@ -43,12 +43,9 @@ #include "concurrentqueue.h" #include "pugixml.hpp" #include "utils/Export.h" +#include "utils/RegexUtils.h" -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace processors { +namespace org::apache::nifi::minifi::processors { struct EventRender { std::map<std::string, std::string> matched_fields; @@ -63,7 +60,7 @@ class ConsumeWindowsEventLog : public core::Processor { public: explicit ConsumeWindowsEventLog(const std::string& name, const utils::Identifier& uuid = {}); - virtual ~ConsumeWindowsEventLog(); + ~ConsumeWindowsEventLog() override; EXTENSIONAPI static constexpr const char* Description = "Windows Event Log Subscribe Callback to receive FlowFiles from Events on Windows."; @@ -83,20 +80,20 @@ class ConsumeWindowsEventLog : public core::Processor { EXTENSIONAPI static const core::Property ProcessOldEvents; static auto properties() { return std::array{ - Channel, - Query, - MaxBufferSize, - InactiveDurationToReconnect, - IdentifierMatcher, - IdentifierFunction, - ResolveAsAttributes, - EventHeaderDelimiter, - EventHeader, - OutputFormat, - JSONFormat, - BatchCommitSize, - BookmarkRootDirectory, - ProcessOldEvents + Channel, + Query, + MaxBufferSize, + InactiveDurationToReconnect, + IdentifierMatcher, + IdentifierFunction, + ResolveAsAttributes, + EventHeaderDelimiter, + EventHeader, + OutputFormat, + JSONFormat, + BatchCommitSize, + BookmarkRootDirectory, + ProcessOldEvents }; } @@ -112,18 +109,20 @@ class ConsumeWindowsEventLog : public core::Processor { void onSchedule(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) override; void onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSession> &session) override; - void initialize(void) override; + void initialize() override; void notifyStop() override; - protected: + private: void refreshTimeZoneData(); void putEventRenderFlowFileToSession(const EventRender& eventRender, core::ProcessSession& session) const; - wel::WindowsEventLogHandler& getEventLogHandler(const std::string & name); - bool insertHeaderName(wel::METADATA_NAMES &header, const std::string &key, const std::string &value) const; - void LogWindowsError(std::string error = "Error") const; - bool createEventRender(EVT_HANDLE eventHandle, EventRender& eventRender); + wel::WindowsEventLogHandler& getEventLogHandler(const std::string& name); + static bool insertHeaderName(wel::METADATA_NAMES& header, const std::string& key, const std::string& value); + void LogWindowsError(const std::string& error = "Error") const; + nonstd::expected<EventRender, std::string> createEventRender(EVT_HANDLE eventHandle); void substituteXMLPercentageItems(pugi::xml_document& doc); + nonstd::expected<std::string, std::string> renderEventAsXml(EVT_HANDLE event_handle); + static constexpr const char* XML = "XML"; static constexpr const char* Both = "Both"; static constexpr const char* Plaintext = "Plaintext"; @@ -140,29 +139,29 @@ class ConsumeWindowsEventLog : public core::Processor { const decltype(std::chrono::steady_clock::now()) time_ = std::chrono::steady_clock::now(); }; - bool commitAndSaveBookmark(const std::wstring &bookmarkXml, const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSession> &session); + bool commitAndSaveBookmark(const std::wstring& bookmarkXml, const std::shared_ptr<core::ProcessContext>& context, const std::shared_ptr<core::ProcessSession>& session); - std::tuple<size_t, std::wstring> processEventLogs(const std::shared_ptr<core::ProcessContext> &context, - const std::shared_ptr<core::ProcessSession> &session, const EVT_HANDLE& event_query_results); + std::tuple<size_t, std::wstring> processEventLogs(const std::shared_ptr<core::ProcessContext>& context, + const std::shared_ptr<core::ProcessSession>& session, + const EVT_HANDLE& event_query_results); std::shared_ptr<core::logging::Logger> logger_; - core::CoreComponentStateManager* state_manager_; + core::CoreComponentStateManager* state_manager_{nullptr}; wel::METADATA_NAMES header_names_; - std::string header_delimiter_; + std::optional<std::string> header_delimiter_; std::string channel_; std::wstring wstrChannel_; std::wstring wstrQuery_; - std::string regex_; + std::optional<utils::Regex> regex_; bool resolve_as_attributes_{false}; bool apply_identifier_function_{false}; std::string provenanceUri_; std::string computerName_; - uint64_t maxBufferSize_{}; - DWORD lastActivityTimestamp_{}; - std::map<std::string, wel::WindowsEventLogHandler > providers_; + uint64_t max_buffer_size_{}; + std::map<std::string, wel::WindowsEventLogHandler> providers_; uint64_t batch_commit_size_{}; - enum class JSONType {None, Raw, Simple, Flattened}; + enum class JSONType { None, Raw, Simple, Flattened }; struct OutputFormat { bool xml{false}; @@ -185,8 +184,4 @@ class ConsumeWindowsEventLog : public core::Processor { std::string timezone_offset_; // Represented as UTC offset in (+|-)HH:MM format, like +02:00 }; -} // namespace processors -} // namespace minifi -} // namespace nifi -} // namespace apache -} // namespace org +} // namespace org::apache::nifi::minifi::processors diff --git a/extensions/windows-event-log/tests/BookmarkTests.cpp b/extensions/windows-event-log/tests/BookmarkTests.cpp index a05f15988..eeb2c7b1c 100644 --- a/extensions/windows-event-log/tests/BookmarkTests.cpp +++ b/extensions/windows-event-log/tests/BookmarkTests.cpp @@ -217,10 +217,10 @@ TEST_CASE("Bookmark::getNewBookmarkXml() updates the bookmark", "[add_event]") { unique_evt_handle results = queryEvents(); unique_evt_handle event = getFirstEventFromResults(results); - std::wstring bookmark_xml_after; - REQUIRE(bookmark->getNewBookmarkXml(event.get(), bookmark_xml_after)); + auto bookmark_xml_after = bookmark->getNewBookmarkXml(event.get()); - REQUIRE(bookmark_xml_before != bookmark_xml_after); + REQUIRE(bookmark_xml_after); + CHECK(bookmark_xml_before != *bookmark_xml_after); } TEST_CASE("Bookmark::saveBookmarkXml() updates the bookmark and saves it to the state manager", "[save_bookmark][state]") { diff --git a/extensions/windows-event-log/tests/CWELTestUtils.h b/extensions/windows-event-log/tests/CWELTestUtils.h index f1bd1a597..0a0bd85ee 100644 --- a/extensions/windows-event-log/tests/CWELTestUtils.h +++ b/extensions/windows-event-log/tests/CWELTestUtils.h @@ -45,7 +45,7 @@ class OutputFormatTestController : public TestController { json_format_(std::move(json_format)) {} std::string run() { - LogTestController::getInstance().setDebug<ConsumeWindowsEventLog>(); + LogTestController::getInstance().setTrace<ConsumeWindowsEventLog>(); LogTestController::getInstance().setDebug<PutFile>(); std::shared_ptr<TestPlan> test_plan = createPlan(); @@ -69,7 +69,7 @@ class OutputFormatTestController : public TestController { } test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); { diff --git a/extensions/windows-event-log/tests/ConsumeWindowsEventLogTests.cpp b/extensions/windows-event-log/tests/ConsumeWindowsEventLogTests.cpp index 47d69bc43..c16767ee5 100644 --- a/extensions/windows-event-log/tests/ConsumeWindowsEventLogTests.cpp +++ b/extensions/windows-event-log/tests/ConsumeWindowsEventLogTests.cpp @@ -22,19 +22,19 @@ #include "processors/PutFile.h" #include "TestBase.h" #include "Catch.h" -#include "utils/TestUtils.h" #include "utils/file/FileUtils.h" -#include "rapidjson/document.h" - #include "CWELTestUtils.h" #include "Utils.h" +#include "../wel/UniqueEvtHandle.h" +#include "utils/Deleters.h" using ConsumeWindowsEventLog = org::apache::nifi::minifi::processors::ConsumeWindowsEventLog; using LogAttribute = org::apache::nifi::minifi::processors::LogAttribute; using PutFile = org::apache::nifi::minifi::processors::PutFile; using ConfigurableComponent = org::apache::nifi::minifi::core::ConfigurableComponent; using IdGenerator = org::apache::nifi::minifi::utils::IdGenerator; +using unique_evt_handle = org::apache::nifi::minifi::wel::unique_evt_handle; namespace org::apache::nifi::minifi::test { @@ -56,10 +56,10 @@ class SimpleFormatTestController : public OutputFormatTestController { using OutputFormatTestController::OutputFormatTestController; protected: - void dispatchBookmarkEvent() { + void dispatchBookmarkEvent() override { reportEvent(APPLICATION_CHANNEL, "Event zero: this is in the past"); } - void OutputFormatTestController::dispatchCollectedEvent() { + void dispatchCollectedEvent() override { reportEvent(APPLICATION_CHANNEL, "Event one"); } }; @@ -82,7 +82,7 @@ TEST_CASE("ConsumeWindowsEventLog properties work with default values", "[create std::shared_ptr<TestPlan> test_plan = test_controller.createPlan(); auto processor = test_plan->addProcessor("ConsumeWindowsEventLog", "cwel"); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); auto properties_required_or_with_default_value = { ConsumeWindowsEventLog::Channel, @@ -114,7 +114,7 @@ TEST_CASE("ConsumeWindowsEventLog properties work with default values", "[create } } - REQUIRE(LogTestController::getInstance().contains("Successfully configured CWEL")); + CHECK(LogTestController::getInstance().contains("Successfully configured CWEL")); } TEST_CASE("ConsumeWindowsEventLog onSchedule throws if it cannot create the bookmark", "[create][bookmark]") { @@ -144,35 +144,37 @@ TEST_CASE("ConsumeWindowsEventLog can consume new events", "[onTrigger]") { reportEvent(APPLICATION_CHANNEL, "Event zero"); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 0 Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 0 Events")); // event zero is not reported as the bookmark is created on the first run // and we use the default config setting ProcessOldEvents = false // later runs will start with a bookmark saved in the state manager test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); SECTION("Read one event") { reportEvent(APPLICATION_CHANNEL, "Event one"); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 1 Events")); - REQUIRE(LogTestController::getInstance().contains("<EventData><Data>Event one</Data></EventData>")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 1 Events")); + CHECK(LogTestController::getInstance().contains("<EventData><Data>Event one</Data></EventData>")); // make sure timezone attributes are present - REQUIRE(LogTestController::getInstance().contains("key:timezone.offset value:")); - REQUIRE(LogTestController::getInstance().contains("key:timezone.name value:")); + CHECK(LogTestController::getInstance().contains("key:timezone.offset value:")); + CHECK(LogTestController::getInstance().contains("key:timezone.name value:")); } - SECTION("Read two events") { + SECTION("Read three events") { reportEvent(APPLICATION_CHANNEL, "Event two"); reportEvent(APPLICATION_CHANNEL, "Event three"); + reportEvent(APPLICATION_CHANNEL, "%%1844"); // %%1844 expands to System - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 2 Events")); - REQUIRE(LogTestController::getInstance().contains("<EventData><Data>Event two</Data></EventData>")); - REQUIRE(LogTestController::getInstance().contains("<EventData><Data>Event three</Data></EventData>")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 3 Events")); + CHECK(LogTestController::getInstance().contains("<EventData><Data>Event two</Data></EventData>")); + CHECK(LogTestController::getInstance().contains("<EventData><Data>Event three</Data></EventData>")); + CHECK(LogTestController::getInstance().contains("<EventData><Data>System</Data></EventData>")); } } @@ -191,35 +193,35 @@ TEST_CASE("ConsumeWindowsEventLog bookmarking works", "[onTrigger]") { reportEvent(APPLICATION_CHANNEL, "Event zero"); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 0 Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 0 Events")); test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); SECTION("Read in one go") { reportEvent(APPLICATION_CHANNEL, "Event one"); reportEvent(APPLICATION_CHANNEL, "Event two"); reportEvent(APPLICATION_CHANNEL, "Event three"); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 3 Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 3 Events")); } SECTION("Read in two batches") { reportEvent(APPLICATION_CHANNEL, "Event one"); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 1 Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 1 Events")); reportEvent(APPLICATION_CHANNEL, "Event two"); reportEvent(APPLICATION_CHANNEL, "Event three"); test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed 2 Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed 2 Events")); } } @@ -240,33 +242,33 @@ TEST_CASE("ConsumeWindowsEventLog extracts some attributes by default", "[onTrig { reportEvent(APPLICATION_CHANNEL, "Event zero: this is in the past"); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); } test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); // 1st event, on Info level { reportEvent(APPLICATION_CHANNEL, "Event one: something interesting happened", EVENTLOG_INFORMATION_TYPE); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("key:Keywords value:Classic")); - REQUIRE(LogTestController::getInstance().contains("key:Level value:Information")); + CHECK(LogTestController::getInstance().contains("key:Keywords value:Classic")); + CHECK(LogTestController::getInstance().contains("key:Level value:Information")); } test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); // 2st event, on Warning level { reportEvent(APPLICATION_CHANNEL, "Event two: something fishy happened!", EVENTLOG_WARNING_TYPE); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("key:Keywords value:Classic")); - REQUIRE(LogTestController::getInstance().contains("key:Level value:Warning")); + CHECK(LogTestController::getInstance().contains("key:Keywords value:Classic")); + CHECK(LogTestController::getInstance().contains("key:Level value:Warning")); } } @@ -289,18 +291,18 @@ void outputFormatSetterTestHelper(const std::string &output_format, int expected { reportEvent(APPLICATION_CHANNEL, "Event zero: this is in the past"); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); } test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); { reportEvent(APPLICATION_CHANNEL, "Event one"); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("Logged " + std::to_string(expected_num_flow_files) + " flow files")); + CHECK(LogTestController::getInstance().contains("Logged " + std::to_string(expected_num_flow_files) + " flow files")); } } @@ -316,31 +318,44 @@ TEST_CASE("ConsumeWindowsEventLog output format can be set", "[create][output_fo outputFormatSetterTestHelper("InvalidValue", 0); } -// NOTE(fgerlits): I don't know how to unit test this, as my manually published events all result in an empty string if OutputFormat is Plaintext -// but it does seem to work, based on manual tests reading system logs -// TEST_CASE("ConsumeWindowsEventLog prints events in plain text correctly", "[onTrigger]") +TEST_CASE("ConsumeWindowsEventLog prints events in plain text correctly", "[onTrigger]") { + std::string event = SimpleFormatTestController{APPLICATION_CHANNEL, QUERY, "Plaintext"}.run(); + CHECK(!event.empty()); + CHECK(event.find(R"(Log Name: Application)") != std::string::npos); + CHECK(event.find(R"(Source: Application)") != std::string::npos); + CHECK(event.find(R"(Date: )") != std::string::npos); + CHECK(event.find(R"(Record ID: )") != std::string::npos); + CHECK(event.find(R"(Event ID: 14985)") != std::string::npos); + CHECK(event.find(R"(Task Category: N/A)") != std::string::npos); + CHECK(event.find(R"(Level: Information)") != std::string::npos); + CHECK(event.find(R"(Keywords: Classic)") != std::string::npos); + CHECK(event.find(R"(User: N/A)") != std::string::npos); + CHECK(event.find(R"(Computer: )") != std::string::npos); + CHECK(event.find(R"(EventType: 4)") != std::string::npos); + CHECK(event.find(R"(Error: The message resource is present but the message was not found in the message table.)") != std::string::npos); +} TEST_CASE("ConsumeWindowsEventLog prints events in XML correctly", "[onTrigger]") { std::string event = SimpleFormatTestController{APPLICATION_CHANNEL, QUERY, "XML"}.run(); - REQUIRE(event.find(R"(<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"><System><Provider Name="Application"/>)") != std::string::npos); - REQUIRE(event.find(R"(<EventID Qualifiers="0">14985</EventID>)") != std::string::npos); - REQUIRE(event.find(R"(<Level>4</Level>)") != std::string::npos); - REQUIRE(event.find(R"(<Task>0</Task>)") != std::string::npos); - REQUIRE(event.find(R"(<Keywords>0x80000000000000</Keywords><TimeCreated SystemTime=")") != std::string::npos); + CHECK(event.find(R"(<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"><System><Provider Name="Application"/>)") != std::string::npos); + CHECK(event.find(R"(<EventID Qualifiers="0">14985</EventID>)") != std::string::npos); + CHECK(event.find(R"(<Level>4</Level>)") != std::string::npos); + CHECK(event.find(R"(<Task>0</Task>)") != std::string::npos); + CHECK(event.find(R"(<Keywords>0x80000000000000</Keywords><TimeCreated SystemTime=")") != std::string::npos); // the timestamp (when the event was published) goes here - REQUIRE(event.find(R"("/><EventRecordID>)") != std::string::npos); + CHECK(event.find(R"("/><EventRecordID>)") != std::string::npos); // the ID of the event goes here (a number) - REQUIRE(event.find(R"(</EventRecordID>)") != std::string::npos); - REQUIRE(event.find(R"(<Channel>Application</Channel><Computer>)") != std::string::npos); + CHECK(event.find(R"(</EventRecordID>)") != std::string::npos); + CHECK(event.find(R"(<Channel>Application</Channel><Computer>)") != std::string::npos); // the computer name goes here - REQUIRE(event.find(R"(</Computer><Security/></System><EventData><Data>Event one</Data></EventData></Event>)") != std::string::npos); + CHECK(event.find(R"(</Computer><Security/></System><EventData><Data>Event one</Data></EventData></Event>)") != std::string::npos); } TEST_CASE("ConsumeWindowsEventLog prints events in JSON::Simple correctly", "[onTrigger]") { std::string event = SimpleFormatTestController{APPLICATION_CHANNEL, "*", "JSON", "Simple"}.run(); // the json must be single-line - REQUIRE(event.find('\n') == std::string::npos); + CHECK(event.find('\n') == std::string::npos); utils::verifyJSON(event, R"json( { "System": { @@ -410,11 +425,11 @@ void batchCommitSizeTestHelper(std::size_t num_events_read, std::size_t batch_co { reportEvent(APPLICATION_CHANNEL, "Event zero: this is in the past"); - test_controller.runSession(test_plan); + TestController::runSession(test_plan); } test_plan->reset(); - LogTestController::getInstance().resetStream(LogTestController::getInstance().log_output); + LogTestController::resetStream(LogTestController::getInstance().log_output); auto generate_events = [](const std::size_t event_count) { std::vector<std::string> events; @@ -427,8 +442,8 @@ void batchCommitSizeTestHelper(std::size_t num_events_read, std::size_t batch_co for (const auto& event : generate_events(num_events_read)) reportEvent(APPLICATION_CHANNEL, event.c_str()); - test_controller.runSession(test_plan); - REQUIRE(LogTestController::getInstance().contains("processed " + std::to_string(expected_event_count) + " Events")); + TestController::runSession(test_plan); + CHECK(LogTestController::getInstance().contains("processed " + std::to_string(expected_event_count) + " Events")); } } // namespace diff --git a/extensions/windows-event-log/tests/MetadataWalkerTests.cpp b/extensions/windows-event-log/tests/MetadataWalkerTests.cpp index c84fba61f..ee0339cfe 100644 --- a/extensions/windows-event-log/tests/MetadataWalkerTests.cpp +++ b/extensions/windows-event-log/tests/MetadataWalkerTests.cpp @@ -16,12 +16,8 @@ * limitations under the License. */ -#include <fstream> #include <map> -#include <memory> -#include <utility> #include <string> -#include <set> #include "TestBase.h" #include "Catch.h" @@ -32,12 +28,29 @@ using METADATA = org::apache::nifi::minifi::wel::METADATA; using MetadataWalker = org::apache::nifi::minifi::wel::MetadataWalker; -using WindowsEventLogHandler = org::apache::nifi::minifi::wel::WindowsEventLogHandler; using WindowsEventLogMetadata = org::apache::nifi::minifi::wel::WindowsEventLogMetadata; +using WindowsEventLogMetadataImpl = org::apache::nifi::minifi::wel::WindowsEventLogMetadataImpl; using XmlString = org::apache::nifi::minifi::wel::XmlString; namespace { +std::string updateXmlMetadata(const std::string &xml, EVT_HANDLE metadata_ptr, EVT_HANDLE event_ptr, bool update_xml, bool resolve, utils::Regex const* regex = nullptr) { + WindowsEventLogMetadataImpl metadata{metadata_ptr, event_ptr}; + MetadataWalker walker(metadata, "", update_xml, resolve, regex); + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_string(xml.c_str()); + + if (result) { + doc.traverse(walker); + XmlString writer; + doc.print(writer, "", pugi::format_raw); // no indentation or formatting + return writer.xml_; + } else { + throw std::runtime_error("Could not parse XML document"); + } +} + std::string formatXml(const std::string &xml) { pugi::xml_document doc; pugi::xml_parse_result result = doc.load_string(xml.c_str()); @@ -60,8 +73,8 @@ const short event_type_index = 178; // NOLINT short comes from WINDOWS API class FakeWindowsEventLogMetadata : public WindowsEventLogMetadata { public: - std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const override { return "event_data_for_flag_" + std::to_string(flags); } - std::string getEventTimestamp() const override { return "event_timestamp"; } + [[nodiscard]] std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const override { return "event_data_for_flag_" + std::to_string(flags); } + [[nodiscard]] std::string getEventTimestamp() const override { return "event_timestamp"; } short getEventTypeIndex() const override { return event_type_index; } // NOLINT short comes from WINDOWS API }; @@ -71,33 +84,35 @@ TEST_CASE("MetadataWalker updates the Sid in the XML if both update_xml and reso std::string xml = readFile("resources/nobodysid.xml"); SECTION("No resolution") { - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true) == formatXml(xml)); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, false, true) == formatXml(xml)); } SECTION("Resolve nobody") { std::string nobody = readFile("resources/withsids.xml"); - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, true, true, ".*Sid") == formatXml(nobody)); + auto regex = utils::Regex(".*Sid"); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, true, true, ®ex) == formatXml(nobody)); } } TEST_CASE("MetadataWalker works even when there is no Data block", "[updateXmlMetadata]") { std::string xml = readFile("resources/nodata.xml"); - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true) == formatXml(xml)); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, false, true) == formatXml(xml)); } TEST_CASE("MetadataWalker throws if the input XML is invalid", "[updateXmlMetadata]") { std::string xml = readFile("resources/invalidxml.xml"); - REQUIRE_THROWS(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true) == formatXml(xml)); + REQUIRE_THROWS(updateXmlMetadata(xml, nullptr, nullptr, false, true) == formatXml(xml)); } TEST_CASE("MetadataWalker will leave a Sid unchanged if it doesn't correspond to a user", "[updateXmlMetadata]") { std::string xml = readFile("resources/unknownsid.xml"); - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true) == formatXml(xml)); - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, true, true) == formatXml(xml)); - REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, true, true, ".*Sid") == formatXml(xml)); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, false, true) == formatXml(xml)); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, true, true) == formatXml(xml)); + auto regex = utils::Regex(".*Sid"); + REQUIRE(updateXmlMetadata(xml, nullptr, nullptr, true, true, ®ex) == formatXml(xml)); } TEST_CASE("MetadataWalker can replace multiple Sids", "[updateXmlMetadata]") { @@ -106,7 +121,7 @@ TEST_CASE("MetadataWalker can replace multiple Sids", "[updateXmlMetadata]") { std::string programmaticallyResolved; pugi::xml_document doc; - xml = MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true); + xml = updateXmlMetadata(xml, nullptr, nullptr, false, true); pugi::xml_parse_result result = doc.load_string(xml.c_str()); for (const auto &node : doc.child("Event").child("EventData").children()) { @@ -128,21 +143,23 @@ namespace { void extractMappingsTestHelper(const std::string &file_name, bool update_xml, bool resolve, - std::map<std::string, std::string> expected_identifiers, - std::map<METADATA, std::string> expected_metadata, - std::map<std::string, std::string> expected_field_values) { + const std::map<std::string, std::string>& expected_identifiers, + const std::map<METADATA, std::string>& expected_metadata, + const std::map<std::string, std::string>& expected_field_values) { std::string input_xml = readFile(file_name); + REQUIRE(!input_xml.empty()); pugi::xml_document doc; pugi::xml_parse_result result = doc.load_string(input_xml.c_str()); - REQUIRE(result); + CHECK(result); - MetadataWalker walker(FakeWindowsEventLogMetadata{}, METADATA_WALKER_TESTS_LOG_NAME, update_xml, resolve, ".*Sid"); + auto regex = utils::Regex(".*Sid"); + MetadataWalker walker(FakeWindowsEventLogMetadata{}, METADATA_WALKER_TESTS_LOG_NAME, update_xml, resolve, ®ex); doc.traverse(walker); - REQUIRE(walker.getIdentifiers() == expected_identifiers); - REQUIRE(walker.getFieldValues() == expected_field_values); + CHECK(walker.getIdentifiers() == expected_identifiers); + CHECK(walker.getFieldValues() == expected_field_values); for (const auto &key_value_pair : expected_metadata) { - REQUIRE(walker.getMetadata(key_value_pair.first) == key_value_pair.second); + CHECK(walker.getMetadata(key_value_pair.first) == key_value_pair.second); } } diff --git a/extensions/windows-event-log/wel/MetadataWalker.cpp b/extensions/windows-event-log/wel/MetadataWalker.cpp index bf7eb6784..8b5ca7c5e 100644 --- a/extensions/windows-event-log/wel/MetadataWalker.cpp +++ b/extensions/windows-event-log/wel/MetadataWalker.cpp @@ -30,11 +30,7 @@ #include "MetadataWalker.h" #include "XMLString.h" -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace wel { +namespace org::apache::nifi::minifi::wel { bool MetadataWalker::for_each(pugi::xml_node &node) { // don't shortcut resolution here so that we can log attributes. @@ -43,7 +39,7 @@ bool MetadataWalker::for_each(pugi::xml_node &node) { for (pugi::xml_attribute attr : node.attributes()) { const auto idUpdate = [&](const std::string &input) { if (resolve_) { - const auto resolved = utils::OsUtils::userIdToUsername(input); + auto resolved = utils::OsUtils::userIdToUsername(input); replaced_identifiers_[input] = resolved; return resolved; } @@ -106,11 +102,11 @@ bool MetadataWalker::for_each(pugi::xml_node &node) { return true; } -std::vector<std::string> MetadataWalker::getIdentifiers(const std::string &text) const { +std::vector<std::string> MetadataWalker::getIdentifiers(const std::string &text) { auto pos = text.find("%{"); std::vector<std::string> found_strings; while (pos != std::string::npos) { - auto next_pos = text.find("}", pos); + auto next_pos = text.find('}', pos); if (next_pos != std::string::npos) { auto potential_identifier = text.substr(pos + 2, next_pos - (pos + 2)); std::smatch match; @@ -146,7 +142,7 @@ std::string MetadataWalker::getMetadata(METADATA metadata) const { case EVENT_TYPE: return std::to_string(windows_event_log_metadata_.getEventTypeIndex()); case COMPUTER: - return windows_event_log_metadata_.getComputerName(); + return WindowsEventLogMetadata::getComputerName(); } return "N/A"; } @@ -159,23 +155,6 @@ std::map<std::string, std::string> MetadataWalker::getIdentifiers() const { return replaced_identifiers_; } -std::string MetadataWalker::updateXmlMetadata(const std::string &xml, EVT_HANDLE metadata_ptr, EVT_HANDLE event_ptr, bool update_xml, bool resolve, const std::string ®ex) { - WindowsEventLogMetadataImpl metadata{metadata_ptr, event_ptr}; - MetadataWalker walker(metadata, "", update_xml, resolve, regex); - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_string(xml.c_str()); - - if (result) { - doc.traverse(walker); - wel::XmlString writer; - doc.print(writer, "", pugi::format_raw); // no indentation or formatting - return writer.xml_; - } else { - throw std::runtime_error("Could not parse XML document"); - } -} - std::string MetadataWalker::to_string(const wchar_t* pChar) { return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(pChar); } @@ -193,8 +172,4 @@ void MetadataWalker::updateText(pugi::xml_node &node, const std::string &field_n } } -} /* namespace wel */ -} /* namespace minifi */ -} /* namespace nifi */ -} /* namespace apache */ -} /* namespace org */ +} // namespace org::apache::nifi::minifi::wel diff --git a/extensions/windows-event-log/wel/MetadataWalker.h b/extensions/windows-event-log/wel/MetadataWalker.h index 05fc2ea61..014dadbdb 100644 --- a/extensions/windows-event-log/wel/MetadataWalker.h +++ b/extensions/windows-event-log/wel/MetadataWalker.h @@ -29,6 +29,7 @@ #include <string> #include <vector> #include <optional> +#include <utility> #include "core/Core.h" #include "core/Processor.h" @@ -41,11 +42,7 @@ #include "pugixml.hpp" #include "utils/RegexUtils.h" -namespace org { -namespace apache { -namespace nifi { -namespace minifi { -namespace wel { +namespace org::apache::nifi::minifi::wel { /** * Defines a tree walker for the XML input @@ -53,10 +50,10 @@ namespace wel { */ class MetadataWalker : public pugi::xml_tree_walker { public: - MetadataWalker(const WindowsEventLogMetadata& windows_event_log_metadata, const std::string &log_name, bool update_xml, bool resolve, const std::string ®ex = "") + MetadataWalker(const WindowsEventLogMetadata& windows_event_log_metadata, std::string log_name, bool update_xml, bool resolve, utils::Regex const* regex) : windows_event_log_metadata_(windows_event_log_metadata), - log_name_(log_name), - regex_(regex.empty() ? std::nullopt : std::make_optional(regex)), + log_name_(std::move(log_name)), + regex_(regex), update_xml_(update_xml), resolve_(resolve) { } @@ -67,16 +64,14 @@ class MetadataWalker : public pugi::xml_tree_walker { */ bool for_each(pugi::xml_node &node) override; - static std::string updateXmlMetadata(const std::string &xml, EVT_HANDLE metadata_ptr, EVT_HANDLE event_ptr, bool update_xml, bool resolve, const std::string ®ex = ""); + [[nodiscard]] std::map<std::string, std::string> getFieldValues() const; - std::map<std::string, std::string> getFieldValues() const; + [[nodiscard]] std::map<std::string, std::string> getIdentifiers() const; - std::map<std::string, std::string> getIdentifiers() const; - - std::string getMetadata(METADATA metadata) const; + [[nodiscard]] std::string getMetadata(METADATA metadata) const; private: - std::vector<std::string> getIdentifiers(const std::string &text) const; + static std::vector<std::string> getIdentifiers(const std::string &text); static std::string getString(const std::map<std::string, std::string> &map, const std::string &field) { auto srch = map.find(field); @@ -94,17 +89,13 @@ class MetadataWalker : public pugi::xml_tree_walker { void updateText(pugi::xml_node &node, const std::string &field_name, std::function<std::string(const std::string &)> &&fn); const WindowsEventLogMetadata& windows_event_log_metadata_; - std::string log_name_; - std::optional<utils::Regex> regex_; - bool update_xml_; - bool resolve_; + const std::string log_name_; + utils::Regex const * const regex_; + const bool update_xml_; + const bool resolve_; std::map<std::string, std::string> metadata_; std::map<std::string, std::string> fields_values_; std::map<std::string, std::string> replaced_identifiers_; }; -} /* namespace wel */ -} /* namespace minifi */ -} /* namespace nifi */ -} /* namespace apache */ -} /* namespace org */ +} // namespace org::apache::nifi::minifi::wel diff --git a/extensions/windows-event-log/wel/WindowsEventLog.cpp b/extensions/windows-event-log/wel/WindowsEventLog.cpp index 1fa624872..ce10b91d0 100644 --- a/extensions/windows-event-log/wel/WindowsEventLog.cpp +++ b/extensions/windows-event-log/wel/WindowsEventLog.cpp @@ -18,6 +18,7 @@ #include <winmeta.h> #include <algorithm> +#include <chrono> #include <memory> #include <string> @@ -25,64 +26,53 @@ #include "UnicodeConversion.h" #include "utils/Deleters.h" #include "utils/gsl.h" +#include "UniqueEvtHandle.h" namespace org::apache::nifi::minifi::wel { +namespace { +std::string getEventTimestampStr(uint64_t event_timestamp) { + constexpr std::chrono::duration<int64_t> nt_to_unix_epoch{-11644473600}; // January 1, 1601 (NT epoch) - January 1, 1970 (Unix epoch): + + const std::chrono::duration<int64_t, std::ratio<1, 10'000'000>> event_timestamp_as_duration{event_timestamp}; + const auto converted_timestamp = std::chrono::system_clock::time_point{event_timestamp_as_duration + nt_to_unix_epoch}; + + return date::format("%m/%d/%Y %r %p", std::chrono::floor<std::chrono::milliseconds>(converted_timestamp)); +} +} // namespace + void WindowsEventLogMetadataImpl::renderMetadata() { DWORD status = ERROR_SUCCESS; EVT_VARIANT stackBuffer[4096]; DWORD dwBufferSize = sizeof(stackBuffer); using Deleter = utils::StackAwareDeleter<EVT_VARIANT, utils::FreeDeleter>; - std::unique_ptr<EVT_VARIANT, Deleter> rendered_values{ stackBuffer, Deleter{stackBuffer} }; + std::unique_ptr<EVT_VARIANT, Deleter> rendered_values{stackBuffer, Deleter{stackBuffer}}; DWORD dwBufferUsed = 0; DWORD dwPropertyCount = 0; - auto context = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem); - if (context == NULL) { + unique_evt_handle context{EvtCreateRenderContext(0, nullptr, EvtRenderContextSystem)}; + if (!context) return; - } - const auto contextGuard = gsl::finally([&context](){ - EvtClose(context); - }); - if (!EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount)) { + + if (!EvtRender(context.get(), event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount)) { if (ERROR_INSUFFICIENT_BUFFER != (status = GetLastError())) { return; } dwBufferSize = dwBufferUsed; - rendered_values.reset((PEVT_VARIANT)(malloc(dwBufferSize))); + rendered_values.reset((PEVT_VARIANT) (malloc(dwBufferSize))); if (!rendered_values) { return; } - EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount); + EvtRender(context.get(), event_ptr_, EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, &dwPropertyCount); if (ERROR_SUCCESS != (status = GetLastError())) { return; } } - event_timestamp_ = static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemTimeCreated].FileTimeVal; - - SYSTEMTIME st; - FILETIME ft; - - ft.dwHighDateTime = (DWORD)((event_timestamp_ >> 32) & 0xFFFFFFFF); - ft.dwLowDateTime = (DWORD)(event_timestamp_ & 0xFFFFFFFF); - - FileTimeToSystemTime(&ft, &st); - std::stringstream datestr; + event_timestamp_str_ = getEventTimestampStr(static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemTimeCreated].FileTimeVal); - std::string period = "AM"; - auto hour = st.wHour; - if (hour >= 12 && hour < 24) - period = "PM"; - if (hour > 12) - hour -= 12; - if (hour == 0) - hour = 12; - datestr << st.wMonth << "/" << st.wDay << "/" << st.wYear << " " << std::setfill('0') << std::setw(2) << hour << ":" << std::setfill('0') - << std::setw(2) << st.wMinute << ":" << std::setfill('0') << std::setw(2) << st.wSecond << " " << period; - event_timestamp_str_ = datestr.str(); auto level = static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemLevel]; auto keyword = static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemKeywords]; if (level.Type == EvtVarTypeByte) { @@ -121,20 +111,18 @@ std::string WindowsEventLogMetadataImpl::getEventData(EVT_FORMAT_MESSAGE_FLAGS f WCHAR stack_buffer[4096]; DWORD num_chars_in_buffer = sizeof(stack_buffer) / sizeof(stack_buffer[0]); using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>; - std::unique_ptr<WCHAR, Deleter> buffer{ stack_buffer, Deleter{stack_buffer} }; + std::unique_ptr<WCHAR, Deleter> buffer{stack_buffer, Deleter{stack_buffer}}; DWORD num_chars_used = 0; - DWORD result = 0; std::string event_data; - if (metadata_ptr_ == NULL || event_ptr_ == NULL) { + if (!metadata_ptr_ || !event_ptr_) { return event_data; } - - if (!EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, num_chars_in_buffer, buffer.get(), &num_chars_used)) { - result = GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == result) { + if (!EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, nullptr, flags, num_chars_in_buffer, buffer.get(), &num_chars_used)) { + auto last_error = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER == last_error) { num_chars_in_buffer = num_chars_used; buffer.reset((LPWSTR) malloc(num_chars_in_buffer * sizeof(WCHAR))); @@ -142,7 +130,7 @@ std::string WindowsEventLogMetadataImpl::getEventData(EVT_FORMAT_MESSAGE_FLAGS f return event_data; } - EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, NULL, flags, num_chars_in_buffer, buffer.get(), &num_chars_used); + EvtFormatMessage(metadata_ptr_, event_ptr_, 0, 0, nullptr, flags, num_chars_in_buffer, buffer.get(), &num_chars_used); } } @@ -158,51 +146,59 @@ std::string WindowsEventLogMetadataImpl::getEventData(EVT_FORMAT_MESSAGE_FLAGS f return event_data; } -std::string WindowsEventLogHandler::getEventMessage(EVT_HANDLE eventHandle) const { +nonstd::expected<std::string, std::error_code> WindowsEventLogHandler::getEventMessage(EVT_HANDLE eventHandle) const { std::string returnValue; WCHAR stack_buffer[4096]; DWORD num_chars_in_buffer = sizeof(stack_buffer) / sizeof(stack_buffer[0]); using Deleter = utils::StackAwareDeleter<WCHAR, utils::FreeDeleter>; - std::unique_ptr<WCHAR, Deleter> buffer{ stack_buffer, Deleter{stack_buffer} }; + std::unique_ptr<WCHAR, Deleter> buffer{stack_buffer, Deleter{stack_buffer}}; DWORD num_chars_used = 0; - DWORD status = 0; - EvtFormatMessage(metadata_provider_.get(), eventHandle, 0, 0, NULL, EvtFormatMessageEvent, num_chars_in_buffer, buffer.get(), &num_chars_used); - if (num_chars_used == 0) { - return returnValue; - } + bool evt_format_succeeded = EvtFormatMessage(metadata_provider_.get(), eventHandle, 0, 0, nullptr, EvtFormatMessageEvent, num_chars_in_buffer, buffer.get(), &num_chars_used); + if (evt_format_succeeded) + return to_string(buffer.get()); - // we need to get the size of the buffer - status = GetLastError(); - if (ERROR_INSUFFICIENT_BUFFER == status) { - num_chars_in_buffer = num_chars_used; - - /* All C++ examples use malloc and even HeapAlloc in some cases. To avoid any problems ( with EvtFormatMessage calling - free for example ) we will continue to use malloc and use a custom deleter with unique_ptr. - '*/ - buffer.reset((LPWSTR)malloc(num_chars_in_buffer * sizeof(WCHAR))); - if (!buffer) { - return returnValue; - } + DWORD status = GetLastError(); - EvtFormatMessage(metadata_provider_.get(), eventHandle, 0, 0, NULL, EvtFormatMessageEvent, num_chars_in_buffer, buffer.get(), &num_chars_used); - } + if (status != ERROR_INSUFFICIENT_BUFFER) + return nonstd::make_unexpected(utils::OsUtils::windowsErrorToErrorCode(status)); + + num_chars_in_buffer = num_chars_used; + buffer.reset((LPWSTR) malloc(num_chars_in_buffer * sizeof(WCHAR))); + if (!buffer) + return nonstd::make_unexpected(utils::OsUtils::windowsErrorToErrorCode(ERROR_OUTOFMEMORY)); + if (EvtFormatMessage(metadata_provider_.get(), eventHandle, 0, 0, nullptr, + EvtFormatMessageEvent, num_chars_in_buffer, + buffer.get(), &num_chars_used)) + return to_string(buffer.get()); + return nonstd::make_unexpected(utils::OsUtils::windowsErrorToErrorCode(GetLastError())); +} - if (ERROR_EVT_MESSAGE_NOT_FOUND == status || ERROR_EVT_MESSAGE_ID_NOT_FOUND == status) { - return returnValue; +namespace { +size_t findLongestHeaderNameSize(const METADATA_NAMES& header_names, const size_t minimum_size) { + size_t max = minimum_size; + for (const auto& option : header_names) { + max = (std::max(max, option.second.size())); } + return ++max; +} +} // namespace - // convert wstring to std::string - return to_string(buffer.get()); +WindowsEventLogHeader::WindowsEventLogHeader(const METADATA_NAMES& header_names, const std::optional<std::string>& custom_delimiter, const size_t minimum_size) + : header_names_(header_names), + custom_delimiter_(custom_delimiter), + longest_header_name_(findLongestHeaderNameSize(header_names, minimum_size)) { } -void WindowsEventLogHeader::setDelimiter(const std::string &delim) { - delimiter_ = delim; +std::string WindowsEventLogHeader::getDelimiterFor(size_t length) const { + if (custom_delimiter_) + return *custom_delimiter_; + return createDefaultDelimiter(length); } -std::string WindowsEventLogHeader::createDefaultDelimiter(size_t max, size_t length) const { - if (max > length) { - return ":" + std::string(max - length, ' '); +std::string WindowsEventLogHeader::createDefaultDelimiter(size_t length) const { + if (longest_header_name_ > length) { + return ":" + std::string(longest_header_name_ - length, ' '); } else { return ": "; } diff --git a/extensions/windows-event-log/wel/WindowsEventLog.h b/extensions/windows-event-log/wel/WindowsEventLog.h index 4eb44c5b3..e12ecbe9a 100644 --- a/extensions/windows-event-log/wel/WindowsEventLog.h +++ b/extensions/windows-event-log/wel/WindowsEventLog.h @@ -39,6 +39,7 @@ #include "UniqueEvtHandle.h" #include "pugixml.hpp" +#include "utils/expected.h" namespace org::apache::nifi::minifi::wel { @@ -59,9 +60,9 @@ enum METADATA { }; -// this is a continuous enum so we can rely on the array +// this is a continuous enum, so we can rely on the array -typedef std::vector<std::pair<METADATA, std::string>> METADATA_NAMES; +using METADATA_NAMES = std::vector<std::pair<METADATA, std::string>>; class WindowsEventLogHandler { public: @@ -71,9 +72,9 @@ class WindowsEventLogHandler { explicit WindowsEventLogHandler(EVT_HANDLE metadataProvider) : metadata_provider_(metadataProvider) { } - std::string getEventMessage(EVT_HANDLE eventHandle) const; + nonstd::expected<std::string, std::error_code> getEventMessage(EVT_HANDLE eventHandle) const; - EVT_HANDLE getMetadata() const; + [[nodiscard]] EVT_HANDLE getMetadata() const; private: unique_evt_handle metadata_provider_; @@ -82,8 +83,8 @@ class WindowsEventLogHandler { class WindowsEventLogMetadata { public: virtual ~WindowsEventLogMetadata() = default; - virtual std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const = 0; - virtual std::string getEventTimestamp() const = 0; + [[nodiscard]] virtual std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const = 0; + [[nodiscard]] virtual std::string getEventTimestamp() const = 0; virtual short getEventTypeIndex() const = 0; // NOLINT short comes from WINDOWS API static std::string getMetadataString(METADATA val) { @@ -146,22 +147,21 @@ class WindowsEventLogMetadata { class WindowsEventLogMetadataImpl : public WindowsEventLogMetadata { public: - WindowsEventLogMetadataImpl(EVT_HANDLE metadataProvider, EVT_HANDLE event_ptr) : metadata_ptr_(metadataProvider), event_timestamp_(0), event_ptr_(event_ptr) { + WindowsEventLogMetadataImpl(EVT_HANDLE metadataProvider, EVT_HANDLE event_ptr) : metadata_ptr_(metadataProvider), event_ptr_(event_ptr) { renderMetadata(); } - std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const override; + [[nodiscard]] std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags) const override; - std::string getEventTimestamp() const override { return event_timestamp_str_; } + [[nodiscard]] std::string getEventTimestamp() const override { return event_timestamp_str_; } short getEventTypeIndex() const override { return event_type_index_; } // NOLINT short comes from WINDOWS API private: void renderMetadata(); - uint64_t event_timestamp_; std::string event_type_; - short event_type_index_; // NOLINT short comes from WINDOWS API + short event_type_index_ = 0; // NOLINT short comes from WINDOWS API std::string event_timestamp_str_; EVT_HANDLE event_ptr_; EVT_HANDLE metadata_ptr_; @@ -169,33 +169,31 @@ class WindowsEventLogMetadataImpl : public WindowsEventLogMetadata { class WindowsEventLogHeader { public: - explicit WindowsEventLogHeader(METADATA_NAMES header_names) : header_names_(header_names) {} - - void setDelimiter(const std::string& delim); + explicit WindowsEventLogHeader(const METADATA_NAMES& header_names, const std::optional<std::string>& custom_delimiter, size_t minimum_size); template<typename MetadataCollection> std::string getEventHeader(const MetadataCollection& metadata_collection) const; + [[nodiscard]] std::string getDelimiterFor(size_t length) const; private: - std::string createDefaultDelimiter(size_t max, size_t length) const; + [[nodiscard]] std::string createDefaultDelimiter(size_t length) const; - std::string delimiter_; - METADATA_NAMES header_names_; + const METADATA_NAMES& header_names_; + const std::optional<std::string>& custom_delimiter_; + const size_t longest_header_name_; }; template<typename MetadataCollection> std::string WindowsEventLogHeader::getEventHeader(const MetadataCollection& metadata_collection) const { std::stringstream eventHeader; - size_t max = 1; - for (const auto& option : header_names_) { - max = (std::max(max, option.second.size())); - } - ++max; // increment by one to get space. + for (const auto& option : header_names_) { auto name = option.second; - if (!name.empty()) { - eventHeader << name << (delimiter_.empty() ? createDefaultDelimiter(max, name.size()) : delimiter_); - } + eventHeader << name; + if (custom_delimiter_) + eventHeader << *custom_delimiter_; + else + eventHeader << createDefaultDelimiter(name.size()); eventHeader << utils::StringUtils::trim(metadata_collection(option.first)) << std::endl; } diff --git a/libminifi/include/utils/OsUtils.h b/libminifi/include/utils/OsUtils.h index 66c9ea85c..a6ef83d58 100644 --- a/libminifi/include/utils/OsUtils.h +++ b/libminifi/include/utils/OsUtils.h @@ -18,6 +18,7 @@ #include <string> #include <optional> +#include <system_error> struct sockaddr; @@ -38,6 +39,8 @@ int64_t getSystemTotalPhysicalMemory(); #ifdef WIN32 /// Returns the total paging file size in bytes int64_t getTotalPagingFileSize(); + +std::error_code windowsErrorToErrorCode(unsigned long error_code); // NOLINT [runtime/int] #endif /// Returns the host architecture (e.g. x32, arm64) diff --git a/libminifi/src/utils/OsUtils.cpp b/libminifi/src/utils/OsUtils.cpp index 219cfe2b9..708ce426f 100644 --- a/libminifi/src/utils/OsUtils.cpp +++ b/libminifi/src/utils/OsUtils.cpp @@ -284,6 +284,10 @@ int64_t OsUtils::getTotalPagingFileSize() { DWORDLONG total_paging_file_size = memory_info.ullTotalPageFile; return total_paging_file_size; } + +std::error_code OsUtils::windowsErrorToErrorCode(DWORD error_code) { + return {gsl::narrow_cast<int>(error_code), std::system_category()}; +} #endif std::string OsUtils::getMachineArchitecture() {
