Skip to content

Commit

Permalink
feat: metric_exporter, metric_reader
Browse files Browse the repository at this point in the history
draft version of metric reader and exporter
  • Loading branch information
ricktu-mw committed Sep 25, 2023
1 parent f59aa11 commit f6a1c55
Show file tree
Hide file tree
Showing 12 changed files with 561 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
classdef OtlpGrpcMetricExporter < opentelemetry.sdk.metrics.MetricExporter
% OtlpGrpcMetricExporter exports Metrics in OpenTelemetry Protocol format via
% gRPC. By default, it exports to the default address of the OpenTelemetry
% Collector.

% 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
end

methods
function obj = OtlpGrpcMetricExporter(optionnames, optionvalues)
% OtlpGrpcMetricExporter exports Metrics in OpenTelemetry Protocol format via gRPC.
% EXP = OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCMetricEXPORTER
% creates an exporter that uses default configurations.
%
% EXP =
% OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCMetricEXPORTER(PARAM1,
% VALUE1, PARAM2, VALUE2, ...) specifies optional parameter
% name/value pairs. Parameters are:
% "Endpoint" - Endpoint to export to
% "UseCredentials" - Whether to use SSL credentials.
% Default is false. If true, use
% .pem file specified in
% "CertificatePath" or
% "CertificateString".
% "CertificatePath" - Path to .pem file for SSL encryption
% "CertificateString" - .pem file specified in memory as
% a string
% "Timeout" - Maximum time above which exports
% will abort
% "HTTPHeaders" - Additional HTTP Headers
%
% See also OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPMetricEXPORTER
arguments (Repeating)
optionnames (1,:) {mustBeTextScalar}
optionvalues
end

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:OtlpGrpcMetricExporter: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:OtlpGrpcMetricExporter: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:OtlpGrpcMetricExporter: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:OtlpGrpcMetricExporter:CertificateStringNotScalarText", "CertificateString must be a scalar string.");
end
certificatestring = string(valuei);
elseif strcmp(namei, "Timeout")
if ~(isduration(valuei) && isscalar(valuei))
error("opentelemetry:exporters:otlp:OtlpGrpcMetricExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration.");
end
timeout = valuei;
timeout_millis = milliseconds(timeout);
else % HttpHeaders
if ~isa(valuei, "dictionary")
error("opentelemetry:exporters:otlp:OtlpGrpcMetricExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary.");
end
httpheaders = valuei;
headerkeys = keys(valuei);
headervalues = values(valuei);
if ~isstring(headervalues)
error("opentelemetry:exporters:otlp:OtlpGrpcMetricExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.")
end
end
end

obj = [email protected](...
"libmexclass.opentelemetry.exporters.OtlpGrpcMetricExporterProxy", ...
endpoint, usessl, certificatepath, certificatestring, ...
timeout_millis, headerkeys, headervalues);

% 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;
end
if certificatestring == "" % not specified, use default value
obj.CertificateString = defaultcertstring;
else
obj.CertificateString = certificatestring;
end
if timeout_millis < 0 % not specified, use default value
obj.Timeout = milliseconds(defaultmillis);
else
obj.Timeout = timeout;
end
if isempty(headerkeys) % not specified, return empty dictionary
obj.HttpHeaders = dictionary(headerkeys, headervalues);
else
obj.HttpHeaders = httpheaders;
end

end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
classdef OtlpHttpMetricExporter < opentelemetry.sdk.metrics.MetricExporter
% OtlpHttpMetricExporter exports Metrics in OpenTelemetry Protocol format via
% HTTP. By default, it exports to the default address of the OpenTelemetry
% Collector.

% 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
end

methods
function obj = OtlpHttpMetricExporter(optionnames, optionvalues)
% OtlpHttpMetricExporter exports Metrics in OpenTelemetry Protocol format via HTTP.
% EXP = OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPMetricEXPORTER
% creates an exporter that uses default configurations.
%
% EXP =
% OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPMetricEXPORTER(PARAM1,
% VALUE1, PARAM2, VALUE2, ...) specifies optional parameter
% name/value pairs. Parameters are:
% "Endpoint" - Endpoint to export to
% "Format" - Data format: "JSON" (default) or "binary"
% "JsonBytesMapping" - What to convert JSON bytes to. Supported
% values are "hex", "hexId" (default), and
% "base64". Default "hexId"
% converts to base 64 except for IDs
% which are converted to hexadecimals.
% "UseJsonName" - Whether to use JSON name of protobuf
% field to set the key of JSON
% "Timeout" - Maximum time above which exports
% will abort
% "HTTPHeaders" - Additional HTTP Headers
%
% See also OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCMetricEXPORTER
arguments (Repeating)
optionnames (1,:) {mustBeTextScalar}
optionvalues
end

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:OtlpHttpMetricExporter: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:OtlpHttpMetricExporter:UseJsonNameNotScalarLogical", "UseJsonName must be a scalar logical.")
end
usejsonname = logical(valuei);
elseif strcmp(namei, "Timeout")
if ~(isduration(valuei) && isscalar(valuei))
error("opentelemetry:exporters:otlp:OtlpHttpMetricExporter:TimeoutNotScalarDuration", "Timeout must be a scalar duration.");
end
timeout = valuei;
timeout_millis = milliseconds(timeout);
else % HttpHeaders
if ~isa(valuei, "dictionary")
error("opentelemetry:exporters:otlp:OtlpHttpMetricExporter:HttpHeadersNotDictionary", "HttpHeaders input must be a dictionary.");
end
httpheaders = valuei;
headerkeys = keys(valuei);
headervalues = values(valuei);
if ~isstring(headervalues)
error("opentelemetry:exporters:otlp:OtlpHttpMetricExporter:HttpHeadersNonStringValues", "HttpHeaders dictionary values must be strings.")
end
end
end

obj = [email protected](...
"libmexclass.opentelemetry.exporters.OtlpHttpMetricExporterProxy", ...
endpoint, dataformat, jsonbytesmapping, usejsonname, ...
timeout_millis, headerkeys, headervalues);

% populate immutable properties
if endpoint == "" || dataformat == "" || jsonbytesmapping == "" || ...
timeout_millis < 0
[defaultendpoint, defaultformat, defaultmapping, defaultmillis] = ...
getDefaultOptionValues(obj);
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;
end
if jsonbytesmapping == "" % not specified, use default value
obj.JsonBytesMapping = defaultmapping;
else
obj.JsonBytesMapping = jsonbytesmapping;
end
obj.UseJsonName = usejsonname;
if timeout_millis < 0 % not specified, use default value
obj.Timeout = milliseconds(defaultmillis);
else
obj.Timeout = timeout;
end
if isempty(headerkeys) % not specified, return empty dictionary
obj.HttpHeaders = dictionary(headerkeys, headervalues);
else
obj.HttpHeaders = httpheaders;
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function dexp = defaultMetricExporter
% Get the default Metric exporter depending on installation
% EXP = OPENTELEMETRY.EXPORTERS.OTLP.DEFAULTMetricEXPORTER returns the
% default Metric exporter. OtlpHttpMetricExporter is the default if it is
% installed. Otherwise, OtlpGrpcMetricExporter is the default.
%
% See also OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPMetricEXPORTER,
% OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCMetricEXPORTER

% Copyright 2023 The MathWorks, Inc.

if exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")
dexp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter;
else
dexp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter;
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 The MathWorks, Inc.

#pragma once

#include "opentelemetry-matlab/sdk/metrics/MetricExporterProxy.h"

#include "libmexclass/proxy/Proxy.h"
#include "libmexclass/proxy/method/Context.h"

#include "opentelemetry/sdk/metrics/push_metric_exporter.h"
#include "opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h"

This comment has been minimized.

Copy link
@ricktu-mw

ricktu-mw Sep 25, 2023

Author Member

change to #include "opentelemetry/exporters/otlp/otlp_grpc_metric_exporter_options.h"


namespace metric_sdk = opentelemetry::sdk::metric;
namespace otlp_exporter = opentelemetry::exporter::otlp;

namespace libmexclass::opentelemetry::exporters {
class OtlpGrpcMetricExporterProxy: public libmexclass::opentelemetry::sdk::MetricExporterProxy {
public:
OtlpGrpcMetricExporterProxy(otlp_exporter::OtlpGrpcExporterOptions options) : CppOptions(options) {
REGISTER_METHOD(OtlpGrpcMetricExporterProxy, getDefaultOptionValues);
}

static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments);

std::unique_ptr<metric_sdk::MetricExporter> getInstance() override;

void getDefaultOptionValues(libmexclass::proxy::method::Context& context);

private:
otlp_exporter::OtlpGrpcExporterOptions CppOptions;
};
} // namespace libmexclass::opentelemetry
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 The MathWorks, Inc.

#pragma once

#include "opentelemetry-matlab/sdk/metrics/MetricExporterProxy.h"

#include "libmexclass/proxy/Proxy.h"
#include "libmexclass/proxy/method/Context.h"

#include "opentelemetry/sdk/metrics/push_metric_exporter.h"
#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"

namespace metric_sdk = opentelemetry::sdk::metrics;
namespace otlp_exporter = opentelemetry::exporter::otlp;

namespace libmexclass::opentelemetry::exporters {
class OtlpHttpMetricExporterProxy: public libmexclass::opentelemetry::sdk::MetricExporterProxy {
public:
OtlpHttpMetricExporterProxy(otlp_exporter::OtlpHttpExporterOptions options) : CppOptions(options) {
REGISTER_METHOD(OtlpHttpMetricExporterProxy, getDefaultOptionValues);
}

static libmexclass::proxy::MakeResult make(const libmexclass::proxy::FunctionArguments& constructor_arguments);

std::unique_ptr<metric_sdk::PushMetricExporter> getInstance() override;

void getDefaultOptionValues(libmexclass::proxy::method::Context& context);

private:
otlp_exporter::OtlpHttpExporterOptions CppOptions;
};
} // namespace libmexclass::opentelemetry
Loading

0 comments on commit f6a1c55

Please sign in to comment.