Skip to content

Commit 1aa60f8

Browse files
authored
Merge pull request #140 from mathworks/SpanContext
Support SpanContext creation
2 parents 96863e2 + daba015 commit 1aa60f8

File tree

8 files changed

+262
-24
lines changed

8 files changed

+262
-24
lines changed

api/context/+opentelemetry/+context/Context.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
% Copyright 2023-2024 The MathWorks, Inc.
66

77
properties (Access={?opentelemetry.context.propagation.TextMapPropagator, ...
8-
?opentelemetry.trace.Span, ?opentelemetry.trace.Tracer, ...
9-
?opentelemetry.logs.Logger, ?opentelemetry.baggage.Baggage})
8+
?opentelemetry.trace.Span, ?opentelemetry.trace.SpanContext, ...
9+
?opentelemetry.trace.Tracer, ?opentelemetry.logs.Logger, ...
10+
?opentelemetry.baggage.Baggage})
1011
Proxy % Proxy object to interface C++ code
1112
end
1213

api/trace/+opentelemetry/+trace/Context.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
classdef Context
22
% Tracing-related actions on context instances
33

4-
% Copyright 2023 The MathWorks, Inc.
4+
% Copyright 2023-2024 The MathWorks, Inc.
55

66
methods (Static)
77
function sp = extractSpan(context)
@@ -22,12 +22,12 @@
2222
function context = insertSpan(context, span)
2323
% Insert span into context
2424
% NEWCTXT = OPENTELEMETRY.TRACE.CONTEXT.INSERTSPAN(CTXT, SP) inserts
25-
% span SP into a context object CTXT and returns a new context.
25+
% span or span context SP into a context object CTXT and returns a new context.
2626
%
2727
% See also EXTRACTSPAN, OPENTELEMETRY.CONTEXT.CONTEXT
2828
arguments
2929
context (1,1) opentelemetry.context.Context
30-
span (1,1) opentelemetry.trace.Span
30+
span (1,1) {mustBeA(span, ["opentelemetry.trace.Span", "opentelemetry.trace.SpanContext"])}
3131
end
3232
context = span.insertSpan(context); % call span method
3333
end

api/trace/+opentelemetry/+trace/Link.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
classdef Link
22
% Specifies a link to a span
33

4-
% Copyright 2023 The MathWorks, Inc.
4+
% Copyright 2023-2024 The MathWorks, Inc.
55

66
properties (SetAccess=immutable)
7-
Target (1,1) opentelemetry.trace.SpanContext % Target span
7+
Target % Target span context
88
end
99

1010
properties (Access=?opentelemetry.trace.Tracer)

api/trace/+opentelemetry/+trace/Scope.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
% Controls the duration when a span is current. Deleting a scope object
33
% makes the associated span no longer current.
44

5-
% Copyright 2023 The MathWorks, Inc.
5+
% Copyright 2023-2024 The MathWorks, Inc.
66

77
properties (Access=private)
88
Proxy % Proxy object to interface C++ code
99
end
1010

11-
methods (Access=?opentelemetry.trace.Span)
11+
methods (Access={?opentelemetry.trace.Span, ...
12+
?opentelemetry.trace.SpanContext})
1213
function obj = Scope(proxy)
1314
obj.Proxy = proxy;
1415
end

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

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
classdef SpanContext < handle
22
% The part of a span that is propagated.
33

4-
% Copyright 2023 The MathWorks, Inc.
4+
% Copyright 2023-2024 The MathWorks, Inc.
55

66
properties (Dependent, SetAccess=private)
77
TraceId (1,1) string % Trace identifier represented as a string of 32 hexadecimal digits
@@ -14,14 +14,71 @@
1414
Proxy % Proxy object to interface C++ code
1515
end
1616

17-
methods (Access={?opentelemetry.trace.Span,?opentelemetry.trace.Link})
18-
function obj = SpanContext(proxy)
19-
if nargin < 1
17+
methods
18+
function obj = SpanContext(traceid, spanid, varargin)
19+
% Span context
20+
% SC = OPENTELEMETRY.TRACE.SPANCONTEXT(TRACEID, SPANID)
21+
% creates a span context with the specified trace and span
22+
% IDs. Trace and span IDs must be strings or char vectors
23+
% containing a hexadecimal number. Trace IDs must be 32
24+
% hexadecimal digits long and span IDs must be 16
25+
% hexadecimal digits long. Valid IDs must be non-zero.
26+
%
27+
% SC = OPENTELEMETRY.TRACE.SPANCONTEXT(TRACEID, SPANID,
28+
% PARAM1, VALUE1, PARAM2, VALUE2, ...) specifies optional
29+
% parameter name/value pairs. Parameters are:
30+
% "IsSampled" - Whether span is sampled. Default is
31+
% true.
32+
% "IsRemote" - Whether span is created in a remote
33+
% process. Default is true.
34+
35+
if nargin == 1 && isa(traceid, "libmexclass.proxy.Proxy")
36+
% internal calls to constructor with a proxy
37+
obj.Proxy = traceid;
38+
else
39+
narginchk(2, inf);
40+
traceid_len = 32;
41+
spanid_len = 16;
42+
if ~((isstring(traceid) || (ischar(traceid) && isrow(traceid))) && ...
43+
strlength(traceid) == traceid_len && all(isstrprop(traceid, "xdigit")))
44+
traceid = repmat('0', 1, traceid_len); % replace any illegal values with an all-zeros invalid ID
45+
end
46+
if ~((isstring(spanid) || (ischar(spanid) && isrow(spanid))) && ...
47+
strlength(spanid) == spanid_len && all(isstrprop(spanid, "xdigit")))
48+
spanid = repmat('0', 1, spanid_len); % replace any illegal values with an all-zeros invalid ID
49+
end
50+
% convert IDs from string to uint8 array
51+
traceid = uint8(hex2dec(reshape(char(traceid), 2, traceid_len/2).'));
52+
spanid = uint8(hex2dec(reshape(char(spanid), 2, spanid_len/2).'));
53+
54+
% default option values
55+
issampled = true;
56+
isremote = true;
57+
if nargin > 2
58+
optionnames = ["IsSampled", "IsRemote"];
59+
for i = 1:2:length(varargin)
60+
try
61+
namei = validatestring(varargin{i}, optionnames);
62+
catch
63+
% invalid option, ignore
64+
continue
65+
end
66+
valuei = varargin{i+1};
67+
if strcmp(namei, "IsSampled")
68+
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
69+
issampled = logical(valuei);
70+
end
71+
else % strcmp(namei, "IsRemote")
72+
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
73+
isremote = logical(valuei);
74+
end
75+
end
76+
end
77+
end
78+
2079
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
2180
"libmexclass.opentelemetry.SpanContextProxy", ...
22-
"ConstructorArguments", {});
23-
else
24-
obj.Proxy = proxy;
81+
"ConstructorArguments", {traceid, spanid, issampled, isremote});
2582
end
2683
end
2784
end
@@ -70,6 +127,46 @@
70127
% ISSAMPLED, ISVALID
71128
tf = obj.Proxy.isRemote();
72129
end
130+
131+
function scope = makeCurrent(obj)
132+
% MAKECURRENT Make span the current span
133+
% SCOPE = MAKECURRENT(SPCTXT) makes the span represented by
134+
% span context SPCTXT as the current span, by
135+
% inserting it into the current context. Returns a scope
136+
% object SCOPE that determines the duration when span is current.
137+
% When SCOPE is deleted, span will no longer be current.
138+
%
139+
% See also OPENTELEMETRY.CONTEXT.CONTEXT,
140+
% OPENTELEMETRY.GETCURRENTCONTEXT, OPENTELEMETRY.TRACE.SCOPE
141+
142+
% return a warning if no output specified
143+
if nargout == 0
144+
warning("opentelemetry:trace:SpanContext:makeCurrent:NoOutputSpecified", ...
145+
"Calling makeCurrent without specifying an output has no effect.")
146+
end
147+
id = obj.Proxy.makeCurrent();
148+
scopeproxy = libmexclass.proxy.Proxy("Name", ...
149+
"libmexclass.opentelemetry.ScopeProxy", "ID", id);
150+
scope = opentelemetry.trace.Scope(scopeproxy);
151+
end
152+
153+
function context = insertSpan(obj, context)
154+
% INSERTSPAN Insert span into a context and return a new context.
155+
% NEWCTXT = INSERTSPAN(SPCTXT, CTXT) inserts the span
156+
% represented by span context SPCTXT into context CTXT and
157+
% returns a new context.
158+
%
159+
% NEWCTXT = INSERTSPAN(SPCTXT) inserts into the current context.
160+
%
161+
% See also OPENTELEMETRY.TRACE.CONTEXT.EXTRACTSPAN
162+
if nargin < 2
163+
context = opentelemetry.context.getCurrentContext();
164+
end
165+
contextid = obj.Proxy.insertSpan(context.Proxy.ID);
166+
contextproxy = libmexclass.proxy.Proxy("Name", ...
167+
"libmexclass.opentelemetry.ContextProxy", "ID", contextid);
168+
context = opentelemetry.context.Context(contextproxy);
169+
end
73170
end
74171

75172
end

api/trace/include/opentelemetry-matlab/trace/SpanContextProxy.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
// Copyright 2023 The MathWorks, Inc.
1+
// Copyright 2023-2024 The MathWorks, Inc.
22

33
#pragma once
44

55
#include "libmexclass/proxy/Proxy.h"
66
#include "libmexclass/proxy/method/Context.h"
77

88
#include "opentelemetry/trace/span_context.h"
9+
#include "opentelemetry/trace/trace_id.h"
10+
#include "opentelemetry/trace/span_id.h"
11+
#include "opentelemetry/trace/trace_flags.h"
912

1013
namespace trace_api = opentelemetry::trace;
1114
namespace nostd = opentelemetry::nostd;
@@ -22,12 +25,11 @@ class SpanContextProxy : public libmexclass::proxy::Proxy {
2225
REGISTER_METHOD(SpanContextProxy, isSampled);
2326
REGISTER_METHOD(SpanContextProxy, isValid);
2427
REGISTER_METHOD(SpanContextProxy, isRemote);
28+
REGISTER_METHOD(SpanContextProxy, makeCurrent);
29+
REGISTER_METHOD(SpanContextProxy, insertSpan);
2530
}
2631

27-
// dummy make static method, to satisfy proxy registration
28-
static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments) {
29-
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{false, false});
30-
}
32+
static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments);
3133

3234
trace_api::SpanContext getInstance() {
3335
return CppSpanContext;
@@ -47,6 +49,10 @@ class SpanContextProxy : public libmexclass::proxy::Proxy {
4749

4850
void isRemote(libmexclass::proxy::method::Context& context);
4951

52+
void makeCurrent(libmexclass::proxy::method::Context& context);
53+
54+
void insertSpan(libmexclass::proxy::method::Context& context);
55+
5056
private:
5157

5258
trace_api::SpanContext CppSpanContext;

api/trace/src/SpanContextProxy.cpp

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
1-
// Copyright 2023 The MathWorks, Inc.
1+
// Copyright 2023-2024 The MathWorks, Inc.
22

33
#include "opentelemetry-matlab/trace/SpanContextProxy.h"
4+
#include "opentelemetry-matlab/trace/ScopeProxy.h"
5+
#include "opentelemetry-matlab/context/ContextProxy.h"
46

57
#include "libmexclass/proxy/ProxyManager.h"
68

9+
#include "opentelemetry/trace/default_span.h"
10+
#include "opentelemetry/trace/context.h"
11+
#include "opentelemetry/trace/trace_flags.h"
12+
713
namespace common = opentelemetry::common;
14+
namespace context_api = opentelemetry::context;
815

916
namespace libmexclass::opentelemetry {
17+
18+
libmexclass::proxy::MakeResult SpanContextProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) {
19+
matlab::data::TypedArray<uint8_t> traceid_mda = constructor_arguments[0];
20+
trace_api::TraceId traceid(nostd::span<const uint8_t, trace_api::TraceId::kSize>(&(*traceid_mda.cbegin()), 16));
21+
matlab::data::TypedArray<uint8_t> spanid_mda = constructor_arguments[1];
22+
trace_api::SpanId spanid{nostd::span<const uint8_t, trace_api::SpanId::kSize>(&(*spanid_mda.cbegin()), 8)};
23+
matlab::data::TypedArray<bool> issampled_mda = constructor_arguments[2];
24+
bool issampled = issampled_mda[0];
25+
matlab::data::TypedArray<bool> isremote_mda = constructor_arguments[3];
26+
bool isremote = isremote_mda[0];
27+
28+
uint8_t traceflags = 0;
29+
if (issampled) {
30+
traceflags |= trace_api::TraceFlags::kIsSampled;
31+
}
32+
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});
33+
}
34+
1035
void SpanContextProxy::getTraceId(libmexclass::proxy::method::Context& context) {
1136
const trace_api::TraceId& tid = CppSpanContext.trace_id();
1237

@@ -90,4 +115,43 @@ void SpanContextProxy::isRemote(libmexclass::proxy::method::Context& context) {
90115
context.outputs[0] = remote_mda;
91116
}
92117

118+
void SpanContextProxy::makeCurrent(libmexclass::proxy::method::Context& context) {
119+
// create a default span to associate with span context
120+
auto cppspan = nostd::shared_ptr<trace_api::Span>(new trace_api::DefaultSpan(CppSpanContext));
121+
122+
// instantiate a ScopeProxy instance
123+
auto scproxy = std::shared_ptr<libmexclass::proxy::Proxy>(new ScopeProxy{cppspan});
124+
125+
// obtain a proxy ID
126+
libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(scproxy);
127+
128+
// return the ID
129+
matlab::data::ArrayFactory factory;
130+
auto proxyid_mda = factory.createScalar<libmexclass::proxy::ID>(proxyid);
131+
context.outputs[0] = proxyid_mda;
132+
}
133+
134+
void SpanContextProxy::insertSpan(libmexclass::proxy::method::Context& context) {
135+
matlab::data::TypedArray<uint64_t> contextid_mda = context.inputs[0];
136+
libmexclass::proxy::ID contextid = contextid_mda[0];
137+
138+
// create a default span to associate with span context
139+
auto cppspan = nostd::shared_ptr<trace_api::Span>(new trace_api::DefaultSpan(CppSpanContext));
140+
141+
context_api::Context ctxt = std::static_pointer_cast<ContextProxy>(
142+
libmexclass::proxy::ProxyManager::getProxy(contextid))->getInstance();
143+
context_api::Context newctxt = trace_api::SetSpan(ctxt, cppspan);
144+
145+
// instantiate a ContextProxy instance
146+
auto ctxtproxy = std::shared_ptr<libmexclass::proxy::Proxy>(new ContextProxy(std::move(newctxt)));
147+
148+
// obtain a proxy ID
149+
libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(ctxtproxy);
150+
151+
// return the ID
152+
matlab::data::ArrayFactory factory;
153+
auto proxyid_mda = factory.createScalar<libmexclass::proxy::ID>(proxyid);
154+
context.outputs[0] = proxyid_mda;
155+
}
156+
93157
} // namespace libmexclass::opentelemetry

0 commit comments

Comments
 (0)