Skip to content

Commit

Permalink
HPCC-30389 Split JTrace getSpanContext function
Browse files Browse the repository at this point in the history
- Adjusts ISpan getSpanContex signature
- Adds new ISpan getClientHeaders function
- Ensures tracestate is produced

Signed-off-by: Rodrigo Pastrana <rodrigo.pastrana@lexisnexisrisk.com>
  • Loading branch information
rpastrana committed Jan 11, 2024
1 parent c3e6871 commit 7b68ed7
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 118 deletions.
126 changes: 60 additions & 66 deletions system/jlib/jtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,7 @@ class CSpan : public CInterfaceOf<ISpan>

bool getSpanContext(CHPCCHttpTextMapCarrier * carrier) const
{
if (!carrier)
return false;
assertex(carrier);

auto propagator = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();

Expand All @@ -594,66 +593,68 @@ class CSpan : public CInterfaceOf<ISpan>
}

/**
* Retrieves the Span's context as key/value pairs into the provided IProperties.
* Optionally, output follows OpenTelemetry Span context format for propogation
* Retrieves the Span's client headers traceparent and tracestate
* Output follows OpenTelemetry Span context format for propogation
* accross process boundaries.
*
* @param ctxProps IProperties container for span context key/value pairs.
* @param otelFormatted If true, output follows OpenTelemetry Span context format.
* @return True if the span context was successfully retrieved, false otherwise.
* @param clientHeaders IProperties container for client headers.
*/
bool getSpanContext(IProperties * ctxProps, bool otelFormatted) const override
void getClientHeaders(IProperties * clientHeaders) const
{
if (ctxProps == nullptr)
return false;
assertex(clientHeaders);

ctxProps->setNonEmptyProp(kGlobalIdHttpHeaderName, queryGlobalId());
clientHeaders->setNonEmptyProp(kGlobalIdHttpHeaderName, queryGlobalId());

if (otelFormatted)
{
//The localid is passed as the callerid for the client request....
ctxProps->setNonEmptyProp(kCallerIdHttpHeaderName, queryLocalId());
}
else
{
ctxProps->setNonEmptyProp(kCallerIdHttpHeaderName, queryCallerId());
}
//The localid is passed as the callerid for the client request....
clientHeaders->setNonEmptyProp(kCallerIdHttpHeaderName, queryLocalId());

if (span == nullptr)
return false;
return;

if (otelFormatted)
{
if (isEmptyString(traceID.get()) || isEmptyString(spanID.get()) || isEmptyString(traceFlags.get()))
return false;
if (isEmptyString(traceID.get()) || isEmptyString(spanID.get()) || isEmptyString(traceFlags.get()))
return;

//The traceparent header uses the version-trace_id-parent_id-trace_flags format where:
//version is always 00. trace_id is a hex-encoded trace id. span_id is a hex-encoded span id. trace_flags is a hex-encoded 8-bit field that contains tracing flags such as sampling, trace level, etc.
//Example: "traceparent", "00-beca49ca8f3138a2842e5cf21402bfff-4b960b3e4647da3f-01"
//The traceparent header uses the version-trace_id-parent_id-trace_flags format where:
//version is always 00. trace_id is a hex-encoded trace id. span_id is a hex-encoded span id. trace_flags is a hex-encoded 8-bit field that contains tracing flags such as sampling, trace level, etc.
//Example: "traceparent", "00-beca49ca8f3138a2842e5cf21402bfff-4b960b3e4647da3f-01"

StringBuffer contextHTTPHeader;
//https://www.w3.org/TR/trace-context/#header-name
contextHTTPHeader.append("00-").append(traceID.get()).append("-").append(spanID.get()).append("-").append(traceFlags.get());
ctxProps->setProp(opentelemetry::trace::propagation::kTraceParent.data(), contextHTTPHeader.str());
StringBuffer contextHTTPHeader;
//https://www.w3.org/TR/trace-context/#header-name
contextHTTPHeader.append("00-").append(traceID.get()).append("-").append(spanID.get()).append("-").append(traceFlags.get());
clientHeaders->setProp(opentelemetry::trace::propagation::kTraceParent.data(), contextHTTPHeader.str());

//The main purpose of the tracestate HTTP header is to provide additional vendor-specific trace identification
// information across different distributed tracing systems and is a companion header for the traceparent field.
// It also conveys information about the request’s position in multiple distributed tracing graphs.
//The main purpose of the tracestate HTTP header is to provide additional vendor-specific trace identification
// information across different distributed tracing systems and is a companion header for the traceparent field.
// It also conveys information about the request’s position in multiple distributed tracing graphs.

//https://www.w3.org/TR/trace-context/#trace-context-http-headers-format
//StringBuffer traceStateHTTPHeader;
//traceStateHTTPHeader.append("hpcc=").append(spanID.get());
//https://www.w3.org/TR/trace-context/#trace-context-http-headers-format
//StringBuffer traceStateHTTPHeader;
//traceStateHTTPHeader.append("hpcc=").append(spanID.get());

ctxProps->setNonEmptyProp(opentelemetry::trace::propagation::kTraceState.data(), span->GetContext().trace_state()->ToHeader().c_str());
}
else
{
ctxProps->setNonEmptyProp("traceID", traceID.get());
ctxProps->setNonEmptyProp("spanID", spanID.get());
ctxProps->setNonEmptyProp("traceFlags", traceFlags.get());
}
clientHeaders->setNonEmptyProp(opentelemetry::trace::propagation::kTraceState.data(), span->GetContext().trace_state()->ToHeader().c_str());
}

return true;
/**
* Retrieves the Span's context as key/value pairs into the provided IProperties.
* Optionally, output follows OpenTelemetry Span context format for propogation
* accross process boundaries.
*
* @param ctxProps IProperties container for span context key/value pairs.
*/
void getSpanContext(IProperties * ctxProps) const override
{
if (ctxProps == nullptr)
return;

ctxProps->setNonEmptyProp(kGlobalIdHttpHeaderName, queryGlobalId());
ctxProps->setNonEmptyProp(kCallerIdHttpHeaderName, queryCallerId());

if (span == nullptr)
return;

ctxProps->setNonEmptyProp("traceID", traceID.get());
ctxProps->setNonEmptyProp("spanID", spanID.get());
ctxProps->setNonEmptyProp("traceFlags", traceFlags.get());
}

opentelemetry::v1::trace::SpanContext querySpanContext() const
Expand Down Expand Up @@ -783,7 +784,8 @@ class CNullSpan : public CInterfaceOf<ISpan>
virtual void setSpanAttributes(const IProperties * attributes) override {}
virtual void addSpanEvent(const char * eventName) override {}
virtual void addSpanEvent(const char * eventName, IProperties * attributes) override {};
virtual bool getSpanContext(IProperties * ctxProps, bool otelFormatted) const override { return false; }
virtual void getSpanContext(IProperties * ctxProps) const override {}
virtual void getClientHeaders(IProperties * clientHeaders) const override {}

virtual void toString(StringBuffer & out) const override {}
virtual void getLogPrefix(StringBuffer & out) const override {}
Expand Down Expand Up @@ -818,19 +820,13 @@ class CChildSpan : public CSpan
}

public:
virtual bool getSpanContext(IProperties * ctxProps, bool otelFormatted) const override
virtual void getSpanContext(IProperties * ctxProps) const override
{
//MORE: It is not clear what this return value represents, and whether it would ever be checked.
bool ok = CSpan::getSpanContext(ctxProps, otelFormatted);
CSpan::getSpanContext(ctxProps);

if (ok && !otelFormatted)
{
Owned<IProperties> localParentSpanCtxProps = createProperties();
localParentSpan->getSpanContext(localParentSpanCtxProps, false);
ctxProps->setNonEmptyProp("localParentSpanID", localParentSpanCtxProps->queryProp("spanID"));
}

return ok;
Owned<IProperties> localParentSpanCtxProps = createProperties();
localParentSpan->getSpanContext(localParentSpanCtxProps);
ctxProps->setNonEmptyProp("localParentSpanID", localParentSpanCtxProps->queryProp("spanID"));
}

virtual const char* queryGlobalId() const override
Expand Down Expand Up @@ -951,20 +947,18 @@ class CServerSpan : public CSpan
}
}

bool getSpanContext(IProperties * ctxProps, bool otelFormatted) const override
void getSpanContext(IProperties * ctxProps) const override
{
bool success = CSpan::getSpanContext(ctxProps, otelFormatted);
CSpan::getSpanContext(ctxProps);

if (!otelFormatted && remoteParentSpanCtx.IsValid())
if (remoteParentSpanCtx.IsValid())
{
StringBuffer remoteParentSpanID;
char remoteParentSpanId[16] = {0};
remoteParentSpanCtx.span_id().ToLowerBase16(remoteParentSpanId);
remoteParentSpanID.append(16, remoteParentSpanId);
ctxProps->setProp("remoteParentSpanID", remoteParentSpanID.str());
}

return success;
}

void init(SpanFlags flags)
Expand Down Expand Up @@ -1046,14 +1040,14 @@ class CServerSpan : public CSpan
IProperties * getClientHeaders(const ISpan * span)
{
Owned<IProperties> headers = createProperties(true);
span->getSpanContext(headers, true); // Return value is not helpful
span->getClientHeaders(headers);
return headers.getClear();
}

IProperties * getSpanContext(const ISpan * span)
{
Owned<IProperties> headers = createProperties(true);
span->getSpanContext(headers, false);
span->getSpanContext(headers);
return headers.getClear();
}

Expand Down
3 changes: 2 additions & 1 deletion system/jlib/jtrace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ interface ISpan : extends IInterface
virtual void setSpanAttributes(const IProperties * attributes) = 0;
virtual void addSpanEvent(const char * eventName) = 0;
virtual void addSpanEvent(const char * eventName, IProperties * attributes) = 0;
virtual bool getSpanContext(IProperties * ctxProps, bool otelFormatted) const = 0;
virtual void getSpanContext(IProperties * ctxProps) const = 0;
virtual void getClientHeaders(IProperties * clientHeaders) const = 0;
virtual void toString(StringBuffer & out) const = 0;
virtual void getLogPrefix(StringBuffer & out) const = 0;

Expand Down
Loading

0 comments on commit 7b68ed7

Please sign in to comment.