From e1b8944a9d03ad31d7991c3db0b1fbfa88034829 Mon Sep 17 00:00:00 2001 From: duncanpo Date: Wed, 4 Sep 2024 14:08:28 -0400 Subject: [PATCH] Change sdk class constructors to accept N-V pairs without first argument, closes #22 --- .../+sdk/+logs/BatchLogRecordProcessor.m | 42 +++--- .../+sdk/+logs/LoggerProvider.m | 98 ++++++++------ .../+sdk/+metrics/MeterProvider.m | 124 ++++++++++-------- .../+metrics/PeriodicExportingMetricReader.m | 44 ++++--- .../+sdk/+trace/BatchSpanProcessor.m | 45 ++++--- .../+sdk/+trace/TracerProvider.m | 121 +++++++++-------- test/tlogs_sdk.m | 5 +- test/tmetrics_sdk.m | 4 +- test/ttrace_sdk.m | 52 ++++++-- 9 files changed, 315 insertions(+), 220 deletions(-) diff --git a/sdk/logs/+opentelemetry/+sdk/+logs/BatchLogRecordProcessor.m b/sdk/logs/+opentelemetry/+sdk/+logs/BatchLogRecordProcessor.m index dedda78..b82ff03 100644 --- a/sdk/logs/+opentelemetry/+sdk/+logs/BatchLogRecordProcessor.m +++ b/sdk/logs/+opentelemetry/+sdk/+logs/BatchLogRecordProcessor.m @@ -10,7 +10,7 @@ end methods - function obj = BatchLogRecordProcessor(exporter, optionnames, optionvalues) + function obj = BatchLogRecordProcessor(varargin) % Batch log record processor creates batches of log records and passes them to an exporter. % BLP = OPENTELEMETRY.SDK.LOGS.BATCHLOGRECORDPROCESSOR creates a % batch log record processor that uses an OTLP HTTP exporter, which @@ -20,7 +20,7 @@ % the log record exporter. Supported log record exporters are OTLP HTTP % exporter and OTLP gRPC exporter. % - % BLP = OPENTELEMETRY.SDK.LOGS.BATCHLOGRECORDPROCESSOR(EXP, PARAM1, + % BLP = OPENTELEMETRY.SDK.LOGS.BATCHLOGRECORDPROCESSOR(..., PARAM1, % VALUE1, PARAM2, VALUE2, ...) specifies optional parameter % name/value pairs. Parameters are: % "MaximumQueueSize" - Maximum queue size. After queue @@ -35,24 +35,18 @@ % OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPLOGRECORDEXPORTER, % OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCLOGRECORDEXPORTER, % OPENTELEMETRY.SDK.LOGS.LOGGERPROVIDER - arguments - exporter {mustBeA(exporter, "opentelemetry.sdk.logs.LogRecordExporter")} = ... - opentelemetry.exporters.otlp.defaultLogRecordExporter() - end - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues + + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.logs.LogRecordExporter") + exporter = opentelemetry.exporters.otlp.defaultLogRecordExporter; + else % isa(varargin{1}, "opentelemetry.sdk.logs.LogRecordExporter") + exporter = varargin{1}; + varargin(1) = []; end obj = obj@opentelemetry.sdk.logs.LogRecordProcessor(exporter, ... "libmexclass.opentelemetry.sdk.BatchLogRecordProcessorProxy"); - validnames = ["MaximumQueueSize", "ScheduledDelay", "MaximumExportBatchSize"]; - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - obj.(namei) = valuei; - end + obj = obj.processOptions(varargin{:}); end function obj = set.MaximumQueueSize(obj, maxqsz) @@ -86,4 +80,22 @@ obj.MaximumExportBatchSize = maxbatch; end end + + methods(Access=private) + function obj = processOptions(obj, optionnames, optionvalues) + arguments + obj + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + validnames = ["MaximumQueueSize", "ScheduledDelay", "MaximumExportBatchSize"]; + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + obj.(namei) = valuei; + end + end + end end diff --git a/sdk/logs/+opentelemetry/+sdk/+logs/LoggerProvider.m b/sdk/logs/+opentelemetry/+sdk/+logs/LoggerProvider.m index 43a45bc..007d6d3 100644 --- a/sdk/logs/+opentelemetry/+sdk/+logs/LoggerProvider.m +++ b/sdk/logs/+opentelemetry/+sdk/+logs/LoggerProvider.m @@ -14,7 +14,7 @@ end methods - function obj = LoggerProvider(processor, optionnames, optionvalues) + function obj = LoggerProvider(varargin) % SDK implementation of logger provider % LP = OPENTELEMETRY.SDK.LOGS.LOGGERPROVIDER creates a logger % provider that uses a simple log record processor and default configurations. @@ -22,7 +22,7 @@ % LP = OPENTELEMETRY.SDK.LOGS.LOGGERPROVIDER(P) uses log record % processor P. P can be a simple or batched log record processor. % - % LP = OPENTELEMETRY.SDK.LOGS.LOGGERPROVIDER(R, PARAM1, VALUE1, + % LP = OPENTELEMETRY.SDK.LOGS.LOGGERPROVIDER(..., PARAM1, VALUE1, % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. % Parameters are: % "Resource" - Additional resource attributes. @@ -31,60 +31,28 @@ % See also OPENTELEMETRY.SDK.LOGS.SIMPLELOGRECORDPROCESSOR, % OPENTELEMETRY.SDK.LOGS.BATCHLOGRECORDPROCESSOR - arguments - processor {mustBeA(processor, ["opentelemetry.sdk.logs.LogRecordProcessor", ... - "libmexclass.proxy.Proxy"])} = ... - opentelemetry.sdk.logs.SimpleLogRecordProcessor() - end - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues - end - % explicit call to superclass constructor to make it a no-op obj@opentelemetry.logs.LoggerProvider("skip"); - if isa(processor, "libmexclass.proxy.Proxy") + if nargin == 1 && isa(varargin{1}, "libmexclass.proxy.Proxy") % This code branch is used to support conversion from API % LoggerProvider to SDK equivalent, needed internally by % opentelemetry.sdk.logs.Cleanup - lpproxy = processor; % rename the variable + lpproxy = varargin{1}; assert(lpproxy.Name == "libmexclass.opentelemetry.LoggerProviderProxy"); obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.LoggerProviderProxy", ... "ConstructorArguments", {lpproxy.ID}); % leave other properties unassigned, they won't be used else - validnames = "Resource"; - resourcekeys = string.empty(); - resourcevalues = {}; - - resource = dictionary(resourcekeys, resourcevalues); - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - if strcmp(namei, "Resource") - if ~isa(valuei, "dictionary") - error("opentelemetry:sdk:logs:LoggerProvider:InvalidResourceType", ... - "Resource input must be a dictionary."); - end - resource = valuei; - resourcekeys = keys(valuei); - resourcevalues = values(valuei,"cell"); - % collapse one level of cells, as this may be due to - % a behavior of dictionary.values - if all(cellfun(@iscell, resourcevalues)) - resourcevalues = [resourcevalues{:}]; - end - end + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.logs.LogRecordProcessor") + processor = opentelemetry.sdk.logs.SimpleLogRecordProcessor(); % default processor + else + processor = varargin{1}; + varargin(1) = []; end - - obj.Proxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.sdk.LoggerProviderProxy", ... - "ConstructorArguments", {processor.Proxy.ID, resourcekeys, ... - resourcevalues}); - obj.LogRecordProcessor = processor; - obj.Resource = resource; + obj.processOptions(processor, varargin{:}); + end end @@ -139,4 +107,48 @@ function addLogRecordProcessor(obj, processor) end end end + + methods(Access=private) + function processOptions(obj, processor, optionnames, optionvalues) + arguments + obj + processor + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + + validnames = "Resource"; + resourcekeys = string.empty(); + resourcevalues = {}; + + resource = dictionary(resourcekeys, resourcevalues); + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + if strcmp(namei, "Resource") + if ~isa(valuei, "dictionary") + error("opentelemetry:sdk:logs:LoggerProvider:InvalidResourceType", ... + "Resource input must be a dictionary."); + end + resource = valuei; + resourcekeys = keys(valuei); + resourcevalues = values(valuei,"cell"); + % collapse one level of cells, as this may be due to + % a behavior of dictionary.values + if all(cellfun(@iscell, resourcevalues)) + resourcevalues = [resourcevalues{:}]; + end + end + end + + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.LoggerProviderProxy", ... + "ConstructorArguments", {processor.Proxy.ID, resourcekeys, ... + resourcevalues}); + obj.LogRecordProcessor = processor; + obj.Resource = resource; + end + end end diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m index 2fe30c8..0bd63ad 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/MeterProvider.m @@ -2,7 +2,7 @@ % An SDK implementation of meter provider, which stores a set of configurations used % in a metrics system. - % Copyright 2023 The MathWorks, Inc. + % Copyright 2023-2024 The MathWorks, Inc. properties(Access=private) isShutdown (1,1) logical = false @@ -15,7 +15,7 @@ end methods - function obj = MeterProvider(reader, optionnames, optionvalues) + function obj = MeterProvider(varargin) % SDK implementation of meter provider % MP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER creates a meter % provider that uses a periodic exporting metric reader and default configurations. @@ -24,7 +24,7 @@ % reader R. Currently, the only supported metric reader is the periodic % exporting metric reader. % - % TP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(R, PARAM1, VALUE1, + % TP = OPENTELEMETRY.SDK.METRICS.METERPROVIDER(..., PARAM1, VALUE1, % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. % Parameters are: % "View" - View object used to customize collected metrics. @@ -34,73 +34,27 @@ % See also OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER % OPENTELEMETRY.SDK.METRICS.VIEW - arguments - reader {mustBeA(reader, ["opentelemetry.sdk.metrics.PeriodicExportingMetricReader", ... - "libmexclass.proxy.Proxy"])} = ... - opentelemetry.sdk.metrics.PeriodicExportingMetricReader() - end - - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues - end - % explicit call to superclass constructor to make it a no-op obj@opentelemetry.metrics.MeterProvider("skip"); - if isa(reader, "libmexclass.proxy.Proxy") + if nargin == 1 && isa(varargin{1}, "libmexclass.proxy.Proxy") % This code branch is used to support conversion from API % MeterProvider to SDK equivalent, needed internally by % opentelemetry.sdk.metrics.Cleanup - mpproxy = reader; % rename the variable + mpproxy = varargin{1}; assert(mpproxy.Name == "libmexclass.opentelemetry.MeterProviderProxy"); obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... "ConstructorArguments", {mpproxy.ID}); % leave other properties unassigned, they won't be used else - validnames = ["Resource", "View"]; - resourcekeys = string.empty(); - resourcevalues = {}; - resource = dictionary(resourcekeys, resourcevalues); - suppliedview = false; - viewid = 0; - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - if strcmp(namei, "Resource") - if ~isa(valuei, "dictionary") - error("opentelemetry:sdk:metrics:MeterProvider:InvalidResourceType", ... - "Resource input must be a dictionary."); - end - resource = valuei; - resourcekeys = keys(valuei); - resourcevalues = values(valuei,"cell"); - % collapse one level of cells, as this may be due to - % a behavior of dictionary.values - if all(cellfun(@iscell, resourcevalues)) - resourcevalues = [resourcevalues{:}]; - end - elseif strcmp(namei, "View") - suppliedview = true; - view = valuei; - if ~isa(view, "opentelemetry.sdk.metrics.View") - error("opentelemetry:sdk:metrics:MeterProvider:InvalidViewType", ... - "View input must be a opentelemetry.sdk.metrics.View object."); - end - viewid = view.Proxy.ID; - end - end - - obj.Proxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... - "ConstructorArguments", {reader.Proxy.ID, resourcekeys, ... - resourcevalues, suppliedview, viewid}); - obj.MetricReader = reader; - obj.Resource = resource; - if suppliedview - obj.View = view; + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.metrics.PeriodicExportingMetricReader") + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(); % default metric reader + else + reader = varargin{1}; + varargin(1) = []; end + obj.processOptions(reader, varargin{:}); end end @@ -167,4 +121,60 @@ function addView(obj, view) end end + + methods(Access=private) + function processOptions(obj, reader, optionnames, optionvalues) + arguments + obj + reader + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + + validnames = ["Resource", "View"]; + resourcekeys = string.empty(); + resourcevalues = {}; + resource = dictionary(resourcekeys, resourcevalues); + suppliedview = false; + viewid = 0; + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + if strcmp(namei, "Resource") + if ~isa(valuei, "dictionary") + error("opentelemetry:sdk:metrics:MeterProvider:InvalidResourceType", ... + "Resource input must be a dictionary."); + end + resource = valuei; + resourcekeys = keys(valuei); + resourcevalues = values(valuei,"cell"); + % collapse one level of cells, as this may be due to + % a behavior of dictionary.values + if all(cellfun(@iscell, resourcevalues)) + resourcevalues = [resourcevalues{:}]; + end + elseif strcmp(namei, "View") + suppliedview = true; + view = valuei; + if ~isa(view, "opentelemetry.sdk.metrics.View") + error("opentelemetry:sdk:metrics:MeterProvider:InvalidViewType", ... + "View input must be a opentelemetry.sdk.metrics.View object."); + end + viewid = view.Proxy.ID; + end + end + + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.MeterProviderProxy", ... + "ConstructorArguments", {reader.Proxy.ID, resourcekeys, ... + resourcevalues, suppliedview, viewid}); + obj.MetricReader = reader; + obj.Resource = resource; + if suppliedview + obj.View = view; + end + end + end end diff --git a/sdk/metrics/+opentelemetry/+sdk/+metrics/PeriodicExportingMetricReader.m b/sdk/metrics/+opentelemetry/+sdk/+metrics/PeriodicExportingMetricReader.m index 545e39f..d3d38ec 100644 --- a/sdk/metrics/+opentelemetry/+sdk/+metrics/PeriodicExportingMetricReader.m +++ b/sdk/metrics/+opentelemetry/+sdk/+metrics/PeriodicExportingMetricReader.m @@ -2,7 +2,7 @@ % Periodic exporting metric reader passes collected metrics to an exporter % periodically at a fixed time interval. -% Copyright 2023 The MathWorks, Inc. +% Copyright 2023-2024 The MathWorks, Inc. properties (GetAccess=?opentelemetry.sdk.metrics.MeterProvider) Proxy % Proxy object to interface C++ code @@ -18,7 +18,7 @@ end methods - function obj = PeriodicExportingMetricReader(metricexporter, optionnames, optionvalues) + function obj = PeriodicExportingMetricReader(varargin) % Periodic exporting metric reader passes collected metrics to % an exporter periodically at a fixed time interval. % R = OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER @@ -31,7 +31,7 @@ % are OTLP HTTP exporter and OTLP gRPC exporter. % % R = OPENTELEMETRY.SDK.METRICS.PERIODICEXPORTINGMETRICREADER( - % EXP, PARAM1, VALUE1, PARAM2, VALUE2, ...) specifies + % ..., PARAM1, VALUE1, PARAM2, VALUE2, ...) specifies % optional parameter name/value pairs. Parameters are: % "Interval" - Time interval between exports specified as % a duration. Default is 1 minute. @@ -42,25 +42,19 @@ % See also OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPMETRICEXPORTER, % OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCMETRICEXPORTER, % OPENTELEMETRY.SDK.METRICS.METERPROVIDER - arguments - metricexporter {mustBeA(metricexporter, "opentelemetry.sdk.metrics.MetricExporter")} = ... - opentelemetry.exporters.otlp.defaultMetricExporter() - end - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues + + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.metrics.MetricExporter") + metricexporter = opentelemetry.exporters.otlp.defaultMetricExporter; + else % isa(varargin{1}, "opentelemetry.sdk.metrics.MetricExporter") + metricexporter = varargin{1}; + varargin(1) = []; end obj.Proxy = libmexclass.proxy.Proxy("Name", "libmexclass.opentelemetry.sdk.PeriodicExportingMetricReaderProxy" , ... "ConstructorArguments", {metricexporter.Proxy.ID}); obj.MetricExporter = metricexporter; - validnames = ["Interval", "Timeout"]; - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - obj.(namei) = valuei; - end + obj = obj.processOptions(varargin{:}); end function obj = set.Interval(obj, interval) @@ -82,4 +76,22 @@ obj.Timeout = timeout; end end + + methods(Access=private) + function obj = processOptions(obj, optionnames, optionvalues) + arguments + obj + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + validnames = ["Interval", "Timeout"]; + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + obj.(namei) = valuei; + end + end + end end diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m b/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m index 3101dd3..b511f6d 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/BatchSpanProcessor.m @@ -1,7 +1,7 @@ classdef BatchSpanProcessor < opentelemetry.sdk.trace.SpanProcessor % Batch span processor creates batches of spans and passes them to an exporter. -% Copyright 2023 The MathWorks, Inc. +% Copyright 2023-2024 The MathWorks, Inc. properties MaximumQueueSize (1,1) double = 2048 % Maximum queue size. After queue size is reached, spans are dropped. @@ -10,7 +10,7 @@ end methods - function obj = BatchSpanProcessor(spanexporter, optionnames, optionvalues) + function obj = BatchSpanProcessor(varargin) % Batch span processor creates batches of spans and passes them to an exporter. % BSP = OPENTELEMETRY.SDK.TRACE.BATCHSPANPROCESSOR creates a % batch span processor that uses an OTLP HTTP exporter, which @@ -20,7 +20,7 @@ % the span exporter. Supported span exporters are OTLP HTTP % exporter and OTLP gRPC exporter. % - % BSP = OPENTELEMETRY.SDK.TRACE.BATCHSPANPROCESSOR(EXP, PARAM1, + % BSP = OPENTELEMETRY.SDK.TRACE.BATCHSPANPROCESSOR(..., PARAM1, % VALUE1, PARAM2, VALUE2, ...) specifies optional parameter % name/value pairs. Parameters are: % "MaximumQueueSize" - Maximum queue size. After queue @@ -35,24 +35,17 @@ % OPENTELEMETRY.EXPORTERS.OTLP.OTLPHTTPSPANEXPORTER, % OPENTELEMETRY.EXPORTERS.OTLP.OTLPGRPCSPANEXPORTER, % OPENTELEMETRY.SDK.TRACE.TRACERPROVIDER - arguments - spanexporter {mustBeA(spanexporter, "opentelemetry.sdk.trace.SpanExporter")} = ... - opentelemetry.exporters.otlp.defaultSpanExporter() + + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.trace.SpanExporter") + spanexporter = opentelemetry.exporters.otlp.defaultSpanExporter; + else % isa(varargin{1}, "opentelemetry.sdk.trace.SpanExporter") + spanexporter = varargin{1}; + varargin(1) = []; end - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues - end - obj = obj@opentelemetry.sdk.trace.SpanProcessor(spanexporter, ... "libmexclass.opentelemetry.sdk.BatchSpanProcessorProxy"); - validnames = ["MaximumQueueSize", "ScheduledDelay", "MaximumExportBatchSize"]; - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - obj.(namei) = valuei; - end + obj = obj.processOptions(varargin{:}); end function obj = set.MaximumQueueSize(obj, maxqsz) @@ -86,4 +79,22 @@ obj.MaximumExportBatchSize = maxbatch; end end + + methods(Access=private) + function obj = processOptions(obj, optionnames, optionvalues) + arguments + obj + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + validnames = ["MaximumQueueSize", "ScheduledDelay", "MaximumExportBatchSize"]; + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + obj.(namei) = valuei; + end + end + end end diff --git a/sdk/trace/+opentelemetry/+sdk/+trace/TracerProvider.m b/sdk/trace/+opentelemetry/+sdk/+trace/TracerProvider.m index c15d508..00ef472 100644 --- a/sdk/trace/+opentelemetry/+sdk/+trace/TracerProvider.m +++ b/sdk/trace/+opentelemetry/+sdk/+trace/TracerProvider.m @@ -2,7 +2,7 @@ % An SDK implementation of tracer provider, which stores a set of configurations used % in a distributed tracing system. - % Copyright 2023 The MathWorks, Inc. + % Copyright 2023-2024 The MathWorks, Inc. properties(Access=private) isShutdown (1,1) logical = false @@ -15,7 +15,7 @@ end methods - function obj = TracerProvider(processor, optionnames, optionvalues) + function obj = TracerProvider(varargin) % SDK implementation of tracer provider % TP = OPENTELEMETRY.SDK.TRACE.TRACERPROVIDER creates a tracer % provider that uses a simple span processor and default configurations. @@ -23,7 +23,7 @@ % TP = OPENTELEMETRY.SDK.TRACE.TRACERPROVIDER(P) uses span % processor P. P can be a simple or batched span processor. % - % TP = OPENTELEMETRY.SDK.TRACE.TRACERPROVIDER(P, PARAM1, VALUE1, + % TP = OPENTELEMETRY.SDK.TRACE.TRACERPROVIDER(..., PARAM1, VALUE1, % PARAM2, VALUE2, ...) specifies optional parameter name/value pairs. % Parameters are: % "Sampler" - Sampling policy. Default is always on. @@ -37,25 +37,14 @@ % OPENTELEMETRY.SDK.TRACE.TRACEIDRATIOBASEDSAMPLER, % OPENTELEMETRY.SDK.TRACE.PARENTBASEDSAMPLER - arguments - processor {mustBeA(processor, ["opentelemetry.sdk.trace.SpanProcessor", ... - "libmexclass.proxy.Proxy"])} = ... - opentelemetry.sdk.trace.SimpleSpanProcessor() - end - - arguments (Repeating) - optionnames (1,:) {mustBeTextScalar} - optionvalues - end - % explicit call to superclass constructor to make it a no-op obj@opentelemetry.trace.TracerProvider("skip"); - if isa(processor, "libmexclass.proxy.Proxy") + if nargin == 1 && isa(varargin{1}, "libmexclass.proxy.Proxy") % This code branch is used to support conversion from API % TracerProvider to SDK equivalent, needed internally by % opentelemetry.sdk.trace.Cleanup - tpproxy = processor; % rename the variable + tpproxy = varargin{1}; assert(tpproxy.Name == "libmexclass.opentelemetry.TracerProviderProxy"); obj.Proxy = libmexclass.proxy.Proxy("Name", ... "libmexclass.opentelemetry.sdk.TracerProviderProxy", ... @@ -63,47 +52,14 @@ % leave other properties unassigned, they won't be used else % Code branch for construction from inputs - validnames = ["Sampler", "Resource"]; - foundsampler = false; - resourcekeys = string.empty(); - resourcevalues = {}; - resource = dictionary(resourcekeys, resourcevalues); - for i = 1:length(optionnames) - namei = validatestring(optionnames{i}, validnames); - valuei = optionvalues{i}; - if strcmp(namei, "Sampler") - if ~isa(valuei, "opentelemetry.sdk.trace.Sampler") - error("opentelemetry:sdk:trace:TracerProvider:InvalidSamplerType", ... - "Sampler must be an instance of one of the sampler classes"); - end - sampler = valuei; - foundsampler = true; - else % "Resource" - if ~isa(valuei, "dictionary") - error("opentelemetry:sdk:trace:TracerProvider:InvalidResourceType", ... - "Attibutes input must be a dictionary."); - end - resource = valuei; - resourcekeys = keys(valuei); - resourcevalues = values(valuei,"cell"); - % collapse one level of cells, as this may be due to - % a behavior of dictionary.values - if all(cellfun(@iscell, resourcevalues)) - resourcevalues = [resourcevalues{:}]; - end - end + if nargin == 0 || ~isa(varargin{1}, "opentelemetry.sdk.trace.SpanProcessor") + processor = opentelemetry.sdk.trace.SimpleSpanProcessor(); % default span processor + else + processor = varargin{1}; + varargin(1) = []; end - if ~foundsampler - sampler = opentelemetry.sdk.trace.AlwaysOnSampler; - end - obj.Proxy = libmexclass.proxy.Proxy("Name", ... - "libmexclass.opentelemetry.sdk.TracerProviderProxy", ... - "ConstructorArguments", {processor.Proxy.ID, sampler.Proxy.ID, ... - resourcekeys, resourcevalues}); - obj.SpanProcessor = processor; - obj.Sampler = sampler; - obj.Resource = resource; - end + obj.processOptions(processor, varargin{:}); + end end function addSpanProcessor(obj, processor) @@ -158,4 +114,57 @@ function addSpanProcessor(obj, processor) end end end + + methods(Access=private) + function processOptions(obj, processor, optionnames, optionvalues) + arguments + obj + processor + end + arguments (Repeating) + optionnames (1,:) {mustBeTextScalar} + optionvalues + end + validnames = ["Sampler", "Resource"]; + foundsampler = false; + resourcekeys = string.empty(); + resourcevalues = {}; + resource = dictionary(resourcekeys, resourcevalues); + for i = 1:length(optionnames) + namei = validatestring(optionnames{i}, validnames); + valuei = optionvalues{i}; + if strcmp(namei, "Sampler") + if ~isa(valuei, "opentelemetry.sdk.trace.Sampler") + error("opentelemetry:sdk:trace:TracerProvider:InvalidSamplerType", ... + "Sampler must be an instance of one of the sampler classes"); + end + sampler = valuei; + foundsampler = true; + else % "Resource" + if ~isa(valuei, "dictionary") + error("opentelemetry:sdk:trace:TracerProvider:InvalidResourceType", ... + "Attibutes input must be a dictionary."); + end + resource = valuei; + resourcekeys = keys(valuei); + resourcevalues = values(valuei,"cell"); + % collapse one level of cells, as this may be due to + % a behavior of dictionary.values + if all(cellfun(@iscell, resourcevalues)) + resourcevalues = [resourcevalues{:}]; + end + end + end + if ~foundsampler + sampler = opentelemetry.sdk.trace.AlwaysOnSampler; + end + obj.Proxy = libmexclass.proxy.Proxy("Name", ... + "libmexclass.opentelemetry.sdk.TracerProviderProxy", ... + "ConstructorArguments", {processor.Proxy.ID, sampler.Proxy.ID, ... + resourcekeys, resourcevalues}); + obj.SpanProcessor = processor; + obj.Sampler = sampler; + obj.Resource = resource; + end + end end diff --git a/test/tlogs_sdk.m b/test/tlogs_sdk.m index d56b771..4248bb4 100644 --- a/test/tlogs_sdk.m +++ b/test/tlogs_sdk.m @@ -101,7 +101,7 @@ function testAddLogRecordProcessor(testCase) function testBatchLogRecordProcessor(testCase) % testBatchLogRecordProcessor: setting properties of - % BatchRecordProcessor + % BatchLogRecordProcessor loggername = "foo"; logseverity = "debug"; logbody = "bar"; @@ -138,8 +138,7 @@ function testCustomResource(testCase) % emitted log record customkeys = ["foo" "bar"]; customvalues = [1 5]; - lp = opentelemetry.sdk.logs.LoggerProvider(opentelemetry.sdk.logs.SimpleLogRecordProcessor, ... - "Resource", dictionary(customkeys, customvalues)); + lp = opentelemetry.sdk.logs.LoggerProvider("Resource", dictionary(customkeys, customvalues)); lg = getLogger(lp, "baz"); emitLogRecord(lg, "debug", "qux"); diff --git a/test/tmetrics_sdk.m b/test/tmetrics_sdk.m index 2915f16..a4f7091 100644 --- a/test/tmetrics_sdk.m +++ b/test/tmetrics_sdk.m @@ -27,7 +27,6 @@ function setupOnce(testCase) interval = seconds(2); timeout = seconds(1); testCase.ShortIntervalReader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... - opentelemetry.exporters.otlp.defaultMetricExporter(), ... "Interval", interval, "Timeout", timeout); testCase.WaitTime = seconds(interval * 1.25); end @@ -119,10 +118,9 @@ function testDefaultReader(testCase) function testReaderBasic(testCase) - exporter = opentelemetry.exporters.otlp.defaultMetricExporter; interval = hours(1); timeout = minutes(30); - reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(exporter, ... + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader( ... "Interval", interval, ... "Timeout", timeout); verifyEqual(testCase, reader.Interval, interval); diff --git a/test/ttrace_sdk.m b/test/ttrace_sdk.m index 20c81e2..48abd03 100644 --- a/test/ttrace_sdk.m +++ b/test/ttrace_sdk.m @@ -14,6 +14,7 @@ ExtractPid Sigint Sigterm + ForceFlushTimeout end methods (TestClassSetup) @@ -22,6 +23,7 @@ function setupOnce(testCase) utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); commonSetupOnce(testCase); + testCase.ForceFlushTimeout = seconds(2); end end @@ -38,10 +40,45 @@ function teardown(testCase) end methods (Test) + function testBatchSpanProcessor(testCase) + % testBatchSpanProcessor: setting properties of + % BatchSpanProcessor + tracername = "foo"; + spanname = "bar"; + + queuesize = 500; + delay = seconds(2); + batchsize = 50; + b = opentelemetry.sdk.trace.BatchSpanProcessor(... + MaximumQueueSize=queuesize, ... + ScheduledDelay=delay, ... + MaximumExportBatchSize=batchsize); + tp = opentelemetry.sdk.trace.TracerProvider(b); + tr = getTracer(tp, tracername); + sp = startSpan(tr, spanname); + pause(1); + endSpan(sp); + + % verify batch properties set correctly + verifyEqual(testCase, b.MaximumQueueSize, queuesize); + verifyEqual(testCase, b.ScheduledDelay, delay); + verifyEqual(testCase, b.MaximumExportBatchSize, batchsize) + verifyEqual(testCase, class(b.SpanExporter), ... + class(opentelemetry.exporters.otlp.defaultSpanExporter)); + + % perform test comparisons + forceFlush(tp, testCase.ForceFlushTimeout); + 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 tp = opentelemetry.sdk.trace.TracerProvider( ... - opentelemetry.sdk.trace.SimpleSpanProcessor, ... "Sampler", opentelemetry.sdk.trace.AlwaysOffSampler); tr = getTracer(tp, "mytracer"); sp = startSpan(tr, "myspan"); @@ -59,7 +96,6 @@ function testAlwaysOnSampler(testCase) spanname = "bar"; tp = opentelemetry.sdk.trace.TracerProvider( ... - opentelemetry.sdk.trace.SimpleSpanProcessor, ... "Sampler", opentelemetry.sdk.trace.AlwaysOnSampler); tr = getTracer(tp, tracername); sp = startSpan(tr, spanname); @@ -81,8 +117,7 @@ function testTraceIdRatioBasedSampler(testCase) tracername = "mytracer"; offspan = "offspan"; - tp = opentelemetry.sdk.trace.TracerProvider( ... - opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tp = opentelemetry.sdk.trace.TracerProvider("Sampler", s); tr = getTracer(tp, tracername); sp = startSpan(tr, offspan); pause(1); @@ -90,8 +125,7 @@ function testTraceIdRatioBasedSampler(testCase) s.Ratio = 1; % equivalent to always on onspan = "onspan"; - tp = opentelemetry.sdk.trace.TracerProvider( ... - opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tp = opentelemetry.sdk.trace.TracerProvider("Sampler", s); tr = getTracer(tp, tracername); sp = startSpan(tr, onspan); pause(1); @@ -100,8 +134,7 @@ function testTraceIdRatioBasedSampler(testCase) s.Ratio = 0.5; % filter half of the spans sampledspan = "sampledspan"; numspans = 10; - tp = opentelemetry.sdk.trace.TracerProvider( ... - opentelemetry.sdk.trace.SimpleSpanProcessor, "Sampler", s); + tp = opentelemetry.sdk.trace.TracerProvider("Sampler", s); tr = getTracer(tp, tracername); for i = 1:numspans sp = startSpan(tr, sampledspan + i); @@ -168,8 +201,7 @@ function testCustomResource(testCase) % emitted spans customkeys = ["foo" "bar"]; customvalues = [1 5]; - tp = opentelemetry.sdk.trace.TracerProvider(opentelemetry.sdk.trace.SimpleSpanProcessor, ... - "Resource", dictionary(customkeys, customvalues)); + tp = opentelemetry.sdk.trace.TracerProvider("Resource", dictionary(customkeys, customvalues)); tr = getTracer(tp, "mytracer"); sp = startSpan(tr, "myspan"); pause(1);