Skip to content

Commit

Permalink
Add proper support to TraceState, fixes #141
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanpo committed Aug 27, 2024
1 parent 4ed584d commit a4db2ba
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 16 deletions.
34 changes: 28 additions & 6 deletions api/trace/+opentelemetry/+trace/SpanContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@
% default option values
issampled = true;
isremote = true;
includets = false; % whether TraceState is specified
if nargin > 2
optionnames = ["IsSampled", "IsRemote"];
optionnames = ["IsSampled", "IsRemote", "TraceState"];
for i = 1:2:length(varargin)
try
namei = validatestring(varargin{i}, optionnames);
Expand All @@ -68,17 +69,37 @@
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
issampled = logical(valuei);
end
else % strcmp(namei, "IsRemote")
elseif strcmp(namei, "IsRemote")
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
isremote = logical(valuei);
end
else % strcmp(namei, "TraceState")
if isa(valuei, "dictionary")
try
tskeysi = string(keys(valuei));
tsvaluesi = string(values(valuei));
catch
% invalid TraceState, ignore
continue
end
tskeys = tskeysi;
tsvalues = tsvaluesi;
includets = true;
end
end
end
end

obj.Proxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.SpanContextProxy", ...
"ConstructorArguments", {traceid, spanid, issampled, isremote});
if includets
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.SpanContextProxy", ...
"ConstructorArguments", {traceid, spanid, issampled, ...
isremote, tskeys, tsvalues});
else
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.SpanContextProxy", ...
"ConstructorArguments", {traceid, spanid, issampled, isremote});
end
end
end
end
Expand All @@ -93,7 +114,8 @@
end

function tracestate = get.TraceState(obj)
tracestate = obj.Proxy.getTraceState();
[keys, values] = obj.Proxy.getTraceState();
tracestate = dictionary(keys, values);
end

function traceflags = get.TraceFlags(obj)
Expand Down
39 changes: 35 additions & 4 deletions api/trace/src/SpanContextProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "opentelemetry/trace/default_span.h"
#include "opentelemetry/trace/context.h"
#include "opentelemetry/trace/trace_flags.h"
#include "opentelemetry/trace/trace_state.h"

namespace common = opentelemetry::common;
namespace context_api = opentelemetry::context;
Expand All @@ -29,7 +30,23 @@ libmexclass::proxy::MakeResult SpanContextProxy::make(const libmexclass::proxy::
if (issampled) {
traceflags |= trace_api::TraceFlags::kIsSampled;
}
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});

if (constructor_arguments.getNumberOfElements() <= 4) {
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});
} else {
auto tracestate = trace_api::TraceState::GetDefault();
matlab::data::StringArray tracestatekeys_mda = constructor_arguments[4];
matlab::data::StringArray tracestatevalues_mda = constructor_arguments[5];
size_t ntskeys = tracestatekeys_mda.getNumberOfElements();
for (size_t i = 0; i < ntskeys; ++i) {
std::string tracestatekeyi = static_cast<std::string>(tracestatekeys_mda[i]),
tracestatevaluei = static_cast<std::string>(tracestatevalues_mda[i]);
if (tracestate->IsValidKey(tracestatekeyi) && tracestate->IsValidValue(tracestatevaluei)) {
tracestate = tracestate->Set(tracestatekeyi, tracestatevaluei);
}
}
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote, tracestate});
}
}

void SpanContextProxy::getTraceId(libmexclass::proxy::method::Context& context) {
Expand Down Expand Up @@ -71,11 +88,25 @@ void SpanContextProxy::getSpanId(libmexclass::proxy::method::Context& context) {
}

void SpanContextProxy::getTraceState(libmexclass::proxy::method::Context& context) {
nostd::shared_ptr<trace_api::TraceState> tracestate = CppSpanContext.trace_state();
std::list<std::string> keys;
std::list<std::string> values;

// repeatedly invoke the callback lambda to retrieve each entry
bool success = CppSpanContext.trace_state()->GetAllEntries(
[&keys, &values](nostd::string_view currkey, nostd::string_view currvalue) {
keys.push_back(std::string(currkey));
values.push_back(std::string(currvalue));

return true;
});

size_t nkeys = keys.size();
matlab::data::ArrayDimensions dims = {nkeys, 1};
matlab::data::ArrayFactory factory;
auto tracestate_mda = factory.createScalar(tracestate->ToHeader());
context.outputs[0] = tracestate_mda;
auto keys_mda = factory.createArray(dims, keys.cbegin(), keys.cend());
auto values_mda = factory.createArray(dims, values.cbegin(), values.cend());
context.outputs[0] = keys_mda;
context.outputs[1] = values_mda;
}

void SpanContextProxy::getTraceFlags(libmexclass::proxy::method::Context& context) {
Expand Down
10 changes: 7 additions & 3 deletions test/tcontextPropagation.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
TraceId
SpanId
TraceState
TraceStateString
Headers
BaggageKeys
BaggageValues
Expand All @@ -33,9 +34,10 @@ function setupOnce(testCase)
% simulate an HTTP header with relevant fields, used for extraction
testCase.TraceId = "0af7651916cd43dd8448eb211c80319c";
testCase.SpanId = "00f067aa0ba902b7";
testCase.TraceState = "foo=00f067aa0ba902b7";
testCase.TraceState = dictionary("foo", "00f067aa0ba902b7");
testCase.TraceStateString = "foo=00f067aa0ba902b7";
testCase.Headers = ["traceparent", "00-" + testCase.TraceId + ...
"-" + testCase.SpanId + "-01"; "tracestate", testCase.TraceState];
"-" + testCase.SpanId + "-01"; "tracestate", testCase.TraceStateString];
testCase.BaggageKeys = ["userId", "serverNode", "isProduction"];
testCase.BaggageValues = ["alice", "DF28", "false"];
testCase.BaggageHeaders = ["baggage", strjoin(strcat(testCase.BaggageKeys, ...
Expand Down Expand Up @@ -71,11 +73,13 @@ function testExtract(testCase)
results = readJsonResults(testCase);
results = results{1};

% check trace and parent IDs
% check trace and parent IDs, and span context
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.traceId), ...
testCase.TraceId);
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.parentSpanId), ...
testCase.SpanId);
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.traceState), ...
testCase.TraceStateString);
% check trace state in span context
spancontext = getSpanContext(sp);
verifyEqual(testCase, spancontext.TraceState, testCase.TraceState);
Expand Down
13 changes: 10 additions & 3 deletions test/ttrace.m
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ function testGetSpanContext(testCase)
results = readJsonResults(testCase);
verifyEqual(testCase, ctxt.TraceId, string(results{1}.resourceSpans.scopeSpans.spans.traceId));
verifyEqual(testCase, ctxt.SpanId, string(results{1}.resourceSpans.scopeSpans.spans.spanId));
verifyEqual(testCase, ctxt.TraceState, "");
verifyTrue(testCase, isa(ctxt.TraceState, "dictionary") && numEntries(ctxt.TraceState) == 0);
verifyEqual(testCase, ctxt.TraceFlags, "01"); % sampled flag should be on
end

Expand All @@ -265,13 +265,16 @@ function testSpanContext(testCase)
spanid = "0000000000111122";
issampled = false;
isremote = false;
tracestate = dictionary(["foo" "bar"], ["foo1" "bar1"]);
tracestate_str = "bar=bar1,foo=foo1";
sc = opentelemetry.trace.SpanContext(traceid, spanid, ...
"IsSampled", issampled, "IsRemote", isremote);
"IsSampled", issampled, "IsRemote", isremote, ...
"TraceState", tracestate);

% verify SpanContext object created correctly
verifyEqual(testCase, sc.TraceId, lower(traceid));
verifyEqual(testCase, sc.SpanId, spanid);
verifyEqual(testCase, sc.TraceState, "");
verifyEqual(testCase, sc.TraceState, tracestate);
verifyEqual(testCase, sc.TraceFlags, "00"); % sampled flag should be off
verifyEqual(testCase, isRemote(sc), isremote);

Expand All @@ -296,10 +299,14 @@ function testSpanContext(testCase)
lower(traceid));
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.parentSpanId), ...
spanid);
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.traceState), ...
tracestate_str);
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.traceId), ...
lower(traceid));
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.parentSpanId), ...
spanid);
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.traceState), ...
tracestate_str);
end

function testTime(testCase)
Expand Down

0 comments on commit a4db2ba

Please sign in to comment.