From 89df507cb7157a01914dd1c8f88693aa002f44cf Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 28 Jul 2023 16:10:05 -0400 Subject: [PATCH 01/30] Initial submit of metrics --- CMakeLists.txt | 9 +++-- OtelMatlabProxyFactory.cpp | 4 +++ .../+sdk/+metrics/MeterProvider.m | 36 +++++++++++++++++++ .../sdk/metrics/MeterProviderProxy.h | 24 +++++++++++++ sdk/metrics/src/MeterProviderProxy.cpp | 19 ++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m create mode 100644 sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h create mode 100644 sdk/metrics/src/MeterProviderProxy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index da4e649..bf853b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,8 +74,9 @@ set(TRACE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/include) set(CONTEXT_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/include) set(BAGGAGE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/include) set(TRACE_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/include) +set(METRICS_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/include) set(OTLP_EXPORTER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/include) -set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory) set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -83,6 +84,7 @@ set(TRACE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/src) set(CONTEXT_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/src) set(BAGGAGE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/src) set(TRACE_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/src) +set(METRICS_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/src) set(OTLP_EXPORTER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/src) set(OPENTELEMETRY_PROXY_SOURCES ${OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR}/${OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME}.cpp @@ -99,7 +101,8 @@ set(OPENTELEMETRY_PROXY_SOURCES ${TRACE_SDK_SOURCE_DIR}/TracerProviderProxy.cpp ${TRACE_SDK_SOURCE_DIR}/SimpleSpanProcessorProxy.cpp ${TRACE_SDK_SOURCE_DIR}/BatchSpanProcessorProxy.cpp - ${TRACE_SDK_SOURCE_DIR}/ParentBasedSamplerProxy.cpp) + ${TRACE_SDK_SOURCE_DIR}/ParentBasedSamplerProxy.cpp + ${METRICS_SDK_SOURCE_DIR}/MeterProviderProxy.cpp) if(WITH_OTLP_HTTP) set(OPENTELEMETRY_PROXY_SOURCES ${OPENTELEMETRY_PROXY_SOURCES} ${OTLP_EXPORTER_SOURCE_DIR}/OtlpHttpSpanExporterProxy.cpp) @@ -217,6 +220,7 @@ set(CONTEXT_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/context/+opentele set(BAGGAGE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/+opentelemetry) set(UTILS_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/utils/+opentelemetry) set(TRACE_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/+opentelemetry) +set(METRICS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/+opentelemetry) set(DEFAULT_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultSpanExporter.m) set(OTLP_HTTP_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m) set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m) @@ -228,6 +232,7 @@ install(DIRECTORY ${CONTEXT_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${BAGGAGE_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${UTILS_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${TRACE_SDK_MATLAB_SOURCES} DESTINATION .) +install(DIRECTORY ${METRICS_SDK_MATLAB_SOURCES} DESTINATION .) install(FILES ${DEFAULT_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) if(WITH_OTLP_HTTP) install(FILES ${OTLP_HTTP_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) diff --git a/OtelMatlabProxyFactory.cpp b/OtelMatlabProxyFactory.cpp index a2f1315..925094d 100644 --- a/OtelMatlabProxyFactory.cpp +++ b/OtelMatlabProxyFactory.cpp @@ -22,6 +22,7 @@ #include "opentelemetry-matlab/sdk/trace/AlwaysOffSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/TraceIdRatioBasedSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/ParentBasedSamplerProxy.h" +#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" #ifdef WITH_OTLP_HTTP #include "opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h" #endif @@ -54,6 +55,9 @@ OtelMatlabProxyFactory::make_proxy(const libmexclass::proxy::ClassName& class_na REGISTER_PROXY(libmexclass.opentelemetry.sdk.AlwaysOffSamplerProxy, libmexclass::opentelemetry::sdk::AlwaysOffSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.TraceIdRatioBasedSamplerProxy, libmexclass::opentelemetry::sdk::TraceIdRatioBasedSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.ParentBasedSamplerProxy, libmexclass::opentelemetry::sdk::ParentBasedSamplerProxy); + + REGISTER_PROXY(libmexclass.opentelemetry.sdk.MeterProviderProxy, libmexclass::opentelemetry::sdk::MeterProviderProxy); + #ifdef WITH_OTLP_HTTP REGISTER_PROXY(libmexclass.opentelemetry.exporters.OtlpHttpSpanExporterProxy, libmexclass::opentelemetry::exporters::OtlpHttpSpanExporterProxy); #endif diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m new file mode 100644 index 0000000..e475131 --- /dev/null +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -0,0 +1,36 @@ +classdef 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 + + methods + function obj = MeterProvider() + % SDK implementation of tracer provider + % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER creates a meter + % provider that uses a periodic exporting metric reader and default configurations. + % + % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R) uses metric + % reader R. Currently, the only supported metric reader is the periodic + % exporting metric reader. + % + % TP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R, PARAM1, VALUE1, + % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. + % Parameters are: + % "View" - View object used to customize collected metrics. + % "Resource" - Additional resource attributes. + % Specified as a dictionary. + % + % See also OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER + % OPENTELEMETRY.SDK.METRICS.VIEW + + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... + "ConstructorArguments", {}); + 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 new file mode 100644 index 0000000..af34198 --- /dev/null +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -0,0 +1,24 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter_provider.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry::sdk { +class MeterProviderProxy : public libmexclass::proxy::Proxy { + public: + MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { + } + + static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); + + protected: + nostd::shared_ptr CppMeterProvider; +}; +} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp new file mode 100644 index 0000000..d2f8f55 --- /dev/null +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -0,0 +1,19 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + +#include "opentelemetry/sdk/metrics/meter_provider_factory.h" + +namespace metrics_sdk = opentelemetry::sdk::metrics; + +namespace libmexclass::opentelemetry::sdk { +libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + libmexclass::proxy::MakeResult out; + out = std::make_shared(nostd::shared_ptr( + std::move(metrics_sdk::MeterProviderFactory::Create()))); + return out; +} + +} // namespace libmexclass::opentelemetry From 9533d736ca6ae9a4e82f0d93f47be2f3bfbaeb21 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 28 Jul 2023 23:39:55 -0400 Subject: [PATCH 02/30] Update build.yml to use latest version of opentelemetry-cpp --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 310c7d5..a06f075 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: with: repository: open-telemetry/opentelemetry-cpp path: opentelemetry-cpp - ref: v1.9.0 + ref: v1.10.0 - name: Download vcpkg uses: actions/checkout@v3 with: From c1cc7d1930cd6a9ec9c7aab452f6a746af3e12a9 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Sun, 30 Jul 2023 09:01:57 -0400 Subject: [PATCH 03/30] Update build.yml to use latest version of opentelemetry-cpp --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a06f075..68b8c09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: - name: Build OpenTelemetry-cpp run: | cd opentelemetry-cpp - cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_STANDARD=20 -DWITH_OTLP=ON -DWITH_OTLP_HTTP=ON -DWITH_OTLP_GRPC=OFF -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF -DOPENTELEMETRY_INSTALL=ON -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_STANDARD=20 -DWITH_OTLP_HTTP=ON -DWITH_OTLP_GRPC=OFF -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF -DOPENTELEMETRY_INSTALL=ON -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build build --config Release --target all cmake --install build --prefix ${{ env.OPENTELEMETRY_CPP_INSTALL }} - name: Build OpenTelemetry-Matlab From cc208df35e77d7d06492d31e67c68bd35779f3e4 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Fri, 11 Aug 2023 10:18:46 -0400 Subject: [PATCH 04/30] doubleCounter 1. meterProvider->cgetMeter 2. meter->createDoubleCounter 3. counter->add --- CMakeLists.txt | 8 ++- api/metrics/+opentelemetry/+metrics/Counter.m | 40 +++++++++++++++ api/metrics/+opentelemetry/+metrics/Meter.m | 50 ++++++++++++++++++ .../metrics/CounterProxy.h | 32 ++++++++++++ .../opentelemetry-matlab/metrics/MeterProxy.h | 26 ++++++++++ api/metrics/src/CounterProxy.cpp | 25 +++++++++ api/metrics/src/MeterProxy.cpp | 35 +++++++++++++ .../+sdk/+metrics/MeterProvider.m | 20 ++++++++ .../sdk/metrics/MeterProviderProxy.h | 28 ++++++++++ sdk/metrics/src/MeterProviderProxy.cpp | 51 +++++++++++++++++-- 10 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 api/metrics/+opentelemetry/+metrics/Counter.m create mode 100644 api/metrics/+opentelemetry/+metrics/Meter.m create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h create mode 100644 api/metrics/src/CounterProxy.cpp create mode 100644 api/metrics/src/MeterProxy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf853b6..461228d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,16 +71,18 @@ if(WITH_OTLP_GRPC) endif() set(TRACE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/include) +set(METRICS_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/include) set(CONTEXT_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/include) set(BAGGAGE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/include) set(TRACE_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/include) set(METRICS_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/include) set(OTLP_EXPORTER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/include) -set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${METRICS_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory) set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(TRACE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/src) +set(METRICS_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/src) set(CONTEXT_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/src) set(BAGGAGE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/src) set(TRACE_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/src) @@ -93,6 +95,8 @@ set(OPENTELEMETRY_PROXY_SOURCES ${TRACE_API_SOURCE_DIR}/SpanProxy.cpp ${TRACE_API_SOURCE_DIR}/SpanContextProxy.cpp ${TRACE_API_SOURCE_DIR}/attribute.cpp + ${METRICS_API_SOURCE_DIR}/MeterProxy.cpp + ${METRICS_API_SOURCE_DIR}/CounterProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapPropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/CompositePropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapCarrierProxy.cpp @@ -216,6 +220,7 @@ libmexclass_client_install( # Install M files set(TRACE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/+opentelemetry) +set(METRICS_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/+opentelemetry) set(CONTEXT_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/context/+opentelemetry) set(BAGGAGE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/+opentelemetry) set(UTILS_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/utils/+opentelemetry) @@ -228,6 +233,7 @@ set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp set(OTLP_EXPORTERS_DIR +opentelemetry/+exporters/+otlp) install(DIRECTORY ${TRACE_API_MATLAB_SOURCES} DESTINATION .) +install(DIRECTORY ${METRICS_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${CONTEXT_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${BAGGAGE_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${UTILS_API_MATLAB_SOURCES} DESTINATION .) diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m new file mode 100644 index 0000000..f96d4b4 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -0,0 +1,40 @@ +classdef Counter < handle + + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string + Description (1,1) string + Unit (1,1) string + end + + properties (Access=private) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.metrics.Meter}) + + function obj = Counter(proxy, ctname, ctdescription, ctunit) + % Private constructor. Use getCounter method of Meter + % to create Counters. + obj.Proxy = proxy; + obj.Name = ctname; + obj.Description = ctdescription; + obj.Unit = ctunit; + end + + end + + methods + + function add(obj, value) + if isnumeric(value) && isscalar(value) + obj.Proxy.add(value); + end + end + + end + + +end diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m new file mode 100644 index 0000000..e84e172 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -0,0 +1,50 @@ +classdef Meter < handle + % A Meter that is used to create spans. + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string % Meter name + Version (1,1) string % Meter version + Schema (1,1) string % URL that documents the schema of the generated spans + end + + properties (Access=private) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.sdk.metrics.MeterProvider}) + + function obj = Meter(proxy, mtname, mtversion, mtschema) + % Private constructor. Use getMeter method of MeterProvider + % to create Meters. + obj.Proxy = proxy; + obj.Name = mtname; + obj.Version = mtversion; + obj.Schema = mtschema; + end + + end + + methods + + function counter = createCounter(obj, ctname, ctdescription, ctunit) + arguments + obj + ctname + ctdescription = "" + ctunit = "" + end + import opentelemetry.utils.mustBeScalarString + ctname = mustBeScalarString(ctname); + ctdescription = mustBeScalarString(ctdescription); + ctunit = mustBeScalarString(ctunit); + id = obj.Proxy.createCounter(ctname, ctdescription, ctunit); + Counterproxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.CounterProxy", "ID", id); + counter = opentelemetry.metrics.Counter(Counterproxy, ctname, ctdescription, ctunit); + end + + end + +end diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h new file mode 100644 index 0000000..ec0277b --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h @@ -0,0 +1,32 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/sync_instruments.h" + + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class CounterProxy : public libmexclass::proxy::Proxy { + public: + CounterProxy(nostd::shared_ptr > counter) { + CppCounter = std::move(counter); + REGISTER_METHOD(CounterProxy, add); + } + + void add(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr > CppCounter; + +}; +} // namespace libmexclass::opentelemetry + + diff --git a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h new file mode 100644 index 0000000..72d79d6 --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h @@ -0,0 +1,26 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class MeterProxy : public libmexclass::proxy::Proxy { + public: + MeterProxy(nostd::shared_ptr mt) : CppMeter(mt) { + REGISTER_METHOD(MeterProxy, createCounter); + } + + void createCounter(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr CppMeter; +}; +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/CounterProxy.cpp b/api/metrics/src/CounterProxy.cpp new file mode 100644 index 0000000..99a09b9 --- /dev/null +++ b/api/metrics/src/CounterProxy.cpp @@ -0,0 +1,25 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/CounterProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { + + +void CounterProxy::add(libmexclass::proxy::method::Context& context){ + + matlab::data::Array value_mda = context.inputs[0]; + double value = static_cast(value_mda[0]); + CppCounter->Add(value); + +} + + + +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/MeterProxy.cpp b/api/metrics/src/MeterProxy.cpp new file mode 100644 index 0000000..07c574e --- /dev/null +++ b/api/metrics/src/MeterProxy.cpp @@ -0,0 +1,35 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/MeterProxy.h" +#include "opentelemetry-matlab/metrics/CounterProxy.h" +#include "libmexclass/proxy/ProxyManager.h" + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { +void MeterProxy::createCounter(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 description_mda = context.inputs[1]; + std::string description= static_cast(description_mda[0]); + matlab::data::StringArray unit_mda = context.inputs[2]; + std::string unit = static_cast(unit_mda[0]); + + nostd::shared_ptr > ct = std::move(CppMeter->CreateDoubleCounter(name, description, unit)); + + // instantiate a CounterProxy instance + CounterProxy* newproxy = new CounterProxy(ct); + auto proxy = std::shared_ptr(newproxy); + + // obtain a proxy ID + libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); + + // return the ID + matlab::data::ArrayFactory factory; + auto proxyid_mda = factory.createScalar(proxyid); + context.outputs[0] = proxyid_mda; +} +} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index e475131..070b784 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -32,5 +32,25 @@ "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... "ConstructorArguments", {}); 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.utils.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 + 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 af34198..686bd93 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -5,19 +5,47 @@ #include "libmexclass/proxy/Proxy.h" #include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_factory.h" +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h" +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h" +#include "opentelemetry/exporters/otlp/otlp_http_exporter.h" +#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" + +#include "opentelemetry/sdk/common/global_log_handler.h" +#include "opentelemetry/metrics/provider.h" #include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +#include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h" +#include "opentelemetry/sdk/metrics/meter.h" +#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/metrics/view/instrument_selector_factory.h" +#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h" +#include "opentelemetry/sdk/metrics/view/view_factory.h" + +#include "opentelemetry-matlab/metrics/MeterProxy.h" namespace metrics_api = opentelemetry::metrics; namespace nostd = opentelemetry::nostd; +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace common = opentelemetry::common; +namespace otlpexporter = opentelemetry::exporter::otlp; + namespace libmexclass::opentelemetry::sdk { class MeterProviderProxy : public libmexclass::proxy::Proxy { public: MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { + REGISTER_METHOD(MeterProviderProxy, getMeter); } static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); + void getMeter(libmexclass::proxy::method::Context& context); + protected: nostd::shared_ptr CppMeterProvider; }; diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index d2f8f55..64b91c1 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -4,16 +4,57 @@ #include "libmexclass/proxy/ProxyManager.h" -#include "opentelemetry/sdk/metrics/meter_provider_factory.h" - -namespace metrics_sdk = opentelemetry::sdk::metrics; +#include +#include +#include namespace libmexclass::opentelemetry::sdk { libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + libmexclass::proxy::MakeResult out; - out = std::make_shared(nostd::shared_ptr( - std::move(metrics_sdk::MeterProviderFactory::Create()))); + + auto exporter = otlpexporter::OtlpHttpMetricExporterFactory::Create(); + // Initialize and set the periodic metrics reader + metrics_sdk::PeriodicExportingMetricReaderOptions options; + options.export_interval_millis = std::chrono::milliseconds(1000); + options.export_timeout_millis = std::chrono::milliseconds(500); + auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options); + + auto p = metrics_sdk::MeterProviderFactory::Create(); + // auto p = nostd::shared_ptr(std::move(metrics_sdk::MeterProviderFactory::Create())); + 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); + 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; +} + + } // namespace libmexclass::opentelemetry From b89079bf944398f4f4d030439be4eabd3883e163 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Fri, 11 Aug 2023 15:22:38 -0400 Subject: [PATCH 05/30] fix: comment and headers incorporate feedback in 8/11 code review. --- api/metrics/+opentelemetry/+metrics/Counter.m | 3 ++- api/metrics/+opentelemetry/+metrics/Meter.m | 3 ++- .../include/opentelemetry-matlab/metrics/CounterProxy.h | 3 +-- .../opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h | 6 +----- sdk/metrics/src/MeterProviderProxy.cpp | 6 ------ 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m index f96d4b4..237d640 100644 --- a/api/metrics/+opentelemetry/+metrics/Counter.m +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -1,5 +1,6 @@ classdef Counter < handle - + % Counter is a value that accumulates over time, + % you can think of this like an odometer on a car; it only ever goes up. % Copyright 2023 The MathWorks, Inc. diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m index e84e172..77bb8ca 100644 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -1,5 +1,6 @@ classdef Meter < handle - % A Meter that is used to create spans. + % A Meter creates metric instruments, capturing measurements about a service at runtime. + % Meters are created from Meter Providers. % Copyright 2023 The MathWorks, Inc. diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h index ec0277b..3a1cf95 100644 --- a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h +++ b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h @@ -15,8 +15,7 @@ namespace nostd = opentelemetry::nostd; namespace libmexclass::opentelemetry { class CounterProxy : public libmexclass::proxy::Proxy { public: - CounterProxy(nostd::shared_ptr > counter) { - CppCounter = std::move(counter); + CounterProxy(nostd::shared_ptr > ct) : CppCounter(ct) { REGISTER_METHOD(CounterProxy, add); } diff --git a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h index 686bd93..3526822 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -10,11 +10,10 @@ #include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h" #include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h" #include "opentelemetry/exporters/otlp/otlp_http_exporter.h" -#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" -#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/metrics/provider.h" #include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" #include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" #include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h" @@ -22,9 +21,6 @@ #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/metrics/view/instrument_selector_factory.h" -#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h" -#include "opentelemetry/sdk/metrics/view/view_factory.h" #include "opentelemetry-matlab/metrics/MeterProxy.h" diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index 64b91c1..e0eb3c3 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -5,8 +5,6 @@ #include "libmexclass/proxy/ProxyManager.h" #include -#include -#include namespace libmexclass::opentelemetry::sdk { libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { @@ -16,17 +14,13 @@ libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy auto exporter = otlpexporter::OtlpHttpMetricExporterFactory::Create(); // Initialize and set the periodic metrics reader metrics_sdk::PeriodicExportingMetricReaderOptions options; - options.export_interval_millis = std::chrono::milliseconds(1000); - options.export_timeout_millis = std::chrono::milliseconds(500); auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options); auto p = metrics_sdk::MeterProviderFactory::Create(); - // auto p = nostd::shared_ptr(std::move(metrics_sdk::MeterProviderFactory::Create())); 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); return out; From d37755e94c92d4181087dafd8f07c6de500df6aa Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 28 Jul 2023 16:10:05 -0400 Subject: [PATCH 06/30] Initial submit of metrics --- CMakeLists.txt | 7 +++- OtelMatlabProxyFactory.cpp | 4 +++ .../+sdk/+metrics/MeterProvider.m | 36 +++++++++++++++++++ .../sdk/metrics/MeterProviderProxy.h | 24 +++++++++++++ sdk/metrics/src/MeterProviderProxy.cpp | 19 ++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m create mode 100644 sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h create mode 100644 sdk/metrics/src/MeterProviderProxy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cd97df..50229ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,9 +75,10 @@ set(CONTEXT_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/include) set(BAGGAGE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/include) set(COMMON_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/include) set(TRACE_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/include) +set(METRICS_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/include) set(COMMON_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/include) set(OTLP_EXPORTER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/include) -set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory) set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -86,6 +87,7 @@ set(CONTEXT_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/src) set(BAGGAGE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/src) set(COMMON_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/src) set(TRACE_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/src) +set(METRICS_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/src) set(COMMON_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/src) set(OTLP_EXPORTER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/src) set(OPENTELEMETRY_PROXY_SOURCES @@ -104,6 +106,7 @@ set(OPENTELEMETRY_PROXY_SOURCES ${TRACE_SDK_SOURCE_DIR}/SimpleSpanProcessorProxy.cpp ${TRACE_SDK_SOURCE_DIR}/BatchSpanProcessorProxy.cpp ${TRACE_SDK_SOURCE_DIR}/ParentBasedSamplerProxy.cpp + ${METRICS_SDK_SOURCE_DIR}/MeterProviderProxy.cpp ${COMMON_SDK_SOURCE_DIR}/resource.cpp) if(WITH_OTLP_HTTP) set(OPENTELEMETRY_PROXY_SOURCES ${OPENTELEMETRY_PROXY_SOURCES} @@ -222,6 +225,7 @@ set(CONTEXT_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/context/+opentele set(BAGGAGE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/+opentelemetry) set(COMMON_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/common/+opentelemetry) set(TRACE_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/+opentelemetry) +set(METRICS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/+opentelemetry) set(DEFAULT_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultSpanExporter.m) set(OTLP_HTTP_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m) set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m) @@ -233,6 +237,7 @@ install(DIRECTORY ${CONTEXT_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${BAGGAGE_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${COMMON_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${TRACE_SDK_MATLAB_SOURCES} DESTINATION .) +install(DIRECTORY ${METRICS_SDK_MATLAB_SOURCES} DESTINATION .) install(FILES ${DEFAULT_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) if(WITH_OTLP_HTTP) install(FILES ${OTLP_HTTP_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) diff --git a/OtelMatlabProxyFactory.cpp b/OtelMatlabProxyFactory.cpp index a2f1315..925094d 100644 --- a/OtelMatlabProxyFactory.cpp +++ b/OtelMatlabProxyFactory.cpp @@ -22,6 +22,7 @@ #include "opentelemetry-matlab/sdk/trace/AlwaysOffSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/TraceIdRatioBasedSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/ParentBasedSamplerProxy.h" +#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" #ifdef WITH_OTLP_HTTP #include "opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h" #endif @@ -54,6 +55,9 @@ OtelMatlabProxyFactory::make_proxy(const libmexclass::proxy::ClassName& class_na REGISTER_PROXY(libmexclass.opentelemetry.sdk.AlwaysOffSamplerProxy, libmexclass::opentelemetry::sdk::AlwaysOffSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.TraceIdRatioBasedSamplerProxy, libmexclass::opentelemetry::sdk::TraceIdRatioBasedSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.ParentBasedSamplerProxy, libmexclass::opentelemetry::sdk::ParentBasedSamplerProxy); + + REGISTER_PROXY(libmexclass.opentelemetry.sdk.MeterProviderProxy, libmexclass::opentelemetry::sdk::MeterProviderProxy); + #ifdef WITH_OTLP_HTTP REGISTER_PROXY(libmexclass.opentelemetry.exporters.OtlpHttpSpanExporterProxy, libmexclass::opentelemetry::exporters::OtlpHttpSpanExporterProxy); #endif diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m new file mode 100644 index 0000000..e475131 --- /dev/null +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -0,0 +1,36 @@ +classdef 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 + + methods + function obj = MeterProvider() + % SDK implementation of tracer provider + % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER creates a meter + % provider that uses a periodic exporting metric reader and default configurations. + % + % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R) uses metric + % reader R. Currently, the only supported metric reader is the periodic + % exporting metric reader. + % + % TP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R, PARAM1, VALUE1, + % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. + % Parameters are: + % "View" - View object used to customize collected metrics. + % "Resource" - Additional resource attributes. + % Specified as a dictionary. + % + % See also OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER + % OPENTELEMETRY.SDK.METRICS.VIEW + + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... + "ConstructorArguments", {}); + 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 new file mode 100644 index 0000000..af34198 --- /dev/null +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -0,0 +1,24 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter_provider.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry::sdk { +class MeterProviderProxy : public libmexclass::proxy::Proxy { + public: + MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { + } + + static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); + + protected: + nostd::shared_ptr CppMeterProvider; +}; +} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp new file mode 100644 index 0000000..d2f8f55 --- /dev/null +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -0,0 +1,19 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + +#include "opentelemetry/sdk/metrics/meter_provider_factory.h" + +namespace metrics_sdk = opentelemetry::sdk::metrics; + +namespace libmexclass::opentelemetry::sdk { +libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + libmexclass::proxy::MakeResult out; + out = std::make_shared(nostd::shared_ptr( + std::move(metrics_sdk::MeterProviderFactory::Create()))); + return out; +} + +} // namespace libmexclass::opentelemetry From 62d036a8fc80f562a4e2effe06499d68eb0fc986 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 28 Jul 2023 23:39:55 -0400 Subject: [PATCH 07/30] Update build.yml to use latest version of opentelemetry-cpp --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 310c7d5..a06f075 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: with: repository: open-telemetry/opentelemetry-cpp path: opentelemetry-cpp - ref: v1.9.0 + ref: v1.10.0 - name: Download vcpkg uses: actions/checkout@v3 with: From aa3c642f3c068836b572a1e84f91d34bc367e564 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Sun, 30 Jul 2023 09:01:57 -0400 Subject: [PATCH 08/30] Update build.yml to use latest version of opentelemetry-cpp --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a06f075..68b8c09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: - name: Build OpenTelemetry-cpp run: | cd opentelemetry-cpp - cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_STANDARD=20 -DWITH_OTLP=ON -DWITH_OTLP_HTTP=ON -DWITH_OTLP_GRPC=OFF -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF -DOPENTELEMETRY_INSTALL=ON -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_STANDARD=20 -DWITH_OTLP_HTTP=ON -DWITH_OTLP_GRPC=OFF -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF -DOPENTELEMETRY_INSTALL=ON -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake cmake --build build --config Release --target all cmake --install build --prefix ${{ env.OPENTELEMETRY_CPP_INSTALL }} - name: Build OpenTelemetry-Matlab From 03028088c07ca3e7fc6a109e9530ab1d1ceda955 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Fri, 11 Aug 2023 10:18:46 -0400 Subject: [PATCH 09/30] doubleCounter 1. meterProvider->cgetMeter 2. meter->createDoubleCounter 3. counter->add --- CMakeLists.txt | 8 ++- api/metrics/+opentelemetry/+metrics/Counter.m | 40 +++++++++++++++ api/metrics/+opentelemetry/+metrics/Meter.m | 50 ++++++++++++++++++ .../metrics/CounterProxy.h | 32 ++++++++++++ .../opentelemetry-matlab/metrics/MeterProxy.h | 26 ++++++++++ api/metrics/src/CounterProxy.cpp | 25 +++++++++ api/metrics/src/MeterProxy.cpp | 35 +++++++++++++ .../+sdk/+metrics/MeterProvider.m | 20 ++++++++ .../sdk/metrics/MeterProviderProxy.h | 28 ++++++++++ sdk/metrics/src/MeterProviderProxy.cpp | 51 +++++++++++++++++-- 10 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 api/metrics/+opentelemetry/+metrics/Counter.m create mode 100644 api/metrics/+opentelemetry/+metrics/Meter.m create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h create mode 100644 api/metrics/src/CounterProxy.cpp create mode 100644 api/metrics/src/MeterProxy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 50229ff..5c529b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ if(WITH_OTLP_GRPC) endif() set(TRACE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/include) +set(METRICS_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/include) set(CONTEXT_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/include) set(BAGGAGE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/include) set(COMMON_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/include) @@ -78,11 +79,12 @@ set(TRACE_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/include) set(METRICS_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/include) set(COMMON_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/include) set(OTLP_EXPORTER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/include) -set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${METRICS_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory) set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(TRACE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/src) +set(METRICS_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/src) set(CONTEXT_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/src) set(BAGGAGE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/src) set(COMMON_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/src) @@ -97,6 +99,8 @@ 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}/MeterProxy.cpp + ${METRICS_API_SOURCE_DIR}/CounterProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapPropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/CompositePropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapCarrierProxy.cpp @@ -221,6 +225,7 @@ libmexclass_client_install( # Install M files set(TRACE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/+opentelemetry) +set(METRICS_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/+opentelemetry) set(CONTEXT_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/context/+opentelemetry) set(BAGGAGE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/+opentelemetry) set(COMMON_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/common/+opentelemetry) @@ -233,6 +238,7 @@ set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp set(OTLP_EXPORTERS_DIR +opentelemetry/+exporters/+otlp) install(DIRECTORY ${TRACE_API_MATLAB_SOURCES} DESTINATION .) +install(DIRECTORY ${METRICS_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${CONTEXT_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${BAGGAGE_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${COMMON_API_MATLAB_SOURCES} DESTINATION .) diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m new file mode 100644 index 0000000..f96d4b4 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -0,0 +1,40 @@ +classdef Counter < handle + + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string + Description (1,1) string + Unit (1,1) string + end + + properties (Access=private) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.metrics.Meter}) + + function obj = Counter(proxy, ctname, ctdescription, ctunit) + % Private constructor. Use getCounter method of Meter + % to create Counters. + obj.Proxy = proxy; + obj.Name = ctname; + obj.Description = ctdescription; + obj.Unit = ctunit; + end + + end + + methods + + function add(obj, value) + if isnumeric(value) && isscalar(value) + obj.Proxy.add(value); + end + end + + end + + +end diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m new file mode 100644 index 0000000..e84e172 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -0,0 +1,50 @@ +classdef Meter < handle + % A Meter that is used to create spans. + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string % Meter name + Version (1,1) string % Meter version + Schema (1,1) string % URL that documents the schema of the generated spans + end + + properties (Access=private) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.sdk.metrics.MeterProvider}) + + function obj = Meter(proxy, mtname, mtversion, mtschema) + % Private constructor. Use getMeter method of MeterProvider + % to create Meters. + obj.Proxy = proxy; + obj.Name = mtname; + obj.Version = mtversion; + obj.Schema = mtschema; + end + + end + + methods + + function counter = createCounter(obj, ctname, ctdescription, ctunit) + arguments + obj + ctname + ctdescription = "" + ctunit = "" + end + import opentelemetry.utils.mustBeScalarString + ctname = mustBeScalarString(ctname); + ctdescription = mustBeScalarString(ctdescription); + ctunit = mustBeScalarString(ctunit); + id = obj.Proxy.createCounter(ctname, ctdescription, ctunit); + Counterproxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.CounterProxy", "ID", id); + counter = opentelemetry.metrics.Counter(Counterproxy, ctname, ctdescription, ctunit); + end + + end + +end diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h new file mode 100644 index 0000000..ec0277b --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h @@ -0,0 +1,32 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/sync_instruments.h" + + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class CounterProxy : public libmexclass::proxy::Proxy { + public: + CounterProxy(nostd::shared_ptr > counter) { + CppCounter = std::move(counter); + REGISTER_METHOD(CounterProxy, add); + } + + void add(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr > CppCounter; + +}; +} // namespace libmexclass::opentelemetry + + diff --git a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h new file mode 100644 index 0000000..72d79d6 --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h @@ -0,0 +1,26 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class MeterProxy : public libmexclass::proxy::Proxy { + public: + MeterProxy(nostd::shared_ptr mt) : CppMeter(mt) { + REGISTER_METHOD(MeterProxy, createCounter); + } + + void createCounter(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr CppMeter; +}; +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/CounterProxy.cpp b/api/metrics/src/CounterProxy.cpp new file mode 100644 index 0000000..99a09b9 --- /dev/null +++ b/api/metrics/src/CounterProxy.cpp @@ -0,0 +1,25 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/CounterProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { + + +void CounterProxy::add(libmexclass::proxy::method::Context& context){ + + matlab::data::Array value_mda = context.inputs[0]; + double value = static_cast(value_mda[0]); + CppCounter->Add(value); + +} + + + +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/MeterProxy.cpp b/api/metrics/src/MeterProxy.cpp new file mode 100644 index 0000000..07c574e --- /dev/null +++ b/api/metrics/src/MeterProxy.cpp @@ -0,0 +1,35 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/MeterProxy.h" +#include "opentelemetry-matlab/metrics/CounterProxy.h" +#include "libmexclass/proxy/ProxyManager.h" + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { +void MeterProxy::createCounter(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 description_mda = context.inputs[1]; + std::string description= static_cast(description_mda[0]); + matlab::data::StringArray unit_mda = context.inputs[2]; + std::string unit = static_cast(unit_mda[0]); + + nostd::shared_ptr > ct = std::move(CppMeter->CreateDoubleCounter(name, description, unit)); + + // instantiate a CounterProxy instance + CounterProxy* newproxy = new CounterProxy(ct); + auto proxy = std::shared_ptr(newproxy); + + // obtain a proxy ID + libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); + + // return the ID + matlab::data::ArrayFactory factory; + auto proxyid_mda = factory.createScalar(proxyid); + context.outputs[0] = proxyid_mda; +} +} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index e475131..070b784 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -32,5 +32,25 @@ "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... "ConstructorArguments", {}); 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.utils.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 + 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 af34198..686bd93 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -5,19 +5,47 @@ #include "libmexclass/proxy/Proxy.h" #include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_factory.h" +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h" +#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h" +#include "opentelemetry/exporters/otlp/otlp_http_exporter.h" +#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" + +#include "opentelemetry/sdk/common/global_log_handler.h" +#include "opentelemetry/metrics/provider.h" #include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +#include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h" +#include "opentelemetry/sdk/metrics/meter.h" +#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/metrics/view/instrument_selector_factory.h" +#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h" +#include "opentelemetry/sdk/metrics/view/view_factory.h" + +#include "opentelemetry-matlab/metrics/MeterProxy.h" namespace metrics_api = opentelemetry::metrics; namespace nostd = opentelemetry::nostd; +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace common = opentelemetry::common; +namespace otlpexporter = opentelemetry::exporter::otlp; + namespace libmexclass::opentelemetry::sdk { class MeterProviderProxy : public libmexclass::proxy::Proxy { public: MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { + REGISTER_METHOD(MeterProviderProxy, getMeter); } static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); + void getMeter(libmexclass::proxy::method::Context& context); + protected: nostd::shared_ptr CppMeterProvider; }; diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index d2f8f55..64b91c1 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -4,16 +4,57 @@ #include "libmexclass/proxy/ProxyManager.h" -#include "opentelemetry/sdk/metrics/meter_provider_factory.h" - -namespace metrics_sdk = opentelemetry::sdk::metrics; +#include +#include +#include namespace libmexclass::opentelemetry::sdk { libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { + libmexclass::proxy::MakeResult out; - out = std::make_shared(nostd::shared_ptr( - std::move(metrics_sdk::MeterProviderFactory::Create()))); + + auto exporter = otlpexporter::OtlpHttpMetricExporterFactory::Create(); + // Initialize and set the periodic metrics reader + metrics_sdk::PeriodicExportingMetricReaderOptions options; + options.export_interval_millis = std::chrono::milliseconds(1000); + options.export_timeout_millis = std::chrono::milliseconds(500); + auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options); + + auto p = metrics_sdk::MeterProviderFactory::Create(); + // auto p = nostd::shared_ptr(std::move(metrics_sdk::MeterProviderFactory::Create())); + 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); + 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; +} + + } // namespace libmexclass::opentelemetry From e90e6a403f92c192cbdf8db577ae132ddec3d160 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Fri, 11 Aug 2023 15:22:38 -0400 Subject: [PATCH 10/30] fix: comment and headers incorporate feedback in 8/11 code review. --- api/metrics/+opentelemetry/+metrics/Counter.m | 3 ++- api/metrics/+opentelemetry/+metrics/Meter.m | 3 ++- .../include/opentelemetry-matlab/metrics/CounterProxy.h | 3 +-- .../opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h | 6 +----- sdk/metrics/src/MeterProviderProxy.cpp | 6 ------ 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m index f96d4b4..237d640 100644 --- a/api/metrics/+opentelemetry/+metrics/Counter.m +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -1,5 +1,6 @@ classdef Counter < handle - + % Counter is a value that accumulates over time, + % you can think of this like an odometer on a car; it only ever goes up. % Copyright 2023 The MathWorks, Inc. diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m index e84e172..77bb8ca 100644 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -1,5 +1,6 @@ classdef Meter < handle - % A Meter that is used to create spans. + % A Meter creates metric instruments, capturing measurements about a service at runtime. + % Meters are created from Meter Providers. % Copyright 2023 The MathWorks, Inc. diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h index ec0277b..3a1cf95 100644 --- a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h +++ b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h @@ -15,8 +15,7 @@ namespace nostd = opentelemetry::nostd; namespace libmexclass::opentelemetry { class CounterProxy : public libmexclass::proxy::Proxy { public: - CounterProxy(nostd::shared_ptr > counter) { - CppCounter = std::move(counter); + CounterProxy(nostd::shared_ptr > ct) : CppCounter(ct) { REGISTER_METHOD(CounterProxy, add); } diff --git a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h index 686bd93..3526822 100644 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h @@ -10,11 +10,10 @@ #include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h" #include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h" #include "opentelemetry/exporters/otlp/otlp_http_exporter.h" -#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" -#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/metrics/provider.h" #include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" #include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" #include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h" @@ -22,9 +21,6 @@ #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/metrics/view/instrument_selector_factory.h" -#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h" -#include "opentelemetry/sdk/metrics/view/view_factory.h" #include "opentelemetry-matlab/metrics/MeterProxy.h" diff --git a/sdk/metrics/src/MeterProviderProxy.cpp b/sdk/metrics/src/MeterProviderProxy.cpp index 64b91c1..e0eb3c3 100644 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ b/sdk/metrics/src/MeterProviderProxy.cpp @@ -5,8 +5,6 @@ #include "libmexclass/proxy/ProxyManager.h" #include -#include -#include namespace libmexclass::opentelemetry::sdk { libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { @@ -16,17 +14,13 @@ libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy auto exporter = otlpexporter::OtlpHttpMetricExporterFactory::Create(); // Initialize and set the periodic metrics reader metrics_sdk::PeriodicExportingMetricReaderOptions options; - options.export_interval_millis = std::chrono::milliseconds(1000); - options.export_timeout_millis = std::chrono::milliseconds(500); auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options); auto p = metrics_sdk::MeterProviderFactory::Create(); - // auto p = nostd::shared_ptr(std::move(metrics_sdk::MeterProviderFactory::Create())); 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); return out; From 9e729e3218e6676a0250510820bc3a007bbacf59 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Thu, 17 Aug 2023 16:57:33 -0400 Subject: [PATCH 11/30] Update metrics code to use utils function from common folder --- api/metrics/+opentelemetry/+metrics/Meter.m | 2 +- sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m index 77bb8ca..bad9666 100644 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -36,7 +36,7 @@ ctdescription = "" ctunit = "" end - import opentelemetry.utils.mustBeScalarString + import opentelemetry.common.mustBeScalarString ctname = mustBeScalarString(ctname); ctdescription = mustBeScalarString(ctdescription); ctunit = mustBeScalarString(ctunit); diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index 070b784..ec019eb 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -42,7 +42,7 @@ end % name, version, schema accepts any types that can convert to a % string - import opentelemetry.utils.mustBeScalarString + import opentelemetry.common.mustBeScalarString mtname = mustBeScalarString(mtname); mtversion = mustBeScalarString(mtversion); mtschema = mustBeScalarString(mtschema); From 23ec1df5da2bd56ce3b491436f8f9c9f480cc444 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Tue, 29 Aug 2023 12:24:31 -0400 Subject: [PATCH 12/30] fix a fallout from previous merge --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b3c01be..e61d8d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,16 +196,19 @@ target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PRE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_trace${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_version${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_logs${CMAKE_STATIC_LIBRARY_SUFFIX} + ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_metrics${CMAKE_STATIC_LIBRARY_SUFFIX} ${Protobuf_LIBRARIES}) if(WITH_OTLP_HTTP) target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http_client${CMAKE_STATIC_LIBRARY_SUFFIX} + ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http_metric${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_http_client_curl${CMAKE_STATIC_LIBRARY_SUFFIX} ${CURL_LIBRARIES}) endif() if(WITH_OTLP_GRPC) target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc_client${CMAKE_STATIC_LIBRARY_SUFFIX} + ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc_metrics${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}opentelemetry_proto_grpc${OTEL_PROTO_LIBRARY_SUFFIX} gRPC::grpc++ absl::synchronization) From 77831416d71708fa8b4c36fc4997900807e36687 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Wed, 6 Sep 2023 12:18:20 -0400 Subject: [PATCH 13/30] feat: UpDownCounter, Histogram, tmetrics 1. Class: UpDownCounter, Histogram 2. Test: tmetrics --- CMakeLists.txt | 5 +- api/metrics/+opentelemetry/+metrics/Counter.m | 24 +- .../+opentelemetry/+metrics/Histogram.m | 55 +++ api/metrics/+opentelemetry/+metrics/Meter.m | 43 ++- .../+opentelemetry/+metrics/UpDownCounter.m | 58 +++ .../metrics/CounterProxy.h | 2 + .../metrics/HistogramProxy.h | 36 ++ .../opentelemetry-matlab/metrics/MeterProxy.h | 10 + .../metrics/UpDownCounterProxy.h | 33 ++ api/metrics/src/CounterProxy.cpp | 18 +- api/metrics/src/HistogramProxy.cpp | 44 +++ api/metrics/src/MeterProxy.cpp | 55 ++- api/metrics/src/UpDownCounterProxy.cpp | 41 ++ test/tmetrics.m | 349 ++++++++++++++++++ 14 files changed, 764 insertions(+), 9 deletions(-) create mode 100644 api/metrics/+opentelemetry/+metrics/Histogram.m create mode 100644 api/metrics/+opentelemetry/+metrics/UpDownCounter.m create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h create mode 100644 api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h create mode 100644 api/metrics/src/HistogramProxy.cpp create mode 100644 api/metrics/src/UpDownCounterProxy.cpp create mode 100644 test/tmetrics.m diff --git a/CMakeLists.txt b/CMakeLists.txt index e61d8d7..0eefb27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,8 @@ else() set(OTEL_CPP_CXX_STANDARD 11) endif() -set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) +# set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) +set(patch_command "") ExternalProject_Add( ${OTEL_CPP_PROJECT_NAME} @@ -137,6 +138,8 @@ set(OPENTELEMETRY_PROXY_SOURCES ${COMMON_API_SOURCE_DIR}/attribute.cpp ${METRICS_API_SOURCE_DIR}/MeterProxy.cpp ${METRICS_API_SOURCE_DIR}/CounterProxy.cpp + ${METRICS_API_SOURCE_DIR}/UpDownCounterProxy.cpp + ${METRICS_API_SOURCE_DIR}/HistogramProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapPropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/CompositePropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapCarrierProxy.cpp diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m index 237d640..a26d8f0 100644 --- a/api/metrics/+opentelemetry/+metrics/Counter.m +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -17,7 +17,7 @@ methods (Access={?opentelemetry.metrics.Meter}) function obj = Counter(proxy, ctname, ctdescription, ctunit) - % Private constructor. Use getCounter method of Meter + % Private constructor. Use createCounter method of Meter % to create Counters. obj.Proxy = proxy; obj.Name = ctname; @@ -29,10 +29,28 @@ methods - function add(obj, value) + function add(obj, value, varargin) + % input value must be a numerical scalar if isnumeric(value) && isscalar(value) - obj.Proxy.add(value); + + if nargin == 2 + obj.Proxy.add(value); + + elseif isa(varargin{1}, "dictionary") + attrkeys = keys(varargin{1}); + attrvals = values(varargin{1},"cell"); + if all(cellfun(@iscell, attrvals)) + attrvals = [attrvals{:}]; + end + obj.Proxy.add(value,attrkeys,attrvals); + + else + attrkeys = [varargin{1:2:(nargin-2)}]'; + attrvals = [varargin(2:2:(nargin-2))]'; + obj.Proxy.add(value,attrkeys,attrvals); + end end + end end diff --git a/api/metrics/+opentelemetry/+metrics/Histogram.m b/api/metrics/+opentelemetry/+metrics/Histogram.m new file mode 100644 index 0000000..5cb7df5 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/Histogram.m @@ -0,0 +1,55 @@ +classdef Histogram < handle + % Histogram is an instrument that adds or reduce values. + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string + Description (1,1) string + Unit (1,1) string + end + + properties (Access=public) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.metrics.Meter}) + + function obj = Histogram(proxy, hiname, hidescription, hiunit) + % Private constructor. Use createHistogram method of Meter + % to create Histograms. + obj.Proxy = proxy; + obj.Name = hiname; + obj.Description = hidescription; + obj.Unit = hiunit; + end + + end + + methods + + function record(obj, value, varargin) + % input value must be a numerical scalar + if isnumeric(value) && isscalar(value) + if nargin == 2 + obj.Proxy.record(value); + elseif isa(varargin{1}, "dictionary") + attrkeys = keys(varargin{1}); + attrvals = values(varargin{1},"cell"); + if all(cellfun(@iscell, attrvals)) + attrvals = [attrvals{:}]; + end + obj.Proxy.record(value,attrkeys,attrvals); + else + attrkeys = [varargin{1:2:(nargin-2)}]'; + attrvals = [varargin(2:2:(nargin-2))]'; + obj.Proxy.record(value,attrkeys,attrvals); + end + end + + end + + end + + +end diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m index e46fd53..250a5b9 100644 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ b/api/metrics/+opentelemetry/+metrics/Meter.m @@ -36,15 +36,52 @@ ctdescription = "" ctunit = "" end - import opentelemetry.common.mustBeScalarString ctname = mustBeScalarString(ctname); ctdescription = mustBeScalarString(ctdescription); ctunit = mustBeScalarString(ctunit); id = obj.Proxy.createCounter(ctname, ctdescription, ctunit); - Counterproxy = libmexclass.proxy.Proxy("Name", ... + CounterProxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.CounterProxy", "ID", id); - counter = opentelemetry.metrics.Counter(Counterproxy, ctname, ctdescription, ctunit); + counter = opentelemetry.metrics.Counter(CounterProxy, ctname, ctdescription, ctunit); + end + + + function updowncounter = createUpDownCounter(obj, ctname, ctdescription, ctunit) + arguments + obj + ctname + ctdescription = "" + ctunit = "" + end + + import opentelemetry.common.mustBeScalarString + ctname = mustBeScalarString(ctname); + ctdescription = mustBeScalarString(ctdescription); + ctunit = mustBeScalarString(ctunit); + id = obj.Proxy.createUpDownCounter(ctname, ctdescription, ctunit); + UpDownCounterProxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.UpDownCounterProxy", "ID", id); + updowncounter = opentelemetry.metrics.UpDownCounter(UpDownCounterProxy, ctname, ctdescription, ctunit); + end + + + function histogram = createHistogram(obj, hiname, hidescription, hiunit) + arguments + obj + hiname + hidescription = "" + hiunit = "" + end + + import opentelemetry.common.mustBeScalarString + hiname = mustBeScalarString(hiname); + hidescription = mustBeScalarString(hidescription); + hiunit = mustBeScalarString(hiunit); + id = obj.Proxy.createHistogram(hiname, hidescription, hiunit); + HistogramProxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.HistogramProxy", "ID", id); + histogram = opentelemetry.metrics.Histogram(HistogramProxy, hiname, hidescription, hiunit); end end diff --git a/api/metrics/+opentelemetry/+metrics/UpDownCounter.m b/api/metrics/+opentelemetry/+metrics/UpDownCounter.m new file mode 100644 index 0000000..3b84c75 --- /dev/null +++ b/api/metrics/+opentelemetry/+metrics/UpDownCounter.m @@ -0,0 +1,58 @@ +classdef UpDownCounter < handle + % UpDownCounter is an instrument that adds or reduce values. + + % Copyright 2023 The MathWorks, Inc. + + properties (SetAccess=immutable) + Name (1,1) string + Description (1,1) string + Unit (1,1) string + end + + properties (Access=public) + Proxy % Proxy object to interface C++ code + end + + methods (Access={?opentelemetry.metrics.Meter}) + + function obj = UpDownCounter(proxy, ctname, ctdescription, ctunit) + % Private constructor. Use createUpDownCounter method of Meter + % to create UpDownCounters. + obj.Proxy = proxy; + obj.Name = ctname; + obj.Description = ctdescription; + obj.Unit = ctunit; + end + + end + + methods + + function add(obj, value, varargin) + % input value must be a numerical scalar + if isnumeric(value) && isscalar(value) + + if nargin == 2 + obj.Proxy.add(value); + + elseif isa(varargin{1}, "dictionary") + attrkeys = keys(varargin{1}); + attrvals = values(varargin{1},"cell"); + if all(cellfun(@iscell, attrvals)) + attrvals = [attrvals{:}]; + end + obj.Proxy.add(value,attrkeys,attrvals); + + else + attrkeys = [varargin{1:2:(nargin-2)}]'; + attrvals = [varargin(2:2:(nargin-2))]'; + obj.Proxy.add(value,attrkeys,attrvals); + end + end + + end + + end + + +end diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h index 3a1cf95..6271d60 100644 --- a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h +++ b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h @@ -8,6 +8,8 @@ #include "opentelemetry/metrics/meter.h" #include "opentelemetry/metrics/sync_instruments.h" +#include "opentelemetry-matlab/common/attribute.h" +#include "opentelemetry-matlab/common/ProcessedAttributes.h" namespace metrics_api = opentelemetry::metrics; namespace nostd = opentelemetry::nostd; diff --git a/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h new file mode 100644 index 0000000..248c628 --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h @@ -0,0 +1,36 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/context/context.h" +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/sync_instruments.h" + +#include "opentelemetry-matlab/common/attribute.h" +#include "opentelemetry-matlab/common/ProcessedAttributes.h" +#include "opentelemetry-matlab/context/ContextProxy.h" + +namespace metrics_api = opentelemetry::metrics; +namespace context = opentelemetry::context; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class HistogramProxy : public libmexclass::proxy::Proxy { + public: + HistogramProxy(nostd::shared_ptr > hist) : CppHistogram(hist) { + REGISTER_METHOD(HistogramProxy, record); + } + + void record(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr > CppHistogram; + +}; +} // namespace libmexclass::opentelemetry + + diff --git a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h index 72d79d6..ab45e48 100644 --- a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h +++ b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h @@ -5,6 +5,10 @@ #include "libmexclass/proxy/Proxy.h" #include "libmexclass/proxy/method/Context.h" +#include "opentelemetry-matlab/metrics/CounterProxy.h" +#include "opentelemetry-matlab/metrics/HistogramProxy.h" +#include "opentelemetry-matlab/metrics/UpDownCounterProxy.h" + #include "opentelemetry/metrics/meter.h" namespace metrics_api = opentelemetry::metrics; @@ -15,10 +19,16 @@ class MeterProxy : public libmexclass::proxy::Proxy { public: MeterProxy(nostd::shared_ptr mt) : CppMeter(mt) { REGISTER_METHOD(MeterProxy, createCounter); + REGISTER_METHOD(MeterProxy, createUpDownCounter); + REGISTER_METHOD(MeterProxy, createHistogram); } void createCounter(libmexclass::proxy::method::Context& context); + void createUpDownCounter(libmexclass::proxy::method::Context& context); + + void createHistogram(libmexclass::proxy::method::Context& context); + private: nostd::shared_ptr CppMeter; diff --git a/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h new file mode 100644 index 0000000..6a41c75 --- /dev/null +++ b/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h @@ -0,0 +1,33 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include "libmexclass/proxy/Proxy.h" +#include "libmexclass/proxy/method/Context.h" + +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/sync_instruments.h" + +#include "opentelemetry-matlab/common/attribute.h" +#include "opentelemetry-matlab/common/ProcessedAttributes.h" + +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +namespace libmexclass::opentelemetry { +class UpDownCounterProxy : public libmexclass::proxy::Proxy { + public: + UpDownCounterProxy(nostd::shared_ptr > ct) : CppUpDownCounter(ct) { + REGISTER_METHOD(UpDownCounterProxy, add); + } + + void add(libmexclass::proxy::method::Context& context); + + private: + + nostd::shared_ptr > CppUpDownCounter; + +}; +} // namespace libmexclass::opentelemetry + + diff --git a/api/metrics/src/CounterProxy.cpp b/api/metrics/src/CounterProxy.cpp index 99a09b9..77e498f 100644 --- a/api/metrics/src/CounterProxy.cpp +++ b/api/metrics/src/CounterProxy.cpp @@ -16,7 +16,23 @@ void CounterProxy::add(libmexclass::proxy::method::Context& context){ matlab::data::Array value_mda = context.inputs[0]; double value = static_cast(value_mda[0]); - CppCounter->Add(value); + size_t nin = context.inputs.getNumberOfElements(); + if (nin == 1){ + CppCounter->Add(value); + } + // add attributes + else { + ProcessedAttributes attrs; + matlab::data::StringArray attrnames_mda = context.inputs[1]; + matlab::data::Array attrvalues_mda = context.inputs[2]; + size_t nattrs = attrnames_mda.getNumberOfElements(); + for (size_t i = 0; i < nattrs; i ++){ + std::string attrname = static_cast(attrnames_mda[i]); + matlab::data::Array attrvalue = attrvalues_mda[i]; + processAttribute(attrname, attrvalue, attrs); + } + CppCounter->Add(value, attrs.Attributes); + } } diff --git a/api/metrics/src/HistogramProxy.cpp b/api/metrics/src/HistogramProxy.cpp new file mode 100644 index 0000000..3d3ea5f --- /dev/null +++ b/api/metrics/src/HistogramProxy.cpp @@ -0,0 +1,44 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/HistogramProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { + + +void HistogramProxy::record(libmexclass::proxy::method::Context& context){ + // Get value + matlab::data::Array value_mda = context.inputs[0]; + double value = static_cast(value_mda[0]); + // Create empty context + auto ctxt = context_api::Context(); + // If no attributes input, record value and context + size_t nin = context.inputs.getNumberOfElements(); + if (nin == 1){ + CppHistogram->Record(value, ctxt); + } + // Otherwise, get attributes, record value, attributes and context + else { + ProcessedAttributes attrs; + matlab::data::StringArray attrnames_mda = context.inputs[1]; + matlab::data::Array attrvalues_mda = context.inputs[2]; + size_t nattrs = attrnames_mda.getNumberOfElements(); + for (size_t i = 0; i < nattrs; i ++){ + std::string attrname = static_cast(attrnames_mda[i]); + matlab::data::Array attrvalue = attrvalues_mda[i]; + processAttribute(attrname, attrvalue, attrs); + } + CppHistogram->Record(value, attrs.Attributes, ctxt); + } + +} + + + +} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/MeterProxy.cpp b/api/metrics/src/MeterProxy.cpp index 07c574e..3d0d399 100644 --- a/api/metrics/src/MeterProxy.cpp +++ b/api/metrics/src/MeterProxy.cpp @@ -1,7 +1,7 @@ // Copyright 2023 The MathWorks, Inc. #include "opentelemetry-matlab/metrics/MeterProxy.h" -#include "opentelemetry-matlab/metrics/CounterProxy.h" + #include "libmexclass/proxy/ProxyManager.h" #include "MatlabDataArray.hpp" @@ -32,4 +32,57 @@ void MeterProxy::createCounter(libmexclass::proxy::method::Context& context) { auto proxyid_mda = factory.createScalar(proxyid); context.outputs[0] = proxyid_mda; } + + +void MeterProxy::createUpDownCounter(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 description_mda = context.inputs[1]; + std::string description= static_cast(description_mda[0]); + matlab::data::StringArray unit_mda = context.inputs[2]; + std::string unit = static_cast(unit_mda[0]); + + nostd::shared_ptr > ct = std::move(CppMeter->CreateDoubleUpDownCounter +(name, description, unit)); + + // instantiate a UpDownCounterProxy instance + UpDownCounterProxy* newproxy = new UpDownCounterProxy(ct); + auto proxy = std::shared_ptr(newproxy); + + // obtain a proxy ID + libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); + + // return the ID + matlab::data::ArrayFactory factory; + auto proxyid_mda = factory.createScalar(proxyid); + context.outputs[0] = proxyid_mda; +} + + +void MeterProxy::createHistogram(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 description_mda = context.inputs[1]; + std::string description= static_cast(description_mda[0]); + matlab::data::StringArray unit_mda = context.inputs[2]; + std::string unit = static_cast(unit_mda[0]); + + nostd::shared_ptr > hist = std::move(CppMeter->CreateDoubleHistogram(name, description, unit)); + + // instantiate a HistogramProxy instance + HistogramProxy* newproxy = new HistogramProxy(hist); + auto proxy = std::shared_ptr(newproxy); + + // obtain a proxy ID + libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); + + // return the ID + matlab::data::ArrayFactory factory; + auto proxyid_mda = factory.createScalar(proxyid); + context.outputs[0] = proxyid_mda; +} + + } // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/UpDownCounterProxy.cpp b/api/metrics/src/UpDownCounterProxy.cpp new file mode 100644 index 0000000..f4d35ef --- /dev/null +++ b/api/metrics/src/UpDownCounterProxy.cpp @@ -0,0 +1,41 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry-matlab/metrics/UpDownCounterProxy.h" + +#include "libmexclass/proxy/ProxyManager.h" + + +#include "MatlabDataArray.hpp" + +#include + +namespace libmexclass::opentelemetry { + + +void UpDownCounterProxy::add(libmexclass::proxy::method::Context& context){ + + matlab::data::Array value_mda = context.inputs[0]; + double value = static_cast(value_mda[0]); + size_t nin = context.inputs.getNumberOfElements(); + if (nin == 1){ + CppUpDownCounter->Add(value); + } + // add attributes + else { + ProcessedAttributes attrs; + matlab::data::StringArray attrnames_mda = context.inputs[1]; + matlab::data::Array attrvalues_mda = context.inputs[2]; + size_t nattrs = attrnames_mda.getNumberOfElements(); + for (size_t i = 0; i < nattrs; i ++){ + std::string attrname = static_cast(attrnames_mda[i]); + matlab::data::Array attrvalue = attrvalues_mda[i]; + processAttribute(attrname, attrvalue, attrs); + } + CppUpDownCounter->Add(value, attrs.Attributes); + } + +} + + + +} // namespace libmexclass::opentelemetry diff --git a/test/tmetrics.m b/test/tmetrics.m new file mode 100644 index 0000000..f28db40 --- /dev/null +++ b/test/tmetrics.m @@ -0,0 +1,349 @@ +classdef tmetrics < matlab.unittest.TestCase + % tests for traces and spans + + % Copyright 2023 The MathWorks, Inc. + + properties + OtelConfigFile + OtelRoot + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + commonSetup(testCase); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + + function testCounterBasic(testCase) + % test names and added value in Counter + + metername = "foo"; + countername = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + ct = mt.createCounter(countername); + + % verify MATLAB object properties + verifyEqual(testCase, mt.Name, metername); + verifyEqual(testCase, mt.Version, ""); + verifyEqual(testCase, mt.Schema, ""); + verifyEqual(testCase, ct.Name, countername); + + % create testing value + val = 10; + + % add value and attributes + ct.add(val); + + % wait for default collector response time (60s) + pause(70); + + % fetch result + results = readJsonResults(testCase); + results = results{1}; + + % verify meter and counter names + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + + % fetch datapoint + dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; + + % verify counter value + verifyEqual(testCase, dp.asDouble, val); + + end + + + function testCounterAddAttributes(testCase) + % test names, added value and attributes in Counter + + metername = "foo"; + countername = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + ct = mt.createCounter(countername); + + % verify MATLAB object properties + verifyEqual(testCase, mt.Name, metername); + verifyEqual(testCase, mt.Version, ""); + verifyEqual(testCase, mt.Schema, ""); + verifyEqual(testCase, ct.Name, countername); + + % create testing value and dictionary + dict = dictionary("k1","v1","k2",5); + vals = [1,2,3]; + dict_keys = keys(dict); + dict_vals = values(dict); + + % add value and attributes + ct.add(vals(1),dict); + ct.add(vals(2),dict); + ct.add(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); + + % wait for default collector response time (60s) + pause(70); + + % fetch result + results = readJsonResults(testCase); + results = results{1}; + + % verify meter and counter names + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + + % fetch datapoint + dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; + + % verify counter value + verifyEqual(testCase, dp.asDouble, sum(vals)); + + % verify counter attributes + verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + + end + + + function testCounterAddNegative(testCase) + % test if counter value remain 0 when added negative value + + metername = "foo"; + countername = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + ct = mt.createCounter(countername); + + % add negative value to counter + ct.add(-1); + pause(70); + + % fetch results + results = readJsonResults(testCase); + results = results{1}; + dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; + + % verify that the counter value is still 0 + verifyEqual(testCase, dp.asDouble, 0); + + end + + + function testUpDownCounterBasic(testCase) + % test names and added value in UpDownCounter + + metername = "foo"; + countername = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + ct = mt.createUpDownCounter(countername); + + % verify MATLAB object properties + verifyEqual(testCase, mt.Name, metername); + verifyEqual(testCase, mt.Version, ""); + verifyEqual(testCase, mt.Schema, ""); + verifyEqual(testCase, ct.Name, countername); + + % create testing value + val = 10; + + % add value and attributes + ct.add(val); + + % wait for default collector response time (60s) + pause(70); + + % fetch result + results = readJsonResults(testCase); + results = results{1}; + + % verify meter and counter names + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + + % fetch datapoint + dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; + + % verify counter value + verifyEqual(testCase, dp.asDouble, val); + + end + + + function testUpDownCounterAddAttributes(testCase) + % test names, added value and attributes in UpDownCounter + + metername = "foo"; + countername = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + ct = mt.createUpDownCounter(countername); + + % verify MATLAB object properties + verifyEqual(testCase, mt.Name, metername); + verifyEqual(testCase, mt.Version, ""); + verifyEqual(testCase, mt.Schema, ""); + verifyEqual(testCase, ct.Name, countername); + + % create testing value and dictionary + dict = dictionary("k1","v1","k2",5); + vals = [2,-1,3]; + dict_keys = keys(dict); + dict_vals = values(dict); + + % add value and attributes + ct.add(vals(1),dict); + ct.add(vals(2),dict); + ct.add(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); + + % wait for default collector response time (60s) + pause(70); + + % fetch result + results = readJsonResults(testCase); + results = results{1}; + + % verify meter and counter names + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + + % fetch datapoint + dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; + + % verify counter value + verifyEqual(testCase, dp.asDouble, sum(vals)); + + % verify counter attributes + verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + + end + + + function testHistogramBasic(testCase) + % test recorded values in histogram + + metername = "foo"; + histname = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + hist = mt.createHistogram(histname); + + % create value for histogram + val = 1; + + % record value + hist.record(val); + + % wait for collector response + pause(75); + + % fetch results + results = readJsonResults(testCase); + results = results{1}; + dp = results.resourceMetrics.scopeMetrics.metrics.histogram.dataPoints; + bounds = dp.explicitBounds; + counts = dp.bucketCounts; + + % verify statistics + verifyEqual(testCase, dp.min, val); + verifyEqual(testCase, dp.max, val); + verifyEqual(testCase, dp.sum, val); + + % verify count in bucket + lower = bounds(1); + upper = bounds(2); + expect_count = sum(val>lower & val<=upper); + verifyEqual(testCase, str2double(counts{2}), expect_count); + + end + + + function testHistogramRecordAttributes(testCase) + % test recorded values and attributes in histogram + + metername = "foo"; + histname = "bar"; + + p = opentelemetry.sdk.metrics.MeterProvider(); + mt = p.getMeter(metername); + hist = mt.createHistogram(histname); + + % create value and attributes for histogram + dict = dictionary("k1","v1","k2","v2"); + vals = [1,5,10]; + dict_keys = keys(dict); + dict_vals = values(dict); + + % record value and attributes + hist.record(vals(1),dict); + hist.record(vals(2),dict); + hist.record(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); + + % wait for collector response + pause(75); + + % fetch results + results = readJsonResults(testCase); + results = results{1}; + dp = results.resourceMetrics.scopeMetrics.metrics.histogram.dataPoints; + bounds = dp.explicitBounds; + counts = dp.bucketCounts; + + % verify statistics + verifyEqual(testCase, dp.min, min(vals)); + verifyEqual(testCase, dp.max, max(vals)); + verifyEqual(testCase, dp.sum, sum(vals)); + + % verify attributes + verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + + % verify count in bucket + for i = 2:(length(counts)-1) + lower = bounds(i-1); + upper = bounds(i); + expect_count = sum(vals>lower & vals<=upper); + verifyEqual(testCase, str2double(counts{i}), expect_count); + end + + end + + end + +end From e0bd4e72974cc6098be9c2a50da8c41bb8ad231d Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Wed, 6 Sep 2023 12:22:32 -0400 Subject: [PATCH 14/30] fix: reverse patch cmd in cmake --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0eefb27..77dac1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,8 +64,7 @@ else() set(OTEL_CPP_CXX_STANDARD 11) endif() -# set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) -set(patch_command "") +set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) ExternalProject_Add( ${OTEL_CPP_PROJECT_NAME} From 72ac9a321965dc9d6b7afba7e0529f079569cefe Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Wed, 6 Sep 2023 13:19:21 -0400 Subject: [PATCH 15/30] fix: merge updates in cmake patch cmd fix --- CMakeLists.txt | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77dac1c..a03a16f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,9 @@ option(WITH_OTLP_GRPC "Whether to include the OTLP gRPC exporter" OFF) if(NOT WITH_OTLP_HTTP AND NOT WITH_OTLP_GRPC) message(FATAL_ERROR "At least one of WITH_OTLP_HTTP and WITH_OTLP_GRPC must be ON") endif() +if(APPLE) + option(SKIP_OTEL_CPP_PATCH "Whether to skip patching OpenTelemetry-cpp" OFF) +endif() # ###################################### # libmexclass FetchContent Configuration @@ -64,13 +67,18 @@ else() set(OTEL_CPP_CXX_STANDARD 11) endif() -set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) +if(NOT APPLE OR SKIP_OTEL_CPP_PATCH) + set(patch_command "") +else() + set(patch_command git apply ${CMAKE_SOURCE_DIR}/otel-cpp.patch) +endif() ExternalProject_Add( ${OTEL_CPP_PROJECT_NAME} GIT_REPOSITORY ${OTEL_CPP_GIT_REPOSITORY} GIT_TAG ${OTEL_CPP_GIT_TAG} PREFIX ${OTEL_CPP_PREFIX} + UPDATE_DISCONNECTED 1 PATCH_COMMAND ${patch_command} CMAKE_ARGS -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DWITH_OTLP_HTTP=${WITH_OTLP_HTTP} -DWITH_OTLP_GRPC=${WITH_OTLP_GRPC} -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF -DOPENTELEMETRY_INSTALL=ON -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_CXX_STANDARD=${OTEL_CPP_CXX_STANDARD} INSTALL_DIR ${OTEL_CPP_PREFIX} @@ -182,13 +190,20 @@ if(WITH_OTLP_GRPC) endif() endif() +# On Windows, suppress a compiler warning about deprecation of result_of +if(WIN32) + set(CUSTOM_CXX_FLAGS -D_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING) +else() + set(CUSTOM_CXX_FLAGS "") +endif() + if(WIN32) set(OTEL_PROTO_LIBRARY_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) else() set(OTEL_PROTO_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) endif() -target_compile_options(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTLP_MACROS}) +target_compile_options(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTLP_MACROS} ${CUSTOM_CXX_FLAGS}) # link against OpenTelemetry-cpp libraries and their dependencies target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_common${CMAKE_STATIC_LIBRARY_SUFFIX} @@ -323,4 +338,4 @@ if(WITH_OTLP_GRPC) endif() # Install dependent runtime libraries -install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION +libmexclass/+proxy) +install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION +libmexclass/+proxy) \ No newline at end of file From ccfe101591b49ea263401279481e8eb137704f7d Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Wed, 6 Sep 2023 13:45:49 -0400 Subject: [PATCH 16/30] fix: attribute pairs input indexing --- api/metrics/+opentelemetry/+metrics/Counter.m | 4 ++-- api/metrics/+opentelemetry/+metrics/Histogram.m | 4 ++-- api/metrics/+opentelemetry/+metrics/UpDownCounter.m | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m index a26d8f0..f416f41 100644 --- a/api/metrics/+opentelemetry/+metrics/Counter.m +++ b/api/metrics/+opentelemetry/+metrics/Counter.m @@ -45,8 +45,8 @@ function add(obj, value, varargin) obj.Proxy.add(value,attrkeys,attrvals); else - attrkeys = [varargin{1:2:(nargin-2)}]'; - attrvals = [varargin(2:2:(nargin-2))]'; + attrkeys = [varargin{1:2:length(varargin)}]'; + attrvals = [varargin(2:2:length(varargin))]'; obj.Proxy.add(value,attrkeys,attrvals); end end diff --git a/api/metrics/+opentelemetry/+metrics/Histogram.m b/api/metrics/+opentelemetry/+metrics/Histogram.m index 5cb7df5..8abc9ee 100644 --- a/api/metrics/+opentelemetry/+metrics/Histogram.m +++ b/api/metrics/+opentelemetry/+metrics/Histogram.m @@ -41,8 +41,8 @@ function record(obj, value, varargin) end obj.Proxy.record(value,attrkeys,attrvals); else - attrkeys = [varargin{1:2:(nargin-2)}]'; - attrvals = [varargin(2:2:(nargin-2))]'; + attrkeys = [varargin{1:2:length(varargin)}]'; + attrvals = [varargin(2:2:length(varargin))]'; obj.Proxy.record(value,attrkeys,attrvals); end end diff --git a/api/metrics/+opentelemetry/+metrics/UpDownCounter.m b/api/metrics/+opentelemetry/+metrics/UpDownCounter.m index 3b84c75..b008dc5 100644 --- a/api/metrics/+opentelemetry/+metrics/UpDownCounter.m +++ b/api/metrics/+opentelemetry/+metrics/UpDownCounter.m @@ -44,8 +44,8 @@ function add(obj, value, varargin) obj.Proxy.add(value,attrkeys,attrvals); else - attrkeys = [varargin{1:2:(nargin-2)}]'; - attrvals = [varargin(2:2:(nargin-2))]'; + attrkeys = [varargin{1:2:length(varargin)}]'; + attrvals = [varargin(2:2:length(varargin))]'; obj.Proxy.add(value,attrkeys,attrvals); end end From c44d0e574b53cec9a0a7bcc3e5058ac6b3e53456 Mon Sep 17 00:00:00 2001 From: Rick Tu Date: Thu, 7 Sep 2023 11:18:48 -0400 Subject: [PATCH 17/30] fix: attributes and bucket tests incorporate feedback from PR#13 for tmetrics --- test/tmetrics.m | 81 +++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/test/tmetrics.m b/test/tmetrics.m index f28db40..c452504 100644 --- a/test/tmetrics.m +++ b/test/tmetrics.m @@ -89,15 +89,9 @@ function testCounterAddAttributes(testCase) mt = p.getMeter(metername); ct = mt.createCounter(countername); - % verify MATLAB object properties - verifyEqual(testCase, mt.Name, metername); - verifyEqual(testCase, mt.Version, ""); - verifyEqual(testCase, mt.Schema, ""); - verifyEqual(testCase, ct.Name, countername); - % create testing value and dictionary dict = dictionary("k1","v1","k2",5); - vals = [1,2,3]; + vals = [1,2.4,3]; dict_keys = keys(dict); dict_vals = values(dict); @@ -124,10 +118,13 @@ function testCounterAddAttributes(testCase) verifyEqual(testCase, dp.asDouble, sum(vals)); % verify counter attributes - verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + resourcekeys = string({dp.attributes.key}); + idx1 = find(resourcekeys == dict_keys(1)); + idx2 = find(resourcekeys == dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); end @@ -174,7 +171,7 @@ function testUpDownCounterBasic(testCase) verifyEqual(testCase, ct.Name, countername); % create testing value - val = 10; + val = -10; % add value and attributes ct.add(val); @@ -209,15 +206,9 @@ function testUpDownCounterAddAttributes(testCase) mt = p.getMeter(metername); ct = mt.createUpDownCounter(countername); - % verify MATLAB object properties - verifyEqual(testCase, mt.Name, metername); - verifyEqual(testCase, mt.Version, ""); - verifyEqual(testCase, mt.Schema, ""); - verifyEqual(testCase, ct.Name, countername); - % create testing value and dictionary dict = dictionary("k1","v1","k2",5); - vals = [2,-1,3]; + vals = [2,-1.9,3]; dict_keys = keys(dict); dict_vals = values(dict); @@ -244,10 +235,13 @@ function testUpDownCounterAddAttributes(testCase) verifyEqual(testCase, dp.asDouble, sum(vals)); % verify counter attributes - verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + resourcekeys = string({dp.attributes.key}); + idx1 = find(resourcekeys == dict_keys(1)); + idx2 = find(resourcekeys == dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); end @@ -262,6 +256,12 @@ function testHistogramBasic(testCase) mt = p.getMeter(metername); hist = mt.createHistogram(histname); + % verify MATLAB object properties + verifyEqual(testCase, mt.Name, metername); + verifyEqual(testCase, mt.Version, ""); + verifyEqual(testCase, mt.Schema, ""); + verifyEqual(testCase, hist.Name, histname); + % create value for histogram val = 1; @@ -278,16 +278,25 @@ function testHistogramBasic(testCase) bounds = dp.explicitBounds; counts = dp.bucketCounts; + % verify meter and histogram names + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), histname); + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); + % verify statistics verifyEqual(testCase, dp.min, val); verifyEqual(testCase, dp.max, val); verifyEqual(testCase, dp.sum, val); % verify count in bucket - lower = bounds(1); - upper = bounds(2); - expect_count = sum(val>lower & val<=upper); - verifyEqual(testCase, str2double(counts{2}), expect_count); + len = length(counts); + verifyEqual(testCase, str2double(counts{1}), sum(val<=bounds(1))); + for i = 2:(len-1) + lower = bounds(i-1); + upper = bounds(i); + expect_count = sum(val>lower & val<=upper); + verifyEqual(testCase, str2double(counts{i}), expect_count); + end + verifyEqual(testCase, str2double(counts{len}), sum(val>bounds(len-1))); end @@ -304,7 +313,7 @@ function testHistogramRecordAttributes(testCase) % create value and attributes for histogram dict = dictionary("k1","v1","k2","v2"); - vals = [1,5,10]; + vals = [1,5,8.1]; dict_keys = keys(dict); dict_vals = values(dict); @@ -329,18 +338,24 @@ function testHistogramRecordAttributes(testCase) verifyEqual(testCase, dp.sum, sum(vals)); % verify attributes - verifyEqual(testCase, string(dp.attributes(1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(2).value.stringValue), dict_vals(2)); + resourcekeys = string({dp.attributes.key}); + idx1 = find(resourcekeys == dict_keys(1)); + idx2 = find(resourcekeys == dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); + verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); + verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); + verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); % verify count in bucket - for i = 2:(length(counts)-1) + len = length(counts); + verifyEqual(testCase, str2double(counts{1}), sum(vals<=bounds(1))); + for i = 2:(len-1) lower = bounds(i-1); upper = bounds(i); expect_count = sum(vals>lower & vals<=upper); verifyEqual(testCase, str2double(counts{i}), expect_count); end + verifyEqual(testCase, str2double(counts{len}), sum(vals>bounds(len-1))); end From c573adac511377e6e823603cbcb3a325b37ea844 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Thu, 14 Sep 2023 14:47:12 -0400 Subject: [PATCH 18/30] download collector automatically for tests --- test/commonSetupOnce.m | 38 +++++++++++++++++++++++++++++++----- test/performance/traceTest.m | 3 +-- test/tbaggage.m | 7 ++++--- test/tcontextPropagation.m | 3 +-- test/ttrace.m | 3 +-- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/test/commonSetupOnce.m b/test/commonSetupOnce.m index f70a2a5..db37c1d 100644 --- a/test/commonSetupOnce.m +++ b/test/commonSetupOnce.m @@ -5,12 +5,9 @@ function commonSetupOnce(testCase) % file definitions otelcolroot = getenv("OPENTELEMETRY_COLLECTOR_INSTALL"); -assert(~isempty(otelcolroot), "OPENTELEMETRY_COLLECTOR_INSTALL environment must be defined.") testCase.OtelConfigFile = fullfile(fileparts(mfilename("fullpath")), ... "otelcol_config.yml"); otelroot = getenv("OPENTELEMETRY_MATLAB_INSTALL"); -assert(~isempty(otelroot), "OPENTELEMETRY_MATLAB_INSTALL environment must be defined.") -testCase.OtelRoot = otelroot; testCase.JsonFile = "myoutput.json"; testCase.PidFile = "testoutput.txt"; @@ -19,7 +16,13 @@ function commonSetupOnce(testCase) if ispc testCase.ListPid = @(name)"tasklist /fi ""IMAGENAME eq " + name + ".exe"""; testCase.ReadPidList = @(file)readtable(file, "VariableNamingRule", "preserve", "NumHeaderLines", 3, "MultipleDelimsAsOne", true, "Delimiter", " "); - testCase.ExtractPid = @(table)table.Var2; + testCase.ExtractPid = @(table)table.Var2; + + % variables to support downloading OpenTelemetry Collector + otelcol_arch_name = "windows_amd64"; + otelcol_exe_ext = ".exe"; + + % windows_kill windows_killroot = getenv("WINDOWS_KILL_INSTALL"); windows_killname = "windows-kill"; if isempty(windows_killroot) @@ -41,6 +44,10 @@ function commonSetupOnce(testCase) testCase.ExtractPid = @(table)table.PID; testCase.Sigint = @(id)"kill " + id; % kill sends a SIGTERM instead of SIGINT but turns out this is sufficient to terminate OTEL collector on Linux testCase.Sigterm = @(id)"kill " + id; + + % variables to support downloading OpenTelemetry Collector + otelcol_arch_name = "linux_amd64"; + otelcol_exe_ext = ""; elseif ismac testCase.ListPid = @(name)"pgrep -x " + name; testCase.ReadPidList = @readmatrix; @@ -53,10 +60,31 @@ function commonSetupOnce(testCase) end end + +% OpenTelemetry Collector +if isempty(otelcolroot) + % collector not pre-installed + otelcol_version = "0.85.0"; + otelcol_url = "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v" ... + + otelcol_version; + otelcol_zipfilename = "otelcol_" + otelcol_version + "_" + otelcol_arch_name; + otelcolroot = fullfile(tempdir, otelcol_zipfilename); + + % look for it in tempdir, download and install if it doesn't exist + if ~exist(fullfile(otelcolroot, testCase.OtelcolName + otelcol_exe_ext),"file") + otelcol_tar = gunzip(fullfile(otelcol_url, otelcol_zipfilename + ".tar.gz"), otelcolroot); + otelcol_tar = otelcol_tar{1}; % should have only extracted 1 tar file + untar(otelcol_tar, otelcolroot); + delete(otelcol_tar); + end +end + testCase.Otelcol = fullfile(otelcolroot, testCase.OtelcolName); % set up path -testCase.applyFixture(matlab.unittest.fixtures.PathFixture(testCase.OtelRoot)); +if ~isempty(otelroot) + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(otelroot)); +end % remove temporary files if present if exist(testCase.JsonFile, "file") diff --git a/test/performance/traceTest.m b/test/performance/traceTest.m index c9700eb..91ed707 100644 --- a/test/performance/traceTest.m +++ b/test/performance/traceTest.m @@ -3,10 +3,9 @@ properties OtelConfigFile - OtelRoot JsonFile PidFile - OtelcolName + OtelcolName Otelcol ListPid ReadPidList diff --git a/test/tbaggage.m b/test/tbaggage.m index 91c2592..8a99609 100644 --- a/test/tbaggage.m +++ b/test/tbaggage.m @@ -4,7 +4,6 @@ % Copyright 2023 The MathWorks, Inc. properties - OtelRoot BaggageKeys BaggageValues BaggageHeaders @@ -12,10 +11,12 @@ methods (TestClassSetup) function setupOnce(testCase) - testCase.OtelRoot = getenv("OPENTELEMETRY_MATLAB_INSTALL"); + otelroot = getenv("OPENTELEMETRY_MATLAB_INSTALL"); % set up path - addpath(testCase.OtelRoot); + if ~isempty(otelroot) + addpath(otelroot); + end testCase.BaggageKeys = ["userId", "serverNode", "isProduction"]; testCase.BaggageValues = ["alice", "DF28", "false"]; diff --git a/test/tcontextPropagation.m b/test/tcontextPropagation.m index bcf9dc3..8b8ec71 100644 --- a/test/tcontextPropagation.m +++ b/test/tcontextPropagation.m @@ -5,10 +5,9 @@ properties OtelConfigFile - OtelRoot JsonFile PidFile - OtelcolName + OtelcolName Otelcol ListPid ReadPidList diff --git a/test/ttrace.m b/test/ttrace.m index 115fe3d..8b01773 100644 --- a/test/ttrace.m +++ b/test/ttrace.m @@ -5,10 +5,9 @@ properties OtelConfigFile - OtelRoot JsonFile PidFile - OtelcolName + OtelcolName Otelcol ListPid ReadPidList From 27953ad51d0ee81bb4b550d487ecb76e3982f21f Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 15 Sep 2023 09:41:35 -0400 Subject: [PATCH 19/30] Change tests to automatically download collector if not installed --- test/commonSetupOnce.m | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/commonSetupOnce.m b/test/commonSetupOnce.m index db37c1d..06aed5f 100644 --- a/test/commonSetupOnce.m +++ b/test/commonSetupOnce.m @@ -12,7 +12,6 @@ function commonSetupOnce(testCase) testCase.PidFile = "testoutput.txt"; % process definitions -testCase.OtelcolName = "otelcol"; if ispc testCase.ListPid = @(name)"tasklist /fi ""IMAGENAME eq " + name + ".exe"""; testCase.ReadPidList = @(file)readtable(file, "VariableNamingRule", "preserve", "NumHeaderLines", 3, "MultipleDelimsAsOne", true, "Delimiter", " "); @@ -55,13 +54,16 @@ function commonSetupOnce(testCase) testCase.Sigint = @(id)"kill -s INT " + id; testCase.Sigterm = @(id)"kill -s TERM " + id; if computer == "MACA64" - % only the contrib version of OpenTelemetry Collector is available on Apple silicon - testCase.OtelcolName = "otelcol-contrib"; + otelcol_arch_name = "darwin_arm64"; + else + otelcol_arch_name = "darwin_amd64"; end + otelcol_exe_ext = ""; end % OpenTelemetry Collector +otelcolname = "otelcol"; if isempty(otelcolroot) % collector not pre-installed otelcol_version = "0.85.0"; @@ -71,13 +73,21 @@ function commonSetupOnce(testCase) otelcolroot = fullfile(tempdir, otelcol_zipfilename); % look for it in tempdir, download and install if it doesn't exist - if ~exist(fullfile(otelcolroot, testCase.OtelcolName + otelcol_exe_ext),"file") + if ~(exist(fullfile(otelcolroot, otelcolname + otelcol_exe_ext),"file") || ... + exist(fullfile(otelcolroot,otelcolname + "-contrib" + otelcol_exe_ext),"file")) + % download and install otelcol_tar = gunzip(fullfile(otelcol_url, otelcol_zipfilename + ".tar.gz"), otelcolroot); otelcol_tar = otelcol_tar{1}; % should have only extracted 1 tar file untar(otelcol_tar, otelcolroot); delete(otelcol_tar); end end +% check for contrib version +if exist(fullfile(otelcolroot,otelcolname + "-contrib" + otelcol_exe_ext),"file") + testCase.OtelcolName = otelcolname + "-contrib"; +else + testCase.OtelcolName = otelcolname; +end testCase.Otelcol = fullfile(otelcolroot, testCase.OtelcolName); From 15cfc4c7ebbf0bd9715fc70e40cfad15e27011dd Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 15 Sep 2023 10:09:48 -0400 Subject: [PATCH 20/30] update GitHub workflow --- .github/workflows/build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7e5ee2..2dcee3a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,9 +6,7 @@ jobs: build-and-run-tests: runs-on: ubuntu-latest env: - OPENTELEMETRY_CPP_INSTALL: "${{ github.workspace }}/otel_cpp_install" OPENTELEMETRY_MATLAB_INSTALL: "${{ github.workspace }}/otel_matlab_install" - OPENTELEMETRY_COLLECTOR_INSTALL: "${{ github.workspace }}/otelcol" SYSTEM_LIBSTDCPP_PATH: "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" steps: - name: Download OpenTelemetry-Matlab source @@ -17,11 +15,6 @@ jobs: path: opentelemetry-matlab - name: Install MATLAB uses: matlab-actions/setup-matlab@v1 - - name: Download OpenTelemetry Collector binary - run: | - mkdir otelcol && cd otelcol - wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.75.0/otelcol_0.75.0_linux_amd64.tar.gz - tar -xzf otelcol_0.75.0_linux_amd64.tar.gz - name: Build OpenTelemetry-Matlab run: | cd opentelemetry-matlab From d8ac40af0d46f25900473165c9d29b577401dc8f Mon Sep 17 00:00:00 2001 From: duncanpo Date: Wed, 20 Sep 2023 14:43:06 -0400 Subject: [PATCH 21/30] Add example about context propagation --- examples/context_propagation/README.md | 26 ++++ .../context_propagation/cpp/CMakeLists.txt | 16 ++ .../cpp/HttpTextMapCarrier.h | 39 +++++ examples/context_propagation/cpp/client.cc | 145 ++++++++++++++++++ examples/context_propagation/matlab/mymagic.m | 58 +++++++ .../context_propagation/matlab/routes.json | 12 ++ 6 files changed, 296 insertions(+) create mode 100644 examples/context_propagation/README.md create mode 100644 examples/context_propagation/cpp/CMakeLists.txt create mode 100644 examples/context_propagation/cpp/HttpTextMapCarrier.h create mode 100644 examples/context_propagation/cpp/client.cc create mode 100644 examples/context_propagation/matlab/mymagic.m create mode 100644 examples/context_propagation/matlab/routes.json diff --git a/examples/context_propagation/README.md b/examples/context_propagation/README.md new file mode 100644 index 0000000..26d0102 --- /dev/null +++ b/examples/context_propagation/README.md @@ -0,0 +1,26 @@ +# Context Propagation Example + +In this example, a C++ client calls a MATLAB function hosted on MATLAB Production Server that returns a magic square matrix. Both the C++ client and the MATLAB code are instrumented with OpenTelemetry, and their generated spans form a single trace. + +## Building the Example +1. Build the C++ client, which requires an installed opentelemetry-cpp package and a vcpkg library manager + ``` + cd cpp + cmake -S . -B build -DCMAKE_PREFIX_PATH=/lib/cmake/opentelemetry-cpp -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake + cmake --build build --config Release --target + ``` + where is "all" on most platforms but is "ALL_BUILD" on Windows with Visual Studio. +2. Build MATLAB code into a deployable archive. Use the following MATLAB command: + ``` + cd matlab + mcc -W CTF:mymagic -U mymagic.m -a -a \+libmexclass\+proxy + ``` +3. [Create](https://www.mathworks.com/help/mps/server/creating-a-server.html) and [start](https://www.mathworks.com/help/mps/qs/starting-and-stopping.html) a MATLAB Production Server instance. +4. [Deploy](https://www.mathworks.com/help/mps/qs/share-a-ctf-archive-on-the-server-instance.html) archive to server instance by copying to the auto_deploy directory. +5. [Copy](https://www.mathworks.com/help/mps/server/use-web-handler-for-custom-routes-and-custom-payloads.html) matlab/routes.json to the config directory of the server instance. +6. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). +7. Start the C++ client. + ``` + cd cpp/build/Release + http_client + ``` diff --git a/examples/context_propagation/cpp/CMakeLists.txt b/examples/context_propagation/cpp/CMakeLists.txt new file mode 100644 index 0000000..c7292be --- /dev/null +++ b/examples/context_propagation/cpp/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.7.0) + +# allow overriding package roots +cmake_policy(SET CMP0074 NEW) + +project(example_client VERSION 1.0.0) + +find_package(CURL REQUIRED) +find_package(Protobuf REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(opentelemetry-cpp CONFIG REQUIRED) + +add_executable(http_client client.cc) + +target_include_directories(http_client PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +target_link_libraries(http_client PRIVATE ${OPENTELEMETRY_CPP_LIBRARIES}) diff --git a/examples/context_propagation/cpp/HttpTextMapCarrier.h b/examples/context_propagation/cpp/HttpTextMapCarrier.h new file mode 100644 index 0000000..35bf3c2 --- /dev/null +++ b/examples/context_propagation/cpp/HttpTextMapCarrier.h @@ -0,0 +1,39 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/propagation/http_trace_context.h" + +namespace +{ + +template +class HttpTextMapCarrier : public opentelemetry::context::propagation::TextMapCarrier +{ +public: + HttpTextMapCarrier(T &headers) : headers_(headers) {} + HttpTextMapCarrier() = default; + virtual opentelemetry::nostd::string_view Get( + opentelemetry::nostd::string_view key) const noexcept override + { + std::string key_to_compare = key.data(); + auto it = headers_.find(key_to_compare); + if (it != headers_.end()) + { + return it->second; + } + return ""; + } + + virtual void Set(opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) noexcept override + { + headers_.insert(std::pair(std::string(key), std::string(value))); + } + + T headers_; +}; + +} // namespace diff --git a/examples/context_propagation/cpp/client.cc b/examples/context_propagation/cpp/client.cc new file mode 100644 index 0000000..c2c1f94 --- /dev/null +++ b/examples/context_propagation/cpp/client.cc @@ -0,0 +1,145 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "opentelemetry/ext/http/client/http_client_factory.h" +#include "opentelemetry/ext/http/common/url_parser.h" +#include "opentelemetry/trace/semantic_conventions.h" +#include "HttpTextMapCarrier.h" + +#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h" +#include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/sdk/trace/tracer_context_factory.h" +#include "opentelemetry/sdk/trace/tracer_provider_factory.h" +#include "opentelemetry/trace/provider.h" + +#include "opentelemetry/context/propagation/global_propagator.h" +#include "opentelemetry/context/propagation/text_map_propagator.h" + +#include +#include "opentelemetry/ext/http/client/http_client.h" +#include "opentelemetry/nostd/shared_ptr.h" + +namespace +{ + +using namespace opentelemetry::trace; +namespace http_client = opentelemetry::ext::http::client; +namespace context = opentelemetry::context; +namespace nostd = opentelemetry::nostd; + +void InitTracer() +{ + auto exporter = opentelemetry::exporter::otlp::OtlpHttpExporterFactory::Create(); + auto processor = + opentelemetry::sdk::trace::SimpleSpanProcessorFactory::Create(std::move(exporter)); + std::vector> processors; + processors.push_back(std::move(processor)); + std::unique_ptr context = + opentelemetry::sdk::trace::TracerContextFactory::Create(std::move(processors)); + std::shared_ptr provider = + opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(context)); + // Set the global trace provider + opentelemetry::trace::Provider::SetTracerProvider(provider); + + // set global propagator + opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator( + opentelemetry::nostd::shared_ptr( + new opentelemetry::trace::propagation::HttpTraceContext())); +} + +opentelemetry::nostd::shared_ptr get_tracer(std::string tracer_name) +{ + auto provider = opentelemetry::trace::Provider::GetTracerProvider(); + return provider->GetTracer(tracer_name); +} + +void sendRequest(const std::string &url) +{ + auto http_client = http_client::HttpClientFactory::CreateSync(); + // define input to post to destination + std::vector body; + uint8_t magic_square_size = 3; // request 3x3 magic square + body.push_back(magic_square_size); + + // start active span + StartSpanOptions options; + options.kind = SpanKind::kClient; // client + opentelemetry::ext::http::common::UrlParser url_parser(url); + + std::string span_name = url_parser.path_; + auto span = get_tracer("http-client") + ->StartSpan(span_name, + {{SemanticConventions::kHttpUrl, url_parser.url_}, + {SemanticConventions::kHttpScheme, url_parser.scheme_}, + {SemanticConventions::kHttpMethod, "POST"}}, + options); + auto scope = get_tracer("http-client")->WithActiveSpan(span); + + // inject current context into http header + auto current_ctx = context::RuntimeContext::GetCurrent(); + HttpTextMapCarrier carrier; + auto prop = context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); + prop->Inject(carrier, current_ctx); + + // send http request + http_client::Result result = http_client->Post(url, body, carrier.headers_); + if (result) + { + // set span attributes + auto status_code = result.GetResponse().GetStatusCode(); + span->SetAttribute(SemanticConventions::kHttpStatusCode, status_code); + result.GetResponse().ForEachHeader( + [&span](nostd::string_view header_name, nostd::string_view header_value) { + span->SetAttribute("http.header." + std::string(header_name.data()), header_value); + return true; + }); + + if (status_code >= 400) + { + span->SetStatus(StatusCode::kError); + } + } + else + { + span->SetStatus( + StatusCode::kError, + "Response Status :" + + std::to_string( + static_cast::type>( + result.GetSessionState()))); + } + // end span and export data + span->End(); +} + +void CleanupTracer() +{ + std::shared_ptr none; + opentelemetry::trace::Provider::SetTracerProvider(none); +} + +} // namespace + +int main(int argc, char *argv[]) +{ + InitTracer(); + constexpr char default_host[] = "localhost"; + constexpr char default_path[] = "/magic"; + constexpr uint16_t default_port = 9910; + uint16_t port; + + // The port the validation service listens to can be specified via the command line. + if (argc > 1) + { + port = (uint16_t)(atoi(argv[1])); + } + else + { + port = default_port; + } + + std::string url = "http://" + std::string(default_host) + ":" + std::to_string(port) + + std::string(default_path); + sendRequest(url); + CleanupTracer(); +} diff --git a/examples/context_propagation/matlab/mymagic.m b/examples/context_propagation/matlab/mymagic.m new file mode 100644 index 0000000..54597e8 --- /dev/null +++ b/examples/context_propagation/matlab/mymagic.m @@ -0,0 +1,58 @@ +function response = mymagic(request) +% Web request handler function that takes context information (trace ID and span ID) +% from incoming request and set as current context +% +% Copyright 2023 The MathWorks, Inc. + +% initialize tracing +runOnce(@initTracer); + +% extract context passed from header +carrier = opentelemetry.context.propagation.TextMapCarrier(request.Headers); +newcontext = opentelemetry.context.propagation.extractContext(carrier); +scope = setCurrentContext(newcontext); %#ok + +% start a span +tr = opentelemetry.trace.getTracer("MATLABTracer"); +sp = startSpan(tr, "mymagic", "SpanKind", "server"); + +sz = double(request.Body); +magicsquare = magic(request.Body); % generate the magic square +setAttributes(sp, "input", sz, "output", magicsquare); + +% convert magic square to char +magicsquare_char = char(strjoin(string(magicsquare))); + +% return a response +response = struct('ApiVersion', [1 0 0], ... + 'HttpCode', 200, ... + 'HttpMessage', 'OK', ... + 'Headers', {{ ... + 'Server' 'MATLABMagicFunction'; ... + 'X-Magic-Square-Size' sprintf('%d', sz); ... + 'Content-Type' 'text/plain'; ... + }},... + 'Body', uint8(magicsquare_char)); + +% everything seems to run fine if we get to this point +setStatus(sp, "ok", "status ok"); +end + +function initTracer +% set up global TracerProvider +tp = opentelemetry.sdk.trace.TracerProvider(); +setTracerProvider(tp); + +% set up global propagator +prop = opentelemetry.trace.propagation.TraceContextPropagator(); +setTextMapPropagator(prop); +end + +% Thie helper ensures the input function is only run once +function runOnce(fh) +persistent hasrun +if isempty(hasrun) + feval(fh); + hasrun = 1; +end +end diff --git a/examples/context_propagation/matlab/routes.json b/examples/context_propagation/matlab/routes.json new file mode 100644 index 0000000..d0c2cdf --- /dev/null +++ b/examples/context_propagation/matlab/routes.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "pathmap": [ + { + "match": "/magic", + "webhandler": { + "component": "mymagic", + "function": "mymagic" + } + } + ] +} From 2b05d12a810ff94d37212251b6f801013a00a965 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Thu, 21 Sep 2023 12:01:02 -0400 Subject: [PATCH 22/30] webread example --- .gitignore | 4 +- examples/webread/README.md | 14 +++ examples/webread/cpp/CMakeLists.txt | 16 +++ examples/webread/cpp/HttpTextMapCarrier.h | 40 +++++++ examples/webread/cpp/server.cc | 139 ++++++++++++++++++++++ examples/webread/cpp/server.h | 52 ++++++++ examples/webread/matlab/webread_example.m | 44 +++++++ 7 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 examples/webread/README.md create mode 100644 examples/webread/cpp/CMakeLists.txt create mode 100644 examples/webread/cpp/HttpTextMapCarrier.h create mode 100644 examples/webread/cpp/server.cc create mode 100644 examples/webread/cpp/server.h create mode 100644 examples/webread/matlab/webread_example.m diff --git a/.gitignore b/.gitignore index b3ec7c2..8371d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -# build directory +# build directories build +examples/webread/build +examples/context_propagation/build # Autosave files *.asv diff --git a/examples/webread/README.md b/examples/webread/README.md new file mode 100644 index 0000000..a3848d5 --- /dev/null +++ b/examples/webread/README.md @@ -0,0 +1,14 @@ +# Webread Example + +In this example, the MATLAB function webread_example calls a C++ server using webread, passing along context information including trace ID and span ID. Both the C++ server and the MATLAB code are instrumented with OpenTelemetry, and their generated spans form a single trace. + +## Building the Example +1. Build the C++ server, which requires an installed opentelemetry-cpp package and a vcpkg library manager + ``` + cd cpp + cmake -S . -B build -DCMAKE_PREFIX_PATH=/lib/cmake/opentelemetry-cpp -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake + cmake --build build --config Release --target + ``` + where is "all" on most platforms but is "ALL_BUILD" on Windows with Visual Studio. +2. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). +3. Run webread_example in MATLAB. diff --git a/examples/webread/cpp/CMakeLists.txt b/examples/webread/cpp/CMakeLists.txt new file mode 100644 index 0000000..a70b066 --- /dev/null +++ b/examples/webread/cpp/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.7.0) + +# allow overriding package roots +cmake_policy(SET CMP0074 NEW) + +project(webread_example_cpp VERSION 1.0.0) + +find_package(CURL REQUIRED) +find_package(Protobuf REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(opentelemetry-cpp CONFIG REQUIRED) + +add_executable(http_server server.cc) + +target_include_directories(http_server PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) +target_link_libraries(http_server PRIVATE ${OPENTELEMETRY_CPP_LIBRARIES}) diff --git a/examples/webread/cpp/HttpTextMapCarrier.h b/examples/webread/cpp/HttpTextMapCarrier.h new file mode 100644 index 0000000..2594ca8 --- /dev/null +++ b/examples/webread/cpp/HttpTextMapCarrier.h @@ -0,0 +1,40 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once + +#include +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/propagation/http_trace_context.h" + +namespace +{ +template +class HttpTextMapCarrier : public opentelemetry::context::propagation::TextMapCarrier +{ +public: + HttpTextMapCarrier(T &headers) : headers_(headers) {} + HttpTextMapCarrier() = default; + virtual opentelemetry::nostd::string_view Get( + opentelemetry::nostd::string_view key) const noexcept override + { + std::string key_to_compare = key.data(); + // work around a case mismatch in the first letter + key_to_compare[0] = toupper(key_to_compare[0]); + auto it = headers_.find(key_to_compare); + if (it != headers_.end()) + { + return it->second; + } + return ""; + } + + virtual void Set(opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) noexcept override + { + headers_.insert(std::pair(std::string(key), std::string(value))); + } + + T headers_; +}; + +} // namespace diff --git a/examples/webread/cpp/server.cc b/examples/webread/cpp/server.cc new file mode 100644 index 0000000..a3a5280 --- /dev/null +++ b/examples/webread/cpp/server.cc @@ -0,0 +1,139 @@ +// Copyright 2023 The MathWorks, Inc. + +#include "server.h" +#include "opentelemetry/trace/context.h" +#include "opentelemetry/trace/semantic_conventions.h" +#include "HttpTextMapCarrier.h" + +#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h" +#include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/sdk/trace/tracer_context_factory.h" +#include "opentelemetry/sdk/trace/tracer_provider_factory.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/context/propagation/global_propagator.h" +#include "opentelemetry/context/propagation/text_map_propagator.h" +#include "opentelemetry/nostd/shared_ptr.h" + +#include +#include +#include + +namespace +{ + +using namespace opentelemetry::trace; +namespace context = opentelemetry::context; + +uint16_t server_port = 8800; +constexpr const char *server_name = "localhost"; + +void InitTracer() +{ + auto exporter = opentelemetry::exporter::otlp::OtlpHttpExporterFactory::Create(); + auto processor = + opentelemetry::sdk::trace::SimpleSpanProcessorFactory::Create(std::move(exporter)); + std::vector> processors; + processors.push_back(std::move(processor)); + std::unique_ptr context = + opentelemetry::sdk::trace::TracerContextFactory::Create(std::move(processors)); + std::shared_ptr provider = + opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(context)); + // Set the global trace provider + opentelemetry::trace::Provider::SetTracerProvider(provider); + + // set global propagator + opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator( + opentelemetry::nostd::shared_ptr( + new opentelemetry::trace::propagation::HttpTraceContext())); +} + +opentelemetry::nostd::shared_ptr get_tracer(std::string tracer_name) +{ + auto provider = opentelemetry::trace::Provider::GetTracerProvider(); + return provider->GetTracer(tracer_name); +} + +class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback +{ +public: + virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, + HTTP_SERVER_NS::HttpResponse &response) override + { + StartSpanOptions options; + options.kind = SpanKind::kServer; // server + std::string span_name = request.uri; + + // extract context from http header + std::map &request_headers = + const_cast &>(request.headers); + const HttpTextMapCarrier> carrier(request_headers); + auto prop = context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); + auto current_ctx = context::RuntimeContext::GetCurrent(); + auto new_context = prop->Extract(carrier, current_ctx); + options.parent = GetSpan(new_context)->GetContext(); + + // start span with parent context extracted from http header + auto span = get_tracer("http_server") + ->StartSpan(span_name, + {{SemanticConventions::kNetHostName, server_name}, + {SemanticConventions::kNetHostPort, server_port}, + {SemanticConventions::kHttpMethod, request.method}, + {SemanticConventions::kHttpScheme, "http"}, + {SemanticConventions::kHttpRequestContentLength, + static_cast(request.content.length())}, + {SemanticConventions::kHttpClientIp, request.client}}, + options); + + auto scope = get_tracer("http_server")->WithActiveSpan(span); + + for (auto &kv : request.headers) + { + span->SetAttribute("http.header." + std::string(kv.first.data()), kv.second); + } + if (request.uri == "/webreadexample") + { + span->AddEvent("Processing request"); + response.headers[HTTP_SERVER_NS::CONTENT_TYPE] = HTTP_SERVER_NS::CONTENT_TYPE_TEXT; + response.body = "Greetings from webread example server!"; + span->End(); + return 200; + } + span->End(); + return 404; + } +}; +} // namespace + +void CleanupTracer() +{ + std::shared_ptr none; + opentelemetry::trace::Provider::SetTracerProvider(none); +} + +int main(int argc, char *argv[]) +{ + InitTracer(); + + // The port the validation service listens to can be specified via the command line. + if (argc > 1) + { + server_port = (uint16_t)atoi(argv[1]); + } + + HttpServer http_server(server_name, server_port); + RequestHandler req_handler; + http_server.AddHandler("/webreadexample", &req_handler); + auto root_span = get_tracer("http_server")->StartSpan(__func__); + Scope scope(root_span); + http_server.Start(); + std::cout << "Server is running..Press ctrl-c to exit...\n"; + while (1) + { + std::this_thread::sleep_for(std::chrono::seconds(100)); + } + http_server.Stop(); + root_span->End(); + CleanupTracer(); + return 0; +} diff --git a/examples/webread/cpp/server.h b/examples/webread/cpp/server.h new file mode 100644 index 0000000..1f0095d --- /dev/null +++ b/examples/webread/cpp/server.h @@ -0,0 +1,52 @@ +// Copyright 2023 The MathWorks, Inc. + +#pragma once +#include +#include +#include "opentelemetry/ext/http/server/http_server.h" + +namespace +{ + +class HttpServer : public HTTP_SERVER_NS::HttpRequestCallback +{ + +protected: + HTTP_SERVER_NS::HttpServer server_; + std::string server_url_; + uint16_t port_; + std::atomic is_running_{false}; + +public: + HttpServer(std::string server_name = "test_server", uint16_t port = 8800) : port_(port) + { + server_.setServerName(server_name); + server_.setKeepalive(false); + } + + void AddHandler(std::string path, HTTP_SERVER_NS::HttpRequestCallback *request_handler) + { + server_.addHandler(path, *request_handler); + } + + void Start() + { + if (!is_running_.exchange(true)) + { + server_.addListeningPort(port_); + server_.start(); + } + } + + void Stop() + { + if (is_running_.exchange(false)) + { + server_.stop(); + } + } + + ~HttpServer() { Stop(); } +}; + +} // namespace diff --git a/examples/webread/matlab/webread_example.m b/examples/webread/matlab/webread_example.m new file mode 100644 index 0000000..ddfe728 --- /dev/null +++ b/examples/webread/matlab/webread_example.m @@ -0,0 +1,44 @@ +function response = webread_example +% This example shows how to propagate context (span ID and trace ID) +% to another process when using webread. + +% Copyright 2023 The MathWorks, Inc. + +% initialize tracing +runOnce(@initTracer); + +% start a span +tr = opentelemetry.trace.getTracer("webread_example_tracer"); +sp = startSpan(tr, "webread_example", SpanKind="client"); +scope = makeCurrent(sp); %#ok + +% inject context into header +carrier = opentelemetry.context.propagation.injectContext(); + +% call webread +url = "http://localhost:8800/webreadexample"; +options = weboptions("HeaderFields", carrier.Headers); +response = webread(url, options); + +% everything seems to run fine if we get to this point +setStatus(sp, "ok", "status ok"); +end + +function initTracer +% set up global TracerProvider +tp = opentelemetry.sdk.trace.TracerProvider(); +setTracerProvider(tp); + +% set up global propagator +prop = opentelemetry.trace.propagation.TraceContextPropagator(); +setTextMapPropagator(prop); +end + +% This helper ensures the input function is only run once +function runOnce(fh) +persistent hasrun +if isempty(hasrun) + feval(fh); + hasrun = 1; +end +end From 220927e50e0808a1d69d4084c84e25eec5b277a8 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Thu, 21 Sep 2023 14:23:49 -0400 Subject: [PATCH 23/30] New trace example --- examples/trace/README.md | 12 ++++++++ examples/trace/trace_example.m | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 examples/trace/README.md create mode 100644 examples/trace/trace_example.m diff --git a/examples/trace/README.md b/examples/trace/README.md new file mode 100644 index 0000000..8e161aa --- /dev/null +++ b/examples/trace/README.md @@ -0,0 +1,12 @@ +# Trace Example +This example shows how to instrument simple MATLAB code using spans. The instrumented code is a function that fits a line through a cluster of data points. +* At the beginning of the first run, initialization is necessary to create and store a global tracer provider. +* At the beginning of each function, the global tracer provider is used to create a tracer. +* Use the created tracer to start a span. +* Make the new span the current span. Spans subsequently started will be children of this span. + +## Running the Example +1. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). +2. Start MATLAB. +3. Ensure the installation directory of OpenTelemetry-matlab is on the MATLAB path. +4. Run trace_example. diff --git a/examples/trace/trace_example.m b/examples/trace/trace_example.m new file mode 100644 index 0000000..26258de --- /dev/null +++ b/examples/trace/trace_example.m @@ -0,0 +1,55 @@ +function yf = trace_example +% This example creates spans to form a trace to instrument some simple +% MATLAB code that fits a line through a cluster of data points. + +% Copyright 2023 The MathWorks, Inc. + +% initialize tracing during first run +runOnce(@initTracer); + +% start the top level span and make it current +tr = opentelemetry.trace.getTracer("trace_example"); +sp = startSpan(tr, "trace_example"); +scope = makeCurrent(sp); %#ok<*NASGU> + +[x, y] = generate_data(); +yf = best_fit_line(x,y); +end + +function [x, y] = generate_data +% generate some random data +tr = opentelemetry.trace.getTracer("trace_example"); +sp = startSpan(tr, "generate_data"); +scope = makeCurrent(sp); + +a = 1.5; +b = 0.8; +sigma = 5; +x = 1:100; +y = a * x + b + sigma * randn(1, 100); +end + +function yf = best_fit_line(x, y) +% fit a line through points defined by inputs x and y +tr = opentelemetry.trace.getTracer("trace_example"); +sp = startSpan(tr, "best_fit_line"); +scope = makeCurrent(sp); + +coefs = polyfit(x, y, 1); +yf = polyval(coefs , x); +end + +function initTracer +% set up global TracerProvider +tp = opentelemetry.sdk.trace.TracerProvider(); +setTracerProvider(tp); +end + +% This helper ensures the input function is only run once +function runOnce(fh) +persistent hasrun +if isempty(hasrun) + feval(fh); + hasrun = 1; +end +end From 5728dc15b3819423f6713a0e027f2ae1cb53dde0 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Thu, 28 Sep 2023 10:39:56 -0400 Subject: [PATCH 24/30] Integrate examples CMakeLists.txt with the main CMakeList.txt --- .github/workflows/build.yml | 2 +- .gitignore | 2 -- CMakeLists.txt | 19 ++++++++++++---- README.md | 5 ++++- examples/CMakeLists.txt | 3 +++ examples/context_propagation/CMakeLists.txt | 22 +++++++++++++++++++ examples/context_propagation/README.md | 20 ++++++----------- .../context_propagation/cpp/CMakeLists.txt | 16 -------------- examples/context_propagation/cpp/client.cc | 10 ++++----- examples/webread/CMakeLists.txt | 5 +++++ examples/webread/README.md | 9 ++++---- examples/webread/cpp/CMakeLists.txt | 16 -------------- examples/webread/cpp/server.cc | 12 +++++----- 13 files changed, 72 insertions(+), 69 deletions(-) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/context_propagation/CMakeLists.txt delete mode 100644 examples/context_propagation/cpp/CMakeLists.txt create mode 100644 examples/webread/CMakeLists.txt delete mode 100644 examples/webread/cpp/CMakeLists.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2dcee3a..d00d8f1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Build OpenTelemetry-Matlab run: | cd opentelemetry-matlab - cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} cmake --build build --config Release --target install - name: Run tests env: diff --git a/.gitignore b/.gitignore index 8371d7f..340659c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # build directories build -examples/webread/build -examples/context_propagation/build # Autosave files *.asv diff --git a/CMakeLists.txt b/CMakeLists.txt index 53307e7..6aac691 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ else() endif() if(NOT DEFINED VCPKG_INSTALLED_DIR) - set(DVCPKG_INSTALLED_DIR ${CMAKE_BINARY_DIR}/vcpkg_installed) + set(VCPKG_INSTALLED_DIR ${CMAKE_BINARY_DIR}/vcpkg_installed) endif() # ###################################### @@ -71,6 +71,7 @@ endif() if(APPLE) option(SKIP_OTEL_CPP_PATCH "Whether to skip patching OpenTelemetry-cpp" OFF) endif() +option(WITH_EXAMPLES "Whether to build examples" OFF) # set vcpkg features depending on specified options set(VCPKG_MANIFEST_FEATURES "") # start with empty @@ -269,7 +270,7 @@ endif() target_compile_options(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTLP_MACROS} ${CUSTOM_CXX_FLAGS}) # link against OpenTelemetry-cpp libraries and their dependencies -target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_common${CMAKE_STATIC_LIBRARY_SUFFIX} +set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_common${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_otlp_recordable${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}opentelemetry_proto${OTEL_PROTO_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_resources${CMAKE_STATIC_LIBRARY_SUFFIX} @@ -278,19 +279,21 @@ target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PRE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_logs${CMAKE_STATIC_LIBRARY_SUFFIX} ${Protobuf_LIBRARIES}) if(WITH_OTLP_HTTP) - target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http${CMAKE_STATIC_LIBRARY_SUFFIX} + set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_LINK_LIBRARIES} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http_client${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_http_client_curl${CMAKE_STATIC_LIBRARY_SUFFIX} ${CURL_LIBRARIES}) endif() if(WITH_OTLP_GRPC) - target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc${CMAKE_STATIC_LIBRARY_SUFFIX} + set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_LINK_LIBRARIES} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc_client${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}opentelemetry_proto_grpc${OTEL_PROTO_LIBRARY_SUFFIX} gRPC::grpc++ absl::synchronization) endif() +target_link_libraries(${OPENTELEMETRY_PROXY_LIBRARY_NAME} PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) + # On Linux, when linking with certain static libraries, need to force include entire archive to avoid the linker mistakenly leaving out symbols if(UNIX AND NOT APPLE AND NOT CYGWIN) set(OPENTELEMETRY_PROXY_LINK_OPTIONS -Wl,--whole-archive @@ -343,6 +346,7 @@ elseif(UNIX AND NOT CYGWIN) set(OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES ${OTEL_CPP_RUNTIME}) endif() + # ############################## # OpenTelemetry MEX Gateway # ############################## @@ -395,3 +399,10 @@ endif() # Install dependent runtime libraries install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION +libmexclass/+proxy) + +# ############################## +# Subdirectories +# ############################## +if(WITH_EXAMPLES) + add_subdirectory(examples) +endif() diff --git a/README.md b/README.md index c61d746..e79a463 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ Requires MATLAB release R2022b or newer - [MATLAB](https://www.mathworks.com/products/matlab.html) ### 3rd Party Products: -- [Opentelemetry C++](https://github.com/open-telemetry/opentelemetry-cpp) +- [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-releases/releases) +- [OpenTelemetry C++](https://github.com/open-telemetry/opentelemetry-cpp) - [vcpkg C/C++ dependency manager](https://vcpkg.io) ## Installation @@ -57,6 +58,8 @@ otelcol --config ``` 4. If your collector is configured to display the data, you should see your span displayed. +For more examples, see the "examples" folder. + ## Help To view documentation of individual function, type "help \\". For example, ``` diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5f63ae3 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_subdirectory(context_propagation) +add_subdirectory(webread) diff --git a/examples/context_propagation/CMakeLists.txt b/examples/context_propagation/CMakeLists.txt new file mode 100644 index 0000000..aa8513d --- /dev/null +++ b/examples/context_propagation/CMakeLists.txt @@ -0,0 +1,22 @@ + +# C++ target +set(CONTEXTPROP_EXAMPLE_TARGET contextprop_example_client) +add_executable(${CONTEXTPROP_EXAMPLE_TARGET} cpp/client.cc) + +target_include_directories(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_PREFIX}/include) +target_link_libraries(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) + +# MATLAB target +find_package(Matlab REQUIRED COMPONENTS MCC_COMPILER MAIN_PROGRAM) + +set(CONTEXTPROP_EXAMPLE_DEPLOYNAME mymagic) +set(CONTEXTPROP_EXAMPLE_MATLAB_SOURCE ${CMAKE_CURRENT_LIST_DIR}/matlab/${CONTEXTPROP_EXAMPLE_DEPLOYNAME}.m) +set(CONTEXTPROP_EXAMPLE_ROUTES ../../../examples/context_propagation/matlab/routes.json) #somehow, only relative paths are allowed +matlab_get_version_from_matlab_run(${Matlab_MAIN_PROGRAM} Matlab_LIST_VERSION) +if(DEFINED Matlab_LIST_VERSION AND ${Matlab_LIST_VERSION} VERSION_GREATER_EQUAL 23.2.0) + # since MATLAB R2023b, route mapping can be specified at the archive level + set(ARCHIVE_ROUTES ",ROUTES:${CONTEXTPROP_EXAMPLE_ROUTES}") +else() + set(ARCHIVE_ROUTES "") +endif() +install(CODE "execute_process(COMMAND ${Matlab_MCC_COMPILER} -W CTF:${CONTEXTPROP_EXAMPLE_DEPLOYNAME}${ARCHIVE_ROUTES} -U ${CONTEXTPROP_EXAMPLE_MATLAB_SOURCE} -a ${CMAKE_INSTALL_PREFIX} -a ${CMAKE_INSTALL_PREFIX}/+libmexclass/+proxy WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})") diff --git a/examples/context_propagation/README.md b/examples/context_propagation/README.md index 26d0102..679bfe7 100644 --- a/examples/context_propagation/README.md +++ b/examples/context_propagation/README.md @@ -3,21 +3,15 @@ In this example, a C++ client calls a MATLAB function hosted on MATLAB Production Server that returns a magic square matrix. Both the C++ client and the MATLAB code are instrumented with OpenTelemetry, and their generated spans form a single trace. ## Building the Example -1. Build the C++ client, which requires an installed opentelemetry-cpp package and a vcpkg library manager +1. Enable WITH_EXAMPLES when building OpenTelemetry-Matlab ``` - cd cpp - cmake -S . -B build -DCMAKE_PREFIX_PATH=/lib/cmake/opentelemetry-cpp -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake - cmake --build build --config Release --target + cmake -S . -B build -DWITH_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX= + cmake --build build --config Release ``` - where is "all" on most platforms but is "ALL_BUILD" on Windows with Visual Studio. -2. Build MATLAB code into a deployable archive. Use the following MATLAB command: - ``` - cd matlab - mcc -W CTF:mymagic -U mymagic.m -a -a \+libmexclass\+proxy - ``` -3. [Create](https://www.mathworks.com/help/mps/server/creating-a-server.html) and [start](https://www.mathworks.com/help/mps/qs/starting-and-stopping.html) a MATLAB Production Server instance. -4. [Deploy](https://www.mathworks.com/help/mps/qs/share-a-ctf-archive-on-the-server-instance.html) archive to server instance by copying to the auto_deploy directory. -5. [Copy](https://www.mathworks.com/help/mps/server/use-web-handler-for-custom-routes-and-custom-payloads.html) matlab/routes.json to the config directory of the server instance. + The built examples can be found in build/examples/context_propagation and subdirectories. +2. [Create](https://www.mathworks.com/help/mps/server/creating-a-server.html) and [start](https://www.mathworks.com/help/mps/qs/starting-and-stopping.html) a MATLAB Production Server instance. +3. [Deploy](https://www.mathworks.com/help/mps/qs/share-a-ctf-archive-on-the-server-instance.html) archive to server instance by copying to the auto_deploy directory. +4. [Copy](https://www.mathworks.com/help/mps/server/use-web-handler-for-custom-routes-and-custom-payloads.html) matlab/routes.json to the config directory of the server instance. 6. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). 7. Start the C++ client. ``` diff --git a/examples/context_propagation/cpp/CMakeLists.txt b/examples/context_propagation/cpp/CMakeLists.txt deleted file mode 100644 index c7292be..0000000 --- a/examples/context_propagation/cpp/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.7.0) - -# allow overriding package roots -cmake_policy(SET CMP0074 NEW) - -project(example_client VERSION 1.0.0) - -find_package(CURL REQUIRED) -find_package(Protobuf REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(opentelemetry-cpp CONFIG REQUIRED) - -add_executable(http_client client.cc) - -target_include_directories(http_client PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) -target_link_libraries(http_client PRIVATE ${OPENTELEMETRY_CPP_LIBRARIES}) diff --git a/examples/context_propagation/cpp/client.cc b/examples/context_propagation/cpp/client.cc index c2c1f94..3508f88 100644 --- a/examples/context_propagation/cpp/client.cc +++ b/examples/context_propagation/cpp/client.cc @@ -69,9 +69,9 @@ void sendRequest(const std::string &url) std::string span_name = url_parser.path_; auto span = get_tracer("http-client") ->StartSpan(span_name, - {{SemanticConventions::kHttpUrl, url_parser.url_}, - {SemanticConventions::kHttpScheme, url_parser.scheme_}, - {SemanticConventions::kHttpMethod, "POST"}}, + {{SemanticConventions::kUrlFull, url_parser.url_}, + {SemanticConventions::kUrlScheme, url_parser.scheme_}, + {SemanticConventions::kHttpRequestMethod, "POST"}}, options); auto scope = get_tracer("http-client")->WithActiveSpan(span); @@ -87,7 +87,7 @@ void sendRequest(const std::string &url) { // set span attributes auto status_code = result.GetResponse().GetStatusCode(); - span->SetAttribute(SemanticConventions::kHttpStatusCode, status_code); + span->SetAttribute(SemanticConventions::kHttpResponseStatusCode, status_code); result.GetResponse().ForEachHeader( [&span](nostd::string_view header_name, nostd::string_view header_value) { span->SetAttribute("http.header." + std::string(header_name.data()), header_value); @@ -124,7 +124,7 @@ int main(int argc, char *argv[]) { InitTracer(); constexpr char default_host[] = "localhost"; - constexpr char default_path[] = "/magic"; + constexpr char default_path[] = "/mymagic/magic"; constexpr uint16_t default_port = 9910; uint16_t port; diff --git a/examples/webread/CMakeLists.txt b/examples/webread/CMakeLists.txt new file mode 100644 index 0000000..0c003b0 --- /dev/null +++ b/examples/webread/CMakeLists.txt @@ -0,0 +1,5 @@ + +add_executable(http_server cpp/server.cc) + +target_include_directories(http_server PRIVATE ${OTEL_CPP_PREFIX}/include) +target_link_libraries(http_server PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) diff --git a/examples/webread/README.md b/examples/webread/README.md index a3848d5..99258b0 100644 --- a/examples/webread/README.md +++ b/examples/webread/README.md @@ -3,12 +3,11 @@ In this example, the MATLAB function webread_example calls a C++ server using webread, passing along context information including trace ID and span ID. Both the C++ server and the MATLAB code are instrumented with OpenTelemetry, and their generated spans form a single trace. ## Building the Example -1. Build the C++ server, which requires an installed opentelemetry-cpp package and a vcpkg library manager +1. Enable WITH_EXAMPLES when building OpenTelemetry-Matlab ``` - cd cpp - cmake -S . -B build -DCMAKE_PREFIX_PATH=/lib/cmake/opentelemetry-cpp -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake - cmake --build build --config Release --target + cmake -S . -B build -DWITH_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX= + cmake --build build --config Release ``` - where is "all" on most platforms but is "ALL_BUILD" on Windows with Visual Studio. + The built examples can be found in build/examples/webread and subdirectories. 2. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). 3. Run webread_example in MATLAB. diff --git a/examples/webread/cpp/CMakeLists.txt b/examples/webread/cpp/CMakeLists.txt deleted file mode 100644 index a70b066..0000000 --- a/examples/webread/cpp/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.7.0) - -# allow overriding package roots -cmake_policy(SET CMP0074 NEW) - -project(webread_example_cpp VERSION 1.0.0) - -find_package(CURL REQUIRED) -find_package(Protobuf REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(opentelemetry-cpp CONFIG REQUIRED) - -add_executable(http_server server.cc) - -target_include_directories(http_server PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS}) -target_link_libraries(http_server PRIVATE ${OPENTELEMETRY_CPP_LIBRARIES}) diff --git a/examples/webread/cpp/server.cc b/examples/webread/cpp/server.cc index a3a5280..c4dce32 100644 --- a/examples/webread/cpp/server.cc +++ b/examples/webread/cpp/server.cc @@ -76,13 +76,13 @@ class RequestHandler : public HTTP_SERVER_NS::HttpRequestCallback // start span with parent context extracted from http header auto span = get_tracer("http_server") ->StartSpan(span_name, - {{SemanticConventions::kNetHostName, server_name}, - {SemanticConventions::kNetHostPort, server_port}, - {SemanticConventions::kHttpMethod, request.method}, - {SemanticConventions::kHttpScheme, "http"}, - {SemanticConventions::kHttpRequestContentLength, + {{SemanticConventions::kServerAddress, server_name}, + {SemanticConventions::kServerPort, server_port}, + {SemanticConventions::kHttpRequestMethod, request.method}, + {SemanticConventions::kUrlScheme, "http"}, + {SemanticConventions::kHttpRequestBodySize, static_cast(request.content.length())}, - {SemanticConventions::kHttpClientIp, request.client}}, + {SemanticConventions::kClientAddress, request.client}}, options); auto scope = get_tracer("http_server")->WithActiveSpan(span); From 90bea3ae933dffaf51c21533b2c8f8f9c39c4777 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 29 Sep 2023 13:59:37 -0400 Subject: [PATCH 25/30] fix example build steps for Linux and Mac --- CMakeLists.txt | 4 ++-- examples/context_propagation/CMakeLists.txt | 7 +++++++ examples/webread/CMakeLists.txt | 14 +++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6aac691..1b9f509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,7 +346,6 @@ elseif(UNIX AND NOT CYGWIN) set(OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES ${OTEL_CPP_RUNTIME}) endif() - # ############################## # OpenTelemetry MEX Gateway # ############################## @@ -398,7 +397,8 @@ if(WITH_OTLP_GRPC) endif() # Install dependent runtime libraries -install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION +libmexclass/+proxy) +set(LIBMEXCLASS_PROXY_INSTALLED_DIR +libmexclass/+proxy) +install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION ${LIBMEXCLASS_PROXY_INSTALLED_DIR}) # ############################## # Subdirectories diff --git a/examples/context_propagation/CMakeLists.txt b/examples/context_propagation/CMakeLists.txt index aa8513d..e1f9c6c 100644 --- a/examples/context_propagation/CMakeLists.txt +++ b/examples/context_propagation/CMakeLists.txt @@ -5,6 +5,13 @@ add_executable(${CONTEXTPROP_EXAMPLE_TARGET} cpp/client.cc) target_include_directories(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_PREFIX}/include) target_link_libraries(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) +if(UNIX AND NOT APPLE AND NOT CYGWIN) + target_link_options(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE ${OPENTELEMETRY_PROXY_LINK_OPTIONS}) +elseif(APPLE) + set_target_properties(${CONTEXTPROP_EXAMPLE_TARGET} PROPERTIES BUILD_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBMEXCLASS_PROXY_INSTALLED_DIR}") +endif() +# use the same C++ standard as OpenTelemetry-cpp +target_compile_features(${CONTEXTPROP_EXAMPLE_TARGET} PRIVATE cxx_std_${OTEL_CPP_CXX_STANDARD}) # MATLAB target find_package(Matlab REQUIRED COMPONENTS MCC_COMPILER MAIN_PROGRAM) diff --git a/examples/webread/CMakeLists.txt b/examples/webread/CMakeLists.txt index 0c003b0..1e29012 100644 --- a/examples/webread/CMakeLists.txt +++ b/examples/webread/CMakeLists.txt @@ -1,5 +1,13 @@ -add_executable(http_server cpp/server.cc) +set(WEBREAD_EXAMPLE_TARGET webread_example_server) +add_executable(${WEBREAD_EXAMPLE_TARGET} cpp/server.cc) -target_include_directories(http_server PRIVATE ${OTEL_CPP_PREFIX}/include) -target_link_libraries(http_server PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) +target_include_directories(${WEBREAD_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_PREFIX}/include) +target_link_libraries(${WEBREAD_EXAMPLE_TARGET} PRIVATE ${OTEL_CPP_LINK_LIBRARIES}) +if(UNIX AND NOT APPLE AND NOT CYGWIN) + target_link_options(${WEBREAD_EXAMPLE_TARGET} PRIVATE ${OPENTELEMETRY_PROXY_LINK_OPTIONS}) +elseif(APPLE) + set_target_properties(${WEBREAD_EXAMPLE_TARGET} PROPERTIES BUILD_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBMEXCLASS_PROXY_INSTALLED_DIR}") +endif() +# use the same C++ standard as OpenTelemetry-cpp +target_compile_features(${WEBREAD_EXAMPLE_TARGET} PRIVATE cxx_std_${OTEL_CPP_CXX_STANDARD}) From 99894413e8034fb825f8ea91f60e6ddd46f2cda1 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Fri, 29 Sep 2023 14:03:58 -0400 Subject: [PATCH 26/30] Updates to examples --- examples/context_propagation/README.md | 2 +- examples/context_propagation/matlab/mymagic.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/context_propagation/README.md b/examples/context_propagation/README.md index 679bfe7..ddd25a5 100644 --- a/examples/context_propagation/README.md +++ b/examples/context_propagation/README.md @@ -11,7 +11,7 @@ In this example, a C++ client calls a MATLAB function hosted on MATLAB Productio The built examples can be found in build/examples/context_propagation and subdirectories. 2. [Create](https://www.mathworks.com/help/mps/server/creating-a-server.html) and [start](https://www.mathworks.com/help/mps/qs/starting-and-stopping.html) a MATLAB Production Server instance. 3. [Deploy](https://www.mathworks.com/help/mps/qs/share-a-ctf-archive-on-the-server-instance.html) archive to server instance by copying to the auto_deploy directory. -4. [Copy](https://www.mathworks.com/help/mps/server/use-web-handler-for-custom-routes-and-custom-payloads.html) matlab/routes.json to the config directory of the server instance. +4. If using a MATLAB release before R2023b, [copy](https://www.mathworks.com/help/mps/server/use-web-handler-for-custom-routes-and-custom-payloads.html) matlab/routes.json to the config directory of the server instance. 6. Start an instance of [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector). 7. Start the C++ client. ``` diff --git a/examples/context_propagation/matlab/mymagic.m b/examples/context_propagation/matlab/mymagic.m index 54597e8..fb6daf5 100644 --- a/examples/context_propagation/matlab/mymagic.m +++ b/examples/context_propagation/matlab/mymagic.m @@ -17,7 +17,7 @@ sp = startSpan(tr, "mymagic", "SpanKind", "server"); sz = double(request.Body); -magicsquare = magic(request.Body); % generate the magic square +magicsquare = magic(sz); % generate the magic square setAttributes(sp, "input", sz, "output", magicsquare); % convert magic square to char @@ -48,7 +48,7 @@ setTextMapPropagator(prop); end -% Thie helper ensures the input function is only run once +% This helper ensures the input function is only run once function runOnce(fh) persistent hasrun if isempty(hasrun) From 81b34071397b13cffbaac175995a797d7b4d442a Mon Sep 17 00:00:00 2001 From: duncanpo Date: Tue, 10 Oct 2023 10:42:49 -0400 Subject: [PATCH 27/30] Add badges to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e79a463..b6c1225 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # MATLAB Interface to OpenTelemetry +[![View OpenTelemetry-Matlab on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/130979-opentelemetry-matlab) [![MATLAB](https://github.com/mathworks/OpenTelemetry-Matlab/actions/workflows/build.yml/badge.svg)](https://github.com/mathworks/OpenTelemetry-Matlab/actions/workflows/build.yml) MATLAB® interface to [OpenTelemetry™](https://opentelemetry.io/), based on the [OpenTelemetry Specification](https://opentelemetry.io/docs/reference/specification/). OpenTelemetry is an observability framework for creating and managing telemetry data, such as traces, metrics, and logs. This data can then be sent to an observability back-end for monitoring, alerts, and analysis. From 4c6e09f4d959a874c8c8f01d13949ca639fc39c3 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Wed, 25 Oct 2023 10:06:48 -0400 Subject: [PATCH 28/30] Revert "Merge branch 'metrics' into changes_after_v_1_3_0" This reverts commit 083ee564ffc9c07ee4e0fbda4131753eaa0aec34, reversing changes made to 99894413e8034fb825f8ea91f60e6ddd46f2cda1. --- CMakeLists.txt | 18 +- OtelMatlabProxyFactory.cpp | 4 - api/metrics/+opentelemetry/+metrics/Counter.m | 59 --- .../+opentelemetry/+metrics/Histogram.m | 55 --- api/metrics/+opentelemetry/+metrics/Meter.m | 89 ----- .../+opentelemetry/+metrics/UpDownCounter.m | 58 --- .../metrics/CounterProxy.h | 33 -- .../metrics/HistogramProxy.h | 36 -- .../opentelemetry-matlab/metrics/MeterProxy.h | 36 -- .../metrics/UpDownCounterProxy.h | 33 -- api/metrics/src/CounterProxy.cpp | 41 -- api/metrics/src/HistogramProxy.cpp | 44 --- api/metrics/src/MeterProxy.cpp | 88 ----- api/metrics/src/UpDownCounterProxy.cpp | 41 -- .../+sdk/+metrics/MeterProvider.m | 56 --- .../sdk/metrics/MeterProviderProxy.h | 48 --- sdk/metrics/src/MeterProviderProxy.cpp | 54 --- test/tmetrics.m | 364 ------------------ 18 files changed, 1 insertion(+), 1156 deletions(-) delete mode 100644 api/metrics/+opentelemetry/+metrics/Counter.m delete mode 100644 api/metrics/+opentelemetry/+metrics/Histogram.m delete mode 100644 api/metrics/+opentelemetry/+metrics/Meter.m delete mode 100644 api/metrics/+opentelemetry/+metrics/UpDownCounter.m delete mode 100644 api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h delete mode 100644 api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h delete mode 100644 api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h delete mode 100644 api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h delete mode 100644 api/metrics/src/CounterProxy.cpp delete mode 100644 api/metrics/src/HistogramProxy.cpp delete mode 100644 api/metrics/src/MeterProxy.cpp delete mode 100644 api/metrics/src/UpDownCounterProxy.cpp delete mode 100644 sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m delete mode 100644 sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h delete mode 100644 sdk/metrics/src/MeterProviderProxy.cpp delete mode 100644 test/tmetrics.m diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ee427c..1b9f509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,25 +188,21 @@ if(WITH_OTLP_GRPC) endif() set(TRACE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/include) -set(METRICS_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/include) set(CONTEXT_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/include) set(BAGGAGE_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/include) set(COMMON_API_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/include) set(TRACE_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/include) -set(METRICS_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/include) set(COMMON_SDK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/include) set(OTLP_EXPORTER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/include) -set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${METRICS_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${METRICS_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OTEL_CPP_PREFIX}/include) +set(OPENTELEMETRY_PROXY_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${TRACE_API_INCLUDE_DIR} ${CONTEXT_API_INCLUDE_DIR} ${BAGGAGE_API_INCLUDE_DIR} ${COMMON_API_INCLUDE_DIR} ${TRACE_SDK_INCLUDE_DIR} ${COMMON_SDK_INCLUDE_DIR} ${OTLP_EXPORTER_INCLUDE_DIR} ${OTEL_CPP_PREFIX}/include) set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory) set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(TRACE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/src) -set(METRICS_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/src) set(CONTEXT_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/context/src) set(BAGGAGE_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/src) set(COMMON_API_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/api/common/src) set(TRACE_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/src) -set(METRICS_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/src) set(COMMON_SDK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/src) set(OTLP_EXPORTER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/src) set(OPENTELEMETRY_PROXY_SOURCES @@ -216,10 +212,6 @@ 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}/MeterProxy.cpp - ${METRICS_API_SOURCE_DIR}/CounterProxy.cpp - ${METRICS_API_SOURCE_DIR}/UpDownCounterProxy.cpp - ${METRICS_API_SOURCE_DIR}/HistogramProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapPropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/CompositePropagatorProxy.cpp ${CONTEXT_API_SOURCE_DIR}/TextMapCarrierProxy.cpp @@ -229,7 +221,6 @@ set(OPENTELEMETRY_PROXY_SOURCES ${TRACE_SDK_SOURCE_DIR}/SimpleSpanProcessorProxy.cpp ${TRACE_SDK_SOURCE_DIR}/BatchSpanProcessorProxy.cpp ${TRACE_SDK_SOURCE_DIR}/ParentBasedSamplerProxy.cpp - ${METRICS_SDK_SOURCE_DIR}/MeterProviderProxy.cpp ${COMMON_SDK_SOURCE_DIR}/resource.cpp) if(WITH_OTLP_HTTP) set(OPENTELEMETRY_PROXY_SOURCES ${OPENTELEMETRY_PROXY_SOURCES} @@ -286,19 +277,16 @@ set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_trace${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_version${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_logs${CMAKE_STATIC_LIBRARY_SUFFIX} - ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_metrics${CMAKE_STATIC_LIBRARY_SUFFIX} ${Protobuf_LIBRARIES}) if(WITH_OTLP_HTTP) set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_LINK_LIBRARIES} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http_client${CMAKE_STATIC_LIBRARY_SUFFIX} - ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_http_metric${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_http_client_curl${CMAKE_STATIC_LIBRARY_SUFFIX} ${CURL_LIBRARIES}) endif() if(WITH_OTLP_GRPC) set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_LINK_LIBRARIES} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc_client${CMAKE_STATIC_LIBRARY_SUFFIX} - ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentelemetry_exporter_otlp_grpc_metrics${CMAKE_STATIC_LIBRARY_SUFFIX} ${OTEL_CPP_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}opentelemetry_proto_grpc${OTEL_PROTO_LIBRARY_SUFFIX} gRPC::grpc++ absl::synchronization) @@ -385,12 +373,10 @@ libmexclass_client_install( # Install M files set(TRACE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/trace/+opentelemetry) -set(METRICS_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/metrics/+opentelemetry) set(CONTEXT_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/context/+opentelemetry) set(BAGGAGE_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/baggage/+opentelemetry) set(COMMON_API_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/api/common/+opentelemetry) set(TRACE_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/+opentelemetry) -set(METRICS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/+opentelemetry) set(DEFAULT_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultSpanExporter.m) set(OTLP_HTTP_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m) set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m) @@ -398,12 +384,10 @@ set(OTLP_GRPC_EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp set(OTLP_EXPORTERS_DIR +opentelemetry/+exporters/+otlp) install(DIRECTORY ${TRACE_API_MATLAB_SOURCES} DESTINATION .) -install(DIRECTORY ${METRICS_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${CONTEXT_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${BAGGAGE_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${COMMON_API_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${TRACE_SDK_MATLAB_SOURCES} DESTINATION .) -install(DIRECTORY ${METRICS_SDK_MATLAB_SOURCES} DESTINATION .) install(FILES ${DEFAULT_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) if(WITH_OTLP_HTTP) install(FILES ${OTLP_HTTP_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) diff --git a/OtelMatlabProxyFactory.cpp b/OtelMatlabProxyFactory.cpp index 925094d..a2f1315 100644 --- a/OtelMatlabProxyFactory.cpp +++ b/OtelMatlabProxyFactory.cpp @@ -22,7 +22,6 @@ #include "opentelemetry-matlab/sdk/trace/AlwaysOffSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/TraceIdRatioBasedSamplerProxy.h" #include "opentelemetry-matlab/sdk/trace/ParentBasedSamplerProxy.h" -#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" #ifdef WITH_OTLP_HTTP #include "opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h" #endif @@ -55,9 +54,6 @@ OtelMatlabProxyFactory::make_proxy(const libmexclass::proxy::ClassName& class_na REGISTER_PROXY(libmexclass.opentelemetry.sdk.AlwaysOffSamplerProxy, libmexclass::opentelemetry::sdk::AlwaysOffSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.TraceIdRatioBasedSamplerProxy, libmexclass::opentelemetry::sdk::TraceIdRatioBasedSamplerProxy); REGISTER_PROXY(libmexclass.opentelemetry.sdk.ParentBasedSamplerProxy, libmexclass::opentelemetry::sdk::ParentBasedSamplerProxy); - - REGISTER_PROXY(libmexclass.opentelemetry.sdk.MeterProviderProxy, libmexclass::opentelemetry::sdk::MeterProviderProxy); - #ifdef WITH_OTLP_HTTP REGISTER_PROXY(libmexclass.opentelemetry.exporters.OtlpHttpSpanExporterProxy, libmexclass::opentelemetry::exporters::OtlpHttpSpanExporterProxy); #endif diff --git a/api/metrics/+opentelemetry/+metrics/Counter.m b/api/metrics/+opentelemetry/+metrics/Counter.m deleted file mode 100644 index f416f41..0000000 --- a/api/metrics/+opentelemetry/+metrics/Counter.m +++ /dev/null @@ -1,59 +0,0 @@ -classdef Counter < handle - % Counter is a value that accumulates over time, - % you can think of this like an odometer on a car; it only ever goes up. - - % Copyright 2023 The MathWorks, Inc. - - properties (SetAccess=immutable) - Name (1,1) string - Description (1,1) string - Unit (1,1) string - end - - properties (Access=private) - Proxy % Proxy object to interface C++ code - end - - methods (Access={?opentelemetry.metrics.Meter}) - - function obj = Counter(proxy, ctname, ctdescription, ctunit) - % Private constructor. Use createCounter method of Meter - % to create Counters. - obj.Proxy = proxy; - obj.Name = ctname; - obj.Description = ctdescription; - obj.Unit = ctunit; - end - - end - - methods - - function add(obj, value, varargin) - % input value must be a numerical scalar - if isnumeric(value) && isscalar(value) - - if nargin == 2 - obj.Proxy.add(value); - - elseif isa(varargin{1}, "dictionary") - attrkeys = keys(varargin{1}); - attrvals = values(varargin{1},"cell"); - if all(cellfun(@iscell, attrvals)) - attrvals = [attrvals{:}]; - end - obj.Proxy.add(value,attrkeys,attrvals); - - else - attrkeys = [varargin{1:2:length(varargin)}]'; - attrvals = [varargin(2:2:length(varargin))]'; - obj.Proxy.add(value,attrkeys,attrvals); - end - end - - end - - end - - -end diff --git a/api/metrics/+opentelemetry/+metrics/Histogram.m b/api/metrics/+opentelemetry/+metrics/Histogram.m deleted file mode 100644 index 8abc9ee..0000000 --- a/api/metrics/+opentelemetry/+metrics/Histogram.m +++ /dev/null @@ -1,55 +0,0 @@ -classdef Histogram < handle - % Histogram is an instrument that adds or reduce values. - - % Copyright 2023 The MathWorks, Inc. - - properties (SetAccess=immutable) - Name (1,1) string - Description (1,1) string - Unit (1,1) string - end - - properties (Access=public) - Proxy % Proxy object to interface C++ code - end - - methods (Access={?opentelemetry.metrics.Meter}) - - function obj = Histogram(proxy, hiname, hidescription, hiunit) - % Private constructor. Use createHistogram method of Meter - % to create Histograms. - obj.Proxy = proxy; - obj.Name = hiname; - obj.Description = hidescription; - obj.Unit = hiunit; - end - - end - - methods - - function record(obj, value, varargin) - % input value must be a numerical scalar - if isnumeric(value) && isscalar(value) - if nargin == 2 - obj.Proxy.record(value); - elseif isa(varargin{1}, "dictionary") - attrkeys = keys(varargin{1}); - attrvals = values(varargin{1},"cell"); - if all(cellfun(@iscell, attrvals)) - attrvals = [attrvals{:}]; - end - obj.Proxy.record(value,attrkeys,attrvals); - else - attrkeys = [varargin{1:2:length(varargin)}]'; - attrvals = [varargin(2:2:length(varargin))]'; - obj.Proxy.record(value,attrkeys,attrvals); - end - end - - end - - end - - -end diff --git a/api/metrics/+opentelemetry/+metrics/Meter.m b/api/metrics/+opentelemetry/+metrics/Meter.m deleted file mode 100644 index 250a5b9..0000000 --- a/api/metrics/+opentelemetry/+metrics/Meter.m +++ /dev/null @@ -1,89 +0,0 @@ -classdef Meter < handle - % A Meter creates metric instruments, capturing measurements about a service at runtime. - % Meters are created from Meter Providers. - - % Copyright 2023 The MathWorks, Inc. - - properties (SetAccess=immutable) - Name (1,1) string % Meter name - Version (1,1) string % Meter version - Schema (1,1) string % URL that documents the schema of the generated spans - end - - properties (Access=private) - Proxy % Proxy object to interface C++ code - end - - methods (Access={?opentelemetry.sdk.metrics.MeterProvider}) - - function obj = Meter(proxy, mtname, mtversion, mtschema) - % Private constructor. Use getMeter method of MeterProvider - % to create Meters. - obj.Proxy = proxy; - obj.Name = mtname; - obj.Version = mtversion; - obj.Schema = mtschema; - end - - end - - methods - - function counter = createCounter(obj, ctname, ctdescription, ctunit) - arguments - obj - ctname - ctdescription = "" - ctunit = "" - end - import opentelemetry.common.mustBeScalarString - ctname = mustBeScalarString(ctname); - ctdescription = mustBeScalarString(ctdescription); - ctunit = mustBeScalarString(ctunit); - id = obj.Proxy.createCounter(ctname, ctdescription, ctunit); - CounterProxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.CounterProxy", "ID", id); - counter = opentelemetry.metrics.Counter(CounterProxy, ctname, ctdescription, ctunit); - end - - - function updowncounter = createUpDownCounter(obj, ctname, ctdescription, ctunit) - arguments - obj - ctname - ctdescription = "" - ctunit = "" - end - - import opentelemetry.common.mustBeScalarString - ctname = mustBeScalarString(ctname); - ctdescription = mustBeScalarString(ctdescription); - ctunit = mustBeScalarString(ctunit); - id = obj.Proxy.createUpDownCounter(ctname, ctdescription, ctunit); - UpDownCounterProxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.UpDownCounterProxy", "ID", id); - updowncounter = opentelemetry.metrics.UpDownCounter(UpDownCounterProxy, ctname, ctdescription, ctunit); - end - - - function histogram = createHistogram(obj, hiname, hidescription, hiunit) - arguments - obj - hiname - hidescription = "" - hiunit = "" - end - - import opentelemetry.common.mustBeScalarString - hiname = mustBeScalarString(hiname); - hidescription = mustBeScalarString(hidescription); - hiunit = mustBeScalarString(hiunit); - id = obj.Proxy.createHistogram(hiname, hidescription, hiunit); - HistogramProxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.HistogramProxy", "ID", id); - histogram = opentelemetry.metrics.Histogram(HistogramProxy, hiname, hidescription, hiunit); - end - - end - -end diff --git a/api/metrics/+opentelemetry/+metrics/UpDownCounter.m b/api/metrics/+opentelemetry/+metrics/UpDownCounter.m deleted file mode 100644 index b008dc5..0000000 --- a/api/metrics/+opentelemetry/+metrics/UpDownCounter.m +++ /dev/null @@ -1,58 +0,0 @@ -classdef UpDownCounter < handle - % UpDownCounter is an instrument that adds or reduce values. - - % Copyright 2023 The MathWorks, Inc. - - properties (SetAccess=immutable) - Name (1,1) string - Description (1,1) string - Unit (1,1) string - end - - properties (Access=public) - Proxy % Proxy object to interface C++ code - end - - methods (Access={?opentelemetry.metrics.Meter}) - - function obj = UpDownCounter(proxy, ctname, ctdescription, ctunit) - % Private constructor. Use createUpDownCounter method of Meter - % to create UpDownCounters. - obj.Proxy = proxy; - obj.Name = ctname; - obj.Description = ctdescription; - obj.Unit = ctunit; - end - - end - - methods - - function add(obj, value, varargin) - % input value must be a numerical scalar - if isnumeric(value) && isscalar(value) - - if nargin == 2 - obj.Proxy.add(value); - - elseif isa(varargin{1}, "dictionary") - attrkeys = keys(varargin{1}); - attrvals = values(varargin{1},"cell"); - if all(cellfun(@iscell, attrvals)) - attrvals = [attrvals{:}]; - end - obj.Proxy.add(value,attrkeys,attrvals); - - else - attrkeys = [varargin{1:2:length(varargin)}]'; - attrvals = [varargin(2:2:length(varargin))]'; - obj.Proxy.add(value,attrkeys,attrvals); - end - end - - end - - end - - -end diff --git a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h deleted file mode 100644 index 6271d60..0000000 --- a/api/metrics/include/opentelemetry-matlab/metrics/CounterProxy.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#pragma once - -#include "libmexclass/proxy/Proxy.h" -#include "libmexclass/proxy/method/Context.h" - -#include "opentelemetry/metrics/meter.h" -#include "opentelemetry/metrics/sync_instruments.h" - -#include "opentelemetry-matlab/common/attribute.h" -#include "opentelemetry-matlab/common/ProcessedAttributes.h" - -namespace metrics_api = opentelemetry::metrics; -namespace nostd = opentelemetry::nostd; - -namespace libmexclass::opentelemetry { -class CounterProxy : public libmexclass::proxy::Proxy { - public: - CounterProxy(nostd::shared_ptr > ct) : CppCounter(ct) { - REGISTER_METHOD(CounterProxy, add); - } - - void add(libmexclass::proxy::method::Context& context); - - private: - - nostd::shared_ptr > CppCounter; - -}; -} // namespace libmexclass::opentelemetry - - diff --git a/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h deleted file mode 100644 index 248c628..0000000 --- a/api/metrics/include/opentelemetry-matlab/metrics/HistogramProxy.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#pragma once - -#include "libmexclass/proxy/Proxy.h" -#include "libmexclass/proxy/method/Context.h" - -#include "opentelemetry/context/context.h" -#include "opentelemetry/metrics/meter.h" -#include "opentelemetry/metrics/sync_instruments.h" - -#include "opentelemetry-matlab/common/attribute.h" -#include "opentelemetry-matlab/common/ProcessedAttributes.h" -#include "opentelemetry-matlab/context/ContextProxy.h" - -namespace metrics_api = opentelemetry::metrics; -namespace context = opentelemetry::context; -namespace nostd = opentelemetry::nostd; - -namespace libmexclass::opentelemetry { -class HistogramProxy : public libmexclass::proxy::Proxy { - public: - HistogramProxy(nostd::shared_ptr > hist) : CppHistogram(hist) { - REGISTER_METHOD(HistogramProxy, record); - } - - void record(libmexclass::proxy::method::Context& context); - - private: - - nostd::shared_ptr > CppHistogram; - -}; -} // namespace libmexclass::opentelemetry - - diff --git a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h deleted file mode 100644 index ab45e48..0000000 --- a/api/metrics/include/opentelemetry-matlab/metrics/MeterProxy.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#pragma once - -#include "libmexclass/proxy/Proxy.h" -#include "libmexclass/proxy/method/Context.h" - -#include "opentelemetry-matlab/metrics/CounterProxy.h" -#include "opentelemetry-matlab/metrics/HistogramProxy.h" -#include "opentelemetry-matlab/metrics/UpDownCounterProxy.h" - -#include "opentelemetry/metrics/meter.h" - -namespace metrics_api = opentelemetry::metrics; -namespace nostd = opentelemetry::nostd; - -namespace libmexclass::opentelemetry { -class MeterProxy : public libmexclass::proxy::Proxy { - public: - MeterProxy(nostd::shared_ptr mt) : CppMeter(mt) { - REGISTER_METHOD(MeterProxy, createCounter); - REGISTER_METHOD(MeterProxy, createUpDownCounter); - REGISTER_METHOD(MeterProxy, createHistogram); - } - - void createCounter(libmexclass::proxy::method::Context& context); - - void createUpDownCounter(libmexclass::proxy::method::Context& context); - - void createHistogram(libmexclass::proxy::method::Context& context); - - private: - - nostd::shared_ptr CppMeter; -}; -} // namespace libmexclass::opentelemetry diff --git a/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h b/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h deleted file mode 100644 index 6a41c75..0000000 --- a/api/metrics/include/opentelemetry-matlab/metrics/UpDownCounterProxy.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#pragma once - -#include "libmexclass/proxy/Proxy.h" -#include "libmexclass/proxy/method/Context.h" - -#include "opentelemetry/metrics/meter.h" -#include "opentelemetry/metrics/sync_instruments.h" - -#include "opentelemetry-matlab/common/attribute.h" -#include "opentelemetry-matlab/common/ProcessedAttributes.h" - -namespace metrics_api = opentelemetry::metrics; -namespace nostd = opentelemetry::nostd; - -namespace libmexclass::opentelemetry { -class UpDownCounterProxy : public libmexclass::proxy::Proxy { - public: - UpDownCounterProxy(nostd::shared_ptr > ct) : CppUpDownCounter(ct) { - REGISTER_METHOD(UpDownCounterProxy, add); - } - - void add(libmexclass::proxy::method::Context& context); - - private: - - nostd::shared_ptr > CppUpDownCounter; - -}; -} // namespace libmexclass::opentelemetry - - diff --git a/api/metrics/src/CounterProxy.cpp b/api/metrics/src/CounterProxy.cpp deleted file mode 100644 index 77e498f..0000000 --- a/api/metrics/src/CounterProxy.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#include "opentelemetry-matlab/metrics/CounterProxy.h" - -#include "libmexclass/proxy/ProxyManager.h" - - -#include "MatlabDataArray.hpp" - -#include - -namespace libmexclass::opentelemetry { - - -void CounterProxy::add(libmexclass::proxy::method::Context& context){ - - matlab::data::Array value_mda = context.inputs[0]; - double value = static_cast(value_mda[0]); - size_t nin = context.inputs.getNumberOfElements(); - if (nin == 1){ - CppCounter->Add(value); - } - // add attributes - else { - ProcessedAttributes attrs; - matlab::data::StringArray attrnames_mda = context.inputs[1]; - matlab::data::Array attrvalues_mda = context.inputs[2]; - size_t nattrs = attrnames_mda.getNumberOfElements(); - for (size_t i = 0; i < nattrs; i ++){ - std::string attrname = static_cast(attrnames_mda[i]); - matlab::data::Array attrvalue = attrvalues_mda[i]; - processAttribute(attrname, attrvalue, attrs); - } - CppCounter->Add(value, attrs.Attributes); - } - -} - - - -} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/HistogramProxy.cpp b/api/metrics/src/HistogramProxy.cpp deleted file mode 100644 index 3d3ea5f..0000000 --- a/api/metrics/src/HistogramProxy.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#include "opentelemetry-matlab/metrics/HistogramProxy.h" - -#include "libmexclass/proxy/ProxyManager.h" - - -#include "MatlabDataArray.hpp" - -#include - -namespace libmexclass::opentelemetry { - - -void HistogramProxy::record(libmexclass::proxy::method::Context& context){ - // Get value - matlab::data::Array value_mda = context.inputs[0]; - double value = static_cast(value_mda[0]); - // Create empty context - auto ctxt = context_api::Context(); - // If no attributes input, record value and context - size_t nin = context.inputs.getNumberOfElements(); - if (nin == 1){ - CppHistogram->Record(value, ctxt); - } - // Otherwise, get attributes, record value, attributes and context - else { - ProcessedAttributes attrs; - matlab::data::StringArray attrnames_mda = context.inputs[1]; - matlab::data::Array attrvalues_mda = context.inputs[2]; - size_t nattrs = attrnames_mda.getNumberOfElements(); - for (size_t i = 0; i < nattrs; i ++){ - std::string attrname = static_cast(attrnames_mda[i]); - matlab::data::Array attrvalue = attrvalues_mda[i]; - processAttribute(attrname, attrvalue, attrs); - } - CppHistogram->Record(value, attrs.Attributes, ctxt); - } - -} - - - -} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/MeterProxy.cpp b/api/metrics/src/MeterProxy.cpp deleted file mode 100644 index 3d0d399..0000000 --- a/api/metrics/src/MeterProxy.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#include "opentelemetry-matlab/metrics/MeterProxy.h" - -#include "libmexclass/proxy/ProxyManager.h" - -#include "MatlabDataArray.hpp" - -#include - -namespace libmexclass::opentelemetry { -void MeterProxy::createCounter(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 description_mda = context.inputs[1]; - std::string description= static_cast(description_mda[0]); - matlab::data::StringArray unit_mda = context.inputs[2]; - std::string unit = static_cast(unit_mda[0]); - - nostd::shared_ptr > ct = std::move(CppMeter->CreateDoubleCounter(name, description, unit)); - - // instantiate a CounterProxy instance - CounterProxy* newproxy = new CounterProxy(ct); - auto proxy = std::shared_ptr(newproxy); - - // obtain a proxy ID - libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); - - // return the ID - matlab::data::ArrayFactory factory; - auto proxyid_mda = factory.createScalar(proxyid); - context.outputs[0] = proxyid_mda; -} - - -void MeterProxy::createUpDownCounter(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 description_mda = context.inputs[1]; - std::string description= static_cast(description_mda[0]); - matlab::data::StringArray unit_mda = context.inputs[2]; - std::string unit = static_cast(unit_mda[0]); - - nostd::shared_ptr > ct = std::move(CppMeter->CreateDoubleUpDownCounter -(name, description, unit)); - - // instantiate a UpDownCounterProxy instance - UpDownCounterProxy* newproxy = new UpDownCounterProxy(ct); - auto proxy = std::shared_ptr(newproxy); - - // obtain a proxy ID - libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); - - // return the ID - matlab::data::ArrayFactory factory; - auto proxyid_mda = factory.createScalar(proxyid); - context.outputs[0] = proxyid_mda; -} - - -void MeterProxy::createHistogram(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 description_mda = context.inputs[1]; - std::string description= static_cast(description_mda[0]); - matlab::data::StringArray unit_mda = context.inputs[2]; - std::string unit = static_cast(unit_mda[0]); - - nostd::shared_ptr > hist = std::move(CppMeter->CreateDoubleHistogram(name, description, unit)); - - // instantiate a HistogramProxy instance - HistogramProxy* newproxy = new HistogramProxy(hist); - auto proxy = std::shared_ptr(newproxy); - - // obtain a proxy ID - libmexclass::proxy::ID proxyid = libmexclass::proxy::ProxyManager::manageProxy(proxy); - - // return the ID - matlab::data::ArrayFactory factory; - auto proxyid_mda = factory.createScalar(proxyid); - context.outputs[0] = proxyid_mda; -} - - -} // namespace libmexclass::opentelemetry diff --git a/api/metrics/src/UpDownCounterProxy.cpp b/api/metrics/src/UpDownCounterProxy.cpp deleted file mode 100644 index f4d35ef..0000000 --- a/api/metrics/src/UpDownCounterProxy.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#include "opentelemetry-matlab/metrics/UpDownCounterProxy.h" - -#include "libmexclass/proxy/ProxyManager.h" - - -#include "MatlabDataArray.hpp" - -#include - -namespace libmexclass::opentelemetry { - - -void UpDownCounterProxy::add(libmexclass::proxy::method::Context& context){ - - matlab::data::Array value_mda = context.inputs[0]; - double value = static_cast(value_mda[0]); - size_t nin = context.inputs.getNumberOfElements(); - if (nin == 1){ - CppUpDownCounter->Add(value); - } - // add attributes - else { - ProcessedAttributes attrs; - matlab::data::StringArray attrnames_mda = context.inputs[1]; - matlab::data::Array attrvalues_mda = context.inputs[2]; - size_t nattrs = attrnames_mda.getNumberOfElements(); - for (size_t i = 0; i < nattrs; i ++){ - std::string attrname = static_cast(attrnames_mda[i]); - matlab::data::Array attrvalue = attrvalues_mda[i]; - processAttribute(attrname, attrvalue, attrs); - } - CppUpDownCounter->Add(value, attrs.Attributes); - } - -} - - - -} // namespace libmexclass::opentelemetry diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m deleted file mode 100644 index ec019eb..0000000 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ /dev/null @@ -1,56 +0,0 @@ -classdef 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 - - methods - function obj = MeterProvider() - % SDK implementation of tracer provider - % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER creates a meter - % provider that uses a periodic exporting metric reader and default configurations. - % - % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R) uses metric - % reader R. Currently, the only supported metric reader is the periodic - % exporting metric reader. - % - % TP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R, PARAM1, VALUE1, - % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. - % Parameters are: - % "View" - View object used to customize collected metrics. - % "Resource" - Additional resource attributes. - % Specified as a dictionary. - % - % See also OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER - % OPENTELEMETRY.SDK.METRICS.VIEW - - obj.Proxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... - "ConstructorArguments", {}); - 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 - - end -end diff --git a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h b/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h deleted file mode 100644 index 3526822..0000000 --- a/sdk/metrics/include/opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#pragma once - -#include "libmexclass/proxy/Proxy.h" -#include "libmexclass/proxy/method/Context.h" - - -#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_factory.h" -#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter.h" -#include "opentelemetry/exporters/otlp/otlp_http_metric_exporter_options.h" -#include "opentelemetry/exporters/otlp/otlp_http_exporter.h" - -#include "opentelemetry/metrics/provider.h" -#include "opentelemetry/metrics/meter_provider.h" -#include "opentelemetry/sdk/common/global_log_handler.h" -#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" -#include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" -#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h" -#include "opentelemetry/sdk/metrics/meter.h" -#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-matlab/metrics/MeterProxy.h" - -namespace metrics_api = opentelemetry::metrics; -namespace nostd = opentelemetry::nostd; -namespace metrics_sdk = opentelemetry::sdk::metrics; -namespace common = opentelemetry::common; -namespace otlpexporter = opentelemetry::exporter::otlp; - - -namespace libmexclass::opentelemetry::sdk { -class MeterProviderProxy : public libmexclass::proxy::Proxy { - public: - MeterProviderProxy(nostd::shared_ptr mp) : CppMeterProvider(mp) { - REGISTER_METHOD(MeterProviderProxy, getMeter); - } - - static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); - - void getMeter(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 deleted file mode 100644 index e0eb3c3..0000000 --- a/sdk/metrics/src/MeterProviderProxy.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -#include "opentelemetry-matlab/sdk/metrics/MeterProviderProxy.h" - -#include "libmexclass/proxy/ProxyManager.h" - -#include - -namespace libmexclass::opentelemetry::sdk { -libmexclass::proxy::MakeResult MeterProviderProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - - libmexclass::proxy::MakeResult out; - - auto exporter = otlpexporter::OtlpHttpMetricExporterFactory::Create(); - // Initialize and set the periodic metrics reader - metrics_sdk::PeriodicExportingMetricReaderOptions options; - auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), options); - - auto p = metrics_sdk::MeterProviderFactory::Create(); - 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); - - 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; -} - - -} // namespace libmexclass::opentelemetry diff --git a/test/tmetrics.m b/test/tmetrics.m deleted file mode 100644 index c452504..0000000 --- a/test/tmetrics.m +++ /dev/null @@ -1,364 +0,0 @@ -classdef tmetrics < matlab.unittest.TestCase - % tests for traces and spans - - % Copyright 2023 The MathWorks, Inc. - - properties - OtelConfigFile - OtelRoot - JsonFile - PidFile - OtelcolName - Otelcol - ListPid - ReadPidList - ExtractPid - Sigint - Sigterm - end - - methods (TestClassSetup) - function setupOnce(testCase) - commonSetupOnce(testCase); - end - end - - methods (TestMethodSetup) - function setup(testCase) - commonSetup(testCase); - end - end - - methods (TestMethodTeardown) - function teardown(testCase) - commonTeardown(testCase); - end - end - - methods (Test) - - function testCounterBasic(testCase) - % test names and added value in Counter - - metername = "foo"; - countername = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - ct = mt.createCounter(countername); - - % verify MATLAB object properties - verifyEqual(testCase, mt.Name, metername); - verifyEqual(testCase, mt.Version, ""); - verifyEqual(testCase, mt.Schema, ""); - verifyEqual(testCase, ct.Name, countername); - - % create testing value - val = 10; - - % add value and attributes - ct.add(val); - - % wait for default collector response time (60s) - pause(70); - - % fetch result - results = readJsonResults(testCase); - results = results{1}; - - % verify meter and counter names - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); - - % fetch datapoint - dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; - - % verify counter value - verifyEqual(testCase, dp.asDouble, val); - - end - - - function testCounterAddAttributes(testCase) - % test names, added value and attributes in Counter - - metername = "foo"; - countername = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - ct = mt.createCounter(countername); - - % create testing value and dictionary - dict = dictionary("k1","v1","k2",5); - vals = [1,2.4,3]; - dict_keys = keys(dict); - dict_vals = values(dict); - - % add value and attributes - ct.add(vals(1),dict); - ct.add(vals(2),dict); - ct.add(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); - - % wait for default collector response time (60s) - pause(70); - - % fetch result - results = readJsonResults(testCase); - results = results{1}; - - % verify meter and counter names - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); - - % fetch datapoint - dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; - - % verify counter value - verifyEqual(testCase, dp.asDouble, sum(vals)); - - % verify counter attributes - resourcekeys = string({dp.attributes.key}); - idx1 = find(resourcekeys == dict_keys(1)); - idx2 = find(resourcekeys == dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); - - end - - - function testCounterAddNegative(testCase) - % test if counter value remain 0 when added negative value - - metername = "foo"; - countername = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - ct = mt.createCounter(countername); - - % add negative value to counter - ct.add(-1); - pause(70); - - % fetch results - results = readJsonResults(testCase); - results = results{1}; - dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; - - % verify that the counter value is still 0 - verifyEqual(testCase, dp.asDouble, 0); - - end - - - function testUpDownCounterBasic(testCase) - % test names and added value in UpDownCounter - - metername = "foo"; - countername = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - ct = mt.createUpDownCounter(countername); - - % verify MATLAB object properties - verifyEqual(testCase, mt.Name, metername); - verifyEqual(testCase, mt.Version, ""); - verifyEqual(testCase, mt.Schema, ""); - verifyEqual(testCase, ct.Name, countername); - - % create testing value - val = -10; - - % add value and attributes - ct.add(val); - - % wait for default collector response time (60s) - pause(70); - - % fetch result - results = readJsonResults(testCase); - results = results{1}; - - % verify meter and counter names - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); - - % fetch datapoint - dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; - - % verify counter value - verifyEqual(testCase, dp.asDouble, val); - - end - - - function testUpDownCounterAddAttributes(testCase) - % test names, added value and attributes in UpDownCounter - - metername = "foo"; - countername = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - ct = mt.createUpDownCounter(countername); - - % create testing value and dictionary - dict = dictionary("k1","v1","k2",5); - vals = [2,-1.9,3]; - dict_keys = keys(dict); - dict_vals = values(dict); - - % add value and attributes - ct.add(vals(1),dict); - ct.add(vals(2),dict); - ct.add(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); - - % wait for default collector response time (60s) - pause(70); - - % fetch result - results = readJsonResults(testCase); - results = results{1}; - - % verify meter and counter names - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), countername); - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); - - % fetch datapoint - dp = results.resourceMetrics.scopeMetrics.metrics.sum.dataPoints; - - % verify counter value - verifyEqual(testCase, dp.asDouble, sum(vals)); - - % verify counter attributes - resourcekeys = string({dp.attributes.key}); - idx1 = find(resourcekeys == dict_keys(1)); - idx2 = find(resourcekeys == dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); - - end - - - function testHistogramBasic(testCase) - % test recorded values in histogram - - metername = "foo"; - histname = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - hist = mt.createHistogram(histname); - - % verify MATLAB object properties - verifyEqual(testCase, mt.Name, metername); - verifyEqual(testCase, mt.Version, ""); - verifyEqual(testCase, mt.Schema, ""); - verifyEqual(testCase, hist.Name, histname); - - % create value for histogram - val = 1; - - % record value - hist.record(val); - - % wait for collector response - pause(75); - - % fetch results - results = readJsonResults(testCase); - results = results{1}; - dp = results.resourceMetrics.scopeMetrics.metrics.histogram.dataPoints; - bounds = dp.explicitBounds; - counts = dp.bucketCounts; - - % verify meter and histogram names - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.metrics.name), histname); - verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics.scope.name), metername); - - % verify statistics - verifyEqual(testCase, dp.min, val); - verifyEqual(testCase, dp.max, val); - verifyEqual(testCase, dp.sum, val); - - % verify count in bucket - len = length(counts); - verifyEqual(testCase, str2double(counts{1}), sum(val<=bounds(1))); - for i = 2:(len-1) - lower = bounds(i-1); - upper = bounds(i); - expect_count = sum(val>lower & val<=upper); - verifyEqual(testCase, str2double(counts{i}), expect_count); - end - verifyEqual(testCase, str2double(counts{len}), sum(val>bounds(len-1))); - - end - - - function testHistogramRecordAttributes(testCase) - % test recorded values and attributes in histogram - - metername = "foo"; - histname = "bar"; - - p = opentelemetry.sdk.metrics.MeterProvider(); - mt = p.getMeter(metername); - hist = mt.createHistogram(histname); - - % create value and attributes for histogram - dict = dictionary("k1","v1","k2","v2"); - vals = [1,5,8.1]; - dict_keys = keys(dict); - dict_vals = values(dict); - - % record value and attributes - hist.record(vals(1),dict); - hist.record(vals(2),dict); - hist.record(vals(3),dict_keys(1),dict_vals(1),dict_keys(2),dict_vals(2)); - - % wait for collector response - pause(75); - - % fetch results - results = readJsonResults(testCase); - results = results{1}; - dp = results.resourceMetrics.scopeMetrics.metrics.histogram.dataPoints; - bounds = dp.explicitBounds; - counts = dp.bucketCounts; - - % verify statistics - verifyEqual(testCase, dp.min, min(vals)); - verifyEqual(testCase, dp.max, max(vals)); - verifyEqual(testCase, dp.sum, sum(vals)); - - % verify attributes - resourcekeys = string({dp.attributes.key}); - idx1 = find(resourcekeys == dict_keys(1)); - idx2 = find(resourcekeys == dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx1).key), dict_keys(1)); - verifyEqual(testCase, string(dp.attributes(idx1).value.stringValue), dict_vals(1)); - verifyEqual(testCase, string(dp.attributes(idx2).key), dict_keys(2)); - verifyEqual(testCase, string(dp.attributes(idx2).value.stringValue), dict_vals(2)); - - % verify count in bucket - len = length(counts); - verifyEqual(testCase, str2double(counts{1}), sum(vals<=bounds(1))); - for i = 2:(len-1) - lower = bounds(i-1); - upper = bounds(i); - expect_count = sum(vals>lower & vals<=upper); - verifyEqual(testCase, str2double(counts{i}), expect_count); - end - verifyEqual(testCase, str2double(counts{len}), sum(vals>bounds(len-1))); - - end - - end - -end From b55ffdfc42e5ecc8de8c62ae32bf9c41e26a381c Mon Sep 17 00:00:00 2001 From: duncanpo Date: Wed, 25 Oct 2023 12:23:37 -0400 Subject: [PATCH 29/30] Fix a bug in span/setStatus #20 --- api/trace/+opentelemetry/+trace/Span.m | 10 +++++++--- test/ttrace.m | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/api/trace/+opentelemetry/+trace/Span.m b/api/trace/+opentelemetry/+trace/Span.m index 5698920..3ac71b7 100644 --- a/api/trace/+opentelemetry/+trace/Span.m +++ b/api/trace/+opentelemetry/+trace/Span.m @@ -132,7 +132,11 @@ function setStatus(obj, status, description) % new status is not valid, ignore return end - description = opentelemetry.common.mustBeScalarString(description); + if nargin < 3 + description = ""; + else + description = opentelemetry.common.mustBeScalarString(description); + end obj.Proxy.setStatus(status, description); end @@ -181,8 +185,7 @@ function setStatus(obj, status, description) function attrs = processAttributes(attrsin) import opentelemetry.trace.Span.processAttribute - nin = length(attrsin); - if nin == 1 && isa(attrsin{1}, "dictionary") + if isscalar(attrsin) && isa(attrsin{1}, "dictionary") % dictionary case attrtbl = entries(attrsin{1}); nattr = height(attrtbl); @@ -203,6 +206,7 @@ function setStatus(obj, status, description) attrs = attrs(:); else % NV pairs + nin = length(attrsin); if rem(nin,2) ~= 0 % Mismatched name-value pairs. Ignore all attributes. attrs = cell(1,0); diff --git a/test/ttrace.m b/test/ttrace.m index 8b01773..02d2eb7 100644 --- a/test/ttrace.m +++ b/test/ttrace.m @@ -262,6 +262,28 @@ function testTime(testCase) "convertFrom", "posixtime") - endtime), seconds(2)); end + function testStatus(testCase) + % testStatus: setting status + tp = opentelemetry.sdk.trace.TracerProvider(); + tr = getTracer(tp, "foo"); + sp = startSpan(tr, "bar"); + setStatus(sp, "ok"); + endSpan(sp); + + sp1 = startSpan(tr, "quz"); + setStatus(sp1, "Error", "Something went wrong.") % with description + endSpan(sp1); + + % perform test comparisons + results = readJsonResults(testCase); + % status codes + % Unset: 0 + % Ok: 1 + % Error: 2 + verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.status.code, 1); + verifyEqual(testCase, results{2}.resourceSpans.scopeSpans.spans.status.code, 2); + end + function testAttributes(testCase) % testAttributes: specifying attributes when starting spans From ac78aacdf500dd6fd9fb14e424419e66c16f0175 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Wed, 1 Nov 2023 10:37:06 -0400 Subject: [PATCH 30/30] Make properties of exporters, span processors, and samplers no longer read-only #36 --- .../+exporters/+otlp/OtlpGrpcSpanExporter.m | 146 ++++++------ .../+exporters/+otlp/OtlpHttpSpanExporter.m | 134 +++++------ .../otlp/OtlpGrpcSpanExporterProxy.h | 14 +- .../otlp/OtlpHttpSpanExporterProxy.h | 19 +- .../otlp/src/OtlpGrpcSpanExporterProxy.cpp | 93 ++++---- .../otlp/src/OtlpHttpSpanExporterProxy.cpp | 108 ++++----- .../+sdk/+trace/BatchSpanProcessor.m | 85 +++---- .../+opentelemetry/+sdk/+trace/Sampler.m | 9 +- .../+opentelemetry/+sdk/+trace/SpanExporter.m | 4 +- .../+sdk/+trace/TraceIdRatioBasedSampler.m | 18 +- .../sdk/trace/BatchSpanProcessorProxy.h | 9 +- sdk/trace/src/BatchSpanProcessorProxy.cpp | 62 +++-- test/commonSetup.m | 8 +- test/commonTeardown.m | 6 +- test/nondefault_endpoint.yml | 33 +++ test/ttrace_sdk.m | 213 ++++++++++++++++++ 16 files changed, 589 insertions(+), 372 deletions(-) create mode 100644 test/nondefault_endpoint.yml create mode 100644 test/ttrace_sdk.m diff --git a/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m b/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m index 068755a..bcf2bb6 100644 --- a/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m +++ b/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpGrpcSpanExporter.m @@ -5,13 +5,13 @@ % Copyright 2023 The MathWorks, Inc. - properties (SetAccess=immutable) - Endpoint (1,1) string % Export destination - UseCredentials (1,1) logical % Whether to use SSL credentials - CertificatePath (1,1) string % Path to .pem file for SSL encryption - CertificateString (1,1) string % In-memory string representation of .pem file for SSL encryption - Timeout (1,1) duration % Maximum time above which exports will abort - HttpHeaders (1,1) dictionary % Additional HTTP headers + properties + Endpoint (1,1) string = "http://localhost:4317" % Export destination + UseCredentials (1,1) logical = false % Whether to use SSL credentials + CertificatePath (1,1) string = "" % Path to .pem file for SSL encryption + CertificateString (1,1) string = "" % In-memory string representation of .pem file for SSL encryption + Timeout (1,1) duration = seconds(10) % Maximum time above which exports will abort + HttpHeaders (1,1) dictionary = dictionary(string.empty, string.empty) % Additional HTTP headers end methods @@ -43,93 +43,73 @@ optionvalues end + obj = obj@opentelemetry.sdk.trace.SpanExporter(... + "libmexclass.opentelemetry.exporters.OtlpGrpcSpanExporterProxy"); + validnames = ["Endpoint", "UseCredentials ", "CertificatePath", ... "CertificateString", "Timeout", "HttpHeaders"]; - % set default values to empty or negative - endpoint = ""; - usessl = false; - certificatepath = ""; - certificatestring = ""; - timeout_millis = -1; - headerkeys = string.empty(); - headervalues = string.empty(); for i = 1:length(optionnames) namei = validatestring(optionnames{i}, validnames); valuei = optionvalues{i}; - if strcmp(namei, "Endpoint") - if ~(isStringScalar(valuei) || (ischar(valuei) && isrow(valuei))) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:EndpointNotScalarText", "Endpoint must be a scalar string."); - end - endpoint = string(valuei); - elseif strcmp(namei, "UseCredentials ") - if ~((islogical(valuei) || isnumeric(valuei)) && isscalar(valuei)) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:UseCredentialsNotScalarLogical", "UseCredentials must be a scalar logical.") - end - usessl = logical(valuei); - elseif strcmp(namei, "CertificatePath") - if ~(isStringScalar(valuei) || (ischar(valuei) && isrow(valuei))) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:CertificatePathNotScalarText", "CertificatePath must be a scalar string."); - end - certificatepath = string(valuei); - elseif strcmp(namei, "CertificateString") - if ~(isStringScalar(valuei) || (ischar(valuei) && isrow(valuei))) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:CertificateStringNotScalarText", "CertificateString must be a scalar string."); - end - certificatestring = string(valuei); - elseif strcmp(namei, "Timeout") - if ~(isduration(valuei) && isscalar(valuei)) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration."); - end - timeout = valuei; - timeout_millis = milliseconds(timeout); - else % HttpHeaders - if ~isa(valuei, "dictionary") - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary."); - end - httpheaders = valuei; - headerkeys = keys(valuei); - headervalues = values(valuei); - if ~isstring(headervalues) - error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.") - end - end + obj.(namei) = valuei; end - - obj = obj@opentelemetry.sdk.trace.SpanExporter(... - "libmexclass.opentelemetry.exporters.OtlpGrpcSpanExporterProxy", ... - endpoint, usessl, certificatepath, certificatestring, ... - timeout_millis, headerkeys, headervalues); + end - % populate immutable properties - [defaultendpoint, defaultcertpath, defaultcertstring, defaultmillis] = ... - getDefaultOptionValues(obj); - if endpoint == "" % not specified, use default value - obj.Endpoint = defaultendpoint; - else - obj.Endpoint = endpoint; - end - obj.UseCredentials = usessl; - if certificatepath == "" % not specified, use default value - obj.CertificatePath = defaultcertpath; - else - obj.CertificatePath = certificatepath; + function obj = set.Endpoint(obj, ep) + if ~(isStringScalar(ep) || (ischar(ep) && isrow(ep))) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:EndpointNotScalarText", "Endpoint must be a scalar string."); end - if certificatestring == "" % not specified, use default value - obj.CertificateString = defaultcertstring; - else - obj.CertificateString = certificatestring; + ep = string(ep); + obj.Proxy.setEndpoint(ep); + obj.Endpoint = ep; + end + + function obj = set.UseCredentials(obj, uc) + if ~((islogical(uc) || isnumeric(uc)) && isscalar(uc)) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:UseCredentialsNotScalarLogical", "UseCredentials must be a scalar logical.") + end + uc = logical(uc); + obj.Proxy.setUseCredentials(uc); + obj.UseCredentials = uc; + end + + function obj = set.CertificatePath(obj, certpath) + if ~(isStringScalar(certpath) || (ischar(certpath) && isrow(certpath))) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:CertificatePathNotScalarText", "CertificatePath must be a scalar string."); end - if timeout_millis < 0 % not specified, use default value - obj.Timeout = milliseconds(defaultmillis); - else - obj.Timeout = timeout; + certpath = string(certpath); + obj.Proxy.setCertificatePath(certpath); + obj.CertificatePath = certpath; + end + + function obj = set.CertificateString(obj, certstr) + if ~(isStringScalar(certstr) || (ischar(certstr) && isrow(certstr))) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:CertificateStringNotScalarText", "CertificateString must be a scalar string."); + end + certstr = string(certstr); + obj.Proxy.setCertificateString(certstr); + obj.CertificateString = certstr; + end + + function obj = set.Timeout(obj, timeout) + if ~(isduration(timeout) && isscalar(timeout)) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration."); + end + obj.Proxy.setTimeout(milliseconds(timeout)); + obj.Timeout = timeout; + end + + function obj = set.HttpHeaders(obj, httpheaders) + if ~isa(httpheaders, "dictionary") + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary."); end - if isempty(headerkeys) % not specified, return empty dictionary - obj.HttpHeaders = dictionary(headerkeys, headervalues); - else - obj.HttpHeaders = httpheaders; + headerkeys = keys(httpheaders); + headervalues = values(httpheaders); + if ~isstring(headervalues) + error("opentelemetry:exporters:otlp:OtlpGrpcSpanExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.") end - + obj.Proxy.setHttpHeaders(headerkeys, headervalues); + obj.HttpHeaders = httpheaders; end end end diff --git a/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m b/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m index 8bed109..cf79565 100644 --- a/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m +++ b/exporters/otlp/+opentelemetry/+exporters/+otlp/OtlpHttpSpanExporter.m @@ -5,13 +5,13 @@ % Copyright 2023 The MathWorks, Inc. - properties (SetAccess=immutable) - Endpoint (1,1) string % Export destination - Format (1,1) string % Data format, JSON or binary - JsonBytesMapping (1,1) string % What to convert JSON bytes to - UseJsonName (1,1) logical % Whether to use JSON name of protobuf field to set the key of JSON - Timeout (1,1) duration % Maximum time above which exports will abort - HttpHeaders (1,1) dictionary % Additional HTTP headers + properties + Endpoint (1,1) string = "http://localhost:4318/v1/traces" % Export destination + Format (1,1) string = "JSON" % Data format, JSON or binary + JsonBytesMapping (1,1) string = "hexId" % What to convert JSON bytes to + UseJsonName (1,1) logical = false % Whether to use JSON name of protobuf field to set the key of JSON + Timeout (1,1) duration = seconds(10) % Maximum time above which exports will abort + HttpHeaders (1,1) dictionary = dictionary(string.empty, string.empty) % Additional HTTP headers end methods @@ -43,89 +43,67 @@ optionvalues end + obj = obj@opentelemetry.sdk.trace.SpanExporter(... + "libmexclass.opentelemetry.exporters.OtlpHttpSpanExporterProxy"); + validnames = ["Endpoint", "Format", "JsonBytesMapping", ... "UseJsonName", "Timeout", "HttpHeaders"]; - % set default values to empty or negative - endpoint = ""; - dataformat = ""; - jsonbytesmapping = ""; - usejsonname = false; - timeout_millis = -1; - headerkeys = string.empty(); - headervalues = string.empty(); for i = 1:length(optionnames) namei = validatestring(optionnames{i}, validnames); valuei = optionvalues{i}; - if strcmp(namei, "Endpoint") - if ~(isStringScalar(valuei) || (ischar(valuei) && isrow(valuei))) - error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:EndpointNotScalarText", "Endpoint must be a scalar string."); - end - endpoint = string(valuei); - elseif strcmp(namei, "Format") - dataformat = validatestring(valuei, ["JSON", "binary"]); - elseif strcmp(namei, "JsonBytesMapping") - jsonbytesmapping = validatestring(valuei, ["hex", "hexId", "base64"]); - elseif strcmp(namei, "UseJsonName") - if ~((islogical(valuei) || isnumeric(valuei)) && isscalar(valuei)) - error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:UseJsonNameNotScalarLogical", "UseJsonName must be a scalar logical.") - end - usejsonname = logical(valuei); - elseif strcmp(namei, "Timeout") - if ~(isduration(valuei) && isscalar(valuei)) - error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration."); - end - timeout = valuei; - timeout_millis = milliseconds(timeout); - else % HttpHeaders - if ~isa(valuei, "dictionary") - error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary."); - end - httpheaders = valuei; - headerkeys = keys(valuei); - headervalues = values(valuei); - if ~isstring(headervalues) - error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.") - end - end + obj.(namei) = valuei; end - - obj = obj@opentelemetry.sdk.trace.SpanExporter(... - "libmexclass.opentelemetry.exporters.OtlpHttpSpanExporterProxy", ... - endpoint, dataformat, jsonbytesmapping, usejsonname, ... - timeout_millis, headerkeys, headervalues); + end - % populate immutable properties - if endpoint == "" || dataformat == "" || jsonbytesmapping == "" || ... - timeout_millis < 0 - [defaultendpoint, defaultformat, defaultmapping, defaultmillis] = ... - getDefaultOptionValues(obj); + function obj = set.Endpoint(obj, ep) + if ~(isStringScalar(ep) || (ischar(ep) && isrow(ep))) + error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:EndpointNotScalarText", "Endpoint must be a scalar string."); end - if endpoint == "" % not specified, use default value - obj.Endpoint = defaultendpoint; - else - obj.Endpoint = endpoint; - end - if dataformat == "" % not specified, use default value - obj.Format = defaultformat; - else - obj.Format = dataformat; + ep = string(ep); + obj.Proxy.setEndpoint(ep); + obj.Endpoint = ep; + end + + function obj = set.Format(obj, newformat) + newformat = validatestring(newformat, ["JSON", "binary"]); + obj.Proxy.setFormat(newformat); + obj.Format = newformat; + end + + function obj = set.JsonBytesMapping(obj, jbm) + jbm = validatestring(jbm, ["hex", "hexId", "base64"]); + obj.Proxy.setJsonBytesMapping(jbm); + obj.JsonBytesMapping = jbm; + end + + function obj = set.UseJsonName(obj, ujn) + if ~((islogical(ujn) || isnumeric(ujn)) && isscalar(ujn)) + error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:UseJsonNameNotScalarLogical", "UseJsonName must be a scalar logical.") end - if jsonbytesmapping == "" % not specified, use default value - obj.JsonBytesMapping = defaultmapping; - else - obj.JsonBytesMapping = jsonbytesmapping; + ujn = logical(ujn); + obj.Proxy.setUseJsonName(ujn); + obj.UseJsonName = ujn; + end + + function obj = set.Timeout(obj, timeout) + if ~(isduration(timeout) && isscalar(timeout)) + error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration."); end - obj.UseJsonName = usejsonname; - if timeout_millis < 0 % not specified, use default value - obj.Timeout = milliseconds(defaultmillis); - else - obj.Timeout = timeout; + obj.Proxy.setTimeout(milliseconds(timeout)); + obj.Timeout = timeout; + end + + function obj = set.HttpHeaders(obj, httpheaders) + if ~isa(httpheaders, "dictionary") + error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary."); end - if isempty(headerkeys) % not specified, return empty dictionary - obj.HttpHeaders = dictionary(headerkeys, headervalues); - else - obj.HttpHeaders = httpheaders; + headerkeys = keys(httpheaders); + headervalues = values(httpheaders); + if ~isstring(headervalues) + error("opentelemetry:exporters:otlp:OtlpHttpSpanExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.") end + obj.Proxy.setHttpHeaders(headerkeys, headervalues); + obj.HttpHeaders = httpheaders; end end end diff --git a/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpGrpcSpanExporterProxy.h b/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpGrpcSpanExporterProxy.h index 716c941..bcc4879 100644 --- a/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpGrpcSpanExporterProxy.h +++ b/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpGrpcSpanExporterProxy.h @@ -17,14 +17,24 @@ namespace libmexclass::opentelemetry::exporters { class OtlpGrpcSpanExporterProxy: public libmexclass::opentelemetry::sdk::SpanExporterProxy { public: OtlpGrpcSpanExporterProxy(otlp_exporter::OtlpGrpcExporterOptions options) : CppOptions(options) { - REGISTER_METHOD(OtlpGrpcSpanExporterProxy, getDefaultOptionValues); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setEndpoint); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setUseCredentials); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setCertificatePath); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setCertificateString); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setTimeout); + REGISTER_METHOD(OtlpGrpcSpanExporterProxy, setHttpHeaders); } static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); std::unique_ptr getInstance() override; - void getDefaultOptionValues(libmexclass::proxy::method::Context& context); + void setEndpoint(libmexclass::proxy::method::Context& context); + void setUseCredentials(libmexclass::proxy::method::Context& context); + void setCertificatePath(libmexclass::proxy::method::Context& context); + void setCertificateString(libmexclass::proxy::method::Context& context); + void setTimeout(libmexclass::proxy::method::Context& context); + void setHttpHeaders(libmexclass::proxy::method::Context& context); private: otlp_exporter::OtlpGrpcExporterOptions CppOptions; diff --git a/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h b/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h index f843303..f735635 100644 --- a/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h +++ b/exporters/otlp/include/opentelemetry-matlab/exporters/otlp/OtlpHttpSpanExporterProxy.h @@ -17,7 +17,12 @@ namespace libmexclass::opentelemetry::exporters { class OtlpHttpSpanExporterProxy: public libmexclass::opentelemetry::sdk::SpanExporterProxy { public: OtlpHttpSpanExporterProxy(otlp_exporter::OtlpHttpExporterOptions options) : CppOptions(options) { - REGISTER_METHOD(OtlpHttpSpanExporterProxy, getDefaultOptionValues); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setEndpoint); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setFormat); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setJsonBytesMapping); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setUseJsonName); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setTimeout); + REGISTER_METHOD(OtlpHttpSpanExporterProxy, setHttpHeaders); } static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); @@ -26,6 +31,18 @@ class OtlpHttpSpanExporterProxy: public libmexclass::opentelemetry::sdk::SpanExp void getDefaultOptionValues(libmexclass::proxy::method::Context& context); + void setEndpoint(libmexclass::proxy::method::Context& context); + + void setFormat(libmexclass::proxy::method::Context& context); + + void setJsonBytesMapping(libmexclass::proxy::method::Context& context); + + void setUseJsonName(libmexclass::proxy::method::Context& context); + + void setTimeout(libmexclass::proxy::method::Context& context); + + void setHttpHeaders(libmexclass::proxy::method::Context& context); + private: otlp_exporter::OtlpHttpExporterOptions CppOptions; }; diff --git a/exporters/otlp/src/OtlpGrpcSpanExporterProxy.cpp b/exporters/otlp/src/OtlpGrpcSpanExporterProxy.cpp index 1ae66a0..c4e4770 100644 --- a/exporters/otlp/src/OtlpGrpcSpanExporterProxy.cpp +++ b/exporters/otlp/src/OtlpGrpcSpanExporterProxy.cpp @@ -10,60 +10,63 @@ namespace otlp_exporter = opentelemetry::exporter::otlp; namespace libmexclass::opentelemetry::exporters { libmexclass::proxy::MakeResult OtlpGrpcSpanExporterProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - matlab::data::StringArray endpoint_mda = constructor_arguments[0]; + otlp_exporter::OtlpGrpcExporterOptions options; + return std::make_shared(options); +} + +std::unique_ptr OtlpGrpcSpanExporterProxy::getInstance() { + return otlp_exporter::OtlpGrpcExporterFactory::Create(CppOptions); +} + +void OtlpGrpcSpanExporterProxy::setEndpoint(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray endpoint_mda = context.inputs[0]; std::string endpoint = static_cast(endpoint_mda[0]); - matlab::data::TypedArray use_ssl_mda = constructor_arguments[1]; - bool use_ssl = use_ssl_mda[0]; - matlab::data::StringArray certpath_mda = constructor_arguments[2]; - std::string certpath = static_cast(certpath_mda[0]); - matlab::data::StringArray certstring_mda = constructor_arguments[3]; - std::string certstring = static_cast(certstring_mda[0]); - matlab::data::TypedArray timeout_mda = constructor_arguments[4]; - double timeout = timeout_mda[0]; - matlab::data::Array header_mda = constructor_arguments[5]; - size_t nheaders = header_mda.getNumberOfElements(); - matlab::data::StringArray headernames_mda = constructor_arguments[5]; - matlab::data::StringArray headervalues_mda = constructor_arguments[6]; - otlp_exporter::OtlpGrpcExporterOptions options; if (!endpoint.empty()) { - options.endpoint = endpoint; - } - // use_ssl - options.use_ssl_credentials = use_ssl; + CppOptions.endpoint = endpoint; + } +} + + +void OtlpGrpcSpanExporterProxy::setUseCredentials(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray use_credentials_mda = context.inputs[0]; + CppOptions.use_ssl_credentials = use_credentials_mda[0]; +} + +void OtlpGrpcSpanExporterProxy::setCertificatePath(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray certpath_mda = context.inputs[0]; + std::string certpath = static_cast(certpath_mda[0]); + if (!certpath.empty()) { - options.ssl_credentials_cacert_path = certpath; - } - if (!certstring.empty()) { - options.ssl_credentials_cacert_as_string = certstring; - } - // timeout - if (timeout >= 0) { - options.timeout = std::chrono::milliseconds(static_cast(timeout)); + CppOptions.ssl_credentials_cacert_path = certpath; } - // http headers - for (size_t i = 0; i < nheaders; ++i) { - options.metadata.insert(std::pair{static_cast(headernames_mda[i]), - static_cast(headervalues_mda[i])}); +} + +void OtlpGrpcSpanExporterProxy::setCertificateString(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray certstr_mda = context.inputs[0]; + std::string certstr = static_cast(certstr_mda[0]); + + if (!certstr.empty()) { + CppOptions.ssl_credentials_cacert_as_string = certstr; } - return std::make_shared(options); } -std::unique_ptr OtlpGrpcSpanExporterProxy::getInstance() { - return otlp_exporter::OtlpGrpcExporterFactory::Create(CppOptions); +void OtlpGrpcSpanExporterProxy::setTimeout(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray timeout_mda = context.inputs[0]; + double timeout = timeout_mda[0]; + + if (timeout >= 0) { + CppOptions.timeout = std::chrono::milliseconds(static_cast(timeout)); + } } -void OtlpGrpcSpanExporterProxy::getDefaultOptionValues(libmexclass::proxy::method::Context& context) { - otlp_exporter::OtlpGrpcExporterOptions options; - matlab::data::ArrayFactory factory; - auto endpoint_mda = factory.createScalar(options.endpoint); - auto certpath_mda = factory.createScalar(options.ssl_credentials_cacert_path); - auto certstring_mda = factory.createScalar(options.ssl_credentials_cacert_as_string); - auto timeout_millis = std::chrono::duration_cast(options.timeout); - auto timeout_mda = factory.createScalar(static_cast(timeout_millis.count())); - context.outputs[0] = endpoint_mda; - context.outputs[1] = certpath_mda; - context.outputs[2] = certstring_mda; - context.outputs[3] = timeout_mda; +void OtlpGrpcSpanExporterProxy::setHttpHeaders(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray headernames_mda = context.inputs[0]; + matlab::data::StringArray headervalues_mda = context.inputs[1]; + size_t nheaders = headernames_mda.getNumberOfElements(); + for (size_t i = 0; i < nheaders; ++i) { + CppOptions.metadata.insert(std::pair{static_cast(headernames_mda[i]), + static_cast(headervalues_mda[i])}); + } } } // namespace libmexclass::opentelemetry diff --git a/exporters/otlp/src/OtlpHttpSpanExporterProxy.cpp b/exporters/otlp/src/OtlpHttpSpanExporterProxy.cpp index f8c42ce..ebcdaa5 100644 --- a/exporters/otlp/src/OtlpHttpSpanExporterProxy.cpp +++ b/exporters/otlp/src/OtlpHttpSpanExporterProxy.cpp @@ -10,84 +10,68 @@ namespace otlp_exporter = opentelemetry::exporter::otlp; namespace libmexclass::opentelemetry::exporters { libmexclass::proxy::MakeResult OtlpHttpSpanExporterProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { - matlab::data::StringArray endpoint_mda = constructor_arguments[0]; + otlp_exporter::OtlpHttpExporterOptions options; + return std::make_shared(options); +} + +std::unique_ptr OtlpHttpSpanExporterProxy::getInstance() { + return otlp_exporter::OtlpHttpExporterFactory::Create(CppOptions); +} + +void OtlpHttpSpanExporterProxy::setEndpoint(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray endpoint_mda = context.inputs[0]; std::string endpoint = static_cast(endpoint_mda[0]); - matlab::data::StringArray dataformat_mda = constructor_arguments[1]; - std::string dataformat = static_cast(dataformat_mda[0]); - matlab::data::StringArray json_bytes_mapping_mda = constructor_arguments[2]; - std::string json_bytes_mapping = static_cast(json_bytes_mapping_mda[0]); - matlab::data::TypedArray use_json_name_mda = constructor_arguments[3]; - bool use_json_name = use_json_name_mda[0]; - matlab::data::TypedArray timeout_mda = constructor_arguments[4]; - double timeout = timeout_mda[0]; - matlab::data::Array header_mda = constructor_arguments[5]; - size_t nheaders = header_mda.getNumberOfElements(); - matlab::data::StringArray headernames_mda = constructor_arguments[5]; - matlab::data::StringArray headervalues_mda = constructor_arguments[6]; - otlp_exporter::OtlpHttpExporterOptions options; if (!endpoint.empty()) { - options.url = endpoint; + CppOptions.url = endpoint; } - // TODO: store the relationship between strings and enums in an associative container - // dataformat +} + +void OtlpHttpSpanExporterProxy::setFormat(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray dataformat_mda = context.inputs[0]; + std::string dataformat = static_cast(dataformat_mda[0]); + if (dataformat.compare("JSON") == 0) { - options.content_type = otlp_exporter::HttpRequestContentType::kJson; + CppOptions.content_type = otlp_exporter::HttpRequestContentType::kJson; } else if (dataformat.compare("binary") == 0) { - options.content_type = otlp_exporter::HttpRequestContentType::kBinary; + CppOptions.content_type = otlp_exporter::HttpRequestContentType::kBinary; } - // json_bytes_mapping +} + +void OtlpHttpSpanExporterProxy::setJsonBytesMapping(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray json_bytes_mapping_mda = context.inputs[0]; + std::string json_bytes_mapping = static_cast(json_bytes_mapping_mda[0]); + if (json_bytes_mapping.compare("hex") == 0) { - options.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kHex; + CppOptions.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kHex; } else if (json_bytes_mapping.compare("hexId") == 0) { - options.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kHexId; + CppOptions.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kHexId; } else if (json_bytes_mapping.compare("base64") == 0) { - options.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kBase64; + CppOptions.json_bytes_mapping = otlp_exporter::JsonBytesMappingKind::kBase64; } - // use_json_name - options.use_json_name = use_json_name; - // timeout - if (timeout >= 0) { - options.timeout = std::chrono::milliseconds(static_cast(timeout)); - } - // http headers - for (size_t i = 0; i < nheaders; ++i) { - options.http_headers.insert(std::pair{static_cast(headernames_mda[i]), - static_cast(headervalues_mda[i])}); - } - return std::make_shared(options); } -std::unique_ptr OtlpHttpSpanExporterProxy::getInstance() { - return otlp_exporter::OtlpHttpExporterFactory::Create(CppOptions); +void OtlpHttpSpanExporterProxy::setUseJsonName(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray use_json_name_mda = context.inputs[0]; + CppOptions.use_json_name = use_json_name_mda[0]; } -void OtlpHttpSpanExporterProxy::getDefaultOptionValues(libmexclass::proxy::method::Context& context) { - otlp_exporter::OtlpHttpExporterOptions options; - matlab::data::ArrayFactory factory; - auto endpoint_mda = factory.createScalar(options.url); - std::string dataformat, json_bytes_mapping; - // dataformat - if (options.content_type == otlp_exporter::HttpRequestContentType::kJson) { - dataformat = "JSON"; - } else { - dataformat = "binary"; +void OtlpHttpSpanExporterProxy::setTimeout(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray timeout_mda = context.inputs[0]; + double timeout = timeout_mda[0]; + + if (timeout >= 0) { + CppOptions.timeout = std::chrono::milliseconds(static_cast(timeout)); } - // json_bytes_mapping - if (options.json_bytes_mapping == otlp_exporter::JsonBytesMappingKind::kHex) { - json_bytes_mapping = "hex"; - } else if (options.json_bytes_mapping == otlp_exporter::JsonBytesMappingKind::kHexId) { - json_bytes_mapping = "hexId"; - } else { // kBase64 - json_bytes_mapping = "base64"; +} + +void OtlpHttpSpanExporterProxy::setHttpHeaders(libmexclass::proxy::method::Context& context) { + matlab::data::StringArray headernames_mda = context.inputs[0]; + matlab::data::StringArray headervalues_mda = context.inputs[1]; + size_t nheaders = headernames_mda.getNumberOfElements(); + for (size_t i = 0; i < nheaders; ++i) { + CppOptions.http_headers.insert(std::pair{static_cast(headernames_mda[i]), + static_cast(headervalues_mda[i])}); } - auto dataformat_mda = factory.createScalar(dataformat); - auto json_bytes_mapping_mda = factory.createScalar(json_bytes_mapping); - auto timeout_millis = std::chrono::duration_cast(options.timeout); - auto timeout_mda = factory.createScalar(static_cast(timeout_millis.count())); - context.outputs[0] = endpoint_mda; - context.outputs[1] = dataformat_mda; - context.outputs[2] = json_bytes_mapping_mda; - context.outputs[3] = timeout_mda; } } // namespace libmexclass::opentelemetry diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m b/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m index 84b05bc..3101dd3 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m @@ -3,10 +3,10 @@ % Copyright 2023 The MathWorks, Inc. - properties (SetAccess=immutable) - MaximumQueueSize (1,1) double % Maximum queue size. After queue size is reached, spans are dropped. - ScheduledDelay (1,1) duration % Time interval between span exports - MaximumExportBatchSize (1,1) double % Maximum batch size to export. + properties + MaximumQueueSize (1,1) double = 2048 % Maximum queue size. After queue size is reached, spans are dropped. + ScheduledDelay (1,1) duration = seconds(5) % Time interval between span exports + MaximumExportBatchSize (1,1) double = 512 % Maximum batch size to export. end methods @@ -44,61 +44,46 @@ optionvalues end + obj = obj@opentelemetry.sdk.trace.SpanProcessor(spanexporter, ... + "libmexclass.opentelemetry.sdk.BatchSpanProcessorProxy"); + validnames = ["MaximumQueueSize", "ScheduledDelay", "MaximumExportBatchSize"]; - % set default values to negative - qsize = -1; - delaymillis = -1; - batchsize = -1; for i = 1:length(optionnames) namei = validatestring(optionnames{i}, validnames); valuei = optionvalues{i}; - if strcmp(namei, "MaximumQueueSize") - if ~isnumeric(valuei) || ~isscalar(valuei) || valuei <= 0 || ... - round(valuei) ~= valuei - error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidMaxQueueSize", ... - "MaximumQueueSize must be a scalar positive integer."); - end - qsize = double(valuei); - elseif strcmp(namei, "ScheduledDelay") - if ~isduration(valuei) || ~isscalar(valuei) || valuei <= 0 - error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidScheduledDelay", ... - "ScheduledDelay must be a positive duration scalar."); - end - delay = valuei; - delaymillis = milliseconds(valuei); - else % "MaximumExportBatchSize" - if ~isnumeric(valuei) || ~isscalar(valuei) || valuei <= 0 || ... - round(valuei) ~= valuei - error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidMaxExportBatchSize", ... - "MaximumExportBatchSize must be a scalar positive integer."); - end - batchsize = double(valuei); - end + obj.(namei) = valuei; end - - obj = obj@opentelemetry.sdk.trace.SpanProcessor(spanexporter, ... - "libmexclass.opentelemetry.sdk.BatchSpanProcessorProxy", ... - qsize, delaymillis, batchsize); + end - % populate immutable properties - if qsize < 0 || delaymillis < 0 || batchsize < 0 - [defaultqsize, defaultmillis, defaultbatchsize] = obj.Proxy.getDefaultOptionValues(); - end - if qsize < 0 % not specified, use default value - obj.MaximumQueueSize = defaultqsize; - else - obj.MaximumQueueSize = qsize; + function obj = set.MaximumQueueSize(obj, maxqsz) + if ~isnumeric(maxqsz) || ~isscalar(maxqsz) || maxqsz <= 0 || ... + round(maxqsz) ~= maxqsz + error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidMaxQueueSize", ... + "MaximumQueueSize must be a scalar positive integer."); end - if delaymillis < 0 % not specified, use default value - obj.ScheduledDelay = milliseconds(defaultmillis); - else - obj.ScheduledDelay = delay; + maxqsz = double(maxqsz); + obj.Proxy.setMaximumQueueSize(maxqsz); + obj.MaximumQueueSize = maxqsz; + end + + function obj = set.ScheduledDelay(obj, delay) + if ~isduration(delay) || ~isscalar(delay) || delay <= 0 + error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidScheduledDelay", ... + "ScheduledDelay must be a positive duration scalar."); end - if batchsize < 0 % not specified, use default value - obj.MaximumExportBatchSize = defaultbatchsize; - else - obj.MaximumExportBatchSize = batchsize; + obj.Proxy.setScheduledDelay(milliseconds(delay)); + obj.ScheduledDelay = delay; + end + + function obj = set.MaximumExportBatchSize(obj, maxbatch) + if ~isnumeric(maxbatch) || ~isscalar(maxbatch) || maxbatch <= 0 || ... + round(maxbatch) ~= maxbatch + error("opentelemetry:sdk:trace:BatchSpanProcessor:InvalidMaxExportBatchSize", ... + "MaximumExportBatchSize must be a scalar positive integer."); end + maxbatch = double(maxbatch); + obj.Proxy.setMaximumExportBatchSize(maxbatch); + obj.MaximumExportBatchSize = maxbatch; end end end diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/Sampler.m b/sdk/trace/+opentelemetry/+sdk/+trace/Sampler.m index ccaee21..397f310 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/Sampler.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/Sampler.m @@ -4,15 +4,18 @@ % Copyright 2023 The MathWorks, Inc. properties (GetAccess={?opentelemetry.sdk.trace.TracerProvider,... - ?opentelemetry.sdk.trace.ParentBasedSampler}) + ?opentelemetry.sdk.trace.ParentBasedSampler, ... + ?opentelemetry.sdk.trace.TraceIdRatioBasedSampler}) Proxy % Proxy object to interface C++ code end methods (Access=protected) function obj = Sampler(proxyname, varargin) % Base class constructor - obj.Proxy = libmexclass.proxy.Proxy("Name", proxyname, ... - "ConstructorArguments", varargin); + if nargin > 0 + obj.Proxy = libmexclass.proxy.Proxy("Name", proxyname, ... + "ConstructorArguments", varargin); + end end end end diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/SpanExporter.m b/sdk/trace/+opentelemetry/+sdk/+trace/SpanExporter.m index b2bcf4b..846e518 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/SpanExporter.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/SpanExporter.m @@ -3,7 +3,9 @@ % Copyright 2023 The MathWorks, Inc. - properties (GetAccess=?opentelemetry.sdk.trace.SpanProcessor) + properties (GetAccess={?opentelemetry.sdk.trace.SpanProcessor, ... + ?opentelemetry.exporters.otlp.OtlpHttpSpanExporter, ... + ?opentelemetry.exporters.otlp.OtlpGrpcSpanExporter}) Proxy % Proxy object to interface C++ code end diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/TraceIdRatioBasedSampler.m b/sdk/trace/+opentelemetry/+sdk/+trace/TraceIdRatioBasedSampler.m index 3b1f2ca..ab7310c 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/TraceIdRatioBasedSampler.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/TraceIdRatioBasedSampler.m @@ -3,7 +3,7 @@ % Copyright 2023 The MathWorks, Inc. - properties (SetAccess=immutable) + properties Ratio (1,1) double % Sampling ratio between 0 and 1 end @@ -16,12 +16,18 @@ % See also OPENTELEMETRY.SDK.TRACE.ALWAYSONSAMPLER, % OPENTELEMETRY.SDK.TRACE.ALWAYSOFFSAMPLER, % OPENTELEMETRY.SDK.TRACE.PARENTBASEDSAMPLER - arguments - ratio (1,1) {mustBeNumeric, mustBeNonnegative, mustBeLessThanOrEqual(ratio,1)} + + obj.Ratio = ratio; + end + + function obj = set.Ratio(obj, ratio) + if ~(ratio >= 0 && ratio <= 1) + error("opentelemetry:sdk:trace:TraceIdRatioBasedSampler:InvalidRatio", ... + "Ratio must be a numeric scalar between 0 and 1."); end - ratio = double(ratio); - obj = obj@opentelemetry.sdk.trace.Sampler(... - "libmexclass.opentelemetry.sdk.TraceIdRatioBasedSamplerProxy", ratio); + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.TraceIdRatioBasedSamplerProxy", ... + "ConstructorArguments", {ratio}); obj.Ratio = ratio; end end diff --git a/sdk/trace/include/opentelemetry-matlab/sdk/trace/BatchSpanProcessorProxy.h b/sdk/trace/include/opentelemetry-matlab/sdk/trace/BatchSpanProcessorProxy.h index 1afc8a0..00ecd21 100644 --- a/sdk/trace/include/opentelemetry-matlab/sdk/trace/BatchSpanProcessorProxy.h +++ b/sdk/trace/include/opentelemetry-matlab/sdk/trace/BatchSpanProcessorProxy.h @@ -15,14 +15,17 @@ namespace trace_sdk = opentelemetry::sdk::trace; namespace libmexclass::opentelemetry::sdk { class BatchSpanProcessorProxy : public SpanProcessorProxy { public: - BatchSpanProcessorProxy(std::shared_ptr exporter, double qsize, - double delay, double batchsize); + BatchSpanProcessorProxy(std::shared_ptr exporter); static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments); std::unique_ptr getInstance() override; - void getDefaultOptionValues(libmexclass::proxy::method::Context& context); + void setMaximumQueueSize(libmexclass::proxy::method::Context& context); + + void setScheduledDelay(libmexclass::proxy::method::Context& context); + + void setMaximumExportBatchSize(libmexclass::proxy::method::Context& context); private: trace_sdk::BatchSpanProcessorOptions CppOptions; diff --git a/sdk/trace/src/BatchSpanProcessorProxy.cpp b/sdk/trace/src/BatchSpanProcessorProxy.cpp index 4e369b6..75b0cb5 100644 --- a/sdk/trace/src/BatchSpanProcessorProxy.cpp +++ b/sdk/trace/src/BatchSpanProcessorProxy.cpp @@ -8,52 +8,46 @@ #include "opentelemetry/sdk/trace/batch_span_processor_factory.h" namespace libmexclass::opentelemetry::sdk { -BatchSpanProcessorProxy::BatchSpanProcessorProxy(std::shared_ptr exporter, - double qsize, double delay, double batchsize) +BatchSpanProcessorProxy::BatchSpanProcessorProxy(std::shared_ptr exporter) : SpanProcessorProxy(exporter) { - - if (qsize > 0) { - CppOptions.max_queue_size = static_cast(qsize); - } - if (delay > 0) { - CppOptions.schedule_delay_millis = std::chrono::milliseconds(static_cast(delay)); - } - if (batchsize > 0) { - CppOptions.max_export_batch_size = static_cast(batchsize); - } - REGISTER_METHOD(BatchSpanProcessorProxy, getDefaultOptionValues); + REGISTER_METHOD(BatchSpanProcessorProxy, setMaximumQueueSize); + REGISTER_METHOD(BatchSpanProcessorProxy, setScheduledDelay); + REGISTER_METHOD(BatchSpanProcessorProxy, setMaximumExportBatchSize); } libmexclass::proxy::MakeResult BatchSpanProcessorProxy::make(const libmexclass::proxy::FunctionArguments& constructor_arguments) { matlab::data::TypedArray exporterid_mda = constructor_arguments[0]; - libmexclass::proxy::ID exporterid = exporterid_mda[0]; + libmexclass::proxy::ID exporterid = exporterid_mda[0]; std::shared_ptr exporter = std::static_pointer_cast( libmexclass::proxy::ProxyManager::getProxy(exporterid)); - matlab::data::TypedArray qsize_mda = constructor_arguments[1]; - double qsize = qsize_mda[0]; - matlab::data::TypedArray delay_mda = constructor_arguments[2]; - double delay = delay_mda[0]; - matlab::data::TypedArray batchsize_mda = constructor_arguments[3]; - double batchsize = batchsize_mda[0]; - - return std::make_shared(exporter, qsize, delay, batchsize); + return std::make_shared(exporter); } std::unique_ptr BatchSpanProcessorProxy::getInstance() { return trace_sdk::BatchSpanProcessorFactory::Create(std::move(SpanExporter->getInstance()), CppOptions); } -void BatchSpanProcessorProxy::getDefaultOptionValues(libmexclass::proxy::method::Context& context) { - trace_sdk::BatchSpanProcessorOptions options; - matlab::data::ArrayFactory factory; - auto qsize_mda = factory.createScalar(static_cast( - options.max_queue_size)); - auto delay_mda = factory.createScalar(static_cast( - options.schedule_delay_millis.count())); - auto batchsize_mda = factory.createScalar(static_cast( - options.max_export_batch_size)); - context.outputs[0] = qsize_mda; - context.outputs[1] = delay_mda; - context.outputs[2] = batchsize_mda; +void BatchSpanProcessorProxy::setMaximumQueueSize(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray qsize_mda = context.inputs[0]; + double qsize = qsize_mda[0]; + if (qsize > 0) { + CppOptions.max_queue_size = static_cast(qsize); + } +} + +void BatchSpanProcessorProxy::setScheduledDelay(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray delay_mda = context.inputs[0]; + double delay = delay_mda[0]; + if (delay > 0) { + CppOptions.schedule_delay_millis = std::chrono::milliseconds(static_cast(delay)); + } +} + +void BatchSpanProcessorProxy::setMaximumExportBatchSize(libmexclass::proxy::method::Context& context) { + matlab::data::TypedArray batchsize_mda = context.inputs[0]; + double batchsize = batchsize_mda[0]; + if (batchsize > 0) { + CppOptions.max_export_batch_size = static_cast(batchsize); + } } } // namespace libmexclass::opentelemetry diff --git a/test/commonSetup.m b/test/commonSetup.m index 98d1fa8..6ac5190 100644 --- a/test/commonSetup.m +++ b/test/commonSetup.m @@ -1,8 +1,12 @@ -function commonSetup(testCase) +function commonSetup(testCase, configfile) % Setup function for tests % % Copyright 2023 The MathWorks, Inc. +if nargin < 2 + configfile = testCase.OtelConfigFile; +end + % start collector -system(testCase.Otelcol + " --config " + testCase.OtelConfigFile + '&'); +system(testCase.Otelcol + " --config " + configfile + '&'); pause(1); % give a little time for Collector to start up \ No newline at end of file diff --git a/test/commonTeardown.m b/test/commonTeardown.m index 0f037a2..e484629 100644 --- a/test/commonTeardown.m +++ b/test/commonTeardown.m @@ -10,8 +10,10 @@ function commonTeardown(testCase) if ispc system(testCase.ListPid("cmd") + " > " + testCase.PidFile); tbl = testCase.ReadPidList(testCase.PidFile); - pid = tbl.Var2(end-1); - system(testCase.Sigterm(pid)); + if height(tbl) > 1 + pid = tbl.Var2(end-1); + system(testCase.Sigterm(pid)); + end end if exist(testCase.JsonFile, "file") diff --git a/test/nondefault_endpoint.yml b/test/nondefault_endpoint.yml new file mode 100644 index 0000000..c3ec1d7 --- /dev/null +++ b/test/nondefault_endpoint.yml @@ -0,0 +1,33 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:9922 + http: + endpoint: 0.0.0.0:9921 + +processors: + +exporters: + file: + path: ./myoutput.json + +service: + + pipelines: + + logs: + receivers: [otlp] + processors: + exporters: [file] + + traces: + receivers: [otlp] + processors: + exporters: [file] + + metrics: + receivers: [otlp] + processors: + exporters: [file] + diff --git a/test/ttrace_sdk.m b/test/ttrace_sdk.m new file mode 100644 index 0000000..2040c55 --- /dev/null +++ b/test/ttrace_sdk.m @@ -0,0 +1,213 @@ +classdef ttrace_sdk < matlab.unittest.TestCase + % tests for tracing SDK (span processors, exporters, samplers, resource) + + % 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 testNondefaultEndpoint(testCase) + % testNondefaultEndpoint: using an alternative endpoint + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + commonSetup(testCase, "nondefault_endpoint.yml") + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "Endpoint", "http://localhost:9921/v1/traces"); + processor = opentelemetry.sdk.trace.SimpleSpanProcessor(exp); + tp = opentelemetry.sdk.trace.TracerProvider(processor); + tr = getTracer(tp, tracername); + sp = startSpan(tr, spanname); + pause(1); + endSpan(sp); + + % perform test comparisons + results = readJsonResults(testCase); + results = results{1}; + + % check span and tracer names + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.name), spanname); + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.scope.name), tracername); + end + + function testNondefaultGrpcEndpoint(testCase) + % testNondefaultEndpoint: using an alternative endpoint + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + commonSetup(testCase, "nondefault_endpoint.yml") + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "Endpoint", "http://localhost:9922"); + processor = opentelemetry.sdk.trace.SimpleSpanProcessor(exp); + tp = opentelemetry.sdk.trace.TracerProvider(processor); + tr = getTracer(tp, tracername); + sp = startSpan(tr, spanname); + pause(1); + endSpan(sp); + + % perform test comparisons + results = readJsonResults(testCase); + results = results{1}; + + % check span and tracer names + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.name), spanname); + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.scope.name), tracername); + end + + function testAlwaysOffSampler(testCase) + % testAlwaysOffSampler: should not produce any spans + commonSetup(testCase) + + tp = opentelemetry.sdk.trace.TracerProvider( ... + opentelemetry.sdk.trace.SimpleSpanProcessor, ... + "Sampler", opentelemetry.sdk.trace.AlwaysOffSampler); + tr = getTracer(tp, "mytracer"); + sp = startSpan(tr, "myspan"); + pause(1); + endSpan(sp); + + % verify no spans are generated + results = readJsonResults(testCase); + verifyEmpty(testCase, results); + end + + function testAlwaysOnSampler(testCase) + % testAlwaysOnSampler: should produce all spans + commonSetup(testCase) + + tracername = "foo"; + spanname = "bar"; + + tp = opentelemetry.sdk.trace.TracerProvider( ... + opentelemetry.sdk.trace.SimpleSpanProcessor, ... + "Sampler", opentelemetry.sdk.trace.AlwaysOnSampler); + tr = getTracer(tp, tracername); + sp = startSpan(tr, spanname); + pause(1); + endSpan(sp); + + % perform test comparisons + results = readJsonResults(testCase); + results = results{1}; + + % check span and tracer names + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.spans.name), spanname); + verifyEqual(testCase, string(results.resourceSpans.scopeSpans.scope.name), tracername); + end + + function testTraceIdRatioBasedSampler(testCase) + % testTraceIdRatioBasedSampler: filter spans based on a ratio + commonSetup(testCase) + + s = opentelemetry.sdk.trace.TraceIdRatioBasedSampler(0); % equivalent to always off + + tracername = "mytracer"; + offspan = "offspan"; + tp = opentelemetry.sdk.trace.TracerProvider( ... + opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tr = getTracer(tp, tracername); + sp = startSpan(tr, offspan); + pause(1); + endSpan(sp); + + s.Ratio = 1; % equivalent to always on + onspan = "onspan"; + tp = opentelemetry.sdk.trace.TracerProvider( ... + opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tr = getTracer(tp, tracername); + sp = startSpan(tr, onspan); + pause(1); + endSpan(sp); + + s.Ratio = 0.5; % filter half of the spans + sampledspan = "sampledspan"; + numspans = 10; + tp = opentelemetry.sdk.trace.TracerProvider( ... + opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tr = getTracer(tp, tracername); + for i = 1:numspans + sp = startSpan(tr, sampledspan + i); + pause(1); + endSpan(sp); + end + + % perform test comparisons + results = readJsonResults(testCase); + n = length(results); + % total spans should be 1 span when ratio == 1, plus a number of + % spans between 0 and numspans when ratio == 0.5 + % Verifying 1 < total_spans < numspans+1. If this fails, there + % is still a chance nothing went wrong, because number of spans + % are non-deterministic when ratio == 0.5. When ratio == 0.5, + % it is still possible to get 0 or numspans spans. But that + % probability is small, so we fail the test to flag something + % may have gone wrong. + verifyGreaterThan(testCase, n, 1); + verifyLessThan(testCase, n, 1 + numspans); + verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.name), onspan); + for i = 2:n + verifySubstring(testCase, string(results{i}.resourceSpans.scopeSpans.spans.name), ... + sampledspan); + end + end + + function testCustomResource(testCase) + % testCustomResource: check custom resources are included in + % emitted spans + commonSetup(testCase) + + customkeys = ["foo" "bar"]; + customvalues = [1 5]; + tp = opentelemetry.sdk.trace.TracerProvider(opentelemetry.sdk.trace.SimpleSpanProcessor, ... + "Resource", dictionary(customkeys, customvalues)); + tr = getTracer(tp, "mytracer"); + sp = startSpan(tr, "myspan"); + pause(1); + endSpan(sp); + + % perform test comparisons + results = readJsonResults(testCase); + results = results{1}; + + resourcekeys = string({results.resourceSpans.resource.attributes.key}); + for i = length(customkeys) + idx = find(resourcekeys == customkeys(i)); + verifyNotEmpty(testCase, idx); + verifyEqual(testCase, results.resourceSpans.resource.attributes(idx).value.doubleValue, customvalues(i)); + end + end + end +end \ No newline at end of file