Skip to content

Commit

Permalink
Merge pull request #140 from mathworks/SpanContext
Browse files Browse the repository at this point in the history
Support SpanContext creation
  • Loading branch information
duncanpo authored Jul 19, 2024
2 parents 96863e2 + daba015 commit 1aa60f8
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 24 deletions.
5 changes: 3 additions & 2 deletions api/context/+opentelemetry/+context/Context.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
% Copyright 2023-2024 The MathWorks, Inc.

properties (Access={?opentelemetry.context.propagation.TextMapPropagator, ...
?opentelemetry.trace.Span, ?opentelemetry.trace.Tracer, ...
?opentelemetry.logs.Logger, ?opentelemetry.baggage.Baggage})
?opentelemetry.trace.Span, ?opentelemetry.trace.SpanContext, ...
?opentelemetry.trace.Tracer, ?opentelemetry.logs.Logger, ...
?opentelemetry.baggage.Baggage})
Proxy % Proxy object to interface C++ code
end

Expand Down
6 changes: 3 additions & 3 deletions api/trace/+opentelemetry/+trace/Context.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classdef Context
% Tracing-related actions on context instances

% Copyright 2023 The MathWorks, Inc.
% Copyright 2023-2024 The MathWorks, Inc.

methods (Static)
function sp = extractSpan(context)
Expand All @@ -22,12 +22,12 @@
function context = insertSpan(context, span)
% Insert span into context
% NEWCTXT = OPENTELEMETRY.TRACE.CONTEXT.INSERTSPAN(CTXT, SP) inserts
% span SP into a context object CTXT and returns a new context.
% span or span context SP into a context object CTXT and returns a new context.
%
% See also EXTRACTSPAN, OPENTELEMETRY.CONTEXT.CONTEXT
arguments
context (1,1) opentelemetry.context.Context
span (1,1) opentelemetry.trace.Span
span (1,1) {mustBeA(span, ["opentelemetry.trace.Span", "opentelemetry.trace.SpanContext"])}
end
context = span.insertSpan(context); % call span method
end
Expand Down
4 changes: 2 additions & 2 deletions api/trace/+opentelemetry/+trace/Link.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
classdef Link
% Specifies a link to a span

% Copyright 2023 The MathWorks, Inc.
% Copyright 2023-2024 The MathWorks, Inc.

properties (SetAccess=immutable)
Target (1,1) opentelemetry.trace.SpanContext % Target span
Target % Target span context
end

properties (Access=?opentelemetry.trace.Tracer)
Expand Down
5 changes: 3 additions & 2 deletions api/trace/+opentelemetry/+trace/Scope.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
% Controls the duration when a span is current. Deleting a scope object
% makes the associated span no longer current.

% Copyright 2023 The MathWorks, Inc.
% Copyright 2023-2024 The MathWorks, Inc.

properties (Access=private)
Proxy % Proxy object to interface C++ code
end

methods (Access=?opentelemetry.trace.Span)
methods (Access={?opentelemetry.trace.Span, ...
?opentelemetry.trace.SpanContext})
function obj = Scope(proxy)
obj.Proxy = proxy;
end
Expand Down
111 changes: 104 additions & 7 deletions api/trace/+opentelemetry/+trace/SpanContext.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classdef SpanContext < handle
% The part of a span that is propagated.

% Copyright 2023 The MathWorks, Inc.
% Copyright 2023-2024 The MathWorks, Inc.

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

methods (Access={?opentelemetry.trace.Span,?opentelemetry.trace.Link})
function obj = SpanContext(proxy)
if nargin < 1
methods
function obj = SpanContext(traceid, spanid, varargin)
% Span context
% SC = OPENTELEMETRY.TRACE.SPANCONTEXT(TRACEID, SPANID)
% creates a span context with the specified trace and span
% IDs. Trace and span IDs must be strings or char vectors
% containing a hexadecimal number. Trace IDs must be 32
% hexadecimal digits long and span IDs must be 16
% hexadecimal digits long. Valid IDs must be non-zero.
%
% SC = OPENTELEMETRY.TRACE.SPANCONTEXT(TRACEID, SPANID,
% PARAM1, VALUE1, PARAM2, VALUE2, ...) specifies optional
% parameter name/value pairs. Parameters are:
% "IsSampled" - Whether span is sampled. Default is
% true.
% "IsRemote" - Whether span is created in a remote
% process. Default is true.

if nargin == 1 && isa(traceid, "libmexclass.proxy.Proxy")
% internal calls to constructor with a proxy
obj.Proxy = traceid;
else
narginchk(2, inf);
traceid_len = 32;
spanid_len = 16;
if ~((isstring(traceid) || (ischar(traceid) && isrow(traceid))) && ...
strlength(traceid) == traceid_len && all(isstrprop(traceid, "xdigit")))
traceid = repmat('0', 1, traceid_len); % replace any illegal values with an all-zeros invalid ID
end
if ~((isstring(spanid) || (ischar(spanid) && isrow(spanid))) && ...
strlength(spanid) == spanid_len && all(isstrprop(spanid, "xdigit")))
spanid = repmat('0', 1, spanid_len); % replace any illegal values with an all-zeros invalid ID
end
% convert IDs from string to uint8 array
traceid = uint8(hex2dec(reshape(char(traceid), 2, traceid_len/2).'));
spanid = uint8(hex2dec(reshape(char(spanid), 2, spanid_len/2).'));

% default option values
issampled = true;
isremote = true;
if nargin > 2
optionnames = ["IsSampled", "IsRemote"];
for i = 1:2:length(varargin)
try
namei = validatestring(varargin{i}, optionnames);
catch
% invalid option, ignore
continue
end
valuei = varargin{i+1};
if strcmp(namei, "IsSampled")
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
issampled = logical(valuei);
end
else % strcmp(namei, "IsRemote")
if (isnumeric(valuei) || islogical(valuei)) && isscalar(valuei)
isremote = logical(valuei);
end
end
end
end

obj.Proxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.SpanContextProxy", ...
"ConstructorArguments", {});
else
obj.Proxy = proxy;
"ConstructorArguments", {traceid, spanid, issampled, isremote});
end
end
end
Expand Down Expand Up @@ -70,6 +127,46 @@
% ISSAMPLED, ISVALID
tf = obj.Proxy.isRemote();
end

function scope = makeCurrent(obj)
% MAKECURRENT Make span the current span
% SCOPE = MAKECURRENT(SPCTXT) makes the span represented by
% span context SPCTXT as the current span, by
% inserting it into the current context. Returns a scope
% object SCOPE that determines the duration when span is current.
% When SCOPE is deleted, span will no longer be current.
%
% See also OPENTELEMETRY.CONTEXT.CONTEXT,
% OPENTELEMETRY.GETCURRENTCONTEXT, OPENTELEMETRY.TRACE.SCOPE

% return a warning if no output specified
if nargout == 0
warning("opentelemetry:trace:SpanContext:makeCurrent:NoOutputSpecified", ...
"Calling makeCurrent without specifying an output has no effect.")
end
id = obj.Proxy.makeCurrent();
scopeproxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.ScopeProxy", "ID", id);
scope = opentelemetry.trace.Scope(scopeproxy);
end

function context = insertSpan(obj, context)
% INSERTSPAN Insert span into a context and return a new context.
% NEWCTXT = INSERTSPAN(SPCTXT, CTXT) inserts the span
% represented by span context SPCTXT into context CTXT and
% returns a new context.
%
% NEWCTXT = INSERTSPAN(SPCTXT) inserts into the current context.
%
% See also OPENTELEMETRY.TRACE.CONTEXT.EXTRACTSPAN
if nargin < 2
context = opentelemetry.context.getCurrentContext();
end
contextid = obj.Proxy.insertSpan(context.Proxy.ID);
contextproxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.ContextProxy", "ID", contextid);
context = opentelemetry.context.Context(contextproxy);
end
end

end
16 changes: 11 additions & 5 deletions api/trace/include/opentelemetry-matlab/trace/SpanContextProxy.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Copyright 2023 The MathWorks, Inc.
// Copyright 2023-2024 The MathWorks, Inc.

#pragma once

#include "libmexclass/proxy/Proxy.h"
#include "libmexclass/proxy/method/Context.h"

#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/trace_id.h"
#include "opentelemetry/trace/span_id.h"
#include "opentelemetry/trace/trace_flags.h"

namespace trace_api = opentelemetry::trace;
namespace nostd = opentelemetry::nostd;
Expand All @@ -22,12 +25,11 @@ class SpanContextProxy : public libmexclass::proxy::Proxy {
REGISTER_METHOD(SpanContextProxy, isSampled);
REGISTER_METHOD(SpanContextProxy, isValid);
REGISTER_METHOD(SpanContextProxy, isRemote);
REGISTER_METHOD(SpanContextProxy, makeCurrent);
REGISTER_METHOD(SpanContextProxy, insertSpan);
}

// dummy make static method, to satisfy proxy registration
static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments) {
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{false, false});
}
static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments);

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

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

void makeCurrent(libmexclass::proxy::method::Context& context);

void insertSpan(libmexclass::proxy::method::Context& context);

private:

trace_api::SpanContext CppSpanContext;
Expand Down
66 changes: 65 additions & 1 deletion api/trace/src/SpanContextProxy.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
// Copyright 2023 The MathWorks, Inc.
// Copyright 2023-2024 The MathWorks, Inc.

#include "opentelemetry-matlab/trace/SpanContextProxy.h"
#include "opentelemetry-matlab/trace/ScopeProxy.h"
#include "opentelemetry-matlab/context/ContextProxy.h"

#include "libmexclass/proxy/ProxyManager.h"

#include "opentelemetry/trace/default_span.h"
#include "opentelemetry/trace/context.h"
#include "opentelemetry/trace/trace_flags.h"

namespace common = opentelemetry::common;
namespace context_api = opentelemetry::context;

namespace libmexclass::opentelemetry {

libmexclass::proxy::MakeResult SpanContextProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) {
matlab::data::TypedArray<uint8_t> traceid_mda = constructor_arguments[0];
trace_api::TraceId traceid(nostd::span<const uint8_t, trace_api::TraceId::kSize>(&(*traceid_mda.cbegin()), 16));
matlab::data::TypedArray<uint8_t> spanid_mda = constructor_arguments[1];
trace_api::SpanId spanid{nostd::span<const uint8_t, trace_api::SpanId::kSize>(&(*spanid_mda.cbegin()), 8)};
matlab::data::TypedArray<bool> issampled_mda = constructor_arguments[2];
bool issampled = issampled_mda[0];
matlab::data::TypedArray<bool> isremote_mda = constructor_arguments[3];
bool isremote = isremote_mda[0];

uint8_t traceflags = 0;
if (issampled) {
traceflags |= trace_api::TraceFlags::kIsSampled;
}
return std::make_shared<SpanContextProxy>(trace_api::SpanContext{traceid, spanid, trace_api::TraceFlags(traceflags), isremote});
}

void SpanContextProxy::getTraceId(libmexclass::proxy::method::Context& context) {
const trace_api::TraceId& tid = CppSpanContext.trace_id();

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

void SpanContextProxy::makeCurrent(libmexclass::proxy::method::Context& context) {
// create a default span to associate with span context
auto cppspan = nostd::shared_ptr<trace_api::Span>(new trace_api::DefaultSpan(CppSpanContext));

// instantiate a ScopeProxy instance
auto scproxy = std::shared_ptr<libmexclass::proxy::Proxy>(new ScopeProxy{cppspan});

// obtain a proxy ID
libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(scproxy);

// return the ID
matlab::data::ArrayFactory factory;
auto proxyid_mda = factory.createScalar<libmexclass::proxy::ID>(proxyid);
context.outputs[0] = proxyid_mda;
}

void SpanContextProxy::insertSpan(libmexclass::proxy::method::Context& context) {
matlab::data::TypedArray<uint64_t> contextid_mda = context.inputs[0];
libmexclass::proxy::ID contextid = contextid_mda[0];

// create a default span to associate with span context
auto cppspan = nostd::shared_ptr<trace_api::Span>(new trace_api::DefaultSpan(CppSpanContext));

context_api::Context ctxt = std::static_pointer_cast<ContextProxy>(
libmexclass::proxy::ProxyManager::getProxy(contextid))->getInstance();
context_api::Context newctxt = trace_api::SetSpan(ctxt, cppspan);

// instantiate a ContextProxy instance
auto ctxtproxy = std::shared_ptr<libmexclass::proxy::Proxy>(new ContextProxy(std::move(newctxt)));

// obtain a proxy ID
libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(ctxtproxy);

// return the ID
matlab::data::ArrayFactory factory;
auto proxyid_mda = factory.createScalar<libmexclass::proxy::ID>(proxyid);
context.outputs[0] = proxyid_mda;
}

} // namespace libmexclass::opentelemetry
Loading

0 comments on commit 1aa60f8

Please sign in to comment.