diff --git a/dali/ft/filecopy.cpp b/dali/ft/filecopy.cpp index d93ade88777..c92f42fbd86 100644 --- a/dali/ft/filecopy.cpp +++ b/dali/ft/filecopy.cpp @@ -3573,17 +3573,30 @@ void FileSprayer::updateTargetProperties() } // Update @writeCost and @numWrites in subfile properties and update totalWriteCost - if (superTgt) + if (superTgt && superTgt->numSubFiles() > 0) { if (cur.whichOutput != (unsigned)-1) { - unsigned targetPartNum = targets.item(cur.whichOutput).partNum; - IDistributedFile &subfile = superTgt->querySubFile(targetPartNum, true); - DistributedFilePropertyLock lock(&subfile); + IDistributedFile *subfile; + if (superTgt->numSubFiles() > 1) + { + Owned fDesc = superTgt->getFileDescriptor(); + ISuperFileDescriptor *superFDesc = fDesc->querySuperFileDescriptor(); + unsigned subfileNum, subFilePartNum; + superFDesc->mapSubPart(targets.item(cur.whichOutput).partNum, subfileNum, subFilePartNum); + subfile = superTgt->querySubPart(subfileNum, subFilePartNum); + } + else + { + // If there is a single subfile, it is not necessary to map part to subfile + // (also, querySuperFileDescriptor return nullptr if num subfile == 1) + subfile = &superTgt->querySubFile(0); + } + DistributedFilePropertyLock lock(subfile); IPropertyTree &subFileProps = lock.queryAttributes(); cost_type prevNumWrites = subFileProps.getPropInt64(getDFUQResultFieldName(DFUQRFnumDiskWrites)); cost_type prevWriteCost = subFileProps.getPropInt64(getDFUQResultFieldName(DFUQRFwriteCost)); - cost_type curWriteCost = calcFileAccessCost(&subfile, curProgress.numWrites, 0); + cost_type curWriteCost = calcFileAccessCost(subfile, curProgress.numWrites, 0); subFileProps.setPropInt64(getDFUQResultFieldName(DFUQRFwriteCost), prevWriteCost + curWriteCost); subFileProps.setPropInt64(getDFUQResultFieldName(DFUQRFnumDiskWrites), prevNumWrites + curProgress.numWrites); totalWriteCost += curWriteCost; @@ -3786,23 +3799,35 @@ void FileSprayer::updateTargetProperties() if (distributedSource) { IDistributedSuperFile * superSrc = distributedSource->querySuperFile(); - if (superSrc) + if (superSrc && superSrc->numSubFiles() > 0) { + Owned fDesc = superSrc->getFileDescriptor(); + ISuperFileDescriptor *superFDesc = fDesc->querySuperFileDescriptor(); ForEachItemIn(idx, partition) { PartitionPoint & cur = partition.item(idx); OutputProgress & curProgress = progress.item(idx); - if (cur.whichInput != (unsigned)-1) { - unsigned sourcePartNum = sources.item(cur.whichInput).partNum; - IDistributedFile &subfile = superSrc->querySubFile(sourcePartNum, true); - DistributedFilePropertyLock lock(&subfile); + IDistributedFile *subfile; + if (superFDesc) + { + unsigned subfileNum, subFilePartNum; + superFDesc->mapSubPart(sources.item(cur.whichInput).partNum, subfileNum, subFilePartNum); + subfile = superSrc->querySubPart(subfileNum, subFilePartNum); + } + else + { + // superFDesc==nullptr if there is a single file + // so query the first (and only) subfile + subfile = &superSrc->querySubFile(0); + } + DistributedFilePropertyLock lock(subfile); IPropertyTree &subFileProps = lock.queryAttributes(); stat_type prevNumReads = subFileProps.getPropInt64(getDFUQResultFieldName(DFUQRFnumDiskReads), 0); - cost_type legacyReadCost = getLegacyReadCost(subfile.queryAttributes(), &subfile); + cost_type legacyReadCost = getLegacyReadCost(subfile->queryAttributes(), subfile); cost_type prevReadCost = subFileProps.getPropInt64(getDFUQResultFieldName(DFUQRFreadCost), 0); - cost_type curReadCost = calcFileAccessCost(&subfile, 0, curProgress.numReads); + cost_type curReadCost = calcFileAccessCost(subfile, 0, curProgress.numReads); subFileProps.setPropInt64(getDFUQResultFieldName(DFUQRFnumDiskReads), prevNumReads + curProgress.numReads); subFileProps.setPropInt64(getDFUQResultFieldName(DFUQRFreadCost), legacyReadCost + prevReadCost + curReadCost); totalReadCost += curReadCost; diff --git a/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview.xml b/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview.xml index 6cace2d5391..d9ef060c461 100644 --- a/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview.xml +++ b/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview.xml @@ -129,22 +129,6 @@ Install the client tools software to your machine. - - - - Note: - - - For large ECL workunits, the 32-bit compiler (eclcc) - could run out of memory quicker than the 64-bit version. - Therefore in most cases, you should install the 64-bit - version. However, for machines with 4GB memory or less, you - should use the 32-bit client tools. If you have both versions - on your local machine, the IDE will give preference to the - 64-bit version. - - - diff --git a/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview_withoutIDE.xml b/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview_withoutIDE.xml index d6cabd58c77..07fba16fa8e 100644 --- a/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview_withoutIDE.xml +++ b/docs/EN_US/HPCCClientTools/CT_Mods/CT_Overview_withoutIDE.xml @@ -112,20 +112,6 @@ Install the client tools software to your machine. - - - - Note: - - - For large ECL workunits, the 32-bit compiler (eclcc) - could run out of memory quicker than the 64-bit version. - Therefore in most cases, you should install the 64-bit - version. However, for machines with 4GB memory or less, you - should use the 32-bit client tools. - - - diff --git a/ecl/hql/hqlrepository.cpp b/ecl/hql/hqlrepository.cpp index 1c335ebe3cb..8b7c5a47649 100644 --- a/ecl/hql/hqlrepository.cpp +++ b/ecl/hql/hqlrepository.cpp @@ -827,11 +827,11 @@ IEclSourceCollection * EclRepositoryManager::resolveGitCollection(const char * r { if (options.updateRepos) { - unsigned retCode = runGitCommand(nullptr, "fetch origin", repoPath, true); + unsigned retCode = runGitCommand(nullptr, "fetch origin --prune", repoPath, true); if (retCode != 0) { VStringBuffer msg("Failed to download the latest version of '%s' error code (%u)", defaultUrl, retCode); - error.setown(createError(CategoryError, SeverityError, ERR_FAIL_UPDATE_REPO, msg.str(), nullptr, 0, 0, 0)); + error.setown(createError(CategoryError, SeverityWarning, ERR_FAIL_UPDATE_REPO, msg.str(), nullptr, 0, 0, 0)); } } diff --git a/esp/src/src-react/components/Frame.tsx b/esp/src/src-react/components/Frame.tsx index 58967d6ff4d..60355ee8def 100644 --- a/esp/src/src-react/components/Frame.tsx +++ b/esp/src/src-react/components/Frame.tsx @@ -91,7 +91,7 @@ export const Frame: React.FunctionComponent = () => { }, []); React.useEffect(() => { - document.title = `${showEnvironmentTitle && environmentTitle.length ? environmentTitle : "ECL Watch "}${locationPathname.split("/").join(" | ")}`; + document.title = `${(showEnvironmentTitle && environmentTitle) ? environmentTitle : "ECL Watch "}${locationPathname.split("/").join(" | ")}`; }, [environmentTitle, locationPathname, showEnvironmentTitle]); React.useEffect(() => { diff --git a/esp/src/src-react/components/Title.tsx b/esp/src/src-react/components/Title.tsx index 9af34dfa076..ff235f3b50d 100644 --- a/esp/src/src-react/components/Title.tsx +++ b/esp/src/src-react/components/Title.tsx @@ -247,11 +247,6 @@ export const DevTitle: React.FunctionComponent = ({ } }, [currentUser]); - React.useEffect(() => { - if (!environmentTitle) return; - document.title = environmentTitle; - }, [environmentTitle]); - return
@@ -264,7 +259,7 @@ export const DevTitle: React.FunctionComponent = ({ - {showEnvironmentTitle && environmentTitle.length ? environmentTitle : "ECL Watch"} + {(showEnvironmentTitle && environmentTitle) ? environmentTitle : "ECL Watch"} diff --git a/helm/hpcc/templates/_helpers.tpl b/helm/hpcc/templates/_helpers.tpl index 8b3e0e19b77..06675ff8708 100644 --- a/helm/hpcc/templates/_helpers.tpl +++ b/helm/hpcc/templates/_helpers.tpl @@ -1175,7 +1175,9 @@ Generate instance queue names {{- range $auxQueue := $auxThorQueues }} {{- $combinedList = append $combinedList $auxQueue -}} {{- end -}} -{{- toYaml $combinedList -}} +{{- if $combinedList -}} + {{- toYaml $combinedList -}} +{{- end -}} {{- end -}} {{- define "hpcc.usesRemoteIssuer" -}} diff --git a/plugins/eeproxies/lib_addrclean.ecllib b/plugins/eeproxies/lib_addrclean.ecllib index 3c4fc3172ac..95ae781c443 100644 --- a/plugins/eeproxies/lib_addrclean.ecllib +++ b/plugins/eeproxies/lib_addrclean.ecllib @@ -30,4 +30,5 @@ export AddrCleanLib := SERVICE : plugin('addrcleanlib'),time string34 Zip9ToGeo34(const string zip9, const string server = '', unsigned2 port = 0) : c, pure, context, entrypoint='aclZip9ToGeo34Ctx'; boolean SetTracing(boolean trace=true) : c, action, context, entrypoint='aclSetTracingCtx'; boolean SetTiming(boolean timing=true) : c, action, context, entrypoint='aclSetTimingCtx'; + CleanUtil(unsigned4 flags = 0) : c, pure, context, entrypoint='aclCleanUtilCtx'; END; diff --git a/roxie/ccd/ccdlistener.cpp b/roxie/ccd/ccdlistener.cpp index 6427dfc6991..370a83bcdc7 100644 --- a/roxie/ccd/ccdlistener.cpp +++ b/roxie/ccd/ccdlistener.cpp @@ -949,9 +949,9 @@ void ContextLogger::exportStatsToSpan(bool failed, stat_type elapsedNs, unsigned { if (activeSpan->isRecording()) { + activeSpan->setSpanStatus(failed); setSpanAttribute("time_elapsed", elapsedNs); - if (failed) - setSpanAttribute("num_failures", 1); + if (memused) setSpanAttribute("size_peak_row_memory", memused * 0x100000); @@ -1316,7 +1316,7 @@ class RoxieWorkUnitWorker : public RoxieQueryWorker reportUnknownException(wu, logctx); } #endif - unsigned elapsedNs = nsTick() - startNs; + stat_type elapsedNs = nsTick() - startNs; unsigned elapsedMs = nanoToMilli(elapsedNs); noteQuery(failed, elapsedMs, priority); queryFactory->noteQuery(startTime, failed, elapsedMs, memused, agentsReplyLen, 0); diff --git a/roxie/ccd/ccdmain.cpp b/roxie/ccd/ccdmain.cpp index 73ffb10cc15..b59b23e6612 100644 --- a/roxie/ccd/ccdmain.cpp +++ b/roxie/ccd/ccdmain.cpp @@ -1560,7 +1560,7 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml) const char *passPhrase = roxieFarm.queryProp("@passphrase"); if (!isEmptyString(passPhrase)) - decrypt(passPhraseStr, passPhrase); + passPhraseStr.append(passPhrase); // NB passphrase is decrypted in CSecureSocketContext::createNewContext() const char *certFile = roxieFarm.queryProp("@certificateFileName"); if (!certFile) diff --git a/rtl/eclrtl/rtltype.cpp b/rtl/eclrtl/rtltype.cpp index a0f0409b452..ecee7e5a8bc 100644 --- a/rtl/eclrtl/rtltype.cpp +++ b/rtl/eclrtl/rtltype.cpp @@ -62,7 +62,7 @@ void rtlPadTruncString(unsigned & tgtLen, char * & tgt, unsigned newLen, unsigne { if (len > newLen) len = newLen; - char * buff = (char *)malloc(len); + char * buff = (char *)malloc(newLen); memcpy(buff, src, len); if (len < newLen) memset(buff+len, ' ', newLen - len); diff --git a/system/jhtree/jhtree.cpp b/system/jhtree/jhtree.cpp index 26264459d8f..be4413d4356 100644 --- a/system/jhtree/jhtree.cpp +++ b/system/jhtree/jhtree.cpp @@ -626,7 +626,7 @@ class CNodeCacheEntry : public CInterface public: CriticalSection cs; private: - std::atomic node = nullptr; + std::atomic node{nullptr}; public: ~CNodeCacheEntry() { diff --git a/system/jlib/jtrace.cpp b/system/jlib/jtrace.cpp index 7a379dfc1cb..1d619407d4f 100644 --- a/system/jlib/jtrace.cpp +++ b/system/jlib/jtrace.cpp @@ -179,76 +179,85 @@ class JLogSpanExporter final : public opentelemetry::sdk::trace::SpanExporter opentelemetry::sdk::common::ExportResult Export( const nostd::span> &recordables) noexcept override { - if (isShutDown()) - return opentelemetry::sdk::common::ExportResult::kFailure; - - for (auto &recordable : recordables) + try { - //Casting the recordable object to the type of the object that was previously created by - //JLogSpanExporter::MakeRecordable() - - auto span = std::unique_ptr( - static_cast(recordable.release())); + if (isShutDown()) + return opentelemetry::sdk::common::ExportResult::kFailure; - if (span != nullptr) + for (auto &recordable : recordables) { - char traceID[32] = {0}; - char spanID[16] = {0}; + //Casting the recordable object to the type of the object that was previously created by + //JLogSpanExporter::MakeRecordable() - + auto span = std::unique_ptr( + static_cast(recordable.release())); - span->GetTraceId().ToLowerBase16(traceID); - span->GetSpanId().ToLowerBase16(spanID); + if (span != nullptr) + { + char traceID[32] = {0}; + char spanID[16] = {0}; - StringBuffer out("{ \"type\": \"span\""); //for simple identification in log scraping - out.appendf(", \"name\": \"%s\"", span->GetName().data()); - out.append(", \"trace_id\": \"").append(32, traceID).append("\""); - out.append(", \"span_id\": \"").append(16, spanID).append("\""); - out.appendf(", \"start\": %lld", (long long)(span->GetStartTime().time_since_epoch()).count()); - out.appendf(", \"duration\": %lld", (long long)span->GetDuration().count()); + span->GetTraceId().ToLowerBase16(traceID); + span->GetSpanId().ToLowerBase16(spanID); - if (hasMask(logFlags, SpanLogFlags::LogParentInfo)) - { - if (span->GetParentSpanId().IsValid()) + StringBuffer out("{ \"type\": \"span\""); //for simple identification in log scraping + out.appendf(", \"name\": \"%s\"", span->GetName().data()); + out.append(", \"trace_id\": \"").append(32, traceID).append("\""); + out.append(", \"span_id\": \"").append(16, spanID).append("\""); + out.appendf(", \"start\": %lld", (long long)(span->GetStartTime().time_since_epoch()).count()); + out.appendf(", \"duration\": %lld", (long long)span->GetDuration().count()); + + if (hasMask(logFlags, SpanLogFlags::LogParentInfo)) { - char parentSpanID[16] = {0}; - span->GetParentSpanId().ToLowerBase16(parentSpanID); - out.append(", \"parent_span_id\": \"").append(16, parentSpanID).append("\""); - } + if (span->GetParentSpanId().IsValid()) + { + char parentSpanID[16] = {0}; + span->GetParentSpanId().ToLowerBase16(parentSpanID); + out.append(", \"parent_span_id\": \"").append(16, parentSpanID).append("\""); + } - std::string traceStatestr = span->GetSpanContext().trace_state()->ToHeader(); - if (!traceStatestr.empty()) - out.appendf(", \"trace_state\": \"%s\"", traceStatestr.c_str()); - } + std::string traceStatestr = span->GetSpanContext().trace_state()->ToHeader(); + if (!traceStatestr.empty()) + out.appendf(", \"trace_state\": \"%s\"", traceStatestr.c_str()); + } - if (hasMask(logFlags, SpanLogFlags::LogSpanDetails)) - { - out.appendf(", \"status\": \"%s\"", spanStatusToString(span->GetStatus())); - out.appendf(", \"kind\": \"%s\"", spanKindToString(span->GetSpanKind())); - const char * description = span->GetDescription().data(); - if (!isEmptyString(description)) + if (hasMask(logFlags, SpanLogFlags::LogSpanDetails)) { - StringBuffer encoded; - encodeJSON(encoded, description); - out.appendf(", \"description\": \"%s\"", encoded.str()); + out.appendf(", \"status\": \"%s\"", spanStatusToString(span->GetStatus())); + out.appendf(", \"kind\": \"%s\"", spanKindToString(span->GetSpanKind())); + const char * description = span->GetDescription().data(); + if (!isEmptyString(description)) + { + StringBuffer encoded; + encodeJSON(encoded, description); + out.appendf(", \"description\": \"%s\"", encoded.str()); + } + printInstrumentationScope(out, span->GetInstrumentationScope()); } - printInstrumentationScope(out, span->GetInstrumentationScope()); - } - if (hasMask(logFlags, SpanLogFlags::LogAttributes)) - printAttributes(out, span->GetAttributes()); + if (hasMask(logFlags, SpanLogFlags::LogAttributes)) + printAttributes(out, span->GetAttributes()); - if (hasMask(logFlags, SpanLogFlags::LogEvents)) - printEvents(out, span->GetEvents()); + if (hasMask(logFlags, SpanLogFlags::LogEvents)) + printEvents(out, span->GetEvents()); - if (hasMask(logFlags, SpanLogFlags::LogLinks)) - printLinks(out, span->GetLinks()); + if (hasMask(logFlags, SpanLogFlags::LogLinks)) + printLinks(out, span->GetLinks()); - if (hasMask(logFlags, SpanLogFlags::LogResources)) - printResources(out, span->GetResource()); + if (hasMask(logFlags, SpanLogFlags::LogResources)) + printResources(out, span->GetResource()); - out.append(" }"); - LOG(MCmonitorEvent, "%s",out.str()); + out.append(" }"); + LOG(MCmonitorEvent, "%s",out.str()); + } } + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + catch (IException * e) + { + EXCLOG(e, "JLogSpanExporter::Export"); + e->Release(); + return opentelemetry::sdk::common::ExportResult::kFailure; } - return opentelemetry::sdk::common::ExportResult::kSuccess; } /** @@ -282,17 +291,17 @@ class JLogSpanExporter final : public opentelemetry::sdk::trace::SpanExporter bool first = true; for (const auto &kv : map) { - if (!first) - out.append(","); - else - first = false; - const auto & value = kv.second; std::ostringstream attsOS; //used to exploit OTel convenience functions for printing attribute values opentelemetry::exporter::ostream_common::print_value(value, attsOS); std::string val = attsOS.str(); if (val.size() > 0) { + if (!first) + out.append(","); + else + first = false; + switch (value.index()) { case opentelemetry::sdk::common::kTypeBool: @@ -326,7 +335,6 @@ class JLogSpanExporter final : public opentelemetry::sdk::trace::SpanExporter } } out.append(" }"); - } catch(const std::bad_variant_access & e) { @@ -740,6 +748,44 @@ class CSpan : public CInterfaceOf return span ? span->IsRecording() : false; } + virtual void setSpanStatus(bool spanFailed, const char * statusMessage) + { + if (span != nullptr) + { + span->SetStatus(spanFailed ? opentelemetry::trace::StatusCode::kError : opentelemetry::trace::StatusCode::kOk, statusMessage); + } + } + + virtual void recordError(const SpanError & error) + { + if (span != nullptr) + { + if (error.spanFailed) + span->SetStatus(opentelemetry::trace::StatusCode::kError, error.errorMessage); + + //https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/ + //The event name MUST be "exception". + //The table below indicates which attributes should be added to the Event and their types. + //exception.escaped boolean SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span. [1] Recommended + //exception.message string The exception message. Division by zero; Can't convert 'int' object to str implicitly See below + //exception.stacktrace string A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. + // Exception in thread "main" java.lang.RuntimeException: Test exception\n at com.example.GenerateTrace.methodB(GenerateTrace.java:13)\n at com.example.GenerateTrace.methodA(GenerateTrace.java:9) + //exception.type string The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. java.net.ConnectException; OSError See below + + if (error.errorCode != 0 && error.errorCode != -1) + span->AddEvent("Exception", {{"message", error.errorMessage}, {"escaped", error.escapeScope}, {"code", error.errorCode}}); + else + span->AddEvent("Exception", {{"message", error.errorMessage}, {"escaped", error.escapeScope}}); + } + } + + virtual void recordException(IException * e, bool spanFailed, bool escapedScope) + { + StringBuffer msg; + e->errorMessage(msg); + recordError(SpanError(msg.str(), e->errorCode(), spanFailed, escapedScope)); + }; + protected: CSpan(const char * spanName) { @@ -846,6 +892,10 @@ class CNullSpan final : public CInterfaceOf virtual void getLogPrefix(StringBuffer & out) const override {} virtual bool isRecording() const { return false; } + virtual void recordException(IException * e, bool spanFailed, bool escapedScope) override {} + virtual void recordError(const SpanError & error) override {}; + virtual void setSpanStatus(bool spanFailed, const char * statusMessage) override {} + virtual const char* queryGlobalId() const override { return nullptr; } virtual const char* queryCallerId() const override { return nullptr; } virtual const char* queryLocalId() const override { return nullptr; } @@ -1267,9 +1317,12 @@ void CTraceManager::initTracerProviderAndGlobalInternals(const IPropertyTree * t processors.push_back(opentelemetry::sdk::trace::SimpleSpanProcessorFactory::Create(std::move(exporter))); } + opentelemetry::sdk::resource::ResourceAttributes resourceAtts = {{"service.name", moduleName.get()}}; + auto jtraceResource = opentelemetry::sdk::resource::Resource::Create(resourceAtts); + // Default is an always-on sampler. std::shared_ptr context = - opentelemetry::sdk::trace::TracerContextFactory::Create(std::move(processors)); + opentelemetry::sdk::trace::TracerContextFactory::Create(std::move(processors), jtraceResource); std::shared_ptr provider = opentelemetry::sdk::trace::TracerProviderFactory::Create(context); diff --git a/system/jlib/jtrace.hpp b/system/jlib/jtrace.hpp index 3afd169a150..5ada18fc74b 100644 --- a/system/jlib/jtrace.hpp +++ b/system/jlib/jtrace.hpp @@ -55,6 +55,59 @@ enum class SpanFlags : unsigned }; BITMASK_ENUM(SpanFlags); +static constexpr const char * NO_STATUS_MESSAGE = ""; +static constexpr int UNKNOWN_ERROR_CODE = -1; + +struct SpanError +/** + * @brief Represents an error that occurred during a span. + * + * Used to store information about an error that occurred during the execution of a span. + * It includes the error message, error code, + * and flags indicating whether the span failed and whether the exception escaped the span's scope. + */ +{ + const char * errorMessage = NO_STATUS_MESSAGE; /**< The error message associated with the error. */ + int errorCode = UNKNOWN_ERROR_CODE; /**< The error code associated with the error. */ + bool spanFailed = true; /**< Flag indicating whether the span failed. */ + bool escapeScope = false; /**< Flag indicating whether the exception escaped the scope of the span. */ + + /** + * @brief Default constructor. + */ + SpanError() = default; + + /** + * @brief Constructor with error message. + * @param _errorMessage The error message. + */ + SpanError(const char * _errorMessage) : errorMessage(_errorMessage) {} + + /** + * @brief Constructor with error message, error code, span failure flag, and scope escape flag. + * @param _errorMessage The error message. + * @param _errorCode The error code. + * @param _spanFailed Flag indicating whether the span failed. + * @param _escapeScope Flag indicating whether the exception escaped the scope of the span. + */ + SpanError(const char * _errorMessage, int _errorCode, bool _spanFailed, bool _escapeScope) + : errorMessage(_errorMessage), errorCode(_errorCode), spanFailed(_spanFailed), escapeScope(_escapeScope) {} + + /** + * @brief Sets the span status. + * @param _spanFailed Flag indicating whether the span failed. + * @param _spanScopeEscape Flag indicating whether the exception escaped the scope of the span. + */ + void setSpanStatus(bool _spanFailed, bool _spanScopeEscape) { spanFailed = _spanFailed; escapeScope = _spanScopeEscape;} + + /** + * @brief Sets the error message and error code. + * @param _errorMessage The error message. + * @param _errorCode The error code. + */ + void setError(const char * _errorMessage, int _errorCode) { errorMessage = _errorMessage; errorCode = _errorCode; } +}; + interface ISpan : extends IInterface { virtual void setSpanAttribute(const char * key, const char * val) = 0; @@ -68,6 +121,9 @@ interface ISpan : extends IInterface virtual void toString(StringBuffer & out) const = 0; virtual void getLogPrefix(StringBuffer & out) const = 0; virtual bool isRecording() const = 0; // Is it worth adding any events/attributes to this span? + virtual void recordException(IException * e, bool spanFailed = true, bool escapedScope = true) = 0; + virtual void recordError(const SpanError & error = SpanError()) = 0; + virtual void setSpanStatus(bool spanFailed, const char * statusMessage = NO_STATUS_MESSAGE) = 0; virtual ISpan * createClientSpan(const char * name) = 0; virtual ISpan * createInternalSpan(const char * name) = 0; diff --git a/testing/unittests/jlibtests.cpp b/testing/unittests/jlibtests.cpp index d6406999973..899c2d797e3 100644 --- a/testing/unittests/jlibtests.cpp +++ b/testing/unittests/jlibtests.cpp @@ -63,6 +63,8 @@ class JlibTraceTest : public CppUnit::TestFixture CPPUNIT_TEST(testNullSpan); CPPUNIT_TEST(testClientSpanGlobalID); CPPUNIT_TEST(testEnsureTraceID); + CPPUNIT_TEST(manualTestsEventsOutput); + CPPUNIT_TEST(manualTestsDeclaredFailures); //CPPUNIT_TEST(testJTraceJLOGExporterprintResources); //CPPUNIT_TEST(testJTraceJLOGExporterprintAttributes); @@ -189,6 +191,31 @@ class JlibTraceTest : public CppUnit::TestFixture CPPUNIT_ASSERT_EQUAL_MESSAGE("Missing resource attribute detected", true, jtraceAsTree->hasProp("resources/telemetry.sdk.name")); }*/ + //not able to programmatically test yet, but can visually inspect trace output + void manualTestsEventsOutput() + { + Owned emptyMockHTTPHeaders = createProperties(); + { + Owned serverSpan = queryTraceManager().createServerSpan("spanWithEventsNoAtts", emptyMockHTTPHeaders); + Owned emptyEventAtts = createProperties(); + serverSpan->addSpanEvent("event1", emptyEventAtts); + } + + Owned twoEventAtt = createProperties(); + twoEventAtt->setProp("key", "value"); + twoEventAtt->setProp("key2", ""); + + { + Owned serverSpan = queryTraceManager().createServerSpan("spanWithEvent1Att", emptyMockHTTPHeaders); + serverSpan->addSpanEvent("event2", twoEventAtt); + }//{ "type": "span", "name": "spanWithEvents1Att", "trace_id": "3b9f55aaf8fab51fb0d73a32db7d704f", "span_id": "2a25a44ae0b3abe0", "start": 1709696036335278770, "duration": 3363911469, "events":[ { "name": "event2", "time_stamp": 1709696038413023245, "attributes": {"key": "value" } } ] } + + { + Owned serverSpan = queryTraceManager().createServerSpan("spanWith2Events", emptyMockHTTPHeaders); + serverSpan->addSpanEvent("event1", twoEventAtt); + serverSpan->addSpanEvent("event2", twoEventAtt); + }//{ "type": "span", "name": "spanWith2Events", "trace_id": "ff5c5919b9c5f85913652b77f289bf0b", "span_id": "82f91ca1f9d469c1", "start": 1709698012480805016, "duration": 2811601377, "events":[ { "name": "event1", "time_stamp": 1709698013294323139, "attributes": {"key": "value" } },{ "name": "event2", "time_stamp": 1709698014500350802, "attributes": {"key": "value" } } ] } + } //not able to programmatically test yet, but can visually inspect trace output void manualTestsDeclaredSpanStartTime() { @@ -243,6 +270,65 @@ class JlibTraceTest : public CppUnit::TestFixture } } + //not able to programmatically test yet, but can visually inspect trace output + void manualTestsDeclaredFailures() + { + Owned emptyMockHTTPHeaders = createProperties(); + { + Owned serverSpan = queryTraceManager().createServerSpan("defaultErrorSpan", emptyMockHTTPHeaders); + serverSpan->recordError(); + }//{ "type": "span", "name": "defaultErrorSpan", "trace_id": "209b5d8cea0aec9785d2dfa3117e37ad", "span_id": "ab72e76c2f2466c2", "start": 1709675278129335702, "duration": 188292867932, "status": "Error", "kind": "Server", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675465508149013, "attributes": {"escaped": 0 } } ] } + + { + Owned serverSpan = queryTraceManager().createServerSpan("defaultErrorSpanStruct", emptyMockHTTPHeaders); + SpanError error; + serverSpan->recordError(error); + }//{ "type": "span", "name": "defaultErrorSpanStruct", "trace_id": "19803a446b971f2e0bdddc9c00db50fe", "span_id": "04c93a91ab8785a2", "start": 1709675487767044352, "duration": 2287497219, "status": "Error", "kind": "Server", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675489216412154, "attributes": {"escaped": 0 } } ] } + + { + Owned serverSpan = queryTraceManager().createServerSpan("failedErrorSpanEscaped", emptyMockHTTPHeaders); + SpanError * error = new SpanError("hello"); + error->setSpanStatus(true, true); + serverSpan->recordError(*error); + }//{ "type": "span", "name": "failedErrorSpanEscaped", "trace_id": "634f386c18a6140544c980e0d5a15905", "span_id": "e2f59c48f63a8f82", "start": 1709675508231168974, "duration": 7731717678, "status": "Error", "kind": "Server", "description": "hello", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675512164430668, "attributes": {"escaped": 1,"message": "hello" } } ] } + + { + Owned serverSpan = queryTraceManager().createServerSpan("failedErrEscapedMsgErrCode", emptyMockHTTPHeaders); + SpanError * error = new SpanError(); + error->setSpanStatus(true, true); + error->setError("hello", 34); + serverSpan->recordError(*error); + }//failedErrEscapedMsgErrCode + + { + Owned serverSpan = queryTraceManager().createServerSpan("containsErrorAndMessageSpan", emptyMockHTTPHeaders); + serverSpan->recordError(SpanError("Error Message!!")); + }//{ "type": "span", "name": "containsErrorAndMessageSpan", "trace_id": "9a6e00ea309bc0427733f9b2d452f9e2", "span_id": "de63e9c69b64e411", "start": 1709675552302360510, "duration": 5233037523, "status": "Error", "kind": "Server", "description": "Error Message!!", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675555149852711, "attributes": {"escaped": 0,"message": "Error Message!!" } } + + { + Owned serverSpan = queryTraceManager().createServerSpan("containsErrorAndMessageFailedNotEscapedSpan", emptyMockHTTPHeaders); + serverSpan->recordError(SpanError("Error Message!!", 23, true, false)); + }//{ "type": "span", "name": "containsErrorAndMessageFailedNotEscapedSpan", "trace_id": "02f4b2d215f8230b15063862f8a91e41", "span_id": "c665ec371d6db147", "start": 1709675573581678954, "duration": 3467489486, "status": "Error", "kind": "Server", "description": "Error Message!!", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675576145074240, "attributes": {"code": 23,"escaped": 0,"message": "Error Message!!" } } ] } + + { + Owned serverSpan = queryTraceManager().createServerSpan("mockExceptionSpanNotFailedNotEscaped", emptyMockHTTPHeaders); + serverSpan->recordException( makeStringExceptionV(76,"Mock exception"), false, false); + }//{ "type": "span", "name": "mockExceptionSpanNotFailedNotEscaped", "trace_id": "e01766474db05ce9085943fa3955cd73", "span_id": "7da620e96e10e42c", "start": 1709675595987480704, "duration": 2609091267, "status": "Unset", "kind": "Server", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675597728975355, "attributes": {"code": 76,"escaped": 0,"message": "Mock exception" } } ] + + { + Owned serverSpan = queryTraceManager().createServerSpan("thrownExceptionSpan", emptyMockHTTPHeaders); + try + { + throw makeStringExceptionV( 356, "Mock thrown exception"); + } + catch (IException *e) + { + serverSpan->recordException(e, false, true); + e->Release(); + } + }//{ "type": "span", "name": "thrownExceptionSpan", "trace_id": "4d6225e1cefdc6823d1134c71c522426", "span_id": "07f7bd070e008f53", "start": 1709675614823881961, "duration": 4665288686, "status": "Unset", "kind": "Server", "instrumented_library": "unittests", "events":[ { "name": "Exception", "time_stamp": 1709675616485586471, "attributes": {"code": 356,"escaped": 1,"message": "Mock thrown exception" } } ] } + } + void testTraceDisableConfig() { Owned testTree = createPTreeFromYAMLString(disableTracingYaml, ipt_none, ptr_ignoreWhiteSpace, nullptr);