Skip to content

Commit bb8ea06

Browse files
adebreceniszaszm
andcommitted
MINIFICPP-1448 CWEL JSON output
This closes #976 Signed-off-by: Marton Szasz <[email protected]> Co-authored-by: Marton Szasz <[email protected]>
1 parent 4faf65c commit bb8ea06

File tree

13 files changed

+846
-99
lines changed

13 files changed

+846
-99
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ jobs:
4747
- name: Setup PATH
4848
uses: microsoft/[email protected]
4949
- id: build
50-
run: win_build_vs.bat build /CI /S /A
50+
run: |
51+
PATH %PATH%;C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86
52+
PATH %PATH%;C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn
53+
win_build_vs.bat build /CI /S /A
5154
shell: cmd
5255
windows_VS2019:
5356
name: "windows-vs2019"
@@ -59,7 +62,10 @@ jobs:
5962
- name: Setup PATH
6063
uses: microsoft/[email protected]
6164
- id: build
62-
run: win_build_vs.bat build /2019 /64 /CI
65+
run: |
66+
PATH %PATH%;C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64
67+
PATH %PATH%;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Roslyn
68+
win_build_vs.bat build /2019 /64 /CI
6369
shell: cmd
6470
ubuntu_16_04:
6571
name: "ubuntu-16.04"

extensions/windows-event-log/ConsumeWindowsEventLog.cpp

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "wel/MetadataWalker.h"
3535
#include "wel/XMLString.h"
3636
#include "wel/UnicodeConversion.h"
37+
#include "wel/JSONUtils.h"
3738

3839
#include "io/BufferStream.h"
3940
#include "core/ProcessContext.h"
@@ -134,8 +135,16 @@ core::Property ConsumeWindowsEventLog::OutputFormat(
134135
core::PropertyBuilder::createProperty("Output Format")->
135136
isRequired(true)->
136137
withDefaultValue(Both)->
137-
withAllowableValues<std::string>({XML, Plaintext, Both})->
138-
withDescription("Set the output format type. In case \'Both\' is selected the processor generates two flow files for every event captured")->
138+
withAllowableValues<std::string>({XML, Plaintext, Both, JSON})->
139+
withDescription("Set the output format type. In case \'Both\' is selected the processor generates two flow files for every event captured in format XML and Plaintext")->
140+
build());
141+
142+
core::Property ConsumeWindowsEventLog::JSONFormat(
143+
core::PropertyBuilder::createProperty("JSON Format")->
144+
isRequired(true)->
145+
withDefaultValue(JSONSimple)->
146+
withAllowableValues<std::string>({JSONSimple, JSONFlattened, JSONRaw})->
147+
withDescription("Set the json format type. Only applicable if Output Format is set to 'JSON'")->
139148
build());
140149

141150
core::Property ConsumeWindowsEventLog::BatchCommitSize(
@@ -162,17 +171,15 @@ core::Property ConsumeWindowsEventLog::ProcessOldEvents(
162171
core::Relationship ConsumeWindowsEventLog::Success("success", "Relationship for successfully consumed events.");
163172

164173
ConsumeWindowsEventLog::ConsumeWindowsEventLog(const std::string& name, utils::Identifier uuid)
165-
: core::Processor(name, uuid), logger_(logging::LoggerFactory<ConsumeWindowsEventLog>::getLogger()), apply_identifier_function_(false), batch_commit_size_(0U) {
174+
: core::Processor(name, uuid),
175+
logger_(logging::LoggerFactory<ConsumeWindowsEventLog>::getLogger()) {
166176
char buff[MAX_COMPUTERNAME_LENGTH + 1];
167177
DWORD size = sizeof(buff);
168178
if (GetComputerName(buff, &size)) {
169179
computerName_ = buff;
170180
} else {
171181
LogWindowsError();
172182
}
173-
174-
writeXML_ = false;
175-
writePlainText_ = false;
176183
}
177184

178185
void ConsumeWindowsEventLog::notifyStop() {
@@ -199,7 +206,7 @@ void ConsumeWindowsEventLog::initialize() {
199206
//! Set the supported properties
200207
setSupportedProperties({
201208
Channel, Query, MaxBufferSize, InactiveDurationToReconnect, IdentifierMatcher, IdentifierFunction, ResolveAsAttributes,
202-
EventHeaderDelimiter, EventHeader, OutputFormat, BatchCommitSize, BookmarkRootDirectory, ProcessOldEvents
209+
EventHeaderDelimiter, EventHeader, OutputFormat, JSONFormat, BatchCommitSize, BookmarkRootDirectory, ProcessOldEvents
203210
});
204211

205212
//! Set the supported relationships
@@ -252,11 +259,31 @@ void ConsumeWindowsEventLog::onSchedule(const std::shared_ptr<core::ProcessConte
252259
std::string mode;
253260
context->getProperty(OutputFormat.getName(), mode);
254261

255-
writeXML_ = (mode == Both || mode == XML);
256-
257-
writePlainText_ = (mode == Both || mode == Plaintext);
262+
output_ = {};
263+
if (mode == XML) {
264+
output_.xml = true;
265+
} else if (mode == Plaintext) {
266+
output_.plaintext = true;
267+
} else if (mode == Both) {
268+
output_.xml = true;
269+
output_.plaintext = true;
270+
} else if (mode == JSON) {
271+
std::string json_format;
272+
context->getProperty(JSONFormat.getName(), json_format);
273+
if (json_format == JSONRaw) {
274+
output_.json.type = JSONType::Raw;
275+
} else if (json_format == JSONSimple) {
276+
output_.json.type = JSONType::Simple;
277+
} else if (json_format == JSONFlattened) {
278+
output_.json.type = JSONType::Flattened;
279+
}
280+
} else {
281+
// in the future this might be considered an error, but for now due to backwards
282+
// compatibility we just fall through and execute the processor outputing nothing
283+
// throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Unrecognized output format: " + mode);
284+
}
258285

259-
if (writeXML_ && !hMsobjsDll_) {
286+
if ((output_.xml || output_.json) && !hMsobjsDll_) {
260287
char systemDir[MAX_PATH];
261288
if (GetSystemDirectory(systemDir, sizeof(systemDir))) {
262289
hMsobjsDll_ = LoadLibrary((systemDir + std::string("\\msobjs.dll")).c_str());
@@ -564,7 +591,7 @@ bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& e
564591

565592
logger_->log_debug("Finish doc traversing, performing writing...");
566593

567-
if (writePlainText_) {
594+
if (output_.plaintext) {
568595
logger_->log_trace("Writing event in plain text");
569596

570597
auto handler = getEventLogHandler(providerName);
@@ -583,30 +610,47 @@ bool ConsumeWindowsEventLog::createEventRender(EVT_HANDLE hEvent, EventRender& e
583610
// set the delimiter
584611
log_header.setDelimiter(header_delimiter_);
585612
// render the header.
586-
eventRender.rendered_text_ = log_header.getEventHeader([&walker](wel::METADATA metadata) { return walker.getMetadata(metadata); });
587-
eventRender.rendered_text_ += "Message" + header_delimiter_ + " ";
588-
eventRender.rendered_text_ += message;
613+
eventRender.plaintext = log_header.getEventHeader([&walker](wel::METADATA metadata) { return walker.getMetadata(metadata); });
614+
eventRender.plaintext += "Message" + header_delimiter_ + " ";
615+
eventRender.plaintext += message;
589616
}
590617
logger_->log_trace("Finish writing in plain text");
591618
}
592619

593-
if (writeXML_) {
594-
logger_->log_trace("Writing event in XML");
620+
if (output_.xml || output_.json) {
595621
substituteXMLPercentageItems(doc);
596622
logger_->log_trace("Finish substituting %% in XML");
597623

598624
if (resolve_as_attributes_) {
599-
eventRender.matched_fields_ = walker.getFieldValues();
625+
eventRender.matched_fields = walker.getFieldValues();
600626
}
627+
}
628+
629+
if (output_.xml) {
630+
logger_->log_trace("Writing event in XML");
601631

602632
wel::XmlString writer;
603633
doc.print(writer, "", pugi::format_raw); // no indentation or formatting
604634
xml = writer.xml_;
605635

606-
eventRender.text_ = std::move(xml);
636+
eventRender.xml = std::move(xml);
607637
logger_->log_trace("Finish writing in XML");
608638
}
609639

640+
if (output_.json.type == JSONType::Raw) {
641+
logger_->log_trace("Writing event in raw JSON");
642+
eventRender.json = wel::jsonToString(wel::toRawJSON(doc));
643+
logger_->log_trace("Finish writing in raw JSON");
644+
} else if (output_.json.type == JSONType::Simple) {
645+
logger_->log_trace("Writing event in simple JSON");
646+
eventRender.json = wel::jsonToString(wel::toSimpleJSON(doc));
647+
logger_->log_trace("Finish writing in simple JSON");
648+
} else if (output_.json.type == JSONType::Flattened) {
649+
logger_->log_trace("Writing event in flattened JSON");
650+
eventRender.json = wel::jsonToString(wel::toFlattenedJSON(doc));
651+
logger_->log_trace("Finish writing in flattened JSON");
652+
}
653+
610654
return true;
611655
}
612656

@@ -658,39 +702,45 @@ void ConsumeWindowsEventLog::putEventRenderFlowFileToSession(const EventRender&
658702
const std::string& str_;
659703
};
660704

661-
if (writeXML_) {
662-
auto flowFile = session.create();
663-
logger_->log_trace("Writing rendered XML to a flow file");
664-
705+
auto commitFlowFile = [&] (const std::shared_ptr<core::FlowFile>& flowFile, const std::string& content, const std::string& mimeType) {
665706
{
666-
WriteCallback wc{ eventRender.text_ };
707+
WriteCallback wc{ content };
667708
session.write(flowFile, &wc);
668709
}
669-
for (const auto &fieldMapping : eventRender.matched_fields_) {
670-
if (!fieldMapping.second.empty()) {
671-
session.putAttribute(flowFile, fieldMapping.first, fieldMapping.second);
672-
}
673-
}
674-
session.putAttribute(flowFile, core::SpecialFlowAttribute::MIME_TYPE, "application/xml");
710+
session.putAttribute(flowFile, core::SpecialFlowAttribute::MIME_TYPE, mimeType);
675711
session.putAttribute(flowFile, "Timezone name", timezone_name_);
676712
session.putAttribute(flowFile, "Timezone offset", timezone_offset_);
677713
session.getProvenanceReporter()->receive(flowFile, provenanceUri_, getUUIDStr(), "Consume windows event logs", 0);
678714
session.transfer(flowFile, Success);
679-
}
715+
};
680716

681-
if (writePlainText_) {
717+
if (output_.xml) {
682718
auto flowFile = session.create();
683-
logger_->log_trace("Writing rendered plain text to a flow file");
719+
logger_->log_trace("Writing rendered XML to a flow file");
684720

685-
{
686-
WriteCallback wc{ eventRender.rendered_text_ };
687-
session.write(flowFile, &wc);
721+
for (const auto &fieldMapping : eventRender.matched_fields) {
722+
if (!fieldMapping.second.empty()) {
723+
session.putAttribute(flowFile, fieldMapping.first, fieldMapping.second);
724+
}
688725
}
689-
session.putAttribute(flowFile, core::SpecialFlowAttribute::MIME_TYPE, "text/plain");
690-
session.putAttribute(flowFile, "Timezone name", timezone_name_);
691-
session.putAttribute(flowFile, "Timezone offset", timezone_offset_);
692-
session.getProvenanceReporter()->receive(flowFile, provenanceUri_, getUUIDStr(), "Consume windows event logs", 0);
693-
session.transfer(flowFile, Success);
726+
727+
commitFlowFile(flowFile, eventRender.xml, "application/xml");
728+
}
729+
730+
if (output_.plaintext) {
731+
logger_->log_trace("Writing rendered plain text to a flow file");
732+
commitFlowFile(session.create(), eventRender.plaintext, "text/plain");
733+
}
734+
735+
if (output_.json.type == JSONType::Raw) {
736+
logger_->log_trace("Writing rendered raw JSON to a flow file");
737+
commitFlowFile(session.create(), eventRender.json, "application/json");
738+
} else if (output_.json.type == JSONType::Simple) {
739+
logger_->log_trace("Writing rendered simple JSON to a flow file");
740+
commitFlowFile(session.create(), eventRender.json, "application/json");
741+
} else if (output_.json.type == JSONType::Flattened) {
742+
logger_->log_trace("Writing rendered flattened JSON to a flow file");
743+
commitFlowFile(session.create(), eventRender.json, "application/json");
694744
}
695745
}
696746

extensions/windows-event-log/ConsumeWindowsEventLog.h

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ namespace minifi {
4343
namespace processors {
4444

4545
struct EventRender {
46-
std::map<std::string, std::string> matched_fields_;
47-
std::string text_;
48-
std::string rendered_text_;
46+
std::map<std::string, std::string> matched_fields;
47+
std::string xml;
48+
std::string plaintext;
49+
std::string json;
4950
};
5051

5152
class Bookmark;
@@ -77,6 +78,7 @@ class ConsumeWindowsEventLog : public core::Processor {
7778
static core::Property EventHeaderDelimiter;
7879
static core::Property EventHeader;
7980
static core::Property OutputFormat;
81+
static core::Property JSONFormat;
8082
static core::Property BatchCommitSize;
8183
static core::Property BookmarkRootDirectory;
8284
static core::Property ProcessOldEvents;
@@ -107,9 +109,13 @@ class ConsumeWindowsEventLog : public core::Processor {
107109
bool createEventRender(EVT_HANDLE eventHandle, EventRender& eventRender);
108110
void substituteXMLPercentageItems(pugi::xml_document& doc);
109111

110-
static constexpr const char * const XML = "XML";
111-
static constexpr const char * const Both = "Both";
112-
static constexpr const char * const Plaintext = "Plaintext";
112+
static constexpr const char* XML = "XML";
113+
static constexpr const char* Both = "Both";
114+
static constexpr const char* Plaintext = "Plaintext";
115+
static constexpr const char* JSON = "JSON";
116+
static constexpr const char* JSONRaw = "Raw";
117+
static constexpr const char* JSONSimple = "Simple";
118+
static constexpr const char* JSONFlattened = "Flattened";
113119

114120
private:
115121
struct TimeDiff {
@@ -132,18 +138,30 @@ class ConsumeWindowsEventLog : public core::Processor {
132138
std::wstring wstrChannel_;
133139
std::wstring wstrQuery_;
134140
std::string regex_;
135-
bool resolve_as_attributes_;
136-
bool apply_identifier_function_;
141+
bool resolve_as_attributes_{false};
142+
bool apply_identifier_function_{false};
137143
std::string provenanceUri_;
138144
std::string computerName_;
139145
uint64_t maxBufferSize_{};
140146
DWORD lastActivityTimestamp_{};
141147
std::mutex cache_mutex_;
142148
std::map<std::string, wel::WindowsEventLogHandler > providers_;
143-
uint64_t batch_commit_size_;
149+
uint64_t batch_commit_size_{};
150+
151+
enum class JSONType {None, Raw, Simple, Flattened};
152+
153+
struct OutputFormat {
154+
bool xml{false};
155+
bool plaintext{false};
156+
struct JSON {
157+
JSONType type{JSONType::None};
158+
159+
explicit operator bool() const noexcept {
160+
return type != JSONType::None;
161+
}
162+
} json;
163+
} output_;
144164

145-
bool writeXML_;
146-
bool writePlainText_;
147165
std::unique_ptr<Bookmark> bookmark_;
148166
std::mutex on_trigger_mutex_;
149167
std::unordered_map<std::string, std::string> xmlPercentageItemsResolutions_;

extensions/windows-event-log/tests/CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@
1717
# under the License.
1818
#
1919

20-
file(GLOB WEL_INTEGRATION_TESTS "*.cpp")
20+
set(WEL_INTEGRATION_TESTS "BookmarkTests.cpp" "ConsumeWindowsEventLogTests.cpp" "MetadataWalkerTests.cpp")
21+
if (TEST_CUSTOM_WEL_PROVIDER)
22+
execute_process(COMMAND
23+
"${CMAKE_CURRENT_LIST_DIR}/custom-provider/generate-and-register.bat"
24+
"${CMAKE_CURRENT_LIST_DIR}/custom-provider"
25+
)
26+
list(APPEND WEL_INTEGRATION_TESTS "CWELCustomProviderTests.cpp")
27+
endif()
28+
2129
SET(WEL_TEST_COUNT 0)
2230
FOREACH(testfile ${WEL_INTEGRATION_TESTS})
2331
get_filename_component(testfilename "${testfile}" NAME_WE)

0 commit comments

Comments
 (0)