diff --git a/common/thorhelper/thorsoapcall.cpp b/common/thorhelper/thorsoapcall.cpp index 50d71b2bbcc..2af83bd755a 100644 --- a/common/thorhelper/thorsoapcall.cpp +++ b/common/thorhelper/thorsoapcall.cpp @@ -254,6 +254,24 @@ class Url : public CInterface, implements IInterface } }; +//if Url was globally accessible we could define this in jtrace instead +//http span standards documented here: https://opentelemetry.io/docs/specs/semconv/http/http-spans/ +void setSpanURLAttributes(ISpan * clientSpan, const Url & url) +{ + if (clientSpan == nullptr) + return; + + clientSpan->setSpanAttribute("http.request.method", "POST"); //apparently hardcoded to post + //Even though a "service" and + //a "method" are tracked and sometimes + //the target service name is used + //and there's code and comments suggesting only + //GET is supported... + clientSpan->setSpanAttribute("network.peer.address", url.host.get()); + clientSpan->setSpanAttribute("network.peer.port", url.port); + clientSpan->setSpanAttribute("network.protocol.name", url.method.get()); +} + typedef IArrayOf UrlArray; //================================================================================================= @@ -963,12 +981,14 @@ class CWSCHelper : implements IWSCHelper, public CInterface WSCType wscType; public: + Owned activitySpanScope; IMPLEMENT_IINTERFACE; CWSCHelper(IWSCRowProvider *_rowProvider, IEngineRowAllocator * _outputAllocator, const char *_authToken, WSCMode _wscMode, ClientCertificate *_clientCert, const IContextLogger &_logctx, IRoxieAbortMonitor *_roxieAbortMonitor, WSCType _wscType) : logctx(_logctx), outputAllocator(_outputAllocator), clientCert(_clientCert), roxieAbortMonitor(_roxieAbortMonitor) { + activitySpanScope.setown(logctx.queryActiveSpan()->createInternalSpan(_wscType == STsoap ? "SoapCall Activity": "HTTPCall Activity")); wscMode = _wscMode; wscType = _wscType; done = 0; @@ -1034,7 +1054,6 @@ class CWSCHelper : implements IWSCHelper, public CInterface s.setown(helper->getXpathHintsXml()); xpathHints.setown(createPTreeFromXMLString(s.get())); } - if (wscType == STsoap) { soapaction.set(s.setown(helper->getSoapAction())); @@ -1972,7 +1991,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (!httpHeaderBlockContainsHeader(httpheaders, ACCEPT_ENCODING)) request.appendf("%s: gzip, deflate\r\n", ACCEPT_ENCODING); #endif - Owned traceHeaders = master->logctx.getClientHeaders(); + Owned traceHeaders = ::getClientHeaders(master->activitySpanScope); if (traceHeaders) { Owned iter = traceHeaders->getIterator(); @@ -2457,6 +2476,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo checkTimeLimitExceeded(&remainingMS); // after ep.set which might make a potentially long getaddrinfo lookup ... if (strieq(url.method, "https")) proto = PersistentProtocol::ProtoTLS; + bool shouldClose = false; Owned psock = master->usePersistConnections() ? persistentHandler->getAvailable(&ep, &shouldClose, proto) : nullptr; if (psock) @@ -2504,6 +2524,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo { if (master->timeLimitExceeded) { + master->activitySpanScope->recordError(SpanError("Time Limit Exceeded", e->errorCode(), true, true)); master->logctx.CTXLOG("%s exiting: time limit (%ums) exceeded", getWsCallTypeName(master->wscType), master->timeLimitMS); processException(url, inputRows, e); return; @@ -2511,6 +2532,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (e->errorCode() == ROXIE_ABORT_EVENT) { + master->activitySpanScope->recordError(SpanError("Aborted", e->errorCode(), true, true)); StringBuffer s; master->logctx.CTXLOG("%s exiting: Roxie Abort : %s", getWsCallTypeName(master->wscType),e->errorMessage(s).str()); throw; @@ -2523,19 +2545,25 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo idx = 0; if (idx==startidx) { + master->activitySpanScope->recordException(e, true, true); StringBuffer s; master->logctx.CTXLOG("Exception %s", e->errorMessage(s).str()); processException(url, inputRows, e); return; } } while (blacklist->blacklisted(url.port, url.host)); + + master->activitySpanScope->recordException(e, false, false); //Record the exception, but don't set failure } } try { checkTimeLimitExceeded(&remainingMS); checkRoxieAbortMonitor(master->roxieAbortMonitor); + OwnedSpanScope socketOperationSpan = master->activitySpanScope->createClientSpan("Socket Write"); + setSpanURLAttributes(socketOperationSpan, url); socket->write(request.str(), request.length()); + if (soapTraceLevel > 4) master->logctx.CTXLOG("%s: sent request (%s) to %s:%d", getWsCallTypeName(master->wscType),master->service.str(), url.host.str(), url.port); checkTimeLimitExceeded(&remainingMS); @@ -2544,6 +2572,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo bool keepAlive2; StringBuffer contentType; int rval = readHttpResponse(response, socket, keepAlive2, contentType); + socketOperationSpan->setSpanAttribute("http.response.status_code", (int64_t)rval); keepAlive = keepAlive && keepAlive2; if (soapTraceLevel > 4) @@ -2551,16 +2580,22 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (rval != 200) { + socketOperationSpan->setSpanStatusSuccess(false); if (rval == 503) + { + socketOperationSpan->recordError(SpanError("Server Too Busy", 1001, true, true)); throw new ReceivedRoxieException(1001, "Server Too Busy"); + } StringBuffer text; text.appendf("HTTP error (%d) in processQuery",rval); rtlAddExceptionTag(text, "soapresponse", response.str()); + socketOperationSpan->recordError(SpanError(text.str(), -1, true, true)); throw MakeStringExceptionDirect(-1, text.str()); } if (response.length() == 0) { + socketOperationSpan->recordError(SpanError("Zero length response in processQuery", -1, true, true)); throw MakeStringException(-1, "Zero length response in processQuery"); } checkTimeLimitExceeded(&remainingMS); @@ -2575,6 +2610,8 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo else if (keepAlive) persistentHandler->add(socket, &ep, proto); } + + socketOperationSpan->setSpanStatusSuccess(true); break; } catch (IReceivedRoxieException *e) @@ -2606,6 +2643,8 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo processException(url, e->errorRow(), e); else processException(url, inputRows, e); + + master->activitySpanScope->recordException(e, true, true); break; } } @@ -2616,7 +2655,9 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (master->timeLimitExceeded) { processException(url, inputRows, e); - master->logctx.CTXLOG("%s exiting: time limit (%ums) exceeded", getWsCallTypeName(master->wscType), master->timeLimitMS); + VStringBuffer msg("%s exiting: time limit (%ums) exceeded", getWsCallTypeName(master->wscType), master->timeLimitMS); + master->logctx.CTXLOG("%s", msg.str()); + master->activitySpanScope->recordError(SpanError(msg.str(), e->errorCode(), true, true)); break; } @@ -2624,6 +2665,7 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo { StringBuffer s; master->logctx.CTXLOG("%s exiting: Roxie Abort : %s", getWsCallTypeName(master->wscType),e->errorMessage(s).str()); + master->activitySpanScope->recordError(SpanError("Aborted", e->errorCode(), true, true)); throw; } @@ -2634,12 +2676,15 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (numRetries >= master->maxRetries) { // error affects all inputRows - master->logctx.CTXLOG("Exiting: maxRetries %d exceeded", master->maxRetries); + VStringBuffer msg("Exiting: maxRetries %d exceeded", master->maxRetries); + master->logctx.CTXLOG("%s", msg.str()); + master->activitySpanScope->recordError(SpanError(msg.str(), e->errorCode(), true, true)); processException(url, inputRows, e); break; } numRetries++; master->logctx.CTXLOG("Retrying: attempt %d of %d", numRetries, master->maxRetries); + master->activitySpanScope->recordException(e, false, false); e->Release(); } catch (std::exception & es) @@ -2647,13 +2692,20 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo if (master->usePersistConnections() && isReused) persistentHandler->doneUsing(socket, false); if(dynamic_cast(&es)) + { + master->activitySpanScope->recordError("std::exception: out of memory (std::bad_alloc) in CWSCAsyncFor processQuery"); throw MakeStringException(-1, "std::exception: out of memory (std::bad_alloc) in CWSCAsyncFor processQuery"); + } + + master->activitySpanScope->recordError(es.what()); throw MakeStringException(-1, "std::exception: standard library exception (%s) in CWSCAsyncFor processQuery",es.what()); } catch (...) { if (master->usePersistConnections() && isReused) persistentHandler->doneUsing(socket, false); + + master->activitySpanScope->recordError(SpanError("Unknown exception in processQuery", -1, true, true)); throw MakeStringException(-1, "Unknown exception in processQuery"); } } diff --git a/common/workunit/workunit.cpp b/common/workunit/workunit.cpp index ec3b3590e70..e75eca4cae2 100644 --- a/common/workunit/workunit.cpp +++ b/common/workunit/workunit.cpp @@ -4846,6 +4846,7 @@ class CLocalWUException : implements IWUException, public CInterface virtual unsigned getSequence() const override; virtual const char * queryScope() const override; virtual unsigned getPriority() const override; + virtual double getCost() const override; virtual void setExceptionSource(const char *str) override; virtual void setExceptionMessage(const char *str) override; virtual void setExceptionCode(unsigned code) override; @@ -4857,6 +4858,7 @@ class CLocalWUException : implements IWUException, public CInterface virtual void setActivityId(unsigned _id) override; virtual void setScope(const char * _scope) override; virtual void setPriority(unsigned _priority) override; + virtual void setCost(double cost) override; }; //========================================================================================== @@ -7629,6 +7631,18 @@ WUPriorityClass CLocalWorkUnit::getPriority() const return (WUPriorityClass) getEnum(p, "@priorityClass", priorityClasses); } +void CLocalWorkUnit::setCost(double cost) +{ + CriticalBlock block(crit); + p->setPropReal("@cost", cost); +} + +double CLocalWorkUnit::getCost() const +{ + CriticalBlock block(crit); + return p->getPropReal("@cost", 0); +} + const char *CLocalWorkUnit::queryPriorityDesc() const { return getEnumText(getPriority(), priorityClasses); @@ -11926,6 +11940,11 @@ unsigned CLocalWUException::getPriority() const return p->getPropInt("@prio", 0); } +double CLocalWUException::getCost() const +{ + return p->getPropReal("@cost", 0); +} + void CLocalWUException::setExceptionSource(const char *str) { p->setProp("@source", str); @@ -11981,6 +12000,10 @@ void CLocalWUException::setPriority(unsigned _priority) p->setPropInt("@prio", _priority); } +void CLocalWUException::setCost(double _cost) +{ + p->setPropReal("@cost", _cost); +} //========================================================================================== CLocalWUAppValue::CLocalWUAppValue(const IPropertyTree *_owner, const IPropertyTree *_props) : owner(_owner), props(_props) diff --git a/common/workunit/workunit.hpp b/common/workunit/workunit.hpp index 67ba440f36e..4baa31c1c71 100644 --- a/common/workunit/workunit.hpp +++ b/common/workunit/workunit.hpp @@ -520,6 +520,7 @@ interface IConstWUException : extends IInterface virtual unsigned getActivityId() const = 0; virtual const char * queryScope() const = 0; virtual unsigned getPriority() const = 0; // For ordering within a severity - e.g. warnings about inefficiency + virtual double getCost() const = 0; // cost optimizer cost saving estimate. }; @@ -536,6 +537,7 @@ interface IWUException : extends IConstWUException virtual void setActivityId(unsigned _id) = 0; virtual void setScope(const char * _scope) = 0; virtual void setPriority(unsigned _priority) = 0; + virtual void setCost(double cost) = 0; // cost optimizer cost saving estimate. }; diff --git a/common/workunit/workunit.ipp b/common/workunit/workunit.ipp index 975f62565e7..e35f8fef5a8 100644 --- a/common/workunit/workunit.ipp +++ b/common/workunit/workunit.ipp @@ -241,6 +241,7 @@ public: virtual IConstWUPluginIterator & getPlugins() const; virtual IConstWULibraryIterator & getLibraries() const; virtual WUPriorityClass getPriority() const; + virtual double getCost() const; virtual const char *queryPriorityDesc() const; virtual int getPriorityLevel() const; virtual int getPriorityValue() const; @@ -328,6 +329,7 @@ public: void setDebugValueInt(const char * propname, int value, bool overwrite); void setJobName(const char * value); void setPriority(WUPriorityClass cls); + void setCost(double cost); void setPriorityLevel(int level); void setRescheduleFlag(bool value); void setResultLimit(unsigned value); diff --git a/common/wuanalysis/anacommon.cpp b/common/wuanalysis/anacommon.cpp index 89d451607f6..be92d006405 100644 --- a/common/wuanalysis/anacommon.cpp +++ b/common/wuanalysis/anacommon.cpp @@ -65,12 +65,12 @@ void PerformanceIssue::createException(IWorkUnit * wu, double costRate) we->setExceptionFileName(filename); StringBuffer s(comment); // Append scope to comment as scope column is not visible in ECLWatch s.appendf(" (%s)", scope.str()); + we->setExceptionMessage(s.str()); if (costRate!=0.0) { double timePenaltyPerHour = (double)statUnits2seconds(timePenalty) / 3600; - s.appendf(" cost %.2f", timePenaltyPerHour*costRate); + we->setCost(timePenaltyPerHour*costRate); } - we->setExceptionMessage(s.str()); we->setExceptionSource(CostOptimizerName); } diff --git a/dali/base/dadfs.cpp b/dali/base/dadfs.cpp index 5bed33ac926..9dc0d1b708c 100644 --- a/dali/base/dadfs.cpp +++ b/dali/base/dadfs.cpp @@ -10525,6 +10525,18 @@ class CInitGroups } return true; } + void clearLZGroups() + { + if (!writeLock) + throw makeStringException(0, "CInitGroups::clearLZGroups called in read-only mode"); + IPropertyTree *root = groupsconnlock.conn->queryRoot(); + std::vector toDelete; + Owned groups = root->getElements("Group[@kind='dropzone']"); + ForEach(*groups) + toDelete.push_back(&groups->query()); + for (auto &group: toDelete) + root->removeTree(group); + } void constructGroups(bool force, StringBuffer &messages, IPropertyTree *oldEnvironment) { Owned conn = querySDS().connect("/Environment/Software", myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT); @@ -10717,12 +10729,14 @@ class CInitGroups void initClusterGroups(bool force, StringBuffer &response, IPropertyTree *oldEnvironment, unsigned timems) { CInitGroups init(timems, true); + init.clearLZGroups(); // clear existing LZ groups, current ones will be recreated init.constructGroups(force, response, oldEnvironment); } void initClusterAndStoragePlaneGroups(bool force, IPropertyTree *oldEnvironment, unsigned timems) { CInitGroups init(timems, true); + init.clearLZGroups(); // clear existing LZ groups, current ones will be recreated StringBuffer response; init.constructGroups(force, response, oldEnvironment); diff --git a/dali/ft/daftformat.cpp b/dali/ft/daftformat.cpp index 6cc8230ae56..b1b6a8c7795 100644 --- a/dali/ft/daftformat.cpp +++ b/dali/ft/daftformat.cpp @@ -1973,6 +1973,10 @@ void CRemotePartitioner::callRemote() void CRemotePartitioner::getResults(PartitionPointArray & partition) { +#ifdef RUN_SLAVES_ON_THREADS + join(); +#endif + if (error) throw error.getLink(); diff --git a/dali/ft/filecopy.cpp b/dali/ft/filecopy.cpp index 885dc2b2168..b49b59a4c3e 100644 --- a/dali/ft/filecopy.cpp +++ b/dali/ft/filecopy.cpp @@ -2031,6 +2031,8 @@ void FileSprayer::gatherFileSizes(FilePartInfoArray & fileSizeQueue, bool errorI if (alldone) break; } + for (idx = 0; idx < numThreads; idx++) + threads.item(idx).join(); for (idx = 0; idx < numThreads; idx++) threads.item(idx).queryThrowError(); } @@ -2564,6 +2566,12 @@ void FileSprayer::performTransfer() numSlavesCompleted++; } +#ifdef RUN_SLAVES_ON_THREADS + //Ensure that the transfer slave threads have terminated before continuing + ForEachItemIn(idx4, transferSlaves) + transferSlaves.item(idx4).join(); +#endif + if (error) throw LINK(error); diff --git a/esp/scm/ws_workunits_struct.ecm b/esp/scm/ws_workunits_struct.ecm index 3c4114d074b..21883a9e4de 100644 --- a/esp/scm/ws_workunits_struct.ecm +++ b/esp/scm/ws_workunits_struct.ecm @@ -143,6 +143,7 @@ ESPStruct [nil_remove] ECLException [min_ver("1.63")] int Activity; [min_ver("1.69")] string Scope; [min_ver("1.69")] int Priority; + [min_ver("1.70")] double Cost; }; // =========================================================================== ESPStruct [nil_remove] ECLSchemaItem diff --git a/esp/services/ws_workunits/ws_workunitsHelpers.cpp b/esp/services/ws_workunits/ws_workunitsHelpers.cpp index 4b6ff05ad2b..7749d5d9097 100644 --- a/esp/services/ws_workunits/ws_workunitsHelpers.cpp +++ b/esp/services/ws_workunits/ws_workunitsHelpers.cpp @@ -202,6 +202,8 @@ WsWUExceptions::WsWUExceptions(IConstWorkUnit& wu): numerr(0), numwrn(0), numinf e->setActivity(cur.getActivityId()); if (cur.getPriority()) e->setPriority(cur.getPriority()); + if (cur.getCost()) + e->setCost(cur.getCost()); e->setScope(cur.queryScope()); const char * label = ""; diff --git a/roxie/ccd/ccdlistener.cpp b/roxie/ccd/ccdlistener.cpp index 69b231fca0a..d38624421a1 100644 --- a/roxie/ccd/ccdlistener.cpp +++ b/roxie/ccd/ccdlistener.cpp @@ -949,7 +949,7 @@ void ContextLogger::exportStatsToSpan(bool failed, stat_type elapsedNs, unsigned { if (activeSpan->isRecording()) { - activeSpan->setSpanStatus(failed); + activeSpan->setSpanStatusSuccess(!failed); setSpanAttribute("time_elapsed", elapsedNs); if (memused) diff --git a/system/jhtree/ctfile.cpp b/system/jhtree/ctfile.cpp index 132145626a5..949b6028bff 100644 --- a/system/jhtree/ctfile.cpp +++ b/system/jhtree/ctfile.cpp @@ -965,6 +965,17 @@ void CJHLegacySearchNode::load(CKeyHdr *_keyHdr, const void *rawData, offset_t _ else { keyBuf = NULL; expandedSize = 0; + if (hdr.nodeType == NodeBranch) + { + if ((hdr.leftSib == 0) && (hdr.rightSib == 0)) + { + //Sanity check to catch error where a section of the file has unexpectedly been zeroed. + //which is otherwise tricky to track down. + //This can only legally happen if there is an index with 0 entries + if (keyHdr->getNumRecords() != 0) + throw MakeStringException(0, "Zeroed index node detected at offset %llu", getFpos()); + } + } } } } diff --git a/system/jlib/jtrace.cpp b/system/jlib/jtrace.cpp index a2c0b9b8108..129537092c4 100644 --- a/system/jlib/jtrace.cpp +++ b/system/jlib/jtrace.cpp @@ -753,11 +753,11 @@ class CSpan : public CInterfaceOf return span ? span->IsRecording() : false; } - virtual void setSpanStatus(bool spanFailed, const char * statusMessage) + virtual void setSpanStatusSuccess(bool spanSucceeded, const char * statusMessage) { if (span != nullptr) { - span->SetStatus(spanFailed ? opentelemetry::trace::StatusCode::kError : opentelemetry::trace::StatusCode::kOk, statusMessage); + span->SetStatus(spanSucceeded ? opentelemetry::trace::StatusCode::kOk : opentelemetry::trace::StatusCode::kError, statusMessage); } } @@ -899,7 +899,7 @@ class CNullSpan final : public CInterfaceOf 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 void setSpanStatusSuccess(bool spanSucceeded, const char * statusMessage) override {} virtual const char* queryGlobalId() const override { return nullptr; } virtual const char* queryCallerId() const override { return nullptr; } diff --git a/system/jlib/jtrace.hpp b/system/jlib/jtrace.hpp index fbdb6cc40ce..2cf2052c577 100644 --- a/system/jlib/jtrace.hpp +++ b/system/jlib/jtrace.hpp @@ -94,11 +94,11 @@ struct SpanError : errorMessage(_errorMessage), errorCode(_errorCode), spanFailed(_spanFailed), escapeScope(_escapeScope) {} /** - * @brief Sets the span status. - * @param _spanFailed Flag indicating whether the span failed. + * @brief Sets the span status success. + * @param _spanSucceeded Flag indicating whether the span succeeded. * @param _spanScopeEscape Flag indicating whether the exception escaped the scope of the span. */ - void setSpanStatus(bool _spanFailed, bool _spanScopeEscape) { spanFailed = _spanFailed; escapeScope = _spanScopeEscape;} + void setSpanStatusSuccess(bool _spanSucceeded, bool _spanScopeEscape) { spanFailed = !_spanSucceeded; escapeScope = _spanScopeEscape;} /** * @brief Sets the error message and error code. @@ -123,7 +123,7 @@ interface ISpan : extends IInterface 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 void setSpanStatusSuccess(bool spanSucceeded, 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 041913f351b..45d7ccc3a41 100644 --- a/testing/unittests/jlibtests.cpp +++ b/testing/unittests/jlibtests.cpp @@ -288,17 +288,12 @@ class JlibTraceTest : public CppUnit::TestFixture { OwnedSpanScope serverSpan = queryTraceManager().createServerSpan("failedErrorSpanEscaped", emptyMockHTTPHeaders); - SpanError * error = new SpanError("hello"); - error->setSpanStatus(true, true); - serverSpan->recordError(*error); + serverSpan->recordError(SpanError("hello", -1, true, true)); //error message hello, no error code, error caused failure, and error caused escape }//{ "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" } } ] } { OwnedSpanScope serverSpan = queryTraceManager().createServerSpan("failedErrEscapedMsgErrCode", emptyMockHTTPHeaders); - SpanError * error = new SpanError(); - error->setSpanStatus(true, true); - error->setError("hello", 34); - serverSpan->recordError(*error); + serverSpan->recordError(SpanError("hello", 34, true, true)); //error message hello, error code 34, error caused failure, and error caused escape }//failedErrEscapedMsgErrCode {