diff --git a/CMakeLists.txt b/CMakeLists.txt index 8370a83..ad8b889 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,7 @@ set(OPENTELEMETRY_PROXY_SOURCES ${TRACE_API_SOURCE_DIR}/SpanProxy.cpp ${TRACE_API_SOURCE_DIR}/SpanContextProxy.cpp ${COMMON_API_SOURCE_DIR}/attribute.cpp + ${METRICS_API_SOURCE_DIR}/MeterProviderProxy.cpp ${METRICS_API_SOURCE_DIR}/MeterProxy.cpp ${METRICS_API_SOURCE_DIR}/CounterProxy.cpp ${METRICS_API_SOURCE_DIR}/UpDownCounterProxy.cpp diff --git a/OtelMatlabProxyFactory.cpp b/OtelMatlabProxyFactory.cpp index 5b3302c..8b04cf7 100644 --- a/OtelMatlabProxyFactory.cpp +++ b/OtelMatlabProxyFactory.cpp @@ -37,6 +37,7 @@ libmexclass::proxy::MakeResult OtelMatlabProxyFactory::make_proxy(const libmexclass::proxy::ClassName& class_name, const libmexclass::proxy::FunctionArguments& constructor_arguments) { + REGISTER_PROXY(libmexclass.opentelemetry.MeterProviderProxy, libmexclass::opentelemetry::MeterProviderProxy); REGISTER_PROXY(libmexclass.opentelemetry.TracerProviderProxy, libmexclass::opentelemetry::TracerProviderProxy); //REGISTER_PROXY(libmexclass.opentelemetry.TracerProxy, libmexclass::opentelemetry::TracerProxy); REGISTER_PROXY(libmexclass.opentelemetry.SpanProxy, libmexclass::opentelemetry::SpanProxy); diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m index 4e49684..b399bfd 100644 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -14,7 +14,7 @@ Proxy % Proxy object to interface C++ code end - methods (Access={?opentelemetry.sdk.metrics.MeterProvider}) + methods (Access={?opentelemetry.sdk.metrics.MeterProvider, ?opentelemetry.metrics.MeterProvider}) function obj = Meter(proxy, mtname, mtversion, mtschema) % Private constructor. Use getMeter method of MeterProvider diff --git a/api/metrics/+opentelemetry/+metrics/MeterProvider.m b/api/metrics/+opentelemetry/+metrics/MeterProvider.m new file mode 100644 index 0000000..ec3b615 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/MeterProvider.m @@ -0,0 +1,62 @@ +classdef MeterProvider < handle + % A meter provider stores a set of configurations used in a distributed + % metrics system. + + % Copyright 2023 The MathWorks, Inc. + + properties (Access={?opentelemetry.sdk.metrics.MeterProvider}) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.metrics.Provider, ?opentelemetry.sdk.metrics.MeterProvider}) + function obj = MeterProvider(skip) + % constructor + % "skip" input signals skipping construction + if nargin < 1 || skip ~= "skip" + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.MeterProviderProxy", ... + "ConstructorArguments", {}); + end + end + end + + methods + function meter = getMeter(obj, mname, mversion, mschema) + % GETMETER Create a meter object used to generate metrics. + % M = GETMETER(MP, NAME) returns a meter with the name + % NAME that uses all the configurations specified in meter + % provider MP. + % + % M = GETMETER(MP, NAME, VERSION, SCHEMA) also specifies + % the meter version and the URL that documents the schema + % of the generated meters. + % + % See also OPENTELEMETRY.METRICS.METER + arguments + obj + mname + mversion = "" + mschema = "" + end + % name, version, schema accepts any types that can convert to a + % string + import opentelemetry.common.mustBeScalarString + mname = mustBeScalarString(mname); + mversion = mustBeScalarString(mversion); + mschema = mustBeScalarString(mschema); + id = obj.Proxy.getMeter(mname, mversion, mschema); + meterproxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.MeterProxy", "ID", id); + meter = opentelemetry.metrics.Meter(meterproxy, mname, mversion, mschema); + end + + function setMeterProvider(obj) + % SETMETERPROVIDER Set global instance of meter provider + % SETMETERPROVIDER(MP) sets the meter provider MP as + % the global instance. + % + % See also OPENTELEMETRY.METRICS.PROVIDER.GETMETERPROVIDER + obj.Proxy.setMeterProvider(); + end + end +end diff --git a/api/metrics/+opentelemetry/+metrics/Provider.m b/api/metrics/+opentelemetry/+metrics/Provider.m new file mode 100644 index 0000000..beca183 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Provider.m @@ -0,0 +1,27 @@ +classdef Provider +% Get and set the global instance of meter provider + +% Copyright 2023 The MathWorks, Inc. + + methods (Static) + function p = getMeterProvider() + % Get the global instance of meter provider + % MP = OPENTELEMETRY.METRICS.PROVIDER.GETMETERPROVIDER gets + % the global instance of meter provider. + % + % See also OPENTELEMETRY.METRICS.PROVIDER.SETMETERPROVIDER + + p = opentelemetry.metrics.MeterProvider(); + end + + function setMeterProvider(p) + % Set the global instance of meter provider + % OPENTELEMETRY.METRICS.PROVIDER.GETMETERPROVIDER(MP) sets + % MP as the global instance of meter provider. + % + % See also OPENTELEMETRY.METRICS.PROVIDER.GETMETERPROVIDER + p.setMeterProvider(); + end + end + +end diff --git a/api/metrics/+opentelemetry/+metrics/getMeter.m b/api/metrics/+opentelemetry/+metrics/getMeter.m new file mode 100644 index 0000000..7bd3efb --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/getMeter.m @@ -0,0 +1,17 @@ +function meter = getMeter(mname, varargin) +% Create a meter from the global meter provider instance +% M = OPENTELEMETRY.METRICS.GETMETER(NAME) returns a meter with the +% specified name created from the global meter provider instance. +% +% M = OPENTELEMETRY.METRICS.GETMETER(NAME, VERSION, SCHEMA) also +% specifies the meter version and the URL that documents the schema +% of the generated metrics. +% +% See also OPENTELEMETRY.SDK.METRICS.METERPROVIDER, +% OPENTELEMETRY.METRICS.METER, +% OPENTELEMETRY.METRICS.PROVIDER.SETMETERPROVIDER + +% Copyright 2023 The MathWorks, Inc. + +provider = opentelemetry.metrics.Provider.getMeterProvider(); +meter = getMeter(provider, mname, varargin{:}); diff --git a/api/metrics/include/opentelemetry-matlab/metrics/MeterProviderProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/MeterProviderProxy.h new file mode 100644 index 0000000..8ff1dd8 --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/MeterProviderProxy.h @@ -0,0 +1,43 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/sdk/metrics/meter_provider_factory.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h" +#include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/metrics/provider.h" +#include "opentelemetry/metrics/noop.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class MeterProviderProxy : public libmexclass::proxy::Proxy { + public: + MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { + REGISTER_METHOD(MeterProviderProxy, getMeter); + REGISTER_METHOD(MeterProviderProxy, setMeterProvider); + } + + // Static make method should only be used by getMeterProvider. It gets the global instance + // instead of creating a new instance + static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + return std::make_shared(metrics_api::Provider::GetMeterProvider()); + } + + void getMeter(libmexclass::proxy::method::Context& context); + + void setMeterProvider(libmexclass::proxy::method::Context& context); + + nostd::shared_ptr getInstance() { + return CppMeterProvider; + } + + protected: + nostd::shared_ptr CppMeterProvider; +}; +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/MeterProviderProxy.cpp b/api/metrics/src/MeterProviderProxy.cpp new file mode 100644 index 0000000..4263170 --- /dev/null +++ b/api/metrics/src/MeterProviderProxy.cpp @@ -0,0 +1,39 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/MeterProviderProxy.h" +#include "opentelemetry-matlab/metrics/MeterProxy.h" +#include "libmexclass/proxy/ProxyManager.h" + +#include "opentelemetry/metrics/provider.h" + +#include "MatlabDataArray.hpp" +namespace libmexclass::opentelemetry { +void MeterProviderProxy::getMeter(libmexclass::proxy::method::Context& context) { + // Always assumes 3 inputs + matlab::data::StringArray name_mda = context.inputs[0]; + std::string name = static_cast(name_mda[0]); + matlab::data::StringArray version_mda = context.inputs[1]; + std::string version = static_cast(version_mda[0]); + matlab::data::StringArray schema_mda = context.inputs[2]; + std::string schema = static_cast(schema_mda[0]); + + auto mt = CppMeterProvider->GetMeter(name, version, schema); + + // instantiate a MeterProxy instance + MeterProxy* newproxy = new MeterProxy(mt); + auto mtproxy = std::shared_ptr(newproxy); + + // obtain a proxy ID + libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(mtproxy); + + // return the ID + matlab::data::ArrayFactory factory; + auto proxyid_mda = factory.createScalar(proxyid); + context.outputs[0] = proxyid_mda; +} + +void MeterProviderProxy::setMeterProvider(libmexclass::proxy::method::Context& context) { + metrics_api::Provider::SetMeterProvider(CppMeterProvider); +} + +} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index b5ed475..c6c780e 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -1,13 +1,9 @@ -classdef MeterProvider < handle +classdef MeterProvider < opentelemetry.metrics.MeterProvider & handle % An SDK implementation of meter provider, which stores a set of configurations used % in a metrics system. % Copyright 2023 The MathWorks, Inc. - properties (Access=private) - Proxy - end - properties (Access=public) MetricReader end @@ -37,6 +33,9 @@ "libmexclass.proxy.Proxy"])} = ... opentelemetry.sdk.metrics.PeriodicExportingMetricReader() end + + % explicit call to superclass constructor to make it a no-op + obj@opentelemetry.metrics.MeterProvider("skip"); obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... @@ -44,27 +43,6 @@ obj.MetricReader = reader; end - - function meter = getMeter(obj, mtname, mtversion, mtschema) - arguments - obj - mtname - mtversion = "" - mtschema = "" - end - % name, version, schema accepts any types that can convert to a - % string - import opentelemetry.common.mustBeScalarString - mtname = mustBeScalarString(mtname); - mtversion = mustBeScalarString(mtversion); - mtschema = mustBeScalarString(mtschema); - id = obj.Proxy.getMeter(mtname, mtversion, mtschema); - Meterproxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.MeterProxy", "ID", id); - meter = opentelemetry.metrics.Meter(Meterproxy, mtname, mtversion, mtschema); - end - - function addMetricReader(obj, reader) arguments obj @@ -73,15 +51,6 @@ function addMetricReader(obj, reader) obj.Proxy.addMetricReader(reader.Proxy.ID); obj.MetricReader = [obj.MetricReader, reader]; end - - function success = shutdown(obj) - if ~obj.isShutdown - success = obj.Proxy.shutdown(); - obj.isShutdown = success; - else - success = true; - end - end end end diff --git a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h index cc1f8eb..9412a7a 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -23,6 +23,7 @@ #include "opentelemetry/sdk/metrics/push_metric_exporter.h" #include "opentelemetry-matlab/metrics/MeterProxy.h" +#include "opentelemetry-matlab/metrics/MeterProviderProxy.h" namespace metrics_api = opentelemetry::metrics; namespace nostd = opentelemetry::nostd; @@ -32,20 +33,14 @@ namespace otlpexporter = opentelemetry::exporter::otlp; namespace libmexclass::opentelemetry::sdk { -class MeterProviderProxy : public libmexclass::proxy::Proxy { +class MeterProviderProxy : public libmexclass::opentelemetry::MeterProviderProxy { public: - MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { - REGISTER_METHOD(MeterProviderProxy, getMeter); + MeterProviderProxy(nostd::shared_ptr mp) : libmexclass::opentelemetry::MeterProviderProxy(mp) { REGISTER_METHOD(MeterProviderProxy, addMetricReader); } static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); - void getMeter(libmexclass::proxy::method::Context& context); - void addMetricReader(libmexclass::proxy::method::Context& context); - - protected: - nostd::shared_ptr CppMeterProvider; }; } // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index 1fa9587..7b57f09 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -27,32 +27,6 @@ libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy return out; } -void MeterProviderProxy::getMeter(libmexclass::proxy::method::Context& context) { - // Always assumes 3 inputs - matlab::data::StringArray name_mda = context.inputs[0]; - std::string name = static_cast(name_mda[0]); - matlab::data::StringArray version_mda = context.inputs[1]; - std::string version = static_cast(version_mda[0]); - matlab::data::StringArray schema_mda = context.inputs[2]; - std::string schema = static_cast(schema_mda[0]); - - auto mt = CppMeterProvider->GetMeter(name, version, schema); - - // instantiate a MeterProxy instance - MeterProxy* newproxy = new MeterProxy(mt); - auto mtproxy = std::shared_ptr(newproxy); - - // obtain a proxy ID - libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(mtproxy); - - // return the ID - matlab::data::ArrayFactory factory; - auto proxyid_mda = factory.createScalar(proxyid); - context.outputs[0] = proxyid_mda; -} - - - void MeterProviderProxy::addMetricReader(libmexclass::proxy::method::Context& context) { matlab::data::TypedArray readerid_mda = context.inputs[0]; libmexclass::proxy::ID readerid = readerid_mda[0]; @@ -64,6 +38,4 @@ void MeterProviderProxy::addMetricReader(libmexclass::proxy::method::Context& co } - - } // namespace libmexclass::opentelemetry diff --git a/test/tmetrics.m b/test/tmetrics.m index 212f1f0..3cfa779 100644 --- a/test/tmetrics.m +++ b/test/tmetrics.m @@ -537,6 +537,39 @@ function testHistogramDelta(testCase) verifyEqual(testCase, str2double(counts{len}), sum(currentvals>bounds(len-1))); end end + + % function testGetSetMeterProvider(testCase) + % % testGetSetMeterProvider: setting and getting global instance of MeterProvider + % exporter = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(); + % reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(exporter, ... + % "Interval", seconds(2), "Timeout", seconds(1)); + % mp = opentelemetry.sdk.metrics.MeterProvider(reader); + % setMeterProvider(mp); + % + % metername = "foo"; + % countername = "bar"; + % m = opentelemetry.metrics.getMeter(metername); + % c = createCounter(m, countername); + % + % % create testing value + % val = 10; + % + % % add value and attributes + % c.add(val); + % + % pause(2.5); + % + % % perform test comparisons + % clear mp; + % results = readJsonResults(testCase); + % results = results{1}; + % % check a counter has been created, and check its resource to identify the + % % correct MeterProvider has been used + % verifyNotEmpty(testCase, results); + % + % verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); + % verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + % end end