Skip to content

Commit d50ab17

Browse files
authored
Merge pull request #148 from mathworks/tracestate
Properly support TraceState
2 parents 4ed584d + d01f306 commit d50ab17

File tree

4 files changed

+82
-16
lines changed

4 files changed

+82
-16
lines changed

api/trace/+opentelemetry/+trace/SpanContext.m

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@
5454
% default option values
5555
issampled = true;
5656
isremote = true;
57+
includets = false; % whether TraceState is specified
5758
if nargin > 2
58-
optionnames = ["IsSampled", "IsRemote"];
59+
optionnames = ["IsSampled", "IsRemote", "TraceState"];
5960
for i = 1:2:length(varargin)
6061
try
6162
namei = validatestring(varargin{i}, optionnames);
@@ -68,17 +69,37 @@
6869
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
6970
issampled = logical(valuei);
7071
end
71-
else % strcmp(namei, "IsRemote")
72+
elseif strcmp(namei, "IsRemote")
7273
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
7374
isremote = logical(valuei);
7475
end
76+
else % strcmp(namei, "TraceState")
77+
if isa(valuei, "dictionary")
78+
try
79+
tskeysi = string(keys(valuei));
80+
tsvaluesi = string(values(valuei));
81+
catch
82+
% invalid TraceState, ignore
83+
continue
84+
end
85+
tskeys = tskeysi;
86+
tsvalues = tsvaluesi;
87+
includets = true;
88+
end
7589
end
7690
end
7791
end
7892

79-
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
80-
"libmexclass.opentelemetry.SpanContextProxy", ...
81-
"ConstructorArguments", {traceid, spanid, issampled, isremote});
93+
if includets
94+
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
95+
"libmexclass.opentelemetry.SpanContextProxy", ...
96+
"ConstructorArguments", {traceid, spanid, issampled, ...
97+
isremote, tskeys, tsvalues});
98+
else
99+
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
100+
"libmexclass.opentelemetry.SpanContextProxy", ...
101+
"ConstructorArguments", {traceid, spanid, issampled, isremote});
102+
end
82103
end
83104
end
84105
end
@@ -93,7 +114,8 @@
93114
end
94115

95116
function tracestate = get.TraceState(obj)
96-
tracestate = obj.Proxy.getTraceState();
117+
[keys, values] = obj.Proxy.getTraceState();
118+
tracestate = dictionary(keys, values);
97119
end
98120

99121
function traceflags = get.TraceFlags(obj)

api/trace/src/SpanContextProxy.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include "opentelemetry/trace/default_span.h"
1010
#include "opentelemetry/trace/context.h"
1111
#include "opentelemetry/trace/trace_flags.h"
12+
#include "opentelemetry/trace/trace_state.h"
13+
14+
#include <list>
1215

1316
namespace common = opentelemetry::common;
1417
namespace context_api = opentelemetry::context;
@@ -29,7 +32,23 @@ libmexclass::proxy::MakeResult SpanContextProxy::make(const libmexclass::proxy::
2932
if (issampled) {
3033
traceflags |= trace_api::TraceFlags::kIsSampled;
3134
}
32-
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});
35+
36+
if (constructor_arguments.getNumberOfElements() <= 4) {
37+
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});
38+
} else {
39+
auto tracestate = trace_api::TraceState::GetDefault();
40+
matlab::data::StringArray tracestatekeys_mda = constructor_arguments[4];
41+
matlab::data::StringArray tracestatevalues_mda = constructor_arguments[5];
42+
size_t ntskeys = tracestatekeys_mda.getNumberOfElements();
43+
for (size_t i = 0; i < ntskeys; ++i) {
44+
std::string tracestatekeyi = static_cast<std::string>(tracestatekeys_mda[i]),
45+
tracestatevaluei = static_cast<std::string>(tracestatevalues_mda[i]);
46+
if (tracestate->IsValidKey(tracestatekeyi) && tracestate->IsValidValue(tracestatevaluei)) {
47+
tracestate = tracestate->Set(tracestatekeyi, tracestatevaluei);
48+
}
49+
}
50+
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote, tracestate});
51+
}
3352
}
3453

3554
void SpanContextProxy::getTraceId(libmexclass::proxy::method::Context& context) {
@@ -71,11 +90,25 @@ void SpanContextProxy::getSpanId(libmexclass::proxy::method::Context& context) {
7190
}
7291

7392
void SpanContextProxy::getTraceState(libmexclass::proxy::method::Context& context) {
74-
nostd::shared_ptr<trace_api::TraceState> tracestate = CppSpanContext.trace_state();
93+
std::list<std::string> keys;
94+
std::list<std::string> values;
95+
96+
// repeatedly invoke the callback lambda to retrieve each entry
97+
bool success = CppSpanContext.trace_state()->GetAllEntries(
98+
[&keys, &values](nostd::string_view currkey, nostd::string_view currvalue) {
99+
keys.push_back(std::string(currkey));
100+
values.push_back(std::string(currvalue));
101+
102+
return true;
103+
});
75104

105+
size_t nkeys = keys.size();
106+
matlab::data::ArrayDimensions dims = {nkeys, 1};
76107
matlab::data::ArrayFactory factory;
77-
auto tracestate_mda = factory.createScalar(tracestate->ToHeader());
78-
context.outputs[0] = tracestate_mda;
108+
auto keys_mda = factory.createArray(dims, keys.cbegin(), keys.cend());
109+
auto values_mda = factory.createArray(dims, values.cbegin(), values.cend());
110+
context.outputs[0] = keys_mda;
111+
context.outputs[1] = values_mda;
79112
}
80113

81114
void SpanContextProxy::getTraceFlags(libmexclass::proxy::method::Context& context) {

test/tcontextPropagation.m

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
TraceId
1818
SpanId
1919
TraceState
20+
TraceStateString
2021
Headers
2122
BaggageKeys
2223
BaggageValues
@@ -33,9 +34,10 @@ function setupOnce(testCase)
3334
% simulate an HTTP header with relevant fields, used for extraction
3435
testCase.TraceId = "0af7651916cd43dd8448eb211c80319c";
3536
testCase.SpanId = "00f067aa0ba902b7";
36-
testCase.TraceState = "foo=00f067aa0ba902b7";
37+
testCase.TraceState = dictionary("foo", "00f067aa0ba902b7");
38+
testCase.TraceStateString = "foo=00f067aa0ba902b7";
3739
testCase.Headers = ["traceparent", "00-" + testCase.TraceId + ...
38-
"-" + testCase.SpanId + "-01"; "tracestate", testCase.TraceState];
40+
"-" + testCase.SpanId + "-01"; "tracestate", testCase.TraceStateString];
3941
testCase.BaggageKeys = ["userId", "serverNode", "isProduction"];
4042
testCase.BaggageValues = ["alice", "DF28", "false"];
4143
testCase.BaggageHeaders = ["baggage", strjoin(strcat(testCase.BaggageKeys, ...
@@ -71,11 +73,13 @@ function testExtract(testCase)
7173
results = readJsonResults(testCase);
7274
results = results{1};
7375

74-
% check trace and parent IDs
76+
% check trace and parent IDs, and span context
7577
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.traceId), ...
7678
testCase.TraceId);
7779
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.parentSpanId), ...
7880
testCase.SpanId);
81+
verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.traceState), ...
82+
testCase.TraceStateString);
7983
% check trace state in span context
8084
spancontext = getSpanContext(sp);
8185
verifyEqual(testCase, spancontext.TraceState, testCase.TraceState);

test/ttrace.m

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ function testGetSpanContext(testCase)
254254
results = readJsonResults(testCase);
255255
verifyEqual(testCase, ctxt.TraceId, string(results{1}.resourceSpans.scopeSpans.spans.traceId));
256256
verifyEqual(testCase, ctxt.SpanId, string(results{1}.resourceSpans.scopeSpans.spans.spanId));
257-
verifyEqual(testCase, ctxt.TraceState, "");
257+
verifyTrue(testCase, isa(ctxt.TraceState, "dictionary") && numEntries(ctxt.TraceState) == 0);
258258
verifyEqual(testCase, ctxt.TraceFlags, "01"); % sampled flag should be on
259259
end
260260

@@ -265,13 +265,16 @@ function testSpanContext(testCase)
265265
spanid = "0000000000111122";
266266
issampled = false;
267267
isremote = false;
268+
tracestate = dictionary(["foo" "bar"], ["foo1" "bar1"]);
269+
tracestate_str = "bar=bar1,foo=foo1";
268270
sc = opentelemetry.trace.SpanContext(traceid, spanid, ...
269-
"IsSampled", issampled, "IsRemote", isremote);
271+
"IsSampled", issampled, "IsRemote", isremote, ...
272+
"TraceState", tracestate);
270273

271274
% verify SpanContext object created correctly
272275
verifyEqual(testCase, sc.TraceId, lower(traceid));
273276
verifyEqual(testCase, sc.SpanId, spanid);
274-
verifyEqual(testCase, sc.TraceState, "");
277+
verifyEqual(testCase, sc.TraceState, tracestate);
275278
verifyEqual(testCase, sc.TraceFlags, "00"); % sampled flag should be off
276279
verifyEqual(testCase, isRemote(sc), isremote);
277280

@@ -296,10 +299,14 @@ function testSpanContext(testCase)
296299
lower(traceid));
297300
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.parentSpanId), ...
298301
spanid);
302+
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.traceState), ...
303+
tracestate_str);
299304
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.traceId), ...
300305
lower(traceid));
301306
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.parentSpanId), ...
302307
spanid);
308+
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.traceState), ...
309+
tracestate_str);
303310
end
304311

305312
function testTime(testCase)

0 commit comments

Comments
 (0)