diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index 7c47bd0..7c5ce87 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -10,11 +10,12 @@ properties (Access=public) MetricReader + Resource end methods - function obj = MeterProvider(reader) - % SDK implementation of tracer provider + function obj = MeterProvider(reader, optionnames, optionvalues) + % SDK implementation of meter provider % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER creates a meter % provider that uses a periodic exporting metric reader and default configurations. % @@ -38,6 +39,11 @@ opentelemetry.sdk.metrics.PeriodicExportingMetricReader() end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + % explicit call to superclass constructor to make it a no-op obj@opentelemetry.metrics.MeterProvider("skip"); @@ -49,13 +55,37 @@ assert(mpproxy.Name == "libmexclass.opentelemetry.MeterProviderProxy"); obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... - "ConstructorArguments", {mpproxy.ID, true}); + "ConstructorArguments", {mpproxy.ID}); % leave other properties unassigned, they won't be used else + validnames = ["Resource"]; + resourcekeys = string.empty(); + resourcevalues = {}; + resource = dictionary(resourcekeys, resourcevalues); + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + if strcmp(namei, "Resource") + if ~isa(valuei, "dictionary") + error("opentelemetry:sdk:metrics:MeterProvider:InvalidResourceType", ... + "Resource input must be a dictionary."); + end + resource = valuei; + resourcekeys = keys(valuei); + resourcevalues = values(valuei,"cell"); + % collapse one level of cells, as this may be due to + % a behavior of dictionary.values + if all(cellfun(@iscell, resourcevalues)) + resourcevalues = [resourcevalues{:}]; + end + end + end + obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... - "ConstructorArguments", {reader.Proxy.ID, false}); + "ConstructorArguments", {reader.Proxy.ID, resourcekeys, resourcevalues}); obj.MetricReader = reader; + obj.Resource = resource; 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 53a5dee..8d6d144 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -21,9 +21,14 @@ #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/meter_provider_factory.h" #include "opentelemetry/sdk/metrics/push_metric_exporter.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/metrics/view/view_registry.h" +#include "opentelemetry/sdk/metrics/view/view_registry_factory.h" + #include "opentelemetry-matlab/metrics/MeterProxy.h" #include "opentelemetry-matlab/metrics/MeterProviderProxy.h" +#include "opentelemetry-matlab/sdk/common/resource.h" namespace metrics_api = opentelemetry::metrics; namespace nostd = opentelemetry::nostd; diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index e3f5f28..b70706d 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -11,9 +11,8 @@ namespace libmexclass::opentelemetry::sdk { libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { libmexclass::proxy::MakeResult out; - matlab::data::TypedArray is_api = constructor_arguments[1]; - if (is_api[0]) { - // if argument is 1, assume it is an API Meter Provider to support type conversion + if (constructor_arguments.getNumberOfElements() == 1) { + // if only one input, assume it is an API Meter Provider to support type conversion matlab::data::TypedArray mpid_mda = constructor_arguments[0]; libmexclass::proxy::ID mpid = mpid_mda[0]; auto mp = std::static_pointer_cast( @@ -29,12 +28,20 @@ libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy matlab::data::TypedArray readerid_mda = constructor_arguments[0]; libmexclass::proxy::ID readerid = readerid_mda[0]; + matlab::data::StringArray resourcenames_mda = constructor_arguments[1]; + size_t nresourceattrs = resourcenames_mda.getNumberOfElements(); + matlab::data::CellArray resourcevalues_mda = constructor_arguments[2]; + + auto resource_custom = createResource(resourcenames_mda, resourcevalues_mda); + auto reader = std::static_pointer_cast( libmexclass::proxy::ProxyManager::getProxy(readerid))->getInstance(); - auto p = metrics_sdk::MeterProviderFactory::Create(); + + auto view = metrics_sdk::ViewRegistryFactory::Create(); + auto p = metrics_sdk::MeterProviderFactory::Create(std::move(view), resource_custom); auto *p_sdk = static_cast(p.get()); p_sdk->AddMetricReader(std::move(reader)); - + auto p_out = nostd::shared_ptr(std::move(p)); out = std::make_shared(p_out); } diff --git a/test/tmetrics_sdk.m b/test/tmetrics_sdk.m new file mode 100644 index 0000000..528e7ec --- /dev/null +++ b/test/tmetrics_sdk.m @@ -0,0 +1,69 @@ +classdef tmetrics_sdk < matlab.unittest.TestCase + % tests for metrics SDK + + % Copyright 2023 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + commonSetupOnce(testCase); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testCustomResource(testCase) + % testCustomResource: check custom resources are included in + % emitted metrics + commonSetup(testCase) + + customkeys = ["foo" "bar"]; + customvalues = [1 5]; + exporter = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(exporter, ... + "Interval", seconds(2), "Timeout", seconds(1)); + mp = opentelemetry.sdk.metrics.MeterProvider(reader, ... + "Resource", dictionary(customkeys, customvalues)); + + m = getMeter(mp, "mymeter"); + c = createCounter(m, "mycounter"); + + % create testing value + val = 10; + + % add value and attributes + c.add(val); + + pause(2.5); + + % perform test comparisons + results = readJsonResults(testCase); + results = results{1}; + + resourcekeys = string({results.resourceMetrics.resource.attributes.key}); + for i = length(customkeys) + idx = find(resourcekeys == customkeys(i)); + verifyNotEmpty(testCase, idx); + verifyEqual(testCase, results.resourceMetrics.resource.attributes(idx).value.doubleValue, customvalues(i)); + end + end + + end +end \ No newline at end of file