Skip to content

Commit b41694c

Browse files
committed
Support shutdown and forceflush of API TracerProvider objects
1 parent baf7bab commit b41694c

File tree

5 files changed

+161
-64
lines changed

5 files changed

+161
-64
lines changed

api/trace/+opentelemetry/+trace/TracerProvider.m

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
% Copyright 2023 The MathWorks, Inc.
66

7-
properties (Access=protected)
7+
properties (Access={?opentelemetry.sdk.trace.TracerProvider, ...
8+
?opentelemetry.sdk.trace.Cleanup})
89
Proxy % Proxy object to interface C++ code
910
end
1011

@@ -59,4 +60,11 @@ function setTracerProvider(obj)
5960
obj.Proxy.setTracerProvider();
6061
end
6162
end
63+
64+
methods(Access=?opentelemetry.sdk.trace.Cleanup)
65+
function postShutdown(obj)
66+
% POSTSHUTDOWN Handle post-shutdown tasks
67+
obj.Proxy.postShutdown();
68+
end
69+
end
6270
end

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "opentelemetry/exporters/ostream/span_exporter_factory.h"
1313
#include "opentelemetry/trace/tracer_provider.h"
1414
#include "opentelemetry/trace/provider.h"
15+
#include "opentelemetry/trace/noop.h"
1516

1617
#define OTEL_MATLAB_VERSION "0.1.0"
1718

@@ -28,6 +29,7 @@ class TracerProviderProxy : public libmexclass::proxy::Proxy {
2829
TracerProviderProxy(nostd::shared_ptr<trace_api::TracerProvider> tp) : CppTracerProvider(tp) {
2930
REGISTER_METHOD(TracerProviderProxy, getTracer);
3031
REGISTER_METHOD(TracerProviderProxy, setTracerProvider);
32+
REGISTER_METHOD(TracerProviderProxy, postShutdown);
3133
}
3234

3335
// Static make method should only be used by getTracerProvider. It gets the global instance
@@ -40,6 +42,15 @@ class TracerProviderProxy : public libmexclass::proxy::Proxy {
4042

4143
void setTracerProvider(libmexclass::proxy::method::Context& context);
4244

45+
nostd::shared_ptr<trace_api::TracerProvider> getInstance() {
46+
return CppTracerProvider;
47+
}
48+
49+
void postShutdown(libmexclass::proxy::method::Context& context) {
50+
// Replace tracer provider with a no-op instance. Subsequent tracers and spans won't be recorded
51+
CppTracerProvider.swap(nostd::shared_ptr<trace_api::TracerProvider>(new trace_api::NoopTracerProvider));
52+
}
53+
4354
protected:
4455
nostd::shared_ptr<trace_api::TracerProvider> CppTracerProvider;
4556
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
classdef Cleanup
2+
% Clean up methods for TracerProvider in the API
3+
4+
% Copyright 2023 The MathWorks, Inc.
5+
6+
methods (Static)
7+
function success = shutdown(tp)
8+
% SHUTDOWN Shutdown
9+
% SUCCESS = SHUTDOWN(TP) shuts down all span processors associated with
10+
% API tracer provider TP and return a logical that indicates
11+
% whether shutdown was successful.
12+
%
13+
% See also FORCEFLUSH
14+
15+
% return false if input is not the right type
16+
if isa(tp, "opentelemetry.trace.TracerProvider")
17+
% convert to TracerProvider class in sdk
18+
tpsdk = opentelemetry.sdk.trace.TracerProvider(tp.Proxy);
19+
success = tpsdk.shutdown;
20+
postShutdown(tp);
21+
else
22+
success = false;
23+
end
24+
end
25+
26+
function success = forceFlush(tp, timeout)
27+
% FORCEFLUSH Force flush
28+
% SUCCESS = FORCEFLUSH(TP) immediately exports all spans
29+
% that have not yet been exported. Returns a logical that
30+
% indicates whether force flush was successful.
31+
%
32+
% SUCCESS = FORCEFLUSH(TP, TIMEOUT) specifies a TIMEOUT
33+
% duration. Force flush must be completed within this time,
34+
% or else it will fail.
35+
%
36+
% See also SHUTDOWN
37+
38+
% return false if input is not the right type
39+
if isa(tp, "opentelemetry.trace.TracerProvider")
40+
% convert to TracerProvider class in sdk
41+
tpsdk = opentelemetry.sdk.trace.TracerProvider(tp.Proxy);
42+
if nargin < 2 || ~isa(timeout, "duration")
43+
success = tpsdk.forceFlush;
44+
else
45+
success = tpsdk.forceFlush(timeout);
46+
end
47+
else
48+
success = false;
49+
end
50+
end
51+
end
52+
53+
end

sdk/trace/+opentelemetry/+sdk/+trace/TracerProvider.m

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,58 +38,72 @@
3838
% OPENTELEMETRY.SDK.TRACE.PARENTBASEDSAMPLER
3939

4040
arguments
41-
processor {mustBeA(processor, "opentelemetry.sdk.trace.SpanProcessor")} = ...
41+
processor {mustBeA(processor, ["opentelemetry.sdk.trace.SpanProcessor", ...
42+
"libmexclass.proxy.Proxy"])} = ...
4243
opentelemetry.sdk.trace.SimpleSpanProcessor()
4344
end
4445

4546
arguments (Repeating)
4647
optionnames (1,:) {mustBeTextScalar}
4748
optionvalues
4849
end
49-
50+
5051
% explicit call to superclass constructor to make it a no-op
5152
5253

53-
validnames = ["Sampler", "Resource"];
54-
foundsampler = false;
55-
resourcekeys = string.empty();
56-
resourcevalues = {};
57-
resource = dictionary(resourcekeys, resourcevalues);
58-
for i = 1:length(optionnames)
59-
namei = validatestring(optionnames{i}, validnames);
60-
valuei = optionvalues{i};
61-
if strcmp(namei, "Sampler")
62-
if ~isa(valuei, "opentelemetry.sdk.trace.Sampler")
63-
error("opentelemetry:InvalidSamplerType", ...
64-
"Sampler must be an instance of one of the sampler classes");
65-
end
66-
sampler = valuei;
67-
foundsampler = true;
68-
else % "Resource"
69-
if ~isa(valuei, "dictionary")
70-
error("opentelemetry:InvalidResourceType", ...
71-
"Attibutes input must be a dictionary.");
72-
end
73-
resource = valuei;
74-
resourcekeys = keys(valuei);
75-
resourcevalues = values(valuei,"cell");
76-
% collapse one level of cells, as this may be due to
77-
% a behavior of dictionary.values
78-
if all(cellfun(@iscell, resourcevalues))
79-
resourcevalues = [resourcevalues{:}];
54+
if isa(processor, "libmexclass.proxy.Proxy")
55+
% This code branch is used to support conversion from API
56+
% TracerProvider to SDK equivalent, needed internally by
57+
% opentelemetry.sdk.trace.Cleanup
58+
tpproxy = processor; % rename the variable
59+
assert(tpproxy.Name == "libmexclass.opentelemetry.TracerProviderProxy");
60+
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
61+
"libmexclass.opentelemetry.sdk.TracerProviderProxy", ...
62+
"ConstructorArguments", {tpproxy.ID});
63+
% leave other properties unassigned, they won't be used
64+
else
65+
% Code branch for construction from inputs
66+
validnames = ["Sampler", "Resource"];
67+
foundsampler = false;
68+
resourcekeys = string.empty();
69+
resourcevalues = {};
70+
resource = dictionary(resourcekeys, resourcevalues);
71+
for i = 1:length(optionnames)
72+
namei = validatestring(optionnames{i}, validnames);
73+
valuei = optionvalues{i};
74+
if strcmp(namei, "Sampler")
75+
if ~isa(valuei, "opentelemetry.sdk.trace.Sampler")
76+
error("opentelemetry:InvalidSamplerType", ...
77+
"Sampler must be an instance of one of the sampler classes");
78+
end
79+
sampler = valuei;
80+
foundsampler = true;
81+
else % "Resource"
82+
if ~isa(valuei, "dictionary")
83+
error("opentelemetry:InvalidResourceType", ...
84+
"Attibutes input must be a dictionary.");
85+
end
86+
resource = valuei;
87+
resourcekeys = keys(valuei);
88+
resourcevalues = values(valuei,"cell");
89+
% collapse one level of cells, as this may be due to
90+
% a behavior of dictionary.values
91+
if all(cellfun(@iscell, resourcevalues))
92+
resourcevalues = [resourcevalues{:}];
93+
end
8094
end
8195
end
96+
if ~foundsampler
97+
sampler = opentelemetry.sdk.trace.AlwaysOnSampler;
98+
end
99+
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
100+
"libmexclass.opentelemetry.sdk.TracerProviderProxy", ...
101+
"ConstructorArguments", {processor.Proxy.ID, sampler.Proxy.ID, ...
102+
resourcekeys, resourcevalues});
103+
obj.SpanProcessor = processor;
104+
obj.Sampler = sampler;
105+
obj.Resource = resource;
82106
end
83-
if ~foundsampler
84-
sampler = opentelemetry.sdk.trace.AlwaysOnSampler;
85-
end
86-
obj.Proxy = libmexclass.proxy.Proxy("Name", ...
87-
"libmexclass.opentelemetry.sdk.TracerProviderProxy", ...
88-
"ConstructorArguments", {processor.Proxy.ID, sampler.Proxy.ID, ...
89-
resourcekeys, resourcevalues});
90-
obj.SpanProcessor = processor;
91-
obj.Sampler = sampler;
92-
obj.Resource = resource;
93107
end
94108

95109
function addSpanProcessor(obj, processor)

sdk/trace/src/TracerProviderProxy.cpp

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,39 +22,50 @@ namespace nostd = opentelemetry::nostd;
2222

2323
namespace libmexclass::opentelemetry::sdk {
2424
libmexclass::proxy::MakeResult TracerProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) {
25-
matlab::data::TypedArray<uint64_t> processorid_mda = constructor_arguments[0];
26-
libmexclass::proxy::ID processorid = processorid_mda[0];
27-
matlab::data::TypedArray<uint64_t> samplerid_mda = constructor_arguments[1];
28-
libmexclass::proxy::ID samplerid = samplerid_mda[0];
29-
matlab::data::StringArray resourcenames_mda = constructor_arguments[2];
30-
size_t nresourceattrs = resourcenames_mda.getNumberOfElements();
31-
matlab::data::CellArray resourcevalues_mda = constructor_arguments[3];
25+
libmexclass::proxy::MakeResult out;
26+
if (constructor_arguments.getNumberOfElements() == 1) {
27+
// if only one input, assume it is an API Tracer Provider to support type conversion
28+
matlab::data::TypedArray<uint64_t> tpid_mda = constructor_arguments[0];
29+
libmexclass::proxy::ID tpid = tpid_mda[0];
30+
auto tp = std::static_pointer_cast<libmexclass::opentelemetry::TracerProviderProxy>(
31+
libmexclass::proxy::ProxyManager::getProxy(tpid))->getInstance();
32+
out = std::make_shared<TracerProviderProxy>(nostd::shared_ptr<trace_api::TracerProvider>(tp));
33+
} else {
34+
matlab::data::TypedArray<uint64_t> processorid_mda = constructor_arguments[0];
35+
libmexclass::proxy::ID processorid = processorid_mda[0];
36+
matlab::data::TypedArray<uint64_t> samplerid_mda = constructor_arguments[1];
37+
libmexclass::proxy::ID samplerid = samplerid_mda[0];
38+
matlab::data::StringArray resourcenames_mda = constructor_arguments[2];
39+
size_t nresourceattrs = resourcenames_mda.getNumberOfElements();
40+
matlab::data::CellArray resourcevalues_mda = constructor_arguments[3];
3241

33-
auto processor = std::static_pointer_cast<SpanProcessorProxy>(
42+
auto processor = std::static_pointer_cast<SpanProcessorProxy>(
3443
libmexclass::proxy::ProxyManager::getProxy(processorid))->getInstance();
35-
auto sampler = std::static_pointer_cast<SamplerProxy>(
44+
auto sampler = std::static_pointer_cast<SamplerProxy>(
3645
libmexclass::proxy::ProxyManager::getProxy(samplerid))->getInstance();
3746

38-
// resource
39-
std::list<std::pair<std::string, common::AttributeValue> > resourceattrs;
40-
std::list<std::vector<double> > resourcedims_double; // list of vector, to hold the dimensions of array attributes
41-
std::list<std::string> string_resource_attrs; // list of strings as a buffer to hold the string attributes
42-
std::list<std::vector<nostd::string_view> > stringview_resource_attrs; // list of vector of stringviews, used for string array attributes only
43-
for (size_t i = 0; i < nresourceattrs; ++i) {
44-
std::string resourcename = static_cast<std::string>(resourcenames_mda[i]);
45-
matlab::data::Array resourcevalue = resourcevalues_mda[i];
47+
// resource
48+
std::list<std::pair<std::string, common::AttributeValue> > resourceattrs;
49+
std::list<std::vector<double> > resourcedims_double; // list of vector, to hold the dimensions of array attributes
50+
std::list<std::string> string_resource_attrs; // list of strings as a buffer to hold the string attributes
51+
std::list<std::vector<nostd::string_view> > stringview_resource_attrs; // list of vector of stringviews, used for string array attributes only
52+
for (size_t i = 0; i < nresourceattrs; ++i) {
53+
std::string resourcename = static_cast<std::string>(resourcenames_mda[i]);
54+
matlab::data::Array resourcevalue = resourcevalues_mda[i];
4655

47-
processAttribute(resourcename, resourcevalue, resourceattrs, string_resource_attrs, stringview_resource_attrs, resourcedims_double);
48-
}
49-
auto resource_default = resource::Resource::Create({ {"telemetry.sdk.language", "MATLAB"},
56+
processAttribute(resourcename, resourcevalue, resourceattrs, string_resource_attrs, stringview_resource_attrs, resourcedims_double);
57+
}
58+
auto resource_default = resource::Resource::Create({ {"telemetry.sdk.language", "MATLAB"},
5059
{"telemetry.sdk.version", OTEL_MATLAB_VERSION} });
51-
auto resource_custom = resource::Resource::Create(common::KeyValueIterableView{resourceattrs});
52-
// the order matters, default resource must come after custom. Otherwise the default resource will be overwritten.
53-
auto resource_merged = resource_custom.Merge(resource_default);
60+
auto resource_custom = resource::Resource::Create(common::KeyValueIterableView{resourceattrs});
61+
// the order matters, default resource must come after custom. Otherwise the default resource will be overwritten.
62+
auto resource_merged = resource_custom.Merge(resource_default);
5463

55-
return std::make_shared<TracerProviderProxy>(nostd::shared_ptr<trace_api::TracerProvider>(
64+
out = std::make_shared<TracerProviderProxy>(nostd::shared_ptr<trace_api::TracerProvider>(
5665
std::move(trace_sdk::TracerProviderFactory::Create(std::move(processor), resource_merged,
5766
std::move(sampler)))));
67+
}
68+
return out;
5869
}
5970

6071
void TracerProviderProxy::addSpanProcessor(libmexclass::proxy::method::Context& context) {

0 commit comments

Comments
 (0)