Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ endif()

set(OPENTELEMETRY_PROXY_LIBRARY_NAME "OtelMatlabProxy")

find_package(Matlab REQUIRED)
find_package(Protobuf REQUIRED)
find_package(nlohmann_json REQUIRED)
if(WIN32)
Expand Down Expand Up @@ -196,7 +197,7 @@ 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} ${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 ${Matlab_INCLUDE_DIRS})

set(OPENTELEMETRY_PROXY_FACTORY_CLASS_NAME OtelMatlabProxyFactory)
set(OPENTELEMETRY_PROXY_FACTORY_SOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR})
Expand All @@ -222,6 +223,9 @@ set(OPENTELEMETRY_PROXY_SOURCES
${METRICS_API_SOURCE_DIR}/UpDownCounterProxy.cpp
${METRICS_API_SOURCE_DIR}/HistogramProxy.cpp
${METRICS_API_SOURCE_DIR}/SynchronousInstrumentProxyFactory.cpp
${METRICS_API_SOURCE_DIR}/MeasurementFetcher.cpp
${METRICS_API_SOURCE_DIR}/AsynchronousInstrumentProxy.cpp
${METRICS_API_SOURCE_DIR}/AsynchronousInstrumentProxyFactory.cpp
${CONTEXT_API_SOURCE_DIR}/TextMapPropagatorProxy.cpp
${CONTEXT_API_SOURCE_DIR}/CompositePropagatorProxy.cpp
${CONTEXT_API_SOURCE_DIR}/TextMapCarrierProxy.cpp
Expand Down Expand Up @@ -295,7 +299,7 @@ set(OTEL_CPP_LINK_LIBRARIES ${OTEL_CPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX
${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})
${Protobuf_LIBRARIES} ${Matlab_MEX_LIBRARY})
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}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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.

### Status
- Currently only tracing and metrics (synchronous instruments) are supported. Asynchronous metrics instruments and logs will be in the future.
- Currently only tracing and metrics are supported. Logs will be in the future.
- View class in metrics is only partially supported. The properties **Aggregation** and **AllowedAttributes** are not yet supported.
- This package is supported and has been tested on Windows®, Linux®, and macOS.

Expand Down
81 changes: 81 additions & 0 deletions api/metrics/+opentelemetry/+metrics/AsynchronousInstrument.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
classdef AsynchronousInstrument < handle
% Base class inherited by all asynchronous instruments

% Copyright 2023-2024 The MathWorks, Inc.

properties (SetAccess=immutable)
Name (1,1) string % Instrument name
Description (1,1) string % Description of instrument
Unit (1,1) string % Measurement unit
end

properties (SetAccess=private)
Callbacks % Callback function, called at each data export
end

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

methods (Access=protected)
function obj = AsynchronousInstrument(proxy, name, description, unit, callback)
obj.Proxy = proxy;
obj.Name = name;
obj.Description = description;
obj.Unit = unit;
obj.Callbacks = callback;
end

end

methods
function addCallback(obj, callback)
% ADDCALLBACK Add a callback function
% ADDCALLBACK(INST, CALLBACK) adds a callback function to
% collect metrics at every export. CALLBACK is specified as a
% function handle, and must accept no input and return one
% output of type opentelemetry.metrics.ObservableResult.
%
% See also REMOVECALLBACK, OPENTELEMETRY.METRICS.OBSERVABLERESULT
if isa(callback, "function_handle")
obj.Proxy.addCallback(callback);
% append to Callbacks property
if isempty(obj.Callbacks)
obj.Callbacks = callback;
elseif isa(obj.Callbacks, "function_handle")
obj.Callbacks = {obj.Callbacks, callback};
else
obj.Callbacks = [obj.Callbacks, {callback}];
end
end
end

function removeCallback(obj, callback)
% REMOVECALLBACK Remove a callback function
% REMOVECALLBACK(INST, CALLBACK) removes a callback function
% CALLBACK specified as a function handle.
%
% See also ADDCALLBACK
if isa(callback, "function_handle") && ~isempty(obj.Callbacks)
if iscell(obj.Callbacks)
found = cellfun(@(x)isequal(x,callback), obj.Callbacks);
else % scalar function handle
found = isequal(obj.Callbacks, callback);
end
if sum(found) > 0
idx = find(found,1); % remove only the first match
obj.Proxy.removeCallback(idx);
% update Callback property
if isa(obj.Callbacks, "function_handle")
obj.Callbacks = [];
else
obj.Callbacks(idx) = [];
if isscalar(obj.Callbacks) % if there is only one left, remove the cell
obj.Callbacks = obj.Callbacks{1};
end
end
end
end
end
end
end
173 changes: 138 additions & 35 deletions api/metrics/+opentelemetry/+metrics/Meter.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
% A Meter creates metric instruments, capturing measurements about a service at runtime.
% Meters are created from Meter Providers.

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

properties (SetAccess=immutable)
Name (1,1) string % Meter name
Expand All @@ -29,7 +29,7 @@

methods

function counter = createCounter(obj, ctname, ctdescription, ctunit)
function counter = createCounter(obj, name, description, unit)
% CREATECOUNTER Create a counter
% C = CREATECOUNTER(M, NAME) creates a counter with the specified
% name. A counter's value can only increase but not
Expand All @@ -38,25 +38,24 @@
% C = CREATECOUNTER(M, NAME, DESCRIPTION, UNIT) also
% specifies a description and a unit.
%
% See also CREATEUPDOWNCOUNTER, CREATEHISTOGRAM
% See also CREATEUPDOWNCOUNTER, CREATEHISTOGRAM,
% CREATEOBSERVABLECOUNTER
arguments
obj
ctname
ctdescription = ""
ctunit = ""
name
description = ""
unit = ""
end
import opentelemetry.common.mustBeScalarString
ctname = mustBeScalarString(ctname);
ctdescription = mustBeScalarString(ctdescription);
ctunit = mustBeScalarString(ctunit);
id = obj.Proxy.createCounter(ctname, ctdescription, ctunit);
[name, description, unit] = processSynchronousInputs(name, ...
description, unit);
id = obj.Proxy.createCounter(name, description, unit);
CounterProxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.CounterProxy", "ID", id);
counter = opentelemetry.metrics.Counter(CounterProxy, ctname, ctdescription, ctunit);
counter = opentelemetry.metrics.Counter(CounterProxy, name, description, unit);
end


function updowncounter = createUpDownCounter(obj, ctname, ctdescription, ctunit)
function updowncounter = createUpDownCounter(obj, name, description, unit)
% CREATEUPDOWNCOUNTER Create an UpDownCounter
% C = CREATEUPDOWNCOUNTER(M, NAME) creates an UpDownCounter
% with the specified name. An UpDownCounter's value can
Expand All @@ -65,26 +64,25 @@
% C = CREATEUPDOWNCOUNTER(M, NAME, DESCRIPTION, UNIT) also
% specifies a description and a unit.
%
% See also CREATECOUNTER, CREATEHISTOGRAM
% See also CREATECOUNTER, CREATEHISTOGRAM,
% CREATEOBSERVABLEUPDOWNCOUNTER
arguments
obj
ctname
ctdescription = ""
ctunit = ""
name
description = ""
unit = ""
end

import opentelemetry.common.mustBeScalarString
ctname = mustBeScalarString(ctname);
ctdescription = mustBeScalarString(ctdescription);
ctunit = mustBeScalarString(ctunit);
id = obj.Proxy.createUpDownCounter(ctname, ctdescription, ctunit);
[name, description, unit] = processSynchronousInputs(name, ...
description, unit);
id = obj.Proxy.createUpDownCounter(name, description, unit);
UpDownCounterProxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.UpDownCounterProxy", "ID", id);
updowncounter = opentelemetry.metrics.UpDownCounter(UpDownCounterProxy, ctname, ctdescription, ctunit);
updowncounter = opentelemetry.metrics.UpDownCounter(UpDownCounterProxy, name, description, unit);
end


function histogram = createHistogram(obj, hiname, hidescription, hiunit)
function histogram = createHistogram(obj, name, description, unit)
% CREATEHISTOGRAM Create a histogram
% H = CREATEHISTOGRAM(M, NAME) creates a histogram with the specified
% name. A histogram aggregates values into bins. Bins can be
Expand All @@ -97,21 +95,126 @@
% OPENTELEMETRY.SDK.METRICS.VIEW
arguments
obj
hiname
hidescription = ""
hiunit = ""
name
description = ""
unit = ""
end

import opentelemetry.common.mustBeScalarString
hiname = mustBeScalarString(hiname);
hidescription = mustBeScalarString(hidescription);
hiunit = mustBeScalarString(hiunit);
id = obj.Proxy.createHistogram(hiname, hidescription, hiunit);
[name, description, unit] = processSynchronousInputs(name, ...
description, unit);
id = obj.Proxy.createHistogram(name, description, unit);
HistogramProxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.HistogramProxy", "ID", id);
histogram = opentelemetry.metrics.Histogram(HistogramProxy, hiname, hidescription, hiunit);
histogram = opentelemetry.metrics.Histogram(HistogramProxy, name, description, unit);
end

end

function obscounter = createObservableCounter(obj, callback, name, description, unit)
% CREATEOBSERVABLECOUNTER Create an observable counter
% C = CREATEOBSERVABLECOUNTER(M, CALLBACK, NAME) creates an
% observable counter with the specified callback function
% and name. The callback function, specified as a
% function handle, must accept no input and return one
% output of type opentelemetry.metrics.ObservableResult.
% The counter's value can only increase but not decrease.
%
% C = CREATEOBSERVABLECOUNTER(M, CALLBACK NAME, DESCRIPTION, UNIT)
% also specifies a description and a unit.
%
% See also OPENTELEMETRY.METRICS.OBSERVABLERESULT,
% CREATEOBSERVABLEUPDOWNCOUNTER, CREATEOBSERVABLEGAUGE, CREATECOUNTER
arguments
obj
callback
name
description = ""
unit = ""
end

[callback, name, description, unit] = processAsynchronousInputs(...
callback, name, description, unit);
id = obj.Proxy.createObservableCounter(name, description, unit, callback);
ObservableCounterproxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.ObservableCounterProxy", "ID", id);
obscounter = opentelemetry.metrics.ObservableCounter(ObservableCounterproxy, name, description, unit, callback);
end

function obsudcounter = createObservableUpDownCounter(obj, callback, name, description, unit)
% CREATEOBSERVABLEUPDOWNCOUNTER Create an observable UpDownCounter
% C = CREATEOBSERVABLEUPDOWNCOUNTER(M, CALLBACK, NAME)
% creates an observable UpDownCounter with the specified
% callback function and name. The callback function,
% specified as a function handle, must accept no input and
% return one output of type opentelemetry.metrics.ObservableResult.
% The UpDownCounter's value can increase or decrease.
%
% C = CREATEOBSERVABLEUPDOWNCOUNTER(M, CALLBACK, NAME, DESCRIPTION, UNIT)
% also specifies a description and a unit.
%
% See also OPENTELEMETRY.METRICS.OBSERVABLERESULT,
% CREATEOBSERVABLECOUNTER, CREATEOBSERVABLEGAUGE, CREATEUPDOWNCOUNTER
arguments
obj
callback
name
description = ""
unit = ""
end

[callback, name, description, unit] = processAsynchronousInputs(...
callback, name, description, unit);
id = obj.Proxy.createObservableUpDownCounter(name, description, unit, callback);
ObservableUpDownCounterproxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.ObservableUpDownCounterProxy", "ID", id);
obsudcounter = opentelemetry.metrics.ObservableUpDownCounter(...
ObservableUpDownCounterproxy, name, description, unit, callback);
end

function obsgauge = createObservableGauge(obj, callback, name, description, unit)
% CREATEOBSERVABLEGAUGE Create an observable gauge
% C = CREATEOBSERVABLEGAUGE(M, CALLBACK, NAME) creates an
% observable gauge with the specified callback function
% and name. The callback function, specified as a
% function handle, must accept no input and return one
% output of type opentelemetry.metrics.ObservableResult.
% A gauge's value can increase or decrease but it should
% never be summed in aggregation.
%
% C = CREATEOBSERVABLEGAUGE(M, CALLBACK NAME, DESCRIPTION, UNIT)
% also specifies a description and a unit.
%
% See also OPENTELEMETRY.METRICS.OBSERVABLERESULT,
% CREATEOBSERVABLECOUNTER, CREATEOBSERVABLEUPDOWNCOUNTER
arguments
obj
callback
name
description = ""
unit = ""
end

[callback, name, description, unit] = processAsynchronousInputs(...
callback, name, description, unit);
id = obj.Proxy.createObservableGauge(name, description, unit, callback);
ObservableGaugeproxy = libmexclass.proxy.Proxy("Name", ...
"libmexclass.opentelemetry.ObservableGaugeProxy", "ID", id);
obsgauge = opentelemetry.metrics.ObservableGauge(...
ObservableGaugeproxy, name, description, unit, callback);
end
end
end

function [name, description, unit] = processSynchronousInputs(name, ...
description, unit)
import opentelemetry.common.mustBeScalarString
name = mustBeScalarString(name);
description = mustBeScalarString(description);
unit = mustBeScalarString(unit);
end

function [callback, name, description, unit] = processAsynchronousInputs(...
callback, name, description, unit)
[name, description, unit] = processSynchronousInputs(name, description, unit);
if ~isa(callback, "function_handle")
callback = []; % callback is invalid, set to empty double
end
end
17 changes: 17 additions & 0 deletions api/metrics/+opentelemetry/+metrics/ObservableCounter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
classdef ObservableCounter < opentelemetry.metrics.AsynchronousInstrument
% ObservableCounter is an asynchronous counter that records its value
% via a callback and its value can only increase but not decrease

% Copyright 2023 The MathWorks, Inc.

methods (Access={?opentelemetry.metrics.Meter})

function obj = ObservableCounter(proxy, name, description, unit, callback)
% Private constructor. Use getObservableCounter method of Meter
% to create observable counters.
obj@opentelemetry.metrics.AsynchronousInstrument(proxy, name, ...
description, unit, callback);
end

end
end
17 changes: 17 additions & 0 deletions api/metrics/+opentelemetry/+metrics/ObservableGauge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
classdef ObservableGauge < opentelemetry.metrics.AsynchronousInstrument
% ObservableGauge is an asynchronous gauge that report its values via a
% callback and its value cannot be summed in aggregation.

% Copyright 2023 The MathWorks, Inc.

methods (Access={?opentelemetry.metrics.Meter})

function obj = ObservableGauge(proxy, name, description, unit, callback)
% Private constructor. Use getObservableGauge method of Meter
% to create observable gauges.
obj@opentelemetry.metrics.AsynchronousInstrument(proxy, name, ...
description, unit, callback);
end

end
end
Loading