core/models/SpanEvent.cpp (405 lines of code) (raw):

/* * 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 "models/SpanEvent.h" #include "constants/SpanConstants.h" using namespace std; namespace logtail { void SpanEvent::SpanLink::SetTraceId(const string& traceId) { const StringBuffer& b = GetSourceBuffer()->CopyString(traceId); mTraceId = StringView(b.data, b.size); } void SpanEvent::SpanLink::SetSpanId(const string& spanId) { const StringBuffer& b = GetSourceBuffer()->CopyString(spanId); mSpanId = StringView(b.data, b.size); } void SpanEvent::SpanLink::SetTraceState(const string& traceState) { const StringBuffer& b = GetSourceBuffer()->CopyString(traceState); mTraceState = StringView(b.data, b.size); } StringView SpanEvent::SpanLink::GetTag(StringView key) const { auto it = mTags.mInner.find(key); if (it != mTags.mInner.end()) { return it->second; } return gEmptyStringView; } bool SpanEvent::SpanLink::HasTag(StringView key) const { return mTags.mInner.find(key) != mTags.mInner.end(); } void SpanEvent::SpanLink::SetTag(StringView key, StringView val) { SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); } void SpanEvent::SpanLink::SetTag(const string& key, const string& val) { SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); } void SpanEvent::SpanLink::SetTagNoCopy(const StringBuffer& key, const StringBuffer& val) { SetTagNoCopy(StringView(key.data, key.size), StringView(val.data, val.size)); } void SpanEvent::SpanLink::SetTagNoCopy(StringView key, StringView val) { mTags.Insert(key, val); } void SpanEvent::SpanLink::DelTag(StringView key) { mTags.Erase(key); } shared_ptr<SourceBuffer>& SpanEvent::SpanLink::GetSourceBuffer() { return mParent->GetSourceBuffer(); } size_t SpanEvent::SpanLink::DataSize() const { return mTraceId.size() + mSpanId.size() + mTraceState.size() + mTags.DataSize(); } Json::Value SpanEvent::SpanLink::ToJson() const { Json::Value root; root[DEFAULT_TRACE_TAG_TRACE_ID] = mTraceId.to_string(); root[DEFAULT_TRACE_TAG_SPAN_ID] = mSpanId.to_string(); if (!mTraceState.empty()) { root[DEFAULT_TRACE_TAG_TRACE_STATE] = mTraceState.to_string(); } if (!mTags.mInner.empty()) { Json::Value& tags = root[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& tag : mTags.mInner) { tags[tag.first.to_string()] = tag.second.to_string(); } } return root; } #ifdef APSARA_UNIT_TEST_MAIN void SpanEvent::SpanLink::FromJson(const Json::Value& value) { SetTraceId(value[DEFAULT_TRACE_TAG_TRACE_ID].asString()); SetSpanId(value[DEFAULT_TRACE_TAG_SPAN_ID].asString()); if (value.isMember(DEFAULT_TRACE_TAG_TRACE_STATE)) { string s = value[DEFAULT_TRACE_TAG_TRACE_STATE].asString(); if (!s.empty()) { SetTraceState(s); } } if (value.isMember(DEFAULT_TRACE_TAG_ATTRIBUTES)) { Json::Value tags = value[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& key : tags.getMemberNames()) { SetTag(key, tags[key].asString()); } } } #endif void SpanEvent::InnerEvent::SetName(const string& name) { const StringBuffer& b = GetSourceBuffer()->CopyString(name); mName = StringView(b.data, b.size); } StringView SpanEvent::InnerEvent::GetTag(StringView key) const { auto it = mTags.mInner.find(key); if (it != mTags.mInner.end()) { return it->second; } return gEmptyStringView; } bool SpanEvent::InnerEvent::HasTag(StringView key) const { return mTags.mInner.find(key) != mTags.mInner.end(); } void SpanEvent::InnerEvent::SetTag(StringView key, StringView val) { SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); } void SpanEvent::InnerEvent::SetTag(const string& key, const string& val) { SetTagNoCopy(GetSourceBuffer()->CopyString(key), GetSourceBuffer()->CopyString(val)); } void SpanEvent::InnerEvent::SetTagNoCopy(const StringBuffer& key, const StringBuffer& val) { SetTagNoCopy(StringView(key.data, key.size), StringView(val.data, val.size)); } void SpanEvent::InnerEvent::SetTagNoCopy(StringView key, StringView val) { mTags.Insert(key, val); } void SpanEvent::InnerEvent::DelTag(StringView key) { mTags.Erase(key); } shared_ptr<SourceBuffer>& SpanEvent::InnerEvent::GetSourceBuffer() { return mParent->GetSourceBuffer(); } size_t SpanEvent::InnerEvent::DataSize() const { return sizeof(decltype(mTimestampNs)) + mName.size() + mTags.DataSize(); } Json::Value SpanEvent::InnerEvent::ToJson() const { Json::Value root; root[DEFAULT_TRACE_TAG_SPAN_EVENT_NAME] = mName.to_string(); root[DEFAULT_TRACE_TAG_TIMESTAMP] = static_cast<int64_t>(mTimestampNs); if (!mTags.mInner.empty()) { Json::Value& tags = root[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& tag : mTags.mInner) { tags[tag.first.to_string()] = tag.second.to_string(); } } return root; } #ifdef APSARA_UNIT_TEST_MAIN void SpanEvent::InnerEvent::FromJson(const Json::Value& value) { SetName(value[DEFAULT_TRACE_TAG_SPAN_EVENT_NAME].asString()); SetTimestampNs(value[DEFAULT_TRACE_TAG_TIMESTAMP].asUInt64()); if (value.isMember(DEFAULT_TRACE_TAG_ATTRIBUTES)) { Json::Value tags = value[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& key : tags.getMemberNames()) { SetTag(key, tags[key].asString()); } } } #endif const string SpanEvent::OTLP_SCOPE_NAME = "otlp.scope.name"; const string SpanEvent::OTLP_SCOPE_VERSION = "otlp.scope.version"; SpanEvent::SpanEvent(PipelineEventGroup* ptr) : PipelineEvent(Type::SPAN, ptr) { } unique_ptr<PipelineEvent> SpanEvent::Copy() const { return make_unique<SpanEvent>(*this); } void SpanEvent::Reset() { PipelineEvent::Reset(); mTraceId = gEmptyStringView; mSpanId = gEmptyStringView; mTraceState = gEmptyStringView; mParentSpanId = gEmptyStringView; mName = gEmptyStringView; mKind = Kind::Unspecified; mStartTimeNs = 0; mEndTimeNs = 0; mTags.Clear(); mEvents.clear(); mLinks.clear(); mStatus = StatusCode::Unset; mScopeTags.Clear(); } 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 Json::Value SpanEvent::ToJson(bool enableEventMeta) const { Json::Value root; root["type"] = static_cast<int>(GetType()); root["timestamp"] = GetTimestamp(); if (GetTimestampNanosecond()) { root["timestampNanosecond"] = static_cast<int32_t>(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<int>(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<int64_t>(mStartTimeNs); root["endTimeNs"] = static_cast<int64_t>(mEndTimeNs); if (!mTags.mInner.empty()) { Json::Value& tags = root[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& tag : mTags.mInner) { tags[tag.first.to_string()] = tag.second.to_string(); } } if (!mEvents.empty()) { Json::Value& events = root[DEFAULT_TRACE_TAG_EVENTS]; for (const auto& event : mEvents) { events.append(event.ToJson()); } } if (!mLinks.empty()) { Json::Value& links = root[DEFAULT_TRACE_TAG_LINKS]; for (const auto& link : mLinks) { links.append(link.ToJson()); } } if (mStatus != StatusCode::Unset) { root["status"] = static_cast<int>(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; } bool SpanEvent::FromJson(const Json::Value& root) { if (root.isMember("timestampNanosecond")) { SetTimestamp(root["timestamp"].asInt64(), root["timestampNanosecond"].asInt64()); } else { SetTimestamp(root["timestamp"].asInt64()); } SetTraceId(root["traceId"].asString()); SetSpanId(root["spanId"].asString()); if (root.isMember("traceState")) { string s = root["traceState"].asString(); if (!s.empty()) { SetTraceState(s); } } if (root.isMember("parentSpanId")) { string s = root["parentSpanId"].asString(); if (!s.empty()) { SetParentSpanId(s); } } SetName(root["name"].asString()); if (root.isMember("kind")) { SetKind(static_cast<Kind>(root["kind"].asInt())); } SetStartTimeNs(root["startTimeNs"].asUInt64()); SetEndTimeNs(root["endTimeNs"].asUInt64()); if (root.isMember(DEFAULT_TRACE_TAG_ATTRIBUTES)) { Json::Value tags = root[DEFAULT_TRACE_TAG_ATTRIBUTES]; for (const auto& key : tags.getMemberNames()) { SetTag(key, tags[key].asString()); } } if (root.isMember(DEFAULT_TRACE_TAG_EVENTS)) { Json::Value events = root[DEFAULT_TRACE_TAG_EVENTS]; for (const auto& event : events) { InnerEvent* e = AddEvent(); e->FromJson(event); } } if (root.isMember(DEFAULT_TRACE_TAG_LINKS)) { Json::Value links = root[DEFAULT_TRACE_TAG_LINKS]; for (const auto& link : links) { SpanLink* l = AddLink(); l->FromJson(link); } } if (root.isMember("status")) { SetStatus(static_cast<StatusCode>(root["status"].asInt())); } if (root.isMember("scopeTags")) { Json::Value tags = root["scopeTags"]; for (const auto& key : tags.getMemberNames()) { SetScopeTag(key, tags[key].asString()); } } return true; } #endif const static std::string sSpanStatusCodeUnSet = "UNSET"; const static std::string sSpanStatusCodeOk = "OK"; const static std::string sSpanStatusCodeError = "ERROR"; const std::string& GetStatusString(SpanEvent::StatusCode status) { switch (status) { case SpanEvent::StatusCode::Unset: return sSpanStatusCodeUnSet; case SpanEvent::StatusCode::Ok: return sSpanStatusCodeOk; case SpanEvent::StatusCode::Error: return sSpanStatusCodeError; default: return sSpanStatusCodeUnSet; } } const static std::string sSpanKindUnspecified = "unspecified"; const static std::string sSpanKindInternal = "internal"; const static std::string sSpanKindServer = "server"; const static std::string sSpanKindClient = "client"; const static std::string sSpanKindProducer = "producer"; const static std::string sSpanKindConsumer = "consumer"; const static std::string sSpanKindUnknown = "unknown"; const std::string& GetKindString(SpanEvent::Kind kind) { switch (kind) { case SpanEvent::Kind::Unspecified: return sSpanKindUnspecified; case SpanEvent::Kind::Internal: return sSpanKindInternal; case SpanEvent::Kind::Server: return sSpanKindServer; case SpanEvent::Kind::Client: return sSpanKindClient; case SpanEvent::Kind::Producer: return sSpanKindProducer; case SpanEvent::Kind::Consumer: return sSpanKindConsumer; default: return sSpanKindUnknown; } } } // namespace logtail