diff --git a/core/common/TimeUtil.cpp b/core/common/TimeUtil.cpp index cc2dc4bf24..10754cf742 100644 --- a/core/common/TimeUtil.cpp +++ b/core/common/TimeUtil.cpp @@ -302,10 +302,11 @@ void SetLogTime(sls_logs::Log* log, time_t second) { log->set_time(second); } -void SetLogTimeWithNano(sls_logs::Log* log, time_t second, long nanosecond) { - // Usage: set nanosecond first, and then discard at LogProcess@ProcessBufferLegacy +void SetLogTimeWithNano(sls_logs::Log* log, time_t second, std::optional nanosecond) { log->set_time(second); - log->set_time_ns(nanosecond); + if (nanosecond) { + log->set_time_ns(nanosecond.value()); + } } LogtailTime GetCurrentLogtailTime() { diff --git a/core/common/TimeUtil.h b/core/common/TimeUtil.h index 59a6f3ccfc..24fcb81d2c 100644 --- a/core/common/TimeUtil.h +++ b/core/common/TimeUtil.h @@ -16,6 +16,7 @@ #pragma once #include +#include #include #include @@ -83,7 +84,7 @@ uint64_t GetPreciseTimestampFromLogtailTime(LogtailTime logTime, const PreciseTi void SetLogTime(sls_logs::Log* log, time_t second); -void SetLogTimeWithNano(sls_logs::Log* log, time_t second, long nanosecond); +void SetLogTimeWithNano(sls_logs::Log* log, time_t second, std::optional nanosecond); LogtailTime GetCurrentLogtailTime(); diff --git a/core/event/BlockEventManager.cpp b/core/event/BlockEventManager.cpp index 3068c7279b..1dd30acc91 100644 --- a/core/event/BlockEventManager.cpp +++ b/core/event/BlockEventManager.cpp @@ -13,10 +13,12 @@ // limitations under the License. #include "BlockEventManager.h" -#include "processor/daemon/LogProcess.h" + #include "common/HashUtil.h" #include "common/StringTools.h" #include "polling/PollingEventQueue.h" +#include "processor/daemon/LogProcess.h" +#include "queue/ProcessQueueManager.h" DEFINE_FLAG_INT32(max_block_event_timeout, "max block event timeout, seconds", 3); @@ -48,8 +50,9 @@ void BlockedEventManager::UpdateBlockEvent(const LogstoreFeedBackKey& logstoreKe .append(pEvent->GetConfigName()); hashKey = HashSignatureString(key.c_str(), key.size()); } - // LOG_DEBUG(sLogger, ("Add block event ", pEvent->GetSource())(pEvent->GetObject(), - // pEvent->GetInode())(pEvent->GetConfigName(), hashKey)); + LOG_DEBUG(sLogger, + ("Add block event ", pEvent->GetSource())(pEvent->GetObject(), + pEvent->GetInode())(pEvent->GetConfigName(), hashKey)); ScopedSpinLock lock(mLock); mBlockEventMap[hashKey].Update(logstoreKey, pEvent, curTime); } diff --git a/core/event/BlockEventManager.h b/core/event/BlockEventManager.h index 690fb14ce9..ca6b37e459 100644 --- a/core/event/BlockEventManager.h +++ b/core/event/BlockEventManager.h @@ -17,10 +17,12 @@ #pragma once #include #include -#include "common/LogstoreFeedbackQueue.h" + +#include "Event.h" +#include "common/FeedbackInterface.h" #include "common/Flags.h" #include "common/Lock.h" -#include "Event.h" +#include "queue/FeedbackQueueKey.h" DECLARE_FLAG_INT32(max_block_event_timeout); @@ -89,6 +91,11 @@ class BlockedEventManager : public LogstoreFeedBackInterface { std::unordered_map mBlockEventMap; SpinLock mLock; + +private: +#ifdef APSARA_UNIT_TEST_MAIN + friend class ForceReadUnittest; +#endif }; } // namespace logtail diff --git a/core/event_handler/EventHandler.h b/core/event_handler/EventHandler.h index f81966babf..b1a63f9970 100644 --- a/core/event_handler/EventHandler.h +++ b/core/event_handler/EventHandler.h @@ -104,6 +104,7 @@ class ModifyHandler : public EventHandler { friend class EventDispatcherTest; friend class SenderUnittest; friend class ModifyHandlerUnittest; + friend class ForceReadUnittest; #endif }; diff --git a/core/models/LogEvent.cpp b/core/models/LogEvent.cpp index dc10b34be1..7ea70dc7f0 100644 --- a/core/models/LogEvent.cpp +++ b/core/models/LogEvent.cpp @@ -131,7 +131,13 @@ Json::Value LogEvent::ToJson() const { Json::Value root; root["type"] = static_cast(GetType()); root["timestamp"] = GetTimestamp(); - root["timestampNanosecond"] = GetTimestampNanosecond(); + if (GetTimestampNanosecond()) { + root["timestampNanosecond"] = static_cast(GetTimestampNanosecond().value()); + } + if (enableEventMeta) { + root["fileOffset"] = GetPosition().first; + root["rawSize"] = GetPosition().second; + } if (!Empty()) { Json::Value contents; for (const auto& content : *this) { diff --git a/core/models/MetricEvent.cpp b/core/models/MetricEvent.cpp index 402882688b..23e50ac4aa 100644 --- a/core/models/MetricEvent.cpp +++ b/core/models/MetricEvent.cpp @@ -31,7 +31,17 @@ Json::Value MetricEvent::ToJson() const { Json::Value root; root["type"] = static_cast(GetType()); root["timestamp"] = GetTimestamp(); - root["timestampNanosecond"] = GetTimestampNanosecond(); + if (GetTimestampNanosecond()) { + root["timestampNanosecond"] = static_cast(GetTimestampNanosecond().value()); + } + root["name"] = mName.to_string(); + root["value"] = MetricValueToJson(mValue); + if (!mTags.mInner.empty()) { + Json::Value& tags = root["tags"]; + for (const auto& tag : mTags.mInner) { + tags[tag.first.to_string()] = tag.second.to_string(); + } + } return root; } diff --git a/core/models/PipelineEvent.h b/core/models/PipelineEvent.h index 255ae1357a..044b58fd28 100644 --- a/core/models/PipelineEvent.h +++ b/core/models/PipelineEvent.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "models/StringView.h" @@ -38,12 +39,16 @@ class PipelineEvent { virtual ~PipelineEvent() = default; Type GetType() const { return mType; } - time_t GetTimestamp() const { return timestamp; } - long GetTimestampNanosecond() const { return timestampNanosecond; } - void SetTimestamp(time_t t) { timestamp = t; } - void SetTimestamp(time_t t, long ns) { - timestamp = t; - timestampNanosecond = ns; // Only nanosecond part + time_t GetTimestamp() const { return mTimestamp; } + std::optional GetTimestampNanosecond() const { return mTimestampNanosecond; } + void SetTimestamp(time_t t) { mTimestamp = t; } + void SetTimestamp(time_t t, uint32_t ns) { + mTimestamp = t; + mTimestampNanosecond = ns; // Only nanosecond part + } + void SetTimestamp(time_t t, std::optional ns) { + mTimestamp = t; + mTimestampNanosecond = ns; // Only nanosecond part } void ResetPipelineEventGroup(PipelineEventGroup* ptr) { mPipelineEventGroupPtr = ptr; } std::shared_ptr& GetSourceBuffer(); @@ -61,8 +66,8 @@ class PipelineEvent { PipelineEvent(Type type, PipelineEventGroup* ptr); Type mType = Type::NONE; - time_t timestamp = 0; - long timestampNanosecond = 0; + time_t mTimestamp = 0; + std::optional mTimestampNanosecond; PipelineEventGroup* mPipelineEventGroupPtr = nullptr; }; diff --git a/core/models/SpanEvent.cpp b/core/models/SpanEvent.cpp index 108a181b50..431b19878a 100644 --- a/core/models/SpanEvent.cpp +++ b/core/models/SpanEvent.cpp @@ -21,9 +21,121 @@ namespace logtail { SpanEvent::SpanEvent(PipelineEventGroup* ptr) : PipelineEvent(Type::SPAN, ptr) { } -uint64_t SpanEvent::EventsSizeBytes() { - // TODO - return 0; +void SpanEvent::SetTraceId(const string& traceId) { + const StringBuffer& b = GetSourceBuffer()->CopyString(traceId); + mTraceId = StringView(b.data, b.size); +} + +void SpanEvent::SetSpanId(const string& spanId) { + const StringBuffer& b = GetSourceBuffer()->CopyString(spanId); + mSpanId = StringView(b.data, b.size); +} + +void SpanEvent::SetTraceState(const string& traceState) { + const StringBuffer& b = GetSourceBuffer()->CopyString(traceState); + mTraceState = StringView(b.data, b.size); +} + +void SpanEvent::SetParentSpanId(const string& parentSpanId) { + const StringBuffer& b = GetSourceBuffer()->CopyString(parentSpanId); + mParentSpanId = StringView(b.data, b.size); +} + +void SpanEvent::SetName(const string& name) { + const StringBuffer& b = GetSourceBuffer()->CopyString(name); + mName = StringView(b.data, b.size); +} + +StringView SpanEvent::GetTag(StringView key) const { + auto it = mTags.mInner.find(key); + if (it != mTags.mInner.end()) { + return it->second; + } + return gEmptyStringView; +} + +bool SpanEvent::HasTag(StringView key) const { + return mTags.mInner.find(key) != mTags.mInner.end(); +} + +void SpanEvent::SetTag(StringView key, StringView val) { + SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); +} + +void SpanEvent::SetTag(const string& key, const string& val) { + SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); +} + +void SpanEvent::SetTagNoCopy(const StringBuffer& key, const StringBuffer& val) { + SetTagNoCopy(StringView(key.data, key.size), StringView(val.data, val.size)); +} + +void SpanEvent::SetTagNoCopy(StringView key, StringView val) { + mTags.Insert(key, val); +} + +void SpanEvent::DelTag(StringView key) { + mTags.Erase(key); +} + +SpanEvent::InnerEvent* SpanEvent::AddEvent() { + SpanEvent::InnerEvent e(this); + mEvents.emplace_back(std::move(e)); + return &mEvents.back(); +} + +SpanEvent::SpanLink* SpanEvent::AddLink() { + SpanEvent::SpanLink l(this); + mLinks.emplace_back(std::move(l)); + return &mLinks.back(); +} + +StringView SpanEvent::GetScopeTag(StringView key) const { + auto it = mScopeTags.mInner.find(key); + if (it != mScopeTags.mInner.end()) { + return it->second; + } + return gEmptyStringView; +} + +bool SpanEvent::HasScopeTag(StringView key) const { + return mScopeTags.mInner.find(key) != mScopeTags.mInner.end(); +} + +void SpanEvent::SetScopeTag(StringView key, StringView val) { + SetScopeTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); +} + +void SpanEvent::SetScopeTag(const string& key, const string& val) { + SetScopeTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); +} + +void SpanEvent::SetScopeTagNoCopy(const StringBuffer& key, const StringBuffer& val) { + SetScopeTagNoCopy(StringView(key.data, key.size), StringView(val.data, val.size)); +} + +void SpanEvent::SetScopeTagNoCopy(StringView key, StringView val) { + mScopeTags.Insert(key, val); +} + +void SpanEvent::DelScopeTag(StringView key) { + mScopeTags.Erase(key); +} + +size_t SpanEvent::DataSize() const { + // TODO: this is not O(1), however, these two fields are not frequently used, so it can thought of O(1) + size_t eventsSize = sizeof(decltype(mEvents)); + for (const auto& item : mEvents) { + eventsSize += item.DataSize(); + } + size_t linksSize = sizeof(decltype(mLinks)); + for (const auto& item : mLinks) { + linksSize += item.DataSize(); + } + // TODO: for enum, it seems more reasonable to use actual string size instead of size of enum + return PipelineEvent::DataSize() + mTraceId.size() + mSpanId.size() + mTraceState.size() + mParentSpanId.size() + + mName.size() + sizeof(decltype(mKind)) + sizeof(decltype(mStartTimeNs)) + sizeof(decltype(mEndTimeNs)) + + mTags.DataSize() + eventsSize + linksSize + sizeof(decltype(mStatus)) + mScopeTags.DataSize(); } #ifdef APSARA_UNIT_TEST_MAIN @@ -31,7 +143,52 @@ Json::Value SpanEvent::ToJson() const { Json::Value root; root["type"] = static_cast(GetType()); root["timestamp"] = GetTimestamp(); - root["timestampNanosecond"] = GetTimestampNanosecond(); + if (GetTimestampNanosecond()) { + root["timestampNanosecond"] = static_cast(GetTimestampNanosecond().value()); + } + root["traceId"] = mTraceId.to_string(); + root["spanId"] = mSpanId.to_string(); + if (!mTraceState.empty()) { + root["traceState"] = mTraceState.to_string(); + } + if (!mParentSpanId.empty()) { + root["parentSpanId"] = mParentSpanId.to_string(); + } + root["name"] = mName.to_string(); + if (mKind != Kind::Unspecified) { + root["kind"] = static_cast(mKind); + } + // must be int since jsoncpp will take integral as int during parse, which + // will lead to inequality on json comparison + root["startTimeNs"] = static_cast(mStartTimeNs); + root["endTimeNs"] = static_cast(mEndTimeNs); + if (!mTags.mInner.empty()) { + Json::Value& tags = root["tags"]; + for (const auto& tag : mTags.mInner) { + tags[tag.first.to_string()] = tag.second.to_string(); + } + } + if (!mEvents.empty()) { + Json::Value& events = root["events"]; + for (const auto& event : mEvents) { + events.append(event.ToJson()); + } + } + if (!mLinks.empty()) { + Json::Value& links = root["links"]; + for (const auto& link : mLinks) { + links.append(link.ToJson()); + } + } + if (mStatus != StatusCode::Unset) { + root["status"] = static_cast(mStatus); + } + if (!mScopeTags.mInner.empty()) { + Json::Value& tags = root["scopeTags"]; + for (const auto& tag : mScopeTags.mInner) { + tags[tag.first.to_string()] = tag.second.to_string(); + } + } return root; } diff --git a/core/processor/ProcessorSplitLogStringNative.cpp b/core/processor/ProcessorSplitLogStringNative.cpp index e1c5083f5e..edf7c72119 100644 --- a/core/processor/ProcessorSplitLogStringNative.cpp +++ b/core/processor/ProcessorSplitLogStringNative.cpp @@ -83,8 +83,6 @@ void ProcessorSplitLogStringNative::Process(PipelineEventGroup& logGroup) { } *mSplitLines = newEvents.size(); logGroup.SwapEvents(newEvents); - - return; } bool ProcessorSplitLogStringNative::IsSupportedEvent(const PipelineEventPtr& e) const { diff --git a/core/processor/ProcessorSplitMultilineLogStringNative.cpp b/core/processor/ProcessorSplitMultilineLogStringNative.cpp index 12df049b34..2157c1e39c 100644 --- a/core/processor/ProcessorSplitMultilineLogStringNative.cpp +++ b/core/processor/ProcessorSplitMultilineLogStringNative.cpp @@ -92,6 +92,7 @@ void ProcessorSplitMultilineLogStringNative::Process(PipelineEventGroup& logGrou } mProcMatchedLinesCnt->Add(inputLines - unmatchLines); mProcUnmatchedLinesCnt->Add(unmatchLines); + *mSplitLines = newEvents.size(); logGroup.SwapEvents(newEvents); } diff --git a/core/processor/daemon/LogProcess.cpp b/core/processor/daemon/LogProcess.cpp index b69ec37d39..9866bec8d8 100644 --- a/core/processor/daemon/LogProcess.cpp +++ b/core/processor/daemon/LogProcess.cpp @@ -285,6 +285,14 @@ void* LogProcess::ProcessLoop(int32_t threadNo) { #endif { ReadLock lock(mAccessProcessThreadRWL); + + std::unique_ptr item; + std::string configName; + if (!ProcessQueueManager::GetInstance()->PopItem(threadNo, item, configName)) { + ProcessQueueManager::GetInstance()->Wait(100); + continue; + } + mThreadFlags[threadNo] = true; s_processCount++; uint64_t readBytes = logBuffer->rawBuffer.size() + 1; // may not be accurate if input is not utf8 diff --git a/core/reader/LogFileReader.cpp b/core/reader/LogFileReader.cpp index e18ba332f1..973e56df31 100644 --- a/core/reader/LogFileReader.cpp +++ b/core/reader/LogFileReader.cpp @@ -1001,28 +1001,30 @@ bool LogFileReader::ReadLog(LogBuffer& logBuffer, const Event* event) { } size_t lastFilePos = mLastFilePos; - bool allowRollback = true; + bool tryRollback = true; if (event != nullptr && event->IsReaderFlushTimeout()) { // If flush timeout event, we should filter whether the event is legacy. if (event->GetLastReadPos() == GetLastReadPos() && event->GetLastFilePos() == mLastFilePos && event->GetInode() == mDevInode.inode) { +<<<<<<< HEAD allowRollback = false; +======= + // For the scenario: log rotation, the last line needs to be read by timeout, which is a normal situation. + // So here only local warning is given, don't raise alarm. + LOG_WARNING(sLogger, + ("read log", "timeout")("project", GetProject())("logstore", GetLogstore())( + "config", GetConfigName())("log reader queue name", mHostLogPath)("log path", mRealLogPath)( + "file device", ToString(mDevInode.dev))("file inode", ToString(mDevInode.inode))( + "file signature", mLastFileSignatureHash)("file signature size", mLastFileSignatureSize)( + "last file position", mLastFilePos)("last file size", mLastFileSize)( + "read size", mLastFilePos - lastFilePos)("log", logBuffer.rawBuffer)); + tryRollback = false; +>>>>>>> 893a0cfb (fix timeout force read when still can read (#1542)) } else { return false; } } - bool moreData = GetRawData(logBuffer, mLastFileSize, allowRollback); - if (!allowRollback) { - // For the scenario: log rotation, the last line needs to be read by timeout, which is a normal situation. - // So here only local warning is given, don't raise alarm. - LOG_WARNING(sLogger, - ("read timeout", "force read")("project", GetProject())("logstore", GetLogstore())( - "config", GetConfigName())("log reader queue name", mHostLogPath)("log path", mRealLogPath)( - "file device", ToString(mDevInode.dev))("file inode", ToString(mDevInode.inode))( - "file signature", mLastFileSignatureHash)("file signature size", mLastFileSignatureSize)( - "last file position", mLastFilePos)("last file size", mLastFileSize)( - "read size", mLastFilePos - lastFilePos)("log", logBuffer.rawBuffer)); - } + bool moreData = GetRawData(logBuffer, mLastFileSize, tryRollback); if (!logBuffer.rawBuffer.empty() > 0) { if (mEOOption) { // This read was replayed by checkpoint, adjust mLastFilePos to skip hole. @@ -1035,7 +1037,8 @@ bool LogFileReader::ReadLog(LogBuffer& logBuffer, const Event* event) { LOG_DEBUG(sLogger, ("read log file", mRealLogPath)("last file pos", mLastFilePos)("last file size", mLastFileSize)( "read size", mLastFilePos - lastFilePos)); - if (HasDataInCache()) { + if (HasDataInCache() && GetLastReadPos() == mLastFileSize) { + LOG_DEBUG(sLogger, ("add timeout event", mRealLogPath)); auto event = CreateFlushTimeoutEvent(); BlockedEventManager::GetInstance()->UpdateBlockEvent( GetLogstoreKey(), GetConfigName(), *event, mDevInode, time(NULL) + mReaderConfig.first->mFlushTimeoutSecs); @@ -1528,7 +1531,7 @@ bool LogFileReader::GetLogTimeByOffset(const char* buffer, * "SingleLineLog_1\nSingleLineLog_2\nSingleLineLog_3\n" -> "SingleLineLog_1\nSingleLineLog_2\nSingleLineLog_3\0" * "SingleLineLog_1\nSingleLineLog_2\nxxx" -> "SingleLineLog_1\nSingleLineLog_2\0" */ -bool LogFileReader::GetRawData(LogBuffer& logBuffer, int64_t fileSize, bool allowRollback) { +bool LogFileReader::GetRawData(LogBuffer& logBuffer, int64_t fileSize, bool tryRollback) { // Truncate, return false to indicate no more data. if (fileSize == mLastFilePos) { return false; @@ -1536,9 +1539,9 @@ bool LogFileReader::GetRawData(LogBuffer& logBuffer, int64_t fileSize, bool allo bool moreData = false; if (mReaderConfig.first->mFileEncoding == FileReaderOptions::Encoding::GBK) - ReadGBK(logBuffer, fileSize, moreData, allowRollback); + ReadGBK(logBuffer, fileSize, moreData, tryRollback); else - ReadUTF8(logBuffer, fileSize, moreData, allowRollback); + ReadUTF8(logBuffer, fileSize, moreData, tryRollback); int64_t delta = fileSize - mLastFilePos; if (delta > mReaderConfig.first->mReadDelayAlertThresholdBytes && !logBuffer.rawBuffer.empty()) { @@ -1643,7 +1646,7 @@ void LogFileReader::setExactlyOnceCheckpointAfterRead(size_t readSize) { cpt.set_read_length(readSize); } -void LogFileReader::ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, bool allowRollback) { +void LogFileReader::ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, bool tryRollback) { char* stringBuffer = nullptr; size_t nbytes = 0; @@ -1661,7 +1664,7 @@ void LogFileReader::ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, logBuffer.readOffset = mLastFilePos; --nbytes; } - mLastForceRead = !allowRollback; + mLastForceRead = true; mCache.clear(); moreData = false; } else { @@ -1685,6 +1688,11 @@ void LogFileReader::ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, ? ReadFile(mLogFileOp, stringMemory.data + lastCacheSize, READ_BYTE, lastReadPos, &truncateInfo) : 0UL; stringBuffer = stringMemory.data; + bool allowRollback = true; + // Only when there is no new log and not try rollback, then force read + if (!tryRollback && nbytes == 0) { + allowRollback = false; + } if (nbytes == 0 && (!lastCacheSize || allowRollback)) { // read nothing, if no cached data or allow rollback the // reader's state cannot be changed return; @@ -1767,12 +1775,13 @@ void LogFileReader::ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, LOG_DEBUG(sLogger, ("read size", nbytes)("last file pos", mLastFilePos)); } -void LogFileReader::ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, bool allowRollback) { +void LogFileReader::ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, bool tryRollback) { std::unique_ptr gbkMemory; char* gbkBuffer = nullptr; size_t readCharCount = 0, originReadCount = 0; int64_t lastReadPos = 0; bool logTooLongSplitFlag = false, fromCpt = false; + bool allowRollback = true; logBuffer.readOffset = mLastFilePos; if (!mLogFileOp.IsOpen()) { @@ -1788,7 +1797,8 @@ void LogFileReader::ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, b logBuffer.readOffset = mLastFilePos; --readCharCount; } - mLastForceRead = !allowRollback; + mLastForceRead = true; + allowRollback = false; lastReadPos = mLastFilePos + readCharCount; originReadCount = readCharCount; moreData = false; @@ -1807,6 +1817,10 @@ void LogFileReader::ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, b lastReadPos = GetLastReadPos(); readCharCount = READ_BYTE ? ReadFile(mLogFileOp, gbkBuffer + lastCacheSize, READ_BYTE, lastReadPos, &truncateInfo) : 0UL; + // Only when there is no new log and not try rollback, then force read + if (!tryRollback && readCharCount == 0) { + allowRollback = false; + } if (readCharCount == 0 && (!lastCacheSize || allowRollback)) { // just keep last cache return; } diff --git a/core/reader/LogFileReader.h b/core/reader/LogFileReader.h index 2eda081a28..90cfaee80b 100644 --- a/core/reader/LogFileReader.h +++ b/core/reader/LogFileReader.h @@ -36,6 +36,9 @@ #include "file_server/MultilineOptions.h" #include "log_pb/sls_logs.pb.h" #include "logger/Logger.h" +#include "models/StringView.h" +#include "queue/FeedbackQueueKey.h" +#include "rapidjson/allocators.h" #include "reader/FileReaderOptions.h" namespace logtail { @@ -304,6 +307,8 @@ class LogFileReader { mExtraTags.insert(mExtraTags.end(), tags.begin(), tags.end()); } + QueueKey GetQueueKey() const { return mReaderConfig.second->GetProcessQueueKey(); } + // void SetDelaySkipBytes(int64_t value) { mReadDelaySkipBytes = value; } // void SetFuseMode(bool fusemode) { mIsFuseMode = fusemode; } @@ -355,9 +360,9 @@ class LogFileReader { int64_t GetLogGroupKey() const { return mLogGroupKey; } protected: - bool GetRawData(LogBuffer& logBuffer, int64_t fileSize, bool allowRollback = true); - void ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, bool allowRollback = true); - void ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, bool allowRollback = true); + bool GetRawData(LogBuffer& logBuffer, int64_t fileSize, bool tryRollback = true); + void ReadUTF8(LogBuffer& logBuffer, int64_t end, bool& moreData, bool tryRollback = true); + void ReadGBK(LogBuffer& logBuffer, int64_t end, bool& moreData, bool tryRollback = true); size_t ReadFile(LogFileOperator& logFileOp, void* buf, size_t size, int64_t& offset, TruncateInfo** truncateInfo = NULL); @@ -591,7 +596,10 @@ class LogFileReader { friend class LogSplitNoDiscardUnmatchUnittest; friend class RemoveLastIncompleteLogMultilineUnittest; friend class LogFileReaderCheckpointUnittest; - friend class GetLastLineUnittest; + friend class LastMatchedContainerdTextLineUnittest; + friend class LastMatchedDockerJsonFileUnittest; + friend class LastMatchedContainerdTextWithDockerJsonUnittest; + friend class ForceReadUnittest; protected: void UpdateReaderManual(); diff --git a/core/spl/PipelineEventGroupInput.cpp b/core/spl/PipelineEventGroupInput.cpp index 334e0fe2fd..fce5248ee5 100644 --- a/core/spl/PipelineEventGroupInput.cpp +++ b/core/spl/PipelineEventGroupInput.cpp @@ -56,7 +56,7 @@ void PipelineEventGroupInput::getTimeColumns(std::vector& times, for (const auto &event : mLogGroup->GetEvents()) { const LogEvent& sourceEvent = event.Cast(); times.emplace_back(sourceEvent.GetTimestamp()); - timeNanos.emplace_back(sourceEvent.GetTimestampNanosecond()); + timeNanos.emplace_back(sourceEvent.GetTimestampNanosecond() ? sourceEvent.GetTimestampNanosecond().value() : 0); } } diff --git a/core/unittest/models/MetricEventUnittest.cpp b/core/unittest/models/MetricEventUnittest.cpp new file mode 100644 index 0000000000..2e527b83e1 --- /dev/null +++ b/core/unittest/models/MetricEventUnittest.cpp @@ -0,0 +1,189 @@ +// Copyright 2024 iLogtail Authors +// +// 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. + +#include + +#include "common/JsonUtil.h" +#include "models/MetricEvent.h" +#include "unittest/Unittest.h" + +using namespace std; + +namespace logtail { + +class MetricEventUnittest : public ::testing::Test { +public: + void TestName(); + void TestValue(); + void TestTag(); + void TestSize(); + void TestToJson(); + void TestFromJson(); + +protected: + void SetUp() override { + mSourceBuffer.reset(new SourceBuffer); + mEventGroup.reset(new PipelineEventGroup(mSourceBuffer)); + mMetricEvent = mEventGroup->CreateMetricEvent(); + } + +private: + shared_ptr mSourceBuffer; + unique_ptr mEventGroup; + unique_ptr mMetricEvent; +}; + +void MetricEventUnittest::TestName() { + mMetricEvent->SetName("test"); + APSARA_TEST_EQUAL("test", mMetricEvent->GetName().to_string()); +} + +void MetricEventUnittest::TestValue() { + mMetricEvent->SetValue(UntypedSingleValue{10.0}); + APSARA_TEST_TRUE(mMetricEvent->Is()); + APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue()->mValue); + + mMetricEvent->SetValue(100.0); + APSARA_TEST_TRUE(mMetricEvent->Is()); + APSARA_TEST_EQUAL(100.0, mMetricEvent->GetValue()->mValue); +} + +void MetricEventUnittest::TestTag() { + { + string key = "key1"; + string value = "value1"; + mMetricEvent->SetTag(key, value); + APSARA_TEST_TRUE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mMetricEvent->GetTag(key).to_string()); + } + { + string key = "key2"; + string value = "value2"; + mMetricEvent->SetTag(StringView(key.data(), key.size()), StringView(value.data(), value.size())); + APSARA_TEST_TRUE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mMetricEvent->GetTag(key).to_string()); + } + { + string key = "key3"; + string value = "value3"; + mMetricEvent->SetTagNoCopy(mMetricEvent->GetSourceBuffer()->CopyString(key), + mMetricEvent->GetSourceBuffer()->CopyString(value)); + APSARA_TEST_TRUE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mMetricEvent->GetTag(key).to_string()); + } + { + string key = "key4"; + string value = "value4"; + const StringBuffer& kb = mMetricEvent->GetSourceBuffer()->CopyString(key); + const StringBuffer& vb = mMetricEvent->GetSourceBuffer()->CopyString(value); + mMetricEvent->SetTagNoCopy(StringView(kb.data, kb.size), StringView(vb.data, vb.size)); + APSARA_TEST_TRUE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mMetricEvent->GetTag(key).to_string()); + } + { + string key = "key5"; + APSARA_TEST_FALSE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mMetricEvent->GetTag(key).to_string()); + } + { + string key = "key1"; + mMetricEvent->DelTag(key); + APSARA_TEST_FALSE(mMetricEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mMetricEvent->GetTag(key).to_string()); + } +} + +void MetricEventUnittest::TestSize() { + size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(UntypedSingleValue) + sizeof(map); + mMetricEvent->SetName("test"); + basicSize += 4; + + mMetricEvent->SetValue(UntypedSingleValue{10.0}); + + // add tag, and key not existed + mMetricEvent->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mMetricEvent->DataSize()); + + // add tag, and key existed + mMetricEvent->SetTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mMetricEvent->DataSize()); + + // delete tag + mMetricEvent->DelTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); +} + +void MetricEventUnittest::TestToJson() { + mMetricEvent->SetTimestamp(12345678901, 0); + mMetricEvent->SetName("test"); + mMetricEvent->SetValue(UntypedSingleValue{10.0}); + mMetricEvent->SetTag(string("key1"), string("value1")); + Json::Value res = mMetricEvent->ToJson(); + + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "tags": { + "key1": "value1" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 2, + "value": { + "type": "untyped_single_value", + "detail": 10.0 + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + + APSARA_TEST_TRUE(eventJson == res); +} + +void MetricEventUnittest::TestFromJson() { + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "tags": { + "key1": "value1" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "value": { + "type": "untyped_single_value", + "detail": 10.0 + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + mMetricEvent->FromJson(eventJson); + + APSARA_TEST_EQUAL(12345678901, mMetricEvent->GetTimestamp()); + APSARA_TEST_EQUAL(0L, mMetricEvent->GetTimestampNanosecond().value()); + APSARA_TEST_EQUAL("test", mMetricEvent->GetName()); + APSARA_TEST_TRUE(mMetricEvent->Is()); + APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue()->mValue); + APSARA_TEST_EQUAL("value1", mMetricEvent->GetTag("key1").to_string()); +} + +UNIT_TEST_CASE(MetricEventUnittest, TestName) +UNIT_TEST_CASE(MetricEventUnittest, TestValue) +UNIT_TEST_CASE(MetricEventUnittest, TestTag) +UNIT_TEST_CASE(MetricEventUnittest, TestSize) +UNIT_TEST_CASE(MetricEventUnittest, TestToJson) +UNIT_TEST_CASE(MetricEventUnittest, TestFromJson) + +} // namespace logtail + +UNIT_TEST_MAIN diff --git a/core/unittest/models/SpanEventUnittest.cpp b/core/unittest/models/SpanEventUnittest.cpp new file mode 100644 index 0000000000..6321acf44c --- /dev/null +++ b/core/unittest/models/SpanEventUnittest.cpp @@ -0,0 +1,660 @@ +// Copyright 2024 iLogtail Authors +// +// 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. + +#include + +#include "common/JsonUtil.h" +#include "models/SpanEvent.h" +#include "unittest/Unittest.h" + +using namespace std; + +namespace logtail { + +class SpanEventUnittest : public ::testing::Test { +public: + void TestSimpleFields(); + void TestTag(); + void TestLink(); + void TestEvent(); + void TestScopeTag(); + void TestSize(); + void TestToJson(); + void TestFromJson(); + +protected: + void SetUp() override { + mSourceBuffer.reset(new SourceBuffer); + mEventGroup.reset(new PipelineEventGroup(mSourceBuffer)); + mSpanEvent = mEventGroup->CreateSpanEvent(); + } + +private: + shared_ptr mSourceBuffer; + unique_ptr mEventGroup; + unique_ptr mSpanEvent; +}; + +void SpanEventUnittest::TestSimpleFields() { + mSpanEvent->SetTraceId("test_trace_id"); + APSARA_TEST_EQUAL("test_trace_id", mSpanEvent->GetTraceId().to_string()); + + mSpanEvent->SetSpanId("test_span_id"); + APSARA_TEST_EQUAL("test_span_id", mSpanEvent->GetSpanId().to_string()); + + mSpanEvent->SetTraceState("normal"); + APSARA_TEST_EQUAL("normal", mSpanEvent->GetTraceState().to_string()); + + mSpanEvent->SetParentSpanId("test_parent_span_id"); + APSARA_TEST_EQUAL("test_parent_span_id", mSpanEvent->GetParentSpanId().to_string()); + + mSpanEvent->SetName("test_name"); + APSARA_TEST_EQUAL("test_name", mSpanEvent->GetName().to_string()); + + mSpanEvent->SetKind(SpanEvent::Kind::Client); + APSARA_TEST_EQUAL(SpanEvent::Kind::Client, mSpanEvent->GetKind()); + + mSpanEvent->SetStartTimeNs(1715826723000000000); + APSARA_TEST_EQUAL(1715826723000000000U, mSpanEvent->GetStartTimeNs()); + + mSpanEvent->SetEndTimeNs(1715826725000000000); + APSARA_TEST_EQUAL(1715826725000000000U, mSpanEvent->GetEndTimeNs()); + + mSpanEvent->SetStatus(SpanEvent::StatusCode::Ok); + APSARA_TEST_EQUAL(SpanEvent::StatusCode::Ok, mSpanEvent->GetStatus()); +} + +void SpanEventUnittest::TestTag() { + { + string key = "key1"; + string value = "value1"; + mSpanEvent->SetTag(key, value); + APSARA_TEST_TRUE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetTag(key).to_string()); + } + { + string key = "key2"; + string value = "value2"; + mSpanEvent->SetTag(StringView(key.data(), key.size()), StringView(value.data(), value.size())); + APSARA_TEST_TRUE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetTag(key).to_string()); + } + { + string key = "key3"; + string value = "value3"; + mSpanEvent->SetTagNoCopy(mSpanEvent->GetSourceBuffer()->CopyString(key), + mSpanEvent->GetSourceBuffer()->CopyString(value)); + APSARA_TEST_TRUE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetTag(key).to_string()); + } + { + string key = "key4"; + string value = "value4"; + const StringBuffer& kb = mSpanEvent->GetSourceBuffer()->CopyString(key); + const StringBuffer& vb = mSpanEvent->GetSourceBuffer()->CopyString(value); + mSpanEvent->SetTagNoCopy(StringView(kb.data, kb.size), StringView(vb.data, vb.size)); + APSARA_TEST_TRUE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetTag(key).to_string()); + } + { + string key = "key5"; + APSARA_TEST_FALSE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mSpanEvent->GetTag(key).to_string()); + } + { + string key = "key1"; + mSpanEvent->DelTag(key); + APSARA_TEST_FALSE(mSpanEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mSpanEvent->GetTag(key).to_string()); + } +} + +void SpanEventUnittest::TestLink() { + SpanEvent::SpanLink* l = mSpanEvent->AddLink(); + APSARA_TEST_EQUAL(1U, mSpanEvent->GetLinks().size()); + APSARA_TEST_EQUAL(l, &mSpanEvent->GetLinks()[0]); +} + +void SpanEventUnittest::TestEvent() { + SpanEvent::InnerEvent* e = mSpanEvent->AddEvent(); + APSARA_TEST_EQUAL(1U, mSpanEvent->GetEvents().size()); + APSARA_TEST_EQUAL(e, &mSpanEvent->GetEvents()[0]); +} + +void SpanEventUnittest::TestScopeTag() { + { + string key = "key1"; + string value = "value1"; + mSpanEvent->SetScopeTag(key, value); + APSARA_TEST_TRUE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetScopeTag(key).to_string()); + } + { + string key = "key2"; + string value = "value2"; + mSpanEvent->SetScopeTag(StringView(key.data(), key.size()), StringView(value.data(), value.size())); + APSARA_TEST_TRUE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetScopeTag(key).to_string()); + } + { + string key = "key3"; + string value = "value3"; + mSpanEvent->SetScopeTagNoCopy(mSpanEvent->GetSourceBuffer()->CopyString(key), + mSpanEvent->GetSourceBuffer()->CopyString(value)); + APSARA_TEST_TRUE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetScopeTag(key).to_string()); + } + { + string key = "key4"; + string value = "value4"; + const StringBuffer& kb = mSpanEvent->GetSourceBuffer()->CopyString(key); + const StringBuffer& vb = mSpanEvent->GetSourceBuffer()->CopyString(value); + mSpanEvent->SetScopeTagNoCopy(StringView(kb.data, kb.size), StringView(vb.data, vb.size)); + APSARA_TEST_TRUE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL(value, mSpanEvent->GetScopeTag(key).to_string()); + } + { + string key = "key5"; + APSARA_TEST_FALSE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL("", mSpanEvent->GetScopeTag(key).to_string()); + } + { + string key = "key1"; + mSpanEvent->DelScopeTag(key); + APSARA_TEST_FALSE(mSpanEvent->HasScopeTag(key)); + APSARA_TEST_EQUAL("", mSpanEvent->GetScopeTag(key).to_string()); + } +} + +void SpanEventUnittest::TestSize() { + size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(SpanEvent::Kind) + sizeof(uint64_t) + sizeof(uint64_t) + + sizeof(SpanEvent::StatusCode) + sizeof(vector) + sizeof(vector) + + sizeof(map) + sizeof(map); + + mSpanEvent->SetTraceId("test_trace_id"); + mSpanEvent->SetSpanId("test_span_id"); + mSpanEvent->SetTraceState("normal"); + mSpanEvent->SetParentSpanId("test_parent_span_id"); + mSpanEvent->SetName("test_name"); + mSpanEvent->SetKind(SpanEvent::Kind::Client); + mSpanEvent->SetStatus(SpanEvent::StatusCode::Ok); + basicSize += strlen("test_trace_id") + strlen("test_span_id") + strlen("normal") + strlen("test_parent_span_id") + + strlen("test_name"); + + { + // add tag, key not existed + mSpanEvent->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mSpanEvent->DataSize()); + + // add tag, key existed + mSpanEvent->SetTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mSpanEvent->DataSize()); + + // delete tag + mSpanEvent->DelTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mSpanEvent->DataSize()); + } + { + // add scope tag, key not existed + mSpanEvent->SetScopeTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mSpanEvent->DataSize()); + + // add scope tag, key existed + mSpanEvent->SetScopeTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mSpanEvent->DataSize()); + + // delete scope tag + mSpanEvent->DelScopeTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mSpanEvent->DataSize()); + } + { + SpanEvent::InnerEvent* e = mSpanEvent->AddEvent(); + size_t newBasicSize = basicSize + sizeof(uint64_t) + sizeof(map); + + e->SetName("test_event"); + newBasicSize += strlen("test_event"); + + e->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(newBasicSize + 5U, mSpanEvent->DataSize()); + + mSpanEvent->mEvents.clear(); + } + { + SpanEvent::SpanLink* l = mSpanEvent->AddLink(); + size_t newBasicSize = basicSize + sizeof(map); + + l->SetTraceId("other_trace_id"); + l->SetSpanId("other_span_id"); + l->SetTraceState("normal"); + newBasicSize += strlen("other_trace_id") + strlen("other_span_id") + strlen("normal"); + + l->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(newBasicSize + 5U, mSpanEvent->DataSize()); + + mSpanEvent->mLinks.clear(); + } +} + +void SpanEventUnittest::TestToJson() { + mSpanEvent->SetTimestamp(12345678901, 0); + mSpanEvent->SetTraceId("test_trace_id"); + mSpanEvent->SetSpanId("test_span_id"); + mSpanEvent->SetTraceState("normal"); + mSpanEvent->SetParentSpanId("test_parent_span_id"); + mSpanEvent->SetName("test_name"); + mSpanEvent->SetKind(SpanEvent::Kind::Client); + mSpanEvent->SetStartTimeNs(1715826723000000000); + mSpanEvent->SetEndTimeNs(1715826725000000000); + mSpanEvent->SetTag(string("key1"), string("value1")); + SpanEvent::InnerEvent* e = mSpanEvent->AddEvent(); + e->SetName("test_event"); + e->SetTimestampNs(1715826724000000000); + SpanEvent::SpanLink* l = mSpanEvent->AddLink(); + l->SetTraceId("other_trace_id"); + l->SetSpanId("other_span_id"); + mSpanEvent->SetStatus(SpanEvent::StatusCode::Ok); + mSpanEvent->SetScopeTag(string("key2"), string("value2")); + Json::Value res = mSpanEvent->ToJson(); + + Json::Value eventJson; + string eventStr = R"({ + "type": 3, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "traceId": "test_trace_id", + "spanId": "test_span_id", + "traceState": "normal", + "parentSpanId": "test_parent_span_id", + "name": "test_name", + "kind": 3, + "startTimeNs": 1715826723000000000, + "endTimeNs": 1715826725000000000, + "tags": { + "key1": "value1" + }, + "events": [ + { + "name": "test_event", + "timestampNs": 1715826724000000000 + } + ], + "links": [ + { + "traceId": "other_trace_id", + "spanId": "other_span_id" + } + ], + "status": 1, + "scopeTags": { + "key2": "value2" + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + + APSARA_TEST_TRUE(eventJson == res); +} + +void SpanEventUnittest::TestFromJson() { + Json::Value eventJson; + string eventStr = R"({ + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "traceId": "test_trace_id", + "spanId": "test_span_id", + "traceState": "normal", + "parentSpanId": "test_parent_span_id", + "name": "test_name", + "kind": 3, + "startTimeNs": 1715826723000000000, + "endTimeNs": 1715826725000000000, + "tags": { + "key1": "value1" + }, + "events": [ + { + "name": "test_event", + "timestampNs": 1715826724000000000 + } + ], + "links": [ + { + "traceId": "other_trace_id", + "spanId": "other_span_id" + } + ], + "status": 1, + "scopeTags": { + "key2": "value2" + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + mSpanEvent->FromJson(eventJson); + + APSARA_TEST_EQUAL(12345678901, mSpanEvent->GetTimestamp()); + APSARA_TEST_EQUAL(0L, mSpanEvent->GetTimestampNanosecond().value()); + APSARA_TEST_EQUAL("test_trace_id", mSpanEvent->GetTraceId().to_string()); + APSARA_TEST_EQUAL("test_span_id", mSpanEvent->GetSpanId().to_string()); + APSARA_TEST_EQUAL("normal", mSpanEvent->GetTraceState().to_string()); + APSARA_TEST_EQUAL("test_parent_span_id", mSpanEvent->GetParentSpanId().to_string()); + APSARA_TEST_EQUAL("test_name", mSpanEvent->GetName().to_string()); + APSARA_TEST_EQUAL(SpanEvent::Kind::Client, mSpanEvent->GetKind()); + APSARA_TEST_EQUAL(1715826723000000000U, mSpanEvent->GetStartTimeNs()); + APSARA_TEST_EQUAL(1715826725000000000U, mSpanEvent->GetEndTimeNs()); + APSARA_TEST_EQUAL("value1", mSpanEvent->GetTag("key1")); + APSARA_TEST_EQUAL(1U, mSpanEvent->GetEvents().size()); + APSARA_TEST_EQUAL(1U, mSpanEvent->GetLinks().size()); + APSARA_TEST_EQUAL(SpanEvent::StatusCode::Ok, mSpanEvent->GetStatus()); + APSARA_TEST_EQUAL("value2", mSpanEvent->GetScopeTag("key2")); +} + +UNIT_TEST_CASE(SpanEventUnittest, TestSimpleFields) +UNIT_TEST_CASE(SpanEventUnittest, TestTag) +UNIT_TEST_CASE(SpanEventUnittest, TestLink) +UNIT_TEST_CASE(SpanEventUnittest, TestEvent) +UNIT_TEST_CASE(SpanEventUnittest, TestScopeTag) +UNIT_TEST_CASE(SpanEventUnittest, TestSize) +UNIT_TEST_CASE(SpanEventUnittest, TestToJson) +UNIT_TEST_CASE(SpanEventUnittest, TestFromJson) + +class InnerEventUnittest : public ::testing::Test { +public: + void TestSimpleFields(); + void TestTag(); + void TestSize(); + void TestToJson(); + void TestFromJson(); + +protected: + void SetUp() override { + mSourceBuffer.reset(new SourceBuffer); + mEventGroup.reset(new PipelineEventGroup(mSourceBuffer)); + mSpanEvent = mEventGroup->CreateSpanEvent(); + mInnerEvent = mSpanEvent->AddEvent(); + } + +private: + shared_ptr mSourceBuffer; + unique_ptr mEventGroup; + unique_ptr mSpanEvent; + SpanEvent::InnerEvent* mInnerEvent; +}; + +void InnerEventUnittest::TestSimpleFields() { + mInnerEvent->SetTimestampNs(1715826723000000000); + APSARA_TEST_EQUAL(1715826723000000000U, mInnerEvent->GetTimestampNs()); + + mInnerEvent->SetName("test_name"); + APSARA_TEST_EQUAL("test_name", mInnerEvent->GetName().to_string()); +} + +void InnerEventUnittest::TestTag() { + { + string key = "key1"; + string value = "value1"; + mInnerEvent->SetTag(key, value); + APSARA_TEST_TRUE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mInnerEvent->GetTag(key).to_string()); + } + { + string key = "key2"; + string value = "value2"; + mInnerEvent->SetTag(StringView(key.data(), key.size()), StringView(value.data(), value.size())); + APSARA_TEST_TRUE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mInnerEvent->GetTag(key).to_string()); + } + { + string key = "key3"; + string value = "value3"; + mInnerEvent->SetTagNoCopy(mInnerEvent->GetSourceBuffer()->CopyString(key), + mInnerEvent->GetSourceBuffer()->CopyString(value)); + APSARA_TEST_TRUE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mInnerEvent->GetTag(key).to_string()); + } + { + string key = "key4"; + string value = "value4"; + const StringBuffer& kb = mInnerEvent->GetSourceBuffer()->CopyString(key); + const StringBuffer& vb = mInnerEvent->GetSourceBuffer()->CopyString(value); + mInnerEvent->SetTagNoCopy(StringView(kb.data, kb.size), StringView(vb.data, vb.size)); + APSARA_TEST_TRUE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL(value, mInnerEvent->GetTag(key).to_string()); + } + { + string key = "key5"; + APSARA_TEST_FALSE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mInnerEvent->GetTag(key).to_string()); + } + { + string key = "key1"; + mInnerEvent->DelTag(key); + APSARA_TEST_FALSE(mInnerEvent->HasTag(key)); + APSARA_TEST_EQUAL("", mInnerEvent->GetTag(key).to_string()); + } +} + +void InnerEventUnittest::TestSize() { + size_t basicSize = sizeof(uint64_t) + sizeof(map); + + mInnerEvent->SetName("test"); + basicSize += strlen("test"); + + // add tag, and key not existed + mInnerEvent->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mInnerEvent->DataSize()); + + // add tag, and key existed + mInnerEvent->SetTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mInnerEvent->DataSize()); + + // delete tag + mInnerEvent->DelTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mInnerEvent->DataSize()); +} + +void InnerEventUnittest::TestToJson() { + mInnerEvent->SetName("test"); + mInnerEvent->SetTimestampNs(1715826723000000000); + mInnerEvent->SetTag(string("key1"), string("value1")); + Json::Value res = mInnerEvent->ToJson(); + + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "timestampNs": 1715826723000000000, + "tags": { + "key1": "value1" + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + + APSARA_TEST_TRUE(eventJson == res); +} + +void InnerEventUnittest::TestFromJson() { + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "timestampNs": 1715826723000000000, + "tags": { + "key1": "value1" + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + mInnerEvent->FromJson(eventJson); + + APSARA_TEST_EQUAL("test", mInnerEvent->GetName().to_string()); + APSARA_TEST_EQUAL(1715826723000000000U, mInnerEvent->GetTimestampNs()); + APSARA_TEST_EQUAL("value1", mInnerEvent->GetTag("key1").to_string()); +} + +UNIT_TEST_CASE(InnerEventUnittest, TestSimpleFields) +UNIT_TEST_CASE(InnerEventUnittest, TestTag) +UNIT_TEST_CASE(InnerEventUnittest, TestSize) +UNIT_TEST_CASE(InnerEventUnittest, TestToJson) +UNIT_TEST_CASE(InnerEventUnittest, TestFromJson) + +class SpanLinkUnittest : public ::testing::Test { +public: + void TestSimpleFields(); + void TestTag(); + void TestSize(); + void TestToJson(); + void TestFromJson(); + +protected: + void SetUp() override { + mSourceBuffer.reset(new SourceBuffer); + mEventGroup.reset(new PipelineEventGroup(mSourceBuffer)); + mSpanEvent = mEventGroup->CreateSpanEvent(); + mLink = mSpanEvent->AddLink(); + } + +private: + shared_ptr mSourceBuffer; + unique_ptr mEventGroup; + unique_ptr mSpanEvent; + SpanEvent::SpanLink* mLink; +}; + +void SpanLinkUnittest::TestSimpleFields() { + mLink->SetTraceId("test_trace_id"); + APSARA_TEST_EQUAL("test_trace_id", mLink->GetTraceId().to_string()); + + mLink->SetSpanId("test_span_id"); + APSARA_TEST_EQUAL("test_span_id", mLink->GetSpanId().to_string()); + + mLink->SetTraceState("normal"); + APSARA_TEST_EQUAL("normal", mLink->GetTraceState().to_string()); +} + +void SpanLinkUnittest::TestTag() { + { + string key = "key1"; + string value = "value1"; + mLink->SetTag(key, value); + APSARA_TEST_TRUE(mLink->HasTag(key)); + APSARA_TEST_EQUAL(value, mLink->GetTag(key).to_string()); + } + { + string key = "key2"; + string value = "value2"; + mLink->SetTag(StringView(key.data(), key.size()), StringView(value.data(), value.size())); + APSARA_TEST_TRUE(mLink->HasTag(key)); + APSARA_TEST_EQUAL(value, mLink->GetTag(key).to_string()); + } + { + string key = "key3"; + string value = "value3"; + mLink->SetTagNoCopy(mLink->GetSourceBuffer()->CopyString(key), mLink->GetSourceBuffer()->CopyString(value)); + APSARA_TEST_TRUE(mLink->HasTag(key)); + APSARA_TEST_EQUAL(value, mLink->GetTag(key).to_string()); + } + { + string key = "key4"; + string value = "value4"; + const StringBuffer& kb = mLink->GetSourceBuffer()->CopyString(key); + const StringBuffer& vb = mLink->GetSourceBuffer()->CopyString(value); + mLink->SetTagNoCopy(StringView(kb.data, kb.size), StringView(vb.data, vb.size)); + APSARA_TEST_TRUE(mLink->HasTag(key)); + APSARA_TEST_EQUAL(value, mLink->GetTag(key).to_string()); + } + { + string key = "key5"; + APSARA_TEST_FALSE(mLink->HasTag(key)); + APSARA_TEST_EQUAL("", mLink->GetTag(key).to_string()); + } + { + string key = "key1"; + mLink->DelTag(key); + APSARA_TEST_FALSE(mLink->HasTag(key)); + APSARA_TEST_EQUAL("", mLink->GetTag(key).to_string()); + } +} + +void SpanLinkUnittest::TestSize() { + size_t basicSize = sizeof(map); + + mLink->SetTraceId("test_trace_id"); + mLink->SetSpanId("test_span_id"); + mLink->SetTraceState("normal"); + basicSize += strlen("test_trace_id") + strlen("test_span_id") + strlen("normal"); + + // add tag, and key not existed + mLink->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mLink->DataSize()); + + // add tag, and key existed + mLink->SetTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mLink->DataSize()); + + // delete tag + mLink->DelTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mLink->DataSize()); +} + +void SpanLinkUnittest::TestToJson() { + mLink->SetTraceId("test_trace_id"); + mLink->SetSpanId("test_span_id"); + mLink->SetTraceState("normal"); + mLink->SetTag(string("key1"), string("value1")); + Json::Value res = mLink->ToJson(); + + Json::Value linkJson; + string linkStr = R"({ + "traceId": "test_trace_id", + "spanId": "test_span_id", + "traceState": "normal", + "tags": { + "key1": "value1" + } + })"; + string errorMsg; + ParseJsonTable(linkStr, linkJson, errorMsg); + + APSARA_TEST_TRUE(linkJson == res); +} + +void SpanLinkUnittest::TestFromJson() { + Json::Value linkJson; + string linkStr = R"({ + "traceId": "test_trace_id", + "spanId": "test_span_id", + "traceState": "normal", + "tags": { + "key1": "value1" + } + })"; + string errorMsg; + ParseJsonTable(linkStr, linkJson, errorMsg); + mLink->FromJson(linkJson); + + APSARA_TEST_EQUAL("test_trace_id", mLink->GetTraceId().to_string()); + APSARA_TEST_EQUAL("test_span_id", mLink->GetSpanId().to_string()); + APSARA_TEST_EQUAL("normal", mLink->GetTraceState().to_string()); + APSARA_TEST_EQUAL("value1", mLink->GetTag("key1").to_string()); +} + +UNIT_TEST_CASE(SpanLinkUnittest, TestSimpleFields) +UNIT_TEST_CASE(SpanLinkUnittest, TestTag) +UNIT_TEST_CASE(SpanLinkUnittest, TestSize) +UNIT_TEST_CASE(SpanLinkUnittest, TestToJson) +UNIT_TEST_CASE(SpanLinkUnittest, TestFromJson) + +} // namespace logtail + +UNIT_TEST_MAIN diff --git a/core/unittest/processor/ProcessorMergeMultilineLogNativeUnittest.cpp b/core/unittest/processor/ProcessorMergeMultilineLogNativeUnittest.cpp new file mode 100644 index 0000000000..becc7727fb --- /dev/null +++ b/core/unittest/processor/ProcessorMergeMultilineLogNativeUnittest.cpp @@ -0,0 +1,4294 @@ +// Copyright 2023 iLogtail Authors +// +// 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. +#include + +#include "common/Constants.h" +#include "common/JsonUtil.h" +#include "config/Config.h" +#include "models/LogEvent.h" +#include "processor/inner/ProcessorMergeMultilineLogNative.h" +#include "processor/inner/ProcessorSplitLogStringNative.h" +#include "unittest/Unittest.h" + +namespace logtail { + +const std::string LOG_BEGIN_STRING = "Exception in thread 'main' java.lang.NullPointerException"; +const std::string LOG_BEGIN_REGEX = R"(Exception.*)"; +const std::string LOG_CONTINUE_STRING = " at com.example.myproject.Book.getTitle(Book.java:16)"; +const std::string LOG_CONTINUE_REGEX = R"(\s+at\s.*)"; +const std::string LOG_END_STRING = " ...23 more"; +const std::string LOG_END_REGEX = R"(\s*\.\.\.\d+ more)"; +const std::string LOG_UNMATCH = "unmatch log"; + +class ProcessorMergeMultilineLogNativeUnittest : public ::testing::Test { +public: + void SetUp() override { mContext.SetConfigName("project##config_0"); } + void TestInit(); + void TestProcess(); + PipelineContext mContext; +}; + +UNIT_TEST_CASE(ProcessorMergeMultilineLogNativeUnittest, TestInit); +UNIT_TEST_CASE(ProcessorMergeMultilineLogNativeUnittest, TestProcess); + +void ProcessorMergeMultilineLogNativeUnittest::TestInit() { + // 测试合法的正则配置 + { + // start init通过,IsMultiline为true + { + Json::Value config; + config["StartPattern"] = "123123123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + // start + continue init通过,IsMultiline为true + { + Json::Value config; + config["StartPattern"] = "123123123.*"; + config["ContinuePattern"] = "123123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + // continue + end init通过,IsMultiline为true + { + Json::Value config; + config["ContinuePattern"] = "123123.*"; + config["EndPattern"] = "123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + // end init通过,IsMultiline为true + { + Json::Value config; + config["EndPattern"] = "123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + // start + end init通过,IsMultiline为true + { + Json::Value config; + config["StartPattern"] = "123123123.*"; + config["EndPattern"] = "123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + } + // 测试非法的正则配置 + { + // start + continue + end init通过,IsMultiline为true + { + Json::Value config; + config["StartPattern"] = "123123123.*"; + config["ContinuePattern"] = "123123.*"; + config["EndPattern"] = "123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_TRUE(processor.mMultiline.IsMultiline()); + } + // continue init通过,IsMultiline为false + { + Json::Value config; + config["ContinuePattern"] = "123123.*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_FALSE(processor.mMultiline.IsMultiline()); + } + // 正则格式非法 init通过,IsMultiline为false + { + Json::Value config; + config["StartPattern"] = 1; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_FALSE(processor.mMultiline.IsMultiline()); + } + // 正则格式非法 init通过,IsMultiline为false + { + Json::Value config; + config["StartPattern"] = "******"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_FALSE(processor.mMultiline.IsMultiline()); + } + // 正则格式缺失 init通过,IsMultiline为false + { + Json::Value config; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_FALSE(processor.mMultiline.IsMultiline()); + } + } + // 测试mergetype + { + // regex init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + APSARA_TEST_FALSE(processor.mMultiline.IsMultiline()); + } + // flag init通过 + { + Json::Value config; + config["MergeType"] = "flag"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + // unknown init不通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "unknown"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_FALSE(processor.Init(config)); + } + // 格式错误 init不通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = 1; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_FALSE(processor.Init(config)); + } + // 不存在 init不通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_FALSE(processor.Init(config)); + } + } + // 测试UnmatchedContentTreatment + { + // single_line init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "single_line"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + // discard init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "discard"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + // unknown init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "unknown"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + // 格式错误 init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = 1; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + // 不存在 init通过 + { + Json::Value config; + config["StartPattern"] = ".*"; + config["MergeType"] = "regex"; + ProcessorMergeMultilineLogNative processor; + processor.SetContext(mContext); + processor.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processor.Init(config)); + } + } +} + +void ProcessorMergeMultilineLogNativeUnittest::TestProcess() { + // make config + Json::Value config; + config["StartPattern"] = "line.*"; + config["EndPattern"] = "endLine.*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "single_line"; + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // group为空 + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + + // 存在不支持的event类型 + { + // 某个unmatch 后出现了一个不支持 + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson1 = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson1); + eventGroup.AddMetricEvent(); + std::string inJson2 = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson2); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents": { + "content": "line\ncontinue\nendLine" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents": { + "content": "line" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "endLine" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + + // 正在匹配过程中 出现了一个不支持 + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + eventGroup.AddMetricEvent(); + inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "endLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents": { + "content": "line" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents": { + "content": "endLine" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + + // 第一个event就是不支持 + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line1\ncontinue\nline2\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.AddMetricEvent(); + eventGroup.FromJsonString(inJson); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents": { + "content": "line1" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "line2" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + } + // event group中某条event没有mSourceKey + { + // 某个unmatch 后出现一个没有mSourceKey的event + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue\nendLine" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "continue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents": { + "content": "line" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "endLine" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "content": "continue" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // 正在匹配过程中出现没有mSourceKey的event + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "line\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "endLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : "line" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "continue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "endLine" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "continue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // 第一个就出现没有mSourceKey的event + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "aaa" : "line\ncontinue\nendLine\ncontinue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "line" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "continue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "endLine" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : "continue" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + } +} + +class ProcessEventsWithPartLogUnittest : public ::testing::Test { +public: + void SetUp() override { mContext.SetConfigName("project##config_0"); } + void TestProcessEventsWithPartLog(); + void TestProcess(); + PipelineContext mContext; +}; + +UNIT_TEST_CASE(ProcessEventsWithPartLogUnittest, TestProcessEventsWithPartLog); +UNIT_TEST_CASE(ProcessEventsWithPartLogUnittest, TestProcess); + + +void ProcessEventsWithPartLogUnittest::TestProcessEventsWithPartLog() { + // case: P + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents":{ + "content":"Exception" + }, + "timestamp":12345678901, + "timestampNanosecond":0, + "type":1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: F + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents":{ + "content":"Exception" + }, + "timestamp":12345678901, + "timestampNanosecond":0, + "type":1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: P+P + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Except" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents":{ + "content":"Exception" + }, + "timestamp":12345678901, + "timestampNanosecond":0, + "type":1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: P+F + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Except" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents":{ + "content":"Exception" + }, + "timestamp":12345678901, + "timestampNanosecond":0, + "type":1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: F+P + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "content": "Except" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Except" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: F+P+F + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "content": "Exc" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ept" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Exc" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "eption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: P+F+P + { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make processor + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Exc" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ept" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Except" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ion" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessEventsWithPartLogUnittest::TestProcess() { + // make config + Json::Value config; + config["MergeType"] = "flag"; + // make ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE(processorMergeMultilineLogNative.Init(config)); + // event 不支持 + { + // 第一个event就不支持 不支持PPFFF + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.AddMetricEvent(); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents" : + { + "P": "", + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // P匹配过程中不支持 P不支持PFFF + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + eventGroup.AddMetricEvent(); + inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // PF匹配过程中不支持 PP不支持FFF + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + eventGroup.AddMetricEvent(); + inJson = R"({ + "events": [ + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // FF中不支持 PPFF不支持F + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::string inJson = R"({ + "events": [ + { + "contents" : + { + "P": "", + "content": "Ex" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "P": "", + "content": "ce" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "ption" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + ], + "metadata": { + "has.part.log": "P" + } + })"; + eventGroup.FromJsonString(inJson); + eventGroup.AddMetricEvent(); + inJson = R"({ + "events": [ + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson); + // run test function + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events": [ + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "name": "", + "timestamp": 0, + "type": 2, + "value": { + "type": "unknown" + } + }, + { + "contents" : + { + "content": "Exception" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + } +} + +class ProcessorMergeMultilineLogDisacardUnmatchUnittest : public ::testing::Test { +public: + void SetUp() override { mContext.SetConfigName("project##config_0"); } + void TestLogSplitWithBeginContinue(); + void TestLogSplitWithBeginEnd(); + void TestLogSplitWithBegin(); + void TestLogSplitWithContinueEnd(); + void TestLogSplitWithEnd(); + PipelineContext mContext; +}; + +UNIT_TEST_CASE(ProcessorMergeMultilineLogDisacardUnmatchUnittest, TestLogSplitWithBeginContinue); +UNIT_TEST_CASE(ProcessorMergeMultilineLogDisacardUnmatchUnittest, TestLogSplitWithBeginEnd); +UNIT_TEST_CASE(ProcessorMergeMultilineLogDisacardUnmatchUnittest, TestLogSplitWithBegin); +UNIT_TEST_CASE(ProcessorMergeMultilineLogDisacardUnmatchUnittest, TestLogSplitWithContinueEnd); +UNIT_TEST_CASE(ProcessorMergeMultilineLogDisacardUnmatchUnittest, TestLogSplitWithEnd); + +void ProcessorMergeMultilineLogDisacardUnmatchUnittest::TestLogSplitWithBeginContinue() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["MergeType"] = "regex"; + config["ContinuePattern"] = LOG_CONTINUE_REGEX; + config["UnmatchedContentTreatment"] = "discard"; + + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: start + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + start + continue + continue + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + start + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + start + continue + continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogDisacardUnmatchUnittest::TestLogSplitWithBeginEnd() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["MergeType"] = "regex"; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "discard"; + + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: unmatch+start+unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: unmatch+start+End+unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(\n)" << LOG_UNMATCH + << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + + // case: start+start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: unmatch+start+End + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch+start+unmatch+End+unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(\n)" << LOG_END_STRING + << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogDisacardUnmatchUnittest::TestLogSplitWithBegin() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "discard"; + + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: start + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: start + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogDisacardUnmatchUnittest::TestLogSplitWithContinueEnd() { + // make config + Json::Value config; + config["ContinuePattern"] = LOG_CONTINUE_REGEX; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "discard"; + config["MergeType"] = "regex"; + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: Continue + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: Continue + Continue + end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogDisacardUnmatchUnittest::TestLogSplitWithEnd() { + // make config + Json::Value config; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "discard"; + config["MergeType"] = "regex"; + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ("null", CompactJson(outJson).c_str()); + } + // case: unmatch + end + unmatch + { + // make eventGroup + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +class ProcessorMergeMultilineLogKeepUnmatchUnittest : public ::testing::Test { +public: + void SetUp() override { mContext.SetConfigName("project##config_0"); } + void TestLogSplitWithBeginContinue(); + void TestLogSplitWithBeginEnd(); + void TestLogSplitWithBegin(); + void TestLogSplitWithContinueEnd(); + void TestLogSplitWithEnd(); + PipelineContext mContext; +}; + +UNIT_TEST_CASE(ProcessorMergeMultilineLogKeepUnmatchUnittest, TestLogSplitWithBeginContinue); +UNIT_TEST_CASE(ProcessorMergeMultilineLogKeepUnmatchUnittest, TestLogSplitWithBeginEnd); +UNIT_TEST_CASE(ProcessorMergeMultilineLogKeepUnmatchUnittest, TestLogSplitWithBegin); +UNIT_TEST_CASE(ProcessorMergeMultilineLogKeepUnmatchUnittest, TestLogSplitWithContinueEnd); +UNIT_TEST_CASE(ProcessorMergeMultilineLogKeepUnmatchUnittest, TestLogSplitWithEnd); + +void ProcessorMergeMultilineLogKeepUnmatchUnittest::TestLogSplitWithBeginContinue() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["MergeType"] = "regex"; + config["ContinuePattern"] = LOG_CONTINUE_REGEX; + config["UnmatchedContentTreatment"] = "single_line"; + + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // start + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // unmatch + start + continue + continue + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + start + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + start + continue + continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogKeepUnmatchUnittest::TestLogSplitWithBeginEnd() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["MergeType"] = "regex"; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "single_line"; + + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch+start+unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch+start+End+unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(\n)" << LOG_UNMATCH + << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + + // case: start+start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch+start+End + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch+start+unmatch+End + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(\n)" << LOG_END_STRING + << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogKeepUnmatchUnittest::TestLogSplitWithBegin() { + // make config + Json::Value config; + config["StartPattern"] = LOG_BEGIN_REGEX; + config["UnmatchedContentTreatment"] = "single_line"; + config["MergeType"] = "regex"; + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: start + start + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: start + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_BEGIN_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogKeepUnmatchUnittest::TestLogSplitWithContinueEnd() { + // make config + Json::Value config; + config["ContinuePattern"] = LOG_CONTINUE_REGEX; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "single_line"; + config["MergeType"] = "regex"; + // make processor + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: Continue + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: Continue + Continue + end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(\n)" << LOG_CONTINUE_STRING << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: continue + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_CONTINUE_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} + +void ProcessorMergeMultilineLogKeepUnmatchUnittest::TestLogSplitWithEnd() { + // make config + Json::Value config; + config["EndPattern"] = LOG_END_REGEX; + config["UnmatchedContentTreatment"] = "single_line"; + config["MergeType"] = "regex"; + // make processor + + // ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + // ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + // case: end + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } + // case: unmatch + end + unmatch + { + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + std::stringstream inJson; + inJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(\n)" << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + eventGroup.FromJsonString(inJson.str()); + + // run test function + processorSplitLogStringNative.Process(eventGroup); + processorMergeMultilineLogNative.Process(eventGroup); + // judge result + std::stringstream expectJson; + expectJson << R"({ + "events" : + [ + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(\n)" << LOG_END_STRING << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + }, + { + "contents" : + { + "content" : ")" + << LOG_UNMATCH << R"(" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 1 + } + ] + })"; + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ(CompactJson(expectJson.str()).c_str(), CompactJson(outJson).c_str()); + } +} +} // namespace logtail + +UNIT_TEST_MAIN \ No newline at end of file diff --git a/core/unittest/processor/ProcessorParseApsaraNativeUnittest.cpp b/core/unittest/processor/ProcessorParseApsaraNativeUnittest.cpp index b0f83ab094..b0905cb195 100644 --- a/core/unittest/processor/ProcessorParseApsaraNativeUnittest.cpp +++ b/core/unittest/processor/ProcessorParseApsaraNativeUnittest.cpp @@ -492,7 +492,6 @@ void ProcessorParseApsaraNativeUnittest::TestMultipleLines() { "__raw__": "[2023-09-04 13:15" }, "timestamp": 12345678901, - "timestampNanosecond": 0, "type": 1 }, { @@ -500,7 +499,6 @@ void ProcessorParseApsaraNativeUnittest::TestMultipleLines() { "__raw__": ":50.1]\t[ERROR]\t[1]\t/ilogtail/AppConfigBase.cpp:1\t\tAppConfigBase AppConfigBase:1" }, "timestamp": 12345678901, - "timestampNanosecond": 0, "type": 1 }, { @@ -864,7 +862,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessKeyOverwritten() { "rawLog": "value1" }, "timestamp": 12345678901, - "timestampNanosecond": 0, "type": 1 } ] @@ -940,7 +937,6 @@ void ProcessorParseApsaraNativeUnittest::TestUploadRawLog() { "rawLog": "value1" }, "timestamp": 12345678901, - "timestampNanosecond": 0, "type": 1 } ] @@ -1051,7 +1047,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventKeepUnmatch() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond": 0, "type" : 1 }, { @@ -1060,7 +1055,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventKeepUnmatch() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond": 0, "type" : 1 }, { @@ -1069,7 +1063,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventKeepUnmatch() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond": 0, "type" : 1 }, { @@ -1078,7 +1071,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventKeepUnmatch() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond": 0, "type" : 1 }, { @@ -1087,7 +1079,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventKeepUnmatch() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond": 0, "type" : 1 } ] @@ -1293,7 +1284,6 @@ void ProcessorParseApsaraNativeUnittest::TestProcessEventMicrosecondUnmatch() { "rawLog": "[2023-09-04 13:18:04" }, "timestamp": 12345678901, - "timestampNanosecond": 0, "type": 1 } ] diff --git a/core/unittest/processor/ProcessorParseDelimiterNativeUnittest.cpp b/core/unittest/processor/ProcessorParseDelimiterNativeUnittest.cpp index 10463509aa..94a8116aae 100644 --- a/core/unittest/processor/ProcessorParseDelimiterNativeUnittest.cpp +++ b/core/unittest/processor/ProcessorParseDelimiterNativeUnittest.cpp @@ -86,6 +86,7 @@ void ProcessorParseDelimiterNativeUnittest::TestAllowingShortenedFields() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOsAccountOpLog',0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -94,6 +95,7 @@ void ProcessorParseDelimiterNativeUnittest::TestAllowingShortenedFields() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -146,6 +148,7 @@ void ProcessorParseDelimiterNativeUnittest::TestAllowingShortenedFields() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -270,6 +273,7 @@ void ProcessorParseDelimiterNativeUnittest::TestAllowingShortenedFields() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -417,6 +421,7 @@ void ProcessorParseDelimiterNativeUnittest::TestExtend() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -545,6 +550,7 @@ void ProcessorParseDelimiterNativeUnittest::TestExtend() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -676,6 +682,7 @@ void ProcessorParseDelimiterNativeUnittest::TestMultipleLines() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -800,6 +807,7 @@ void ProcessorParseDelimiterNativeUnittest::TestMultipleLines() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -923,6 +931,7 @@ void ProcessorParseDelimiterNativeUnittest::TestMultipleLines() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1039,6 +1048,404 @@ void ProcessorParseDelimiterNativeUnittest::TestMultipleLines() { } } +void ProcessorParseDelimiterNativeUnittest::TestMultipleLinesWithProcessorMergeMultilineLogNative() { + // case < field + { + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "123@@456 +012@@345" + }, + "timestamp" : 12345678901, + "timestampNanosecond": 0, + "type" : 1 + } + ] + })"; + + std::string expectJson = R"({ + "events": [ + { + "contents": { + "__raw__": "123@@456" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "__raw__": "012@@345" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + + // ProcessorSplitLogStringNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["Keys"].append("c"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["SplitChar"] = '\n'; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processor; + processor.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processor.Init(config)); + processor.Process(eventGroup); + + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + // ProcessorMergeMultilineLogNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["Keys"].append("c"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["StartPattern"] = "[123|012].*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "single_line"; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + processorSplitLogStringNative.Process(eventGroup); + + // run function ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + processorMergeMultilineLogNative.Process(eventGroup); + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + } + + // case > field + { + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "123@@456@@789 +012@@345@@678" + }, + "timestamp" : 12345678901, + "timestampNanosecond": 0, + "type" : 1 + } + ] + })"; + + std::string expectJson = R"({ + "events": [ + { + "contents": { + "__column2__": "789", + "a": "123", + "b": "456" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + }, + { + "contents": { + "__column2__": "678", + "a": "012", + "b": "345" + }, + "timestamp": 12345678901, + "timestampNanosecond": 0, + "type": 1 + } + ] + })"; + + // ProcessorSplitLogStringNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["SplitChar"] = '\n'; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processor; + processor.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processor.Init(config)); + processor.Process(eventGroup); + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + // ProcessorMergeMultilineLogNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["StartPattern"] = "[123|012].*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "single_line"; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + processorSplitLogStringNative.Process(eventGroup); + + // run function ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + processorMergeMultilineLogNative.Process(eventGroup); + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + } + + // case = field + { + std::string inJson = R"({ + "events" : + [ + { + "contents" : + { + "content" : "123@@456@@789 +012@@345@@678" + }, + "timestamp" : 12345678901, + "timestampNanosecond": 0, + "type" : 1 + } + ] + })"; + + std::string expectJson = R"({ + "events" : + [ + { + "contents" : + { + "a": "123", + "b": "456", + "c": "789" + }, + "timestamp" : 12345678901, + "timestampNanosecond": 0, + "type" : 1 + }, + { + "contents" : + { + "a": "012", + "b": "345", + "c": "678" + }, + "timestamp" : 12345678901, + "timestampNanosecond": 0, + "type" : 1 + } + ] + })"; + + // ProcessorSplitLogStringNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["Keys"].append("c"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["SplitChar"] = '\n'; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processor; + processor.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processor.Init(config)); + processor.Process(eventGroup); + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + // ProcessorMergeMultilineLogNative + { + // make events + auto sourceBuffer = std::make_shared(); + PipelineEventGroup eventGroup(sourceBuffer); + eventGroup.FromJsonString(inJson); + + // make config + Json::Value config; + config["SourceKey"] = "content"; + config["Separator"] = "@@"; + config["Quote"] = "'"; + config["Keys"] = Json::arrayValue; + config["Keys"].append("a"); + config["Keys"].append("b"); + config["Keys"].append("c"); + config["KeepingSourceWhenParseFail"] = true; + config["KeepingSourceWhenParseSucceed"] = false; + config["CopingRawLog"] = false; + config["RenamedSourceKey"] = "__raw__"; + config["AllowingShortenedFields"] = false; + config["StartPattern"] = "[123|012].*"; + config["MergeType"] = "regex"; + config["UnmatchedContentTreatment"] = "single_line"; + config["AppendingLogPositionMeta"] = false; + + std::string pluginId = "testID"; + // run function ProcessorSplitLogStringNative + ProcessorSplitLogStringNative processorSplitLogStringNative; + processorSplitLogStringNative.SetContext(mContext); + APSARA_TEST_TRUE_FATAL(processorSplitLogStringNative.Init(config)); + processorSplitLogStringNative.Process(eventGroup); + + // run function ProcessorMergeMultilineLogNative + ProcessorMergeMultilineLogNative processorMergeMultilineLogNative; + processorMergeMultilineLogNative.SetContext(mContext); + processorMergeMultilineLogNative.SetMetricsRecordRef(ProcessorMergeMultilineLogNative::sName, "1"); + APSARA_TEST_TRUE_FATAL(processorMergeMultilineLogNative.Init(config)); + processorMergeMultilineLogNative.Process(eventGroup); + // run function ProcessorParseDelimiterNative + ProcessorParseDelimiterNative& processorParseDelimiterNative = *(new ProcessorParseDelimiterNative); + ProcessorInstance processorInstance(&processorParseDelimiterNative, pluginId); + APSARA_TEST_TRUE_FATAL(processorInstance.Init(config, mContext)); + processorParseDelimiterNative.Process(eventGroup); + // judge result + std::string outJson = eventGroup.ToJsonString(); + APSARA_TEST_STREQ_FATAL(CompactJson(expectJson).c_str(), CompactJson(outJson).c_str()); + } + } +} + void ProcessorParseDelimiterNativeUnittest::TestProcessWholeLine() { // make config Json::Value config; @@ -1065,6 +1472,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessWholeLine() { "content" : "2013-10-31 21:03:49,POST,PutData?Category=YunOsAccountOpLog,0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1073,6 +1481,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessWholeLine() { "content" : "2013-10-31 21:04:49,POST,PutData?Category=YunOsAccountOpLog,0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1133,6 +1542,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessQuote() { "__file_offset__": 0 }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1285,6 +1695,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessQuote() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOsAccountOpLog',0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1293,6 +1704,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessQuote() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOsAccountOpLog,0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1301,6 +1713,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessQuote() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOs'AccountOpLog',0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1381,6 +1794,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessKeyOverwritten() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOsAccountOpLog',0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1389,6 +1803,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessKeyOverwritten() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1460,6 +1875,7 @@ void ProcessorParseDelimiterNativeUnittest::TestUploadRawLog() { "content" : "2013-10-31 21:03:49,POST,'PutData?Category=YunOsAccountOpLog',0.024" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1468,6 +1884,7 @@ void ProcessorParseDelimiterNativeUnittest::TestUploadRawLog() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1565,6 +1982,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventKeepUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1573,6 +1991,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventKeepUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1581,6 +2000,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventKeepUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1589,6 +2009,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventKeepUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1597,6 +2018,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventKeepUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] @@ -1705,6 +2127,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventDiscardUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1713,6 +2136,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventDiscardUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1721,6 +2145,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventDiscardUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1729,6 +2154,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventDiscardUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 }, { @@ -1737,6 +2163,7 @@ void ProcessorParseDelimiterNativeUnittest::TestProcessEventDiscardUnmatch() { "content" : "value1" }, "timestamp" : 12345678901, + "timestampNanosecond": 0, "type" : 1 } ] diff --git a/core/unittest/processor/ProcessorParseRegexNativeUnittest.cpp b/core/unittest/processor/ProcessorParseRegexNativeUnittest.cpp index 3e5f7cf438..60573f4caa 100644 --- a/core/unittest/processor/ProcessorParseRegexNativeUnittest.cpp +++ b/core/unittest/processor/ProcessorParseRegexNativeUnittest.cpp @@ -210,7 +210,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegex() { "rawLog" : "value1\tvalue2" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 }, { @@ -221,7 +220,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegex() { "rawLog" : "value3\tvalue4" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 } ] @@ -288,7 +286,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegexRaw() { "rawLog" : "value1" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 }, { @@ -298,7 +295,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegexRaw() { "rawLog" : "value3" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 } ] @@ -366,7 +362,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegexContent() { "rawLog" : "value1\tvalue2" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 }, { @@ -377,7 +372,6 @@ void ProcessorParseRegexNativeUnittest::TestProcessRegexContent() { "rawLog" : "value3\tvalue4" }, "timestamp" : 12345678901, - "timestampNanosecond" : 0, "type" : 1 } ] diff --git a/core/unittest/reader/CMakeLists.txt b/core/unittest/reader/CMakeLists.txt index 8c51ed375e..f1a8d9f46d 100644 --- a/core/unittest/reader/CMakeLists.txt +++ b/core/unittest/reader/CMakeLists.txt @@ -33,6 +33,12 @@ target_link_libraries(log_file_reader_unittest unittest_base) add_executable(source_buffer_unittest SourceBufferUnittest.cpp) target_link_libraries(source_buffer_unittest unittest_base) +add_executable(get_last_line_data_unittest GetLastLineDataUnittest.cpp) +target_link_libraries(get_last_line_data_unittest unittest_base) + +add_executable(force_read_unittest ForceReadUnittest.cpp) +target_link_libraries(force_read_unittest unittest_base) + if (UNIX) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testDataSet) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/testDataSet/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/testDataSet/) @@ -48,3 +54,5 @@ gtest_discover_tests(json_log_file_reader_unittest) gtest_discover_tests(remove_last_incomplete_log_unittest) gtest_discover_tests(log_file_reader_unittest) gtest_discover_tests(source_buffer_unittest) +gtest_discover_tests(get_last_line_data_unittest) +gtest_discover_tests(force_read_unittest) diff --git a/core/unittest/reader/ForceReadUnittest.cpp b/core/unittest/reader/ForceReadUnittest.cpp new file mode 100644 index 0000000000..d40a35fa40 --- /dev/null +++ b/core/unittest/reader/ForceReadUnittest.cpp @@ -0,0 +1,398 @@ +// Copyright 2024 iLogtail Authors +// +// 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. + +#include +#include +#include + +#include +#include + +#include "common/Constants.h" +#include "common/FileSystemUtil.h" +#include "common/Flags.h" +#include "common/JsonUtil.h" +#include "config_manager/ConfigManager.h" +#include "event/BlockEventManager.h" +#include "event/Event.h" +#include "event_handler/EventHandler.h" +#include "file_server/FileServer.h" +#include "logger/Logger.h" +#include "queue/ProcessQueueManager.h" +#include "unittest/Unittest.h" + +using namespace std; + +namespace logtail { + +class ForceReadUnittest : public testing::Test { +public: + void TestTimeoutForceRead(); + void TestFileCloseForceRead(); + void TestAddTimeoutEvent(); + +protected: + void SetUp() override { + logPathDir = GetProcessExecutionDir(); + if (PATH_SEPARATOR[0] == logPathDir.back()) { + logPathDir.resize(logPathDir.size() - 1); + } + logPathDir += PATH_SEPARATOR + "testDataSet" + PATH_SEPARATOR + "ForceReadUnittest"; + utf8File = "utf8.txt"; + std::string filepath = logPathDir + PATH_SEPARATOR + utf8File; + std::unique_ptr fp(std::fopen(filepath.c_str(), "r"), &std::fclose); + if (!fp.get()) { + return; + } + std::fseek(fp.get(), 0, SEEK_END); + long filesize = std::ftell(fp.get()); + std::fseek(fp.get(), 0, SEEK_SET); + expectedContent.reset(new char[filesize + 1]); + fread(expectedContent.get(), filesize, 1, fp.get()); + expectedContent[filesize] = '\0'; + } + + void Init() { + // init pipeline and config + unique_ptr configJson; + string configStr, errorMsg; + unique_ptr config; + unique_ptr pipeline; + list::iterator que; + + // new pipeline + configStr = R"( + { + "global": { + "ProcessPriority": 1 + }, + "inputs": [ + { + "Type": "input_file", + "FilePaths": [ + ")" + + logPathDir + R"(/utf8.txt" + ] + } + ], + "flushers": [ + { + "Type": "flusher_sls", + "Project": "test_project", + "Logstore": "test_logstore", + "Region": "test_region", + "Endpoint": "test_endpoint" + } + ] + } + )"; + configJson.reset(new Json::Value()); + APSARA_TEST_TRUE(ParseJsonTable(configStr, *configJson, errorMsg)); + Json::Value inputConfigJson = (*configJson)["inputs"][0]; + + config.reset(new Config(mConfigName, std::move(configJson))); + APSARA_TEST_TRUE(config->Parse()); + pipeline.reset(new Pipeline()); + APSARA_TEST_TRUE(pipeline->Init(std::move(*config))); + ctx.SetPipeline(*pipeline.get()); + ctx.SetConfigName(mConfigName); + ctx.SetProcessQueueKey(0); + + discoveryOpts = FileDiscoveryOptions(); + discoveryOpts.Init(inputConfigJson, ctx, "test"); + mConfig = std::make_pair(&discoveryOpts, &ctx); + readerOpts.mInputType = FileReaderOptions::InputType::InputFile; + + FileServer::GetInstance()->AddFileDiscoveryConfig(mConfigName, &discoveryOpts, &ctx); + FileServer::GetInstance()->AddFileReaderConfig(mConfigName, &readerOpts, &ctx); + FileServer::GetInstance()->AddMultilineConfig(mConfigName, &multilineOpts, &ctx); + ProcessQueueManager::GetInstance()->CreateOrUpdateQueue(0, 0); + } + + void TearDown() override { remove(utf8File.c_str()); } + +private: + std::unique_ptr expectedContent; + static std::string logPathDir; + static std::string utf8File; + const std::string mConfigName = "##1.0##project-0$config-0"; + + FileDiscoveryOptions discoveryOpts; + FileReaderOptions readerOpts; + MultilineOptions multilineOpts; + PipelineContext ctx; + FileDiscoveryConfig mConfig; +}; + +std::string ForceReadUnittest::logPathDir; +std::string ForceReadUnittest::utf8File; + +void ForceReadUnittest::TestTimeoutForceRead() { + { + // read -> add timeout event -> handle timeout -> valid -> read empty -> not rollback + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + std::unique_ptr item; + std::string configName; + bool result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent1 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart1(expectedContent.get()); + expectedPart1.resize(expectedPart1.rfind("\n")); + APSARA_TEST_STREQ_FATAL(sourceEvent1.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart1.c_str()); + + Event e2 = *pHanlder->mNameReaderMap[utf8File][0]->CreateFlushTimeoutEvent().get(); + pHanlder->Handle(e2); + result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent2 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart2(expectedContent.get()); + expectedPart2 = expectedPart2.substr(expectedPart2.rfind("\n") + 1); + APSARA_TEST_STREQ_FATAL(sourceEvent2.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart2.c_str()); + } + { + // read -> write -> add timeout event -> handle timeout -> valid -> read not empty -> rollback + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + + std::string expectedPart1(expectedContent.get()); + expectedPart1.resize(expectedPart1.find("\n")); + LogFileReader::BUFFER_SIZE = expectedPart1.size() + 1; + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + std::unique_ptr item; + std::string configName; + bool result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent1 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + APSARA_TEST_STREQ_FATAL(sourceEvent1.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart1.c_str()); + + Event e2 = *pHanlder->mNameReaderMap[utf8File][0]->CreateFlushTimeoutEvent().get(); + pHanlder->Handle(e2); + result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent2 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart2(expectedContent.get()); + // should rollback + expectedPart2 + = expectedPart2.substr(expectedPart2.find("\n") + 1, expectedPart2.rfind("\n") - expectedPart1.size() - 1); + APSARA_TEST_STREQ_FATAL(sourceEvent2.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart2.c_str()); + } + { + // read -> add timeout event -> write -> read -> handle timeout -> event invalid + LOG_WARNING(sLogger, ("This case is difficult to test", "test")); + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + + std::string expectedPart1(expectedContent.get()); + expectedPart1.resize(expectedPart1.find("\n")); + LogFileReader::BUFFER_SIZE = expectedPart1.size() + 1; + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + std::unique_ptr item; + std::string configName; + bool result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent1 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + APSARA_TEST_STREQ_FATAL(sourceEvent1.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart1.c_str()); + + LogFileReader::BUFFER_SIZE = 1024 * 512; + Event e3 = *pHanlder->mNameReaderMap[utf8File][0]->CreateFlushTimeoutEvent().get(); + + Event e2 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e2); + result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent2 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart2(expectedContent.get()); + // should rollback + expectedPart2 + = expectedPart2.substr(expectedPart2.find("\n") + 1, expectedPart2.rfind("\n") - expectedPart1.size() - 1); + APSARA_TEST_STREQ_FATAL(sourceEvent2.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart2.c_str()); + + pHanlder->Handle(e3); + // Current timeout event is invalid + result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_FALSE_FATAL(result); + } + { + // TODO: difficult to test, the behavior should be + // read -> add timeout event -> handle timeout -> write -> event valid -> read not empty -> rollback + } + { + // TODO: difficult to test, the behavior should be + // read -> add timeout event -> handle timeout -> event valid -> write -> read not empty -> rollback + } + { + // TODO: difficult to test, the behavior should be + // read -> add timeout event -> handle timeout -> event valid -> read empty -> write -> not rollback + } + { + // TODO: difficult to test, the behavior should be + // read -> add timeout event -> handle timeout -> event valid -> read empty -> not rollback -> write + } +} + +void ForceReadUnittest::TestFileCloseForceRead() { + { + // file close -> handle timeout -> valid -> not rollback + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + LogFileReader::BUFFER_SIZE = 1024 * 512; + + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + std::unique_ptr item; + std::string configName; + bool result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent1 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart1(expectedContent.get()); + expectedPart1.resize(expectedPart1.rfind("\n")); + APSARA_TEST_STREQ_FATAL(sourceEvent1.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart1.c_str()); + + Event e2 = *pHanlder->mNameReaderMap[utf8File][0]->CreateFlushTimeoutEvent().get(); + pHanlder->mNameReaderMap[utf8File][0]->CloseFilePtr(); + + pHanlder->Handle(e2); + result = ProcessQueueManager::GetInstance()->PopItem(1, item, configName); + APSARA_TEST_TRUE_FATAL(result); + LogEvent& sourceEvent2 = item.get()->mEventGroup.MutableEvents()[0].Cast(); + std::string expectedPart2(expectedContent.get()); + expectedPart2 = expectedPart2.substr(expectedPart2.rfind("\n") + 1); + APSARA_TEST_STREQ_FATAL(sourceEvent2.GetContent(DEFAULT_CONTENT_KEY).data(), expectedPart2.c_str()); + } +} + +void ForceReadUnittest::TestAddTimeoutEvent() { + { + // read part -> not add timeout event + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + LogFileReader::BUFFER_SIZE = 10; + BlockedEventManager::GetInstance()->mBlockEventMap.clear(); + APSARA_TEST_EQUAL_FATAL(BlockedEventManager::GetInstance()->mBlockEventMap.size(), 0); + + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + APSARA_TEST_EQUAL_FATAL(BlockedEventManager::GetInstance()->mBlockEventMap.size(), 0); + } + { + // read all -> add timeout event + Init(); + LogFileReader reader( + logPathDir, utf8File, DevInode(), std::make_pair(&readerOpts, &ctx), std::make_pair(&multilineOpts, &ctx)); + reader.UpdateReaderManual(); + reader.InitReader(true, LogFileReader::BACKWARD_TO_BEGINNING); + reader.CheckFileSignatureAndOffset(true); + LogFileReader::BUFFER_SIZE = 1024 * 512; + BlockedEventManager::GetInstance()->mBlockEventMap.clear(); + APSARA_TEST_EQUAL_FATAL(BlockedEventManager::GetInstance()->mBlockEventMap.size(), 0); + + ModifyHandler* pHanlder = new ModifyHandler(mConfigName, mConfig); + pHanlder->mReadFileTimeSlice = 0; // force one read for one event + + Event e1 = Event(reader.mHostLogPathDir, + reader.mHostLogPathFile, + EVENT_MODIFY, + -1, + 0, + reader.mDevInode.dev, + reader.mDevInode.inode); + pHanlder->Handle(e1); + APSARA_TEST_EQUAL_FATAL(BlockedEventManager::GetInstance()->mBlockEventMap.size(), 1); + } +} + +UNIT_TEST_CASE(ForceReadUnittest, TestTimeoutForceRead) +UNIT_TEST_CASE(ForceReadUnittest, TestFileCloseForceRead) +UNIT_TEST_CASE(ForceReadUnittest, TestAddTimeoutEvent) + +} // namespace logtail + +UNIT_TEST_MAIN \ No newline at end of file diff --git a/core/unittest/reader/LogFileReaderUnittest.cpp b/core/unittest/reader/LogFileReaderUnittest.cpp index a37df6956e..5c0947f305 100644 --- a/core/unittest/reader/LogFileReaderUnittest.cpp +++ b/core/unittest/reader/LogFileReaderUnittest.cpp @@ -260,9 +260,11 @@ void LogFileReaderUnittest::TestReadGBK() { expectedPart.resize(firstReadSize); reader.ReadGBK(logBuffer, 127, moreData, false); // first line without \n APSARA_TEST_FALSE_FATAL(moreData); - APSARA_TEST_STREQ_FATAL(expectedPart.c_str(), logBuffer.rawBuffer.data()); - APSARA_TEST_EQUAL_FATAL(reader.mCache.size(), 0UL); + APSARA_TEST_FALSE_FATAL(reader.mLastForceRead); + reader.ReadGBK(logBuffer, 127, moreData, false); // force read, clear cache APSARA_TEST_TRUE_FATAL(reader.mLastForceRead); + APSARA_TEST_EQUAL_FATAL(reader.mCache.size(), 0UL); + APSARA_TEST_STREQ_FATAL(expectedPart.c_str(), logBuffer.rawBuffer.data()); // second read, start with \n but with other lines reader.ReadGBK(logBuffer, fileSize - 1, moreData); @@ -455,9 +457,11 @@ void LogFileReaderUnittest::TestReadUTF8() { reader.mLastForceRead = true; reader.ReadUTF8(logBuffer, firstReadSize, moreData, false); APSARA_TEST_FALSE_FATAL(moreData); - APSARA_TEST_STREQ_FATAL(expectedPart.c_str(), logBuffer.rawBuffer.data()); - APSARA_TEST_EQUAL_FATAL(reader.mCache.size(), 0UL); + APSARA_TEST_FALSE_FATAL(reader.mLastForceRead); + reader.ReadUTF8(logBuffer, firstReadSize, moreData, false); // force read, clear cache APSARA_TEST_TRUE_FATAL(reader.mLastForceRead); + APSARA_TEST_EQUAL_FATAL(reader.mCache.size(), 0UL); + APSARA_TEST_STREQ_FATAL(expectedPart.c_str(), logBuffer.rawBuffer.data()); // second read, start with \n but with other lines reader.ReadUTF8(logBuffer, fileSize - 1, moreData); diff --git a/core/unittest/reader/testDataSet/ForceReadUnittest/utf8.txt b/core/unittest/reader/testDataSet/ForceReadUnittest/utf8.txt new file mode 100644 index 0000000000..c3b32cbb55 --- /dev/null +++ b/core/unittest/reader/testDataSet/ForceReadUnittest/utf8.txt @@ -0,0 +1,3 @@ +iLogtail 为可观测场景而生,拥有的轻量级、高性能、自动化配置等诸多生产级别特性,在阿里巴巴以及外部数万家阿里云客户内部广泛应用。 +你可以将它部署于物理机,虚拟机,Kubernetes等多种环境中来采集遥测数据,例如logs、traces和metrics。 +iLogtail 支持收集多种遥测数据并将其传输到多种不同的后端,例如 SLS 可观测平台。 \ No newline at end of file diff --git a/docs/cn/plugins/input/service-canal.md b/docs/cn/plugins/input/service-canal.md index ae407b6b60..38aca4446b 100644 --- a/docs/cn/plugins/input/service-canal.md +++ b/docs/cn/plugins/input/service-canal.md @@ -31,6 +31,7 @@ | TextToString | Boolean,`false` | 是否将text类型的数据转换成字符串。不设置时,默认为false,表示不转换。| | PackValues | Boolean,`false` | 是否将事件数据打包成JSON格式。默认为false,表示不打包。如果设置为true,Logtail会将事件数据以JSON格式集中打包到data和old_data两个字段中,其中old_data仅在row_update事件中有意义。 示例:假设数据表有三列数据c1,c2,c3,设置为false,row_insert事件数据中会有c1,c2,c3三个字段,而设置为true时,c1,c2,c3会被统一打包为data字段,值为`{"c1":"...", "c2": "...", "c3": "..."}`。| | EnableEventMeta | Boolean,`false` | 是否采集事件的元数据,默认为false,表示不采集。 Binlog事件的元数据包括event_time、event_log_position、event_size和event_server_id。| +| UseDecimal | Boolean,`false` | Binlog解析DECIMAL类型时,是否保持原格式输出,而不是使用科学计数法。如果未设置,系统默认为false,即默认使用科学计数法。| ## 样例 diff --git a/pkg/helper/container_discover_controller.go b/pkg/helper/container_discover_controller.go index aabdf4131f..c06eb72947 100644 --- a/pkg/helper/container_discover_controller.go +++ b/pkg/helper/container_discover_controller.go @@ -45,11 +45,11 @@ type ContainerDiscoverManager struct { fetchOneLock sync.Mutex } -func NewContainerDiscoverManager(enableDockerDiscover, enableCRIDiscover, enableStaticDiscover bool) *ContainerDiscoverManager { +func NewContainerDiscoverManager() *ContainerDiscoverManager { return &ContainerDiscoverManager{ - enableDockerDiscover: enableDockerDiscover, - enableCRIDiscover: enableCRIDiscover, - enableStaticDiscover: enableStaticDiscover, + enableDockerDiscover: false, + enableCRIDiscover: false, + enableStaticDiscover: false, } } @@ -125,7 +125,7 @@ func (c *ContainerDiscoverManager) fetchCRI() error { return criRuntimeWrapper.fetchAll() } -func (c *ContainerDiscoverManager) SyncContainers() { +func (c *ContainerDiscoverManager) StartSyncContainers() { if c.enableCRIDiscover { logger.Debug(context.Background(), "discover manager start sync containers goroutine", "cri") go criRuntimeWrapper.loopSyncContainers() @@ -159,8 +159,42 @@ func (c *ContainerDiscoverManager) LogAlarm(err error, msg string) { } } -func (c *ContainerDiscoverManager) Init(initTryTimes int) { +func (c *ContainerDiscoverManager) Init() bool { defer dockerCenterRecover() + + // discover which runtime is valid + if IsCRIRuntimeValid(containerdUnixSocket) { + var err error + criRuntimeWrapper, err = NewCRIRuntimeWrapper(dockerCenterInstance) + if err != nil { + logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "[CRIRuntime] creare cri-runtime client error: %v", err) + criRuntimeWrapper = nil + } else { + logger.Infof(context.Background(), "[CRIRuntime] create cri-runtime client successfully") + } + } + if ok, err := util.PathExists(DefaultLogtailMountPath); err == nil { + if !ok { + logger.Info(context.Background(), "no docker mount path", "set empty") + DefaultLogtailMountPath = "" + } + } else { + logger.Warning(context.Background(), "check docker mount path error", err.Error()) + } + c.enableCRIDiscover = criRuntimeWrapper != nil + c.enableDockerDiscover = dockerCenterInstance.initClient() == nil + c.enableStaticDiscover = isStaticContainerInfoEnabled() + discoverdRuntime := false + if len(os.Getenv("USE_CONTAINERD")) > 0 { + discoverdRuntime = c.enableCRIDiscover + } else { + discoverdRuntime = c.enableCRIDiscover || c.enableDockerDiscover || c.enableStaticDiscover + } + if !discoverdRuntime { + return false + } + + // try to connect to runtime logger.Info(context.Background(), "input", "param", "docker discover", c.enableDockerDiscover, "cri discover", c.enableCRIDiscover, "static discover", c.enableStaticDiscover) listenLoopIntervalSec := 0 // Get env in the same order as in C Logtail @@ -222,31 +256,22 @@ func (c *ContainerDiscoverManager) Init(initTryTimes int) { var err error if c.enableDockerDiscover { - for i := 0; i < initTryTimes; i++ { - if err = c.fetchDocker(); err == nil { - break - } - } - if err != nil { + if err = c.fetchDocker(); err != nil { c.enableDockerDiscover = false - logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "fetch docker containers error in %d times, close docker discover", initTryTimes) + logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "fetch docker containers error, close docker discover, will retry") } } if c.enableCRIDiscover { - for i := 0; i < initTryTimes; i++ { - if err = c.fetchCRI(); err == nil { - break - } - } - if err != nil { + if err = c.fetchCRI(); err != nil { c.enableCRIDiscover = false - logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "fetch cri containers error in %d times, close cri discover", initTryTimes) + logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "fetch cri containers error, close cri discover, will retry") } } if c.enableStaticDiscover { c.fetchStatic() } logger.Info(context.Background(), "final", "param", "docker discover", c.enableDockerDiscover, "cri discover", c.enableCRIDiscover, "static discover", c.enableStaticDiscover) + return c.enableCRIDiscover || c.enableDockerDiscover || c.enableStaticDiscover } func (c *ContainerDiscoverManager) TimerFetch() { diff --git a/pkg/helper/docker_center.go b/pkg/helper/docker_center.go index 8fd4db31e9..8e117dd1b4 100644 --- a/pkg/helper/docker_center.go +++ b/pkg/helper/docker_center.go @@ -632,39 +632,24 @@ func getDockerCenterInstance() *DockerCenter { dockerCenterInstance = &DockerCenter{} dockerCenterInstance.imageCache = make(map[string]string) dockerCenterInstance.containerMap = make(map[string]*DockerInfoDetail) - if IsCRIRuntimeValid(containerdUnixSocket) { - retryTimes := 10 - for i := 0; i < retryTimes; i++ { - var err error - criRuntimeWrapper, err = NewCRIRuntimeWrapper(dockerCenterInstance) - if err != nil { - logger.Errorf(context.Background(), "DOCKER_CENTER_ALARM", "[CRIRuntime] creare cri-runtime client error: %v", err) - criRuntimeWrapper = nil - } else { - logger.Infof(context.Background(), "[CRIRuntime] create cri-runtime client successfully") + // containerFindingManager works in a producer-consumer model + // so even manager is not initialized, it will not affect consumers like service_stdout + go func() { + retryCount := 0 + containerFindingManager = NewContainerDiscoverManager() + for { + if containerFindingManager.Init() { break } - time.Sleep(time.Second * 1) - if i == retryTimes-1 { - logger.Error(context.Background(), "DOCKER_CENTER_ALARM", "[CRIRuntime] create cri-runtime client failed") + if retryCount%10 == 0 { + logger.Error(context.Background(), "DOCKER_CENTER_ALARM", "docker center init failed", "retry count", retryCount) } + retryCount++ + time.Sleep(time.Second * 1) } - } - if ok, err := util.PathExists(DefaultLogtailMountPath); err == nil { - if !ok { - logger.Info(context.Background(), "no docker mount path", "set empty") - DefaultLogtailMountPath = "" - } - } else { - logger.Warning(context.Background(), "check docker mount path error", err.Error()) - } - var enableCriFinding = criRuntimeWrapper != nil - var enableDocker = dockerCenterInstance.initClient() == nil - var enableStatic = isStaticContainerInfoEnabled() - containerFindingManager = NewContainerDiscoverManager(enableDocker, enableCriFinding, enableStatic) - containerFindingManager.Init(3) - containerFindingManager.TimerFetch() - containerFindingManager.SyncContainers() + containerFindingManager.TimerFetch() + containerFindingManager.StartSyncContainers() + }() }) return dockerCenterInstance } diff --git a/pkg/helper/docker_cri_adapter.go b/pkg/helper/docker_cri_adapter.go index d6ede0462c..00c96f476c 100644 --- a/pkg/helper/docker_cri_adapter.go +++ b/pkg/helper/docker_cri_adapter.go @@ -76,10 +76,6 @@ type CRIRuntimeWrapper struct { } func IsCRIRuntimeValid(criRuntimeEndpoint string) bool { - if len(os.Getenv("USE_CONTAINERD")) > 0 { - return true - } - // Verify containerd.sock cri valid. if fi, err := os.Stat(criRuntimeEndpoint); err == nil && !fi.IsDir() { if IsCRIStatusValid(criRuntimeEndpoint) { @@ -105,30 +101,17 @@ func IsCRIStatusValid(criRuntimeEndpoint string) bool { client := cri.NewRuntimeServiceClient(conn) // check cri status - for tryCount := 0; tryCount < 5; tryCount++ { - _, err = client.Status(ctx, &cri.StatusRequest{}) - if err == nil { - break - } - if strings.Contains(err.Error(), "code = Unimplemented") { - logger.Debug(context.Background(), "Status failed", err) - return false - } - time.Sleep(time.Millisecond * 100) - } + _, err = client.Status(ctx, &cri.StatusRequest{}) if err != nil { logger.Debug(context.Background(), "Status failed", err) return false } // check running containers - for tryCount := 0; tryCount < 5; tryCount++ { - var containersResp *cri.ListContainersResponse - containersResp, err = client.ListContainers(ctx, &cri.ListContainersRequest{Filter: nil}) - if err == nil { - logger.Debug(context.Background(), "ListContainers result", containersResp.Containers) - return containersResp.Containers != nil - } - time.Sleep(time.Millisecond * 100) + var containersResp *cri.ListContainersResponse + containersResp, err = client.ListContainers(ctx, &cri.ListContainersRequest{Filter: nil}) + if err == nil { + logger.Debug(context.Background(), "ListContainers result", containersResp.Containers) + return containersResp.Containers != nil } logger.Debug(context.Background(), "ListContainers failed", err) return false diff --git a/pkg/helper/mount_others.go b/pkg/helper/mount_others.go index b3596d815f..ace958683d 100644 --- a/pkg/helper/mount_others.go +++ b/pkg/helper/mount_others.go @@ -35,6 +35,10 @@ func GetMountedFilePathWithBasePath(basePath, logPath string) string { func TryGetRealPath(path string) (string, fs.FileInfo) { sepLen := len(string(os.PathSeparator)) + if len(path) < sepLen { + return "", nil + } + index := 0 // assume path is absolute for i := 0; i < 10; i++ { if f, err := os.Stat(path); err == nil { diff --git a/plugins/input/canal/input_canal.go b/plugins/input/canal/input_canal.go index 55969667e2..710baa345d 100644 --- a/plugins/input/canal/input_canal.go +++ b/plugins/input/canal/input_canal.go @@ -141,6 +141,8 @@ type ServiceCanal struct { Charset string // Pack values into two fields: new_data and old_data. False by default. PackValues bool + // True时 binlog解析DECIMAL输出原格式而非科学计数法, like:https://github.com/go-mysql-org/go-mysql/blob/6c99b4bff931a5aced0978b78aadb5867afcdcd3/canal/dump.go#L85 + UseDecimal bool shutdown chan struct{} waitGroup sync.WaitGroup @@ -189,6 +191,7 @@ func (sc *ServiceCanal) Init(context pipeline.Context) (int, error) { sc.config.DiscardNoMetaRowEvent = true sc.config.IncludeTableRegex = sc.IncludeTables sc.config.ExcludeTableRegex = sc.ExcludeTables + sc.config.UseDecimal = sc.UseDecimal sc.lastErrorChan = make(chan error, 1)