From 0267d6283f8372c8cea858cd9f3ad1b313a8d9c6 Mon Sep 17 00:00:00 2001 From: Scott Estabrook Date: Wed, 30 Jul 2025 14:13:17 -0400 Subject: [PATCH 1/3] Added test cases to improve code coverage --- test/callbacks/callbackWithAttributes3.m | 8 ++ test/tformat.m | 147 +++++++++++++++++++++++ test/thttpheaders.m | 147 +++++++++++++++++++++++ test/tjsonbytesmapping.m | 147 +++++++++++++++++++++++ test/tlogs_sdk.m | 26 ++++ test/tmetrics.m | 31 +++++ test/tmetrics_sdk.m | 49 ++++++++ test/ttimeout.m | 147 +++++++++++++++++++++++ test/ttrace_sdk.m | 48 ++++++++ test/tusejsonname.m | 147 +++++++++++++++++++++++ 10 files changed, 897 insertions(+) create mode 100644 test/callbacks/callbackWithAttributes3.m create mode 100644 test/tformat.m create mode 100644 test/thttpheaders.m create mode 100644 test/tjsonbytesmapping.m create mode 100644 test/ttimeout.m create mode 100644 test/tusejsonname.m diff --git a/test/callbacks/callbackWithAttributes3.m b/test/callbacks/callbackWithAttributes3.m new file mode 100644 index 0000000..0756373 --- /dev/null +++ b/test/callbacks/callbackWithAttributes3.m @@ -0,0 +1,8 @@ +function result = callbackWithAttributes3() +% Test callback function for asynchronous instruments that uses attributes +% +% Copyright 2024 The MathWorks, Inc. + +value = 30; +result = opentelemetry.metrics.ObservableResult; +result = result.observe(value, dictionary({"Level1"}, {"D"},{"Level2"},{"E"})); \ No newline at end of file diff --git a/test/tformat.m b/test/tformat.m new file mode 100644 index 0000000..f784e6c --- /dev/null +++ b/test/tformat.m @@ -0,0 +1,147 @@ +classdef tformat < matlab.unittest.TestCase + % tests for setting format in the exporter + + % Copyright 2023-2024 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils folder to the path + utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); + commonSetup(testCase, config); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testNondefaultFormat(testCase) + % testNondefaultFormat: using an alternative format + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "Format", "binary"); + 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 testNondefaultGrpcFormat(testCase) + % testNondefaultGrpcFormat: using an alternative format + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "Format", "binary"); + 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 NondefaultMetricsFormat(testCase) + % testNondefaultMetricsFormat: using an alternative format + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(... + "Format", "binary"); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 4; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + function NondefaultGrpcMetricsFormat(testCase) + % testNondefaultGrpcMetricsFormat: using an alternative format + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcMetricExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter(... + "Format", "binary"); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 8; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + end +end \ No newline at end of file diff --git a/test/thttpheaders.m b/test/thttpheaders.m new file mode 100644 index 0000000..cb99502 --- /dev/null +++ b/test/thttpheaders.m @@ -0,0 +1,147 @@ +classdef thttpheaders < matlab.unittest.TestCase + % tests for setting HTTPHeaders in the exporter + + % Copyright 2023-2024 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils folder to the path + utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); + commonSetup(testCase, config); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testNondefaultHTTPHeaders(testCase) + % testNondefaultHTTPHeaders: using an alternative HTTPHeaders + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "HTTPHeaders", dictionary("new","header")); + 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 testNondefaultGrpcHTTPHeaders(testCase) + % testNondefaultGrpcHTTPHeaders: using an alternative HTTPHeaders + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "HTTPHeaders", dictionary("new","header")); + 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 NondefaultMetricsHTTPHeaders(testCase) + % testNondefaultMetricsHTTPHeaders: using an alternative HTTPHeaders + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(... + "HTTPHeaders", dictionary("new","header")); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 4; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + function NondefaultGrpcMetricsHTTPHeaders(testCase) + % testNondefaultGrpcMetricsHTTPHeaders: using an alternative HTTPHeaders + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcMetricExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter(... + "HTTPHeaders", dictionary("new","header")); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 8; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + end +end \ No newline at end of file diff --git a/test/tjsonbytesmapping.m b/test/tjsonbytesmapping.m new file mode 100644 index 0000000..17ebae6 --- /dev/null +++ b/test/tjsonbytesmapping.m @@ -0,0 +1,147 @@ +classdef tjsonbytesmapping < matlab.unittest.TestCase + % tests for setting JsonBytesMapping in the exporter + + % Copyright 2023-2024 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils folder to the path + utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); + commonSetup(testCase, config); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testNondefaultJsonBytesMapping(testCase) + % testNondefaultJsonBytesMapping: using an alternative JsonBytesMapping + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "JsonBytesMapping", "base64"); + 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 testNondefaultGrpcJsonBytesMapping(testCase) + % testNondefaultGrpcJsonBytesMapping: using an alternative JsonBytesMapping + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "JsonBytesMapping", "base64"); + 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 NondefaultMetricsJsonBytesMapping(testCase) + % testNondefaultMetricsJsonBytesMapping: using an alternative JsonBytesMapping + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(... + "JsonBytesMapping", "base64"); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 4; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + function NondefaultGrpcMetricsJsonBytesMapping(testCase) + % testNondefaultGrpcMetricsJsonBytesMapping: using an alternative JsonBytesMapping + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcMetricExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter(... + "JsonBytesMapping", "base64"); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 8; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + end +end \ No newline at end of file diff --git a/test/tlogs_sdk.m b/test/tlogs_sdk.m index eabe95e..858448d 100644 --- a/test/tlogs_sdk.m +++ b/test/tlogs_sdk.m @@ -76,6 +76,32 @@ function testOtlpFileExporter(testCase) verifyEqual(testCase, string(results.resourceLogs.scopeLogs.logRecords.severityText), upper(logseverity)); verifyEqual(testCase, string(results.resourceLogs.scopeLogs.logRecords.body.stringValue), logmessage); end + + function testHttpExporter(testCase) + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpLogRecordExporter", "class")), ... + "Otlp HTTP Log record exporter must be installed."); + + processor1 = opentelemetry.sdk.logs.SimpleLogRecordProcessor; + + HttpExporter = processor1.LogRecordExporter; + + HttpExporter.Endpoint = "endpoint"; + HttpExporter.Format = "JSON"; + HttpExporter.JsonBytesMapping = "base64"; + HttpExporter.UseJsonName = 1; + HttpExporter.Timeout = seconds(10); + HttpExporter.HttpHeaders = dictionary("new","header"); + + exp = opentelemetry.exporters.otlp.OtlpHttpLogRecordExporter("Endpoint","endpoint","Format","JSON","JsonBytesMapping","base64","UseJsonName",1,"Timeout",seconds(10),"HttpHeaders",dictionary("new","header")); + + % Validate that set methods create a OtlpHttpLogRecordExporter + % with expected properties + HttpExporter = rmfield(struct(HttpExporter),"Proxy"); + exp = rmfield(struct(exp),"Proxy"); + testCase.verifyEqual(HttpExporter,exp); + + + end function testAddLogRecordProcessor(testCase) % testAddLogRecordProcessor: addLogRecordProcessor method diff --git a/test/tmetrics.m b/test/tmetrics.m index 0b11a93..018c074 100644 --- a/test/tmetrics.m +++ b/test/tmetrics.m @@ -747,6 +747,37 @@ function testAsynchronousInstrumentMultipleCallbacks(testCase, create_async, dat verifyEqual(testCase, string(dp(idxC).attributes.value.stringValue), "C"); end + function testAsynchronousInstrumentDictionaryCallback(testCase, create_async, datapoint_name) + % removeCallback method + %callback = @callbackNoAttributes3; + + p = opentelemetry.sdk.metrics.MeterProvider(testCase.ShortIntervalReader); + mt = p.getMeter("foo"); + ct = create_async(mt, @callbackWithAttributes3, "bar", "", "", testCase.CallbackTimeout); + + % wait for collector response + pause(testCase.WaitTime); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter name + verifyEqual(testCase, string(results{1}.resourceMetrics.scopeMetrics.metrics.name), "bar"); + + % verify counter values and attributes + dp = results{1}.resourceMetrics.scopeMetrics.metrics.(datapoint_name).dataPoints.attributes; + attrvals = arrayfun(@(x)string(x.value.stringValue),dp); + idxD = (attrvals == "D"); + idxE = (attrvals == "E"); + verifyEqual(testCase, results{1}.resourceMetrics.scopeMetrics.metrics.(datapoint_name).dataPoints.asDouble, 30); + verifyEqual(testCase, string(dp(idxD).key), "Level1"); + verifyEqual(testCase, string(dp(idxD).value.stringValue), "D"); + verifyEqual(testCase, string(dp(idxE).key), "Level2"); + verifyEqual(testCase, string(dp(idxE).value.stringValue), "E"); + + end + function testAsynchronousInstrumentRemoveCallback(testCase, create_async) % removeCallback method callback = @callbackNoAttributes; diff --git a/test/tmetrics_sdk.m b/test/tmetrics_sdk.m index 57901c5..49dd084 100644 --- a/test/tmetrics_sdk.m +++ b/test/tmetrics_sdk.m @@ -229,6 +229,55 @@ function testViewBasic(testCase) verifyEqual(testCase, dp.asDouble, val); end + function testViewProperties(testCase) + % testViewProperties: check view object changes the name and + % description of output metrics when schema, version, and units + view_name = "counter_view"; + view_description = "view_description"; + view = opentelemetry.sdk.metrics.View(Name=view_name, .... + Description=view_description, InstrumentType="Counter",MeterSchema="http://schema.org",MeterVersion="1.0.0",InstrumentUnit="ms"); + mp = opentelemetry.sdk.metrics.MeterProvider(... + testCase.ShortIntervalReader, View=view); + + m = getMeter(mp, "mymeter", "1.0.0", "http://schema.org"); + c = createCounter(m, "mycounter","Counter for meter with view properties","ms"); + + m2 = getMeter(mp, "mymeter2", "2.0.0", "http://schema.org"); + c2 = createCounter(m2, "mycounter","Counter for meter with different version","ms"); + + m3 = getMeter(mp, "mymeter3","1.0.0","http://notmyschema.org"); + c3 = createCounter(m3, "mycounter","Counter for meter with different schema","ms"); + + m4 = getMeter(mp, "mymeter4", "1.0.0", "http://schema.org"); + c4 = createCounter(m4, "mycounter","Counter for meter with view properties different units","s"); + + m4 = getMeter(mp, "mymeter5", "1.0.0", "http://schema.org"); + u = createUpDownCounter(m4, "updowncounter","UpDownCounter for meter with view properties","ms"); + + + + % add value and attributes + val = 10; + c.add(val); + c2.add(val); + c3.add(val); + c4.add(val); + u.add(val); + + pause(testCase.WaitTime); + + clear mp; + results = readJsonResults(testCase); + results = results{end}; + + % verify view name only on meter with matching properties + verifyEqual(testCase, string(results.resourceMetrics.scopeMetrics(1).metrics.name), view_name); + verifyNotEqual(testCase, string(results.resourceMetrics.scopeMetrics(2).metrics.name), view_name); + verifyNotEqual(testCase, string(results.resourceMetrics.scopeMetrics(3).metrics.name), view_name); + verifyNotEqual(testCase, string(results.resourceMetrics.scopeMetrics(4).metrics.name), view_name); + verifyNotEqual(testCase, string(results.resourceMetrics.scopeMetrics(5).metrics.name), view_name); + end + function testViewHistogram(testCase) % testViewHistogram: Change histogram bins mp = opentelemetry.sdk.metrics.MeterProvider(testCase.ShortIntervalReader); diff --git a/test/ttimeout.m b/test/ttimeout.m new file mode 100644 index 0000000..02ac242 --- /dev/null +++ b/test/ttimeout.m @@ -0,0 +1,147 @@ +classdef ttimeout < matlab.unittest.TestCase + % tests for setting Timeout in the exporter + + % Copyright 2023-2024 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils folder to the path + utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); + commonSetup(testCase, config); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testNondefaultTimeout(testCase) + % testNondefaultTimeout: using an alternative Timeout + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "Timeout", seconds(10)); + 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 testNondefaultGrpcTimeout(testCase) + % testNondefaultGrpcTimeout: using an alternative Timeout + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "Timeout", seconds(10)); + 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 NondefaultMetricsTimeout(testCase) + % testNondefaultMetricsTimeout: using an alternative Timeout + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(... + "Timeout", seconds(10)); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 4; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + function NondefaultGrpcMetricsTimeout(testCase) + % testNondefaultGrpcMetricsTimeout: using an alternative Timeout + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcMetricExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter(... + "Timeout", seconds(10)); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 8; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + end +end \ No newline at end of file diff --git a/test/ttrace_sdk.m b/test/ttrace_sdk.m index 2634a62..4ee110c 100644 --- a/test/ttrace_sdk.m +++ b/test/ttrace_sdk.m @@ -111,6 +111,54 @@ function testAlwaysOnSampler(testCase) verifyEqual(testCase, string(results.resourceSpans.scopeSpans.scope.name), tracername); end + function testParentBasedSampler(testCase) + % testAlwaysOnSampler: should produce all spans + tracername = "tracer - AlwaysOnSampler"; + spanname = "span - AlwaysOnSampler"; + + tp = opentelemetry.sdk.trace.TracerProvider( ... + "Sampler", opentelemetry.sdk.trace.ParentBasedSampler(opentelemetry.sdk.trace.AlwaysOnSampler)); + tr = getTracer(tp, tracername); + sp = startSpan(tr, spanname); + pause(1); + endSpan(sp); + + tracername1 = "tracer - AlwaysOffSampler"; + spanname1 = "span - AlwaysOffSampler"; + tp = opentelemetry.sdk.trace.TracerProvider( ... + "Sampler", opentelemetry.sdk.trace.ParentBasedSampler(opentelemetry.sdk.trace.AlwaysOffSampler)); + tr = getTracer(tp, tracername1); + sp = startSpan(tr, spanname1); + pause(1); + endSpan(sp); + + tracername2 = "tracer - TraceIdRatioBasedSampler"; + spanname2 = "span - TraceIdRatioBasedSampler"; + tp = opentelemetry.sdk.trace.TracerProvider( ... + "Sampler", opentelemetry.sdk.trace.ParentBasedSampler(opentelemetry.sdk.trace.TraceIdRatioBasedSampler(1))); + tr = getTracer(tp, tracername2); + sp = startSpan(tr, spanname2); + pause(1); + endSpan(sp); + + % perform test comparisons + results = readJsonResults(testCase); + + % check span and tracer names of an AlwaysOnSampler + verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.name), spanname); + verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.scope.name), tracername); + + % AlwaysOffSampler should return no results + + % check span and tracer names of an TraceIdRatioBasedSampler + verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.name), spanname2); + verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.scope.name), tracername2); + + % check ParentBasedSampler doesn't accept other inputs + verifyError(testCase, @() opentelemetry.sdk.trace.TracerProvider( ... + "Sampler", opentelemetry.sdk.trace.ParentBasedSampler(opentelemetry.sdk.trace.ParentBasedSampler("not a sampler"))), "MATLAB:validators:mustBeA"); + end + function testTraceIdRatioBasedSampler(testCase) % testTraceIdRatioBasedSampler: filter spans based on a ratio s = opentelemetry.sdk.trace.TraceIdRatioBasedSampler(0); % equivalent to always off diff --git a/test/tusejsonname.m b/test/tusejsonname.m new file mode 100644 index 0000000..015179b --- /dev/null +++ b/test/tusejsonname.m @@ -0,0 +1,147 @@ +classdef tusejsonname < matlab.unittest.TestCase + % tests for setting UseJsonName in the exporter + + % Copyright 2023-2024 The MathWorks, Inc. + + properties + OtelConfigFile + JsonFile + PidFile + OtelcolName + Otelcol + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils folder to the path + utilsfolder = fullfile(fileparts(mfilename('fullpath')), "utils"); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(utilsfolder)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); + commonSetup(testCase, config); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function testNondefaultUseJsonName(testCase) + % testNondefaultUseJsonName: using an alternative UseJsonName + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpSpanExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpHttpSpanExporter(... + "UseJsonName", true); + 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 testNondefaultGrpcUseJsonName(testCase) + % testNondefaultGrpcUseJsonName: using an alternative UseJsonName + + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + tracername = "foo"; + spanname = "bar"; + + exp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter(... + "UseJsonName", true); + 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 NondefaultMetricsUseJsonName(testCase) + % testNondefaultMetricsUseJsonName: using an alternative UseJsonName + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpHttpMetricExporter", "class")), ... + "Otlp HTTP exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpHttpMetricExporter(... + "UseJsonName", true); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 4; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + function NondefaultGrpcMetricsUseJsonName(testCase) + % testNondefaultGrpcMetricsUseJsonName: using an alternative UseJsonName + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcMetricExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + + exp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter(... + "UseJsonName", true); + reader = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(... + exp, "Interval", seconds(2), "Timeout", seconds(1)); + p = opentelemetry.sdk.metrics.MeterProvider(reader); + mt = p.getMeter("foo"); + ct = mt.createCounter("bar"); + + val = 8; + ct.add(val); + pause(2.5); + + % fetch result + clear p; + results = readJsonResults(testCase); + + % verify counter value + verifyEqual(testCase, results{end}.resourceMetrics.scopeMetrics.metrics.sum.dataPoints.asDouble, val); + end + + end +end \ No newline at end of file From 26568fd910dbb7f1178aca07a94f6e36185327b8 Mon Sep 17 00:00:00 2001 From: Scott Estabrook Date: Tue, 7 Oct 2025 13:50:42 -0400 Subject: [PATCH 2/3] Added Code coverage changes to seperate branch --- .github/workflows/build_and_test_full.yml | 7 + .github/workflows/build_and_test_simple.yml | 4 + .github/workflows/create_release.yml | 147 +++++++++++++++++- CMakeLists.txt | 17 +- README.md | 9 +- .../arm64-osx-otel-matlab.cmake | 6 + ...matlab.cmake => x64-osx-otel-matlab.cmake} | 5 + .../x64-windows-otel-matlab.cmake | 5 +- test/callbacks/callbackWithAttributes3.m | 3 +- test/tformat.m | 4 +- test/thttpheaders.m | 2 +- test/tjsonbytesmapping.m | 2 +- test/tmetrics.m | 4 +- test/ttimeout.m | 2 +- test/ttrace_sdk.m | 2 +- test/tusejsonname.m | 2 +- tools/packageMatlabInterface.m | 10 +- vcpkg.json | 7 +- 18 files changed, 207 insertions(+), 31 deletions(-) rename cmake/vcpkg_triplets/{x86_64-osx-otel-matlab.cmake => x64-osx-otel-matlab.cmake} (53%) diff --git a/.github/workflows/build_and_test_full.yml b/.github/workflows/build_and_test_full.yml index 6dae576..a2918d6 100644 --- a/.github/workflows/build_and_test_full.yml +++ b/.github/workflows/build_and_test_full.yml @@ -40,6 +40,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -70,6 +71,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -106,6 +108,8 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + # pin to R2024b because R2025a has an issue that causes a failure when building the context_propagation example + release: R2024b products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -140,9 +144,12 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab + env: + CMAKE_POLICY_VERSION_MINIMUM: 3.5 # required by upb because its cmake requirement is not compatible with cmake 4 run: | cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON -DWITH_OTLP_GRPC=ON -DUSE_BATCH_FOR_MCC=ON -DOTEL_MATLAB_VERSION=${{ needs.get_version.outputs.version }} -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} cmake --build build --config Release --target install diff --git a/.github/workflows/build_and_test_simple.yml b/.github/workflows/build_and_test_simple.yml index 27f4fd5..d703e44 100644 --- a/.github/workflows/build_and_test_simple.yml +++ b/.github/workflows/build_and_test_simple.yml @@ -41,6 +41,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -70,6 +71,8 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + # pin to R2024b because R2025a has an issue that causes a failure when building the context_propagation example + release: R2024b products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -104,6 +107,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index b3202b9..f4f17aa 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -18,6 +18,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -46,6 +47,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab @@ -79,9 +81,12 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: + release: R2025a products: MATLAB_Compiler - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab + env: + CMAKE_POLICY_VERSION_MINIMUM: 3.5 # required by upb because its cmake requirement is not compatible with cmake 4 run: | cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_GRPC=ON -DWITH_OTLP_FILE=ON -DOTEL_MATLAB_VERSION=${{ github.ref_name }} -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} cmake --build build --config Release --target install @@ -93,6 +98,98 @@ jobs: with: name: otel-matlab-${{ matrix.os }}.tar.gz path: ${{ github.workspace }}/otel-matlab-${{ matrix.os }}.tar.gz + nogrpc-ubuntu: # without gRPC exporter + runs-on: ubuntu-22.04 + env: + OPENTELEMETRY_MATLAB_INSTALL: "${{ github.workspace }}/otel_matlab_install" + steps: + - name: Download OpenTelemetry-Matlab source + uses: actions/checkout@v3 + with: + path: opentelemetry-matlab + - name: Install ninja-build + run: sudo apt-get install ninja-build + - name: Install MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: R2025a + products: MATLAB_Compiler + - name: Build OpenTelemetry-Matlab + working-directory: opentelemetry-matlab + run: | + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_GRPC=OFF -DWITH_OTLP_FILE=ON -DOTEL_MATLAB_VERSION=${{ github.ref_name }} -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} + cmake --build build --config Release --target install + - name: Compress into single artifact + working-directory: ${{ github.workspace }} + run: tar -czf otel-matlab-nogrpc-ubuntu.tar.gz otel_matlab_install + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: otel-matlab-nogrpc-ubuntu.tar.gz + path: ${{ github.workspace }}/otel-matlab-nogrpc-ubuntu.tar.gz + nogrpc-windows: # without gRPC exporter + runs-on: windows-latest + env: + OPENTELEMETRY_MATLAB_INSTALL: "${{ github.workspace }}/otel_matlab_install" + steps: + - name: Download OpenTelemetry-Matlab source + uses: actions/checkout@v3 + with: + path: opentelemetry-matlab + - name: Install ninja-build + run: choco install ninja + - name: Install MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: R2025a + products: MATLAB_Compiler + - name: Build OpenTelemetry-Matlab + working-directory: opentelemetry-matlab + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 + cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_GRPC=OFF -DWITH_OTLP_FILE=ON -DFETCH_VCPKG=ON -DOTEL_MATLAB_VERSION=${{ github.ref_name }} -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} + cmake --build build --config Release --target install + - name: Compress into single artifact + working-directory: ${{ github.workspace }} + run: tar -czf otel-matlab-nogrpc-windows.tar.gz otel_matlab_install + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: otel-matlab-nogrpc-windows.tar.gz + path: ${{ github.workspace }}/otel-matlab-nogrpc-windows.tar.gz + nogrpc-macos: # without gRPC exporter + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-13, macos-14] # runs on Mac with both Intel (macos-13) and Apple Silicon (macos-14) processors + env: + OPENTELEMETRY_MATLAB_INSTALL: "${{ github.workspace }}/otel_matlab_install" + steps: + - name: Download OpenTelemetry-Matlab source + uses: actions/checkout@v3 + with: + path: opentelemetry-matlab + - name: Install ninja-build + run: brew install ninja + - name: Install MATLAB + uses: matlab-actions/setup-matlab@v2 + with: + release: R2025a + products: MATLAB_Compiler + - name: Build OpenTelemetry-Matlab + working-directory: opentelemetry-matlab + run: | + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP_GRPC=OFF -DWITH_OTLP_FILE=ON -DOTEL_MATLAB_VERSION=${{ github.ref_name }} -DCMAKE_INSTALL_PREFIX=${{ env.OPENTELEMETRY_MATLAB_INSTALL }} + cmake --build build --config Release --target install + - name: Compress into single artifact + working-directory: ${{ github.workspace }} + run: tar -czf otel-matlab-nogrpc-${{ matrix.os }}.tar.gz otel_matlab_install + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: otel-matlab-nogrpc-${{ matrix.os }}.tar.gz + path: ${{ github.workspace }}/otel-matlab-nogrpc-${{ matrix.os }}.tar.gz package-mltbx: name: Package MATLAB Toolbox (MLTBX) Files runs-on: ubuntu-22.04 @@ -102,8 +199,13 @@ jobs: - build-ubuntu - build-windows - build-macos + - nogrpc-ubuntu + - nogrpc-windows + - nogrpc-macos env: OPENTELEMETRY_MATLAB_INSTALL: "${{ github.workspace }}/otel_matlab_install" + WITHGRPC_FOLDER: "${{ github.workspace }}/withgrpc" + NOGRPC_FOLDER: "${{ github.workspace }}/nogrpc" steps: - name: Checkout OpenTelemetry-Matlab uses: actions/checkout@v3 @@ -115,26 +217,61 @@ jobs: path: artifacts-downloaded - name: Decompress Artifacts run: | - mv artifacts-downloaded/*/*.tar.gz . + mkdir $WITHGRPC_FOLDER + cd $WITHGRPC_FOLDER + mv ../artifacts-downloaded/*/otel-matlab-ubuntu.tar.gz . + mv ../artifacts-downloaded/*/otel-matlab-macos*.tar.gz . + mv ../artifacts-downloaded/*/otel-matlab-windows.tar.gz . tar -xzvf otel-matlab-ubuntu.tar.gz tar -xzvf otel-matlab-macos-13.tar.gz tar -xzvf otel-matlab-macos-14.tar.gz tar -xzvf otel-matlab-windows.tar.gz + - name: Decompress Artifacts without gRPC exporter + run: | + mkdir $NOGRPC_FOLDER + cd $NOGRPC_FOLDER + mv ../artifacts-downloaded/*/*nogrpc*.tar.gz . + tar -xzvf otel-matlab-nogrpc-ubuntu.tar.gz + tar -xzvf otel-matlab-nogrpc-macos-13.tar.gz + tar -xzvf otel-matlab-nogrpc-macos-14.tar.gz + tar -xzvf otel-matlab-nogrpc-windows.tar.gz - name: Install MATLAB - uses: matlab-actions/setup-matlab@v1 - - name: Run commands + uses: matlab-actions/setup-matlab@v2 + with: + release: R2025a + - name: Package Toolbox + env: + MATLABPATH: OpenTelemetry-Matlab/tools + WORKING_FOLDER: ${{ env.WITHGRPC_FOLDER }} + OTEL_MATLAB_TOOLBOX_FOLDER: otel_matlab_install + OTEL_MATLAB_TOOLBOX_OUTPUT_FOLDER: ${{ env.WITHGRPC_FOLDER }} + OTEL_MATLAB_TOOLBOX_VERSION: ${{ github.ref_name }} + OTEL_MATLAB_TOOLBOX_NAME: otel-matlab + uses: matlab-actions/run-command@v1 + with: + command: packageMatlabInterface + - name: Package Toolbox without gRPC exporter env: MATLABPATH: OpenTelemetry-Matlab/tools + WORKING_FOLDER: ${{ env.NOGRPC_FOLDER }} OTEL_MATLAB_TOOLBOX_FOLDER: otel_matlab_install - OTEL_MATLAB_TOOLBOX_OUTPUT_FOLDER: + OTEL_MATLAB_TOOLBOX_OUTPUT_FOLDER: ${{ env.NOGRPC_FOLDER }} OTEL_MATLAB_TOOLBOX_VERSION: ${{ github.ref_name }} + OTEL_MATLAB_TOOLBOX_NAME: otel-matlab-nogrpc uses: matlab-actions/run-command@v1 with: command: packageMatlabInterface + - name: Compress Toolbox without gRPC exporter + working-directory: ${{ env.NOGRPC_FOLDER }} + run: tar -czf otel-matlab-nogrpc.mltbx.tar.gz otel-matlab-nogrpc.mltbx - name: Create release uses: softprops/action-gh-release@v2 + # Upload toolbox without gRPC exporter in compressed format + # This is to avoid multiple .mltbx files, which will cause issues in MATLAB File Exchange with: - files: ./otel-matlab.mltbx + files: | + ${{ env.WITHGRPC_FOLDER }}/otel-matlab.mltbx + ${{ env.NOGRPC_FOLDER }}/otel-matlab-nogrpc.mltbx.tar.gz name: "Version ${{ github.ref_name }}" fail_on_unmatched_files: true generate_release_notes: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e782fa..f0b0af3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ else() set(VCPKG_FETCH_CONTENT_NAME vcpkg) set(VCPKG_GIT_REPOSITORY "https://github.com/microsoft/vcpkg.git") - set(VCPKG_GIT_TAG "f7423ee") + set(VCPKG_GIT_TAG "ce613c4") FetchContent_Declare( ${VCPKG_FETCH_CONTENT_NAME} GIT_REPOSITORY ${VCPKG_GIT_REPOSITORY} @@ -56,7 +56,11 @@ if(APPLE) # use libcurl as a shared library and load the MATLAB version at runtime # run uname -m to determine whether arm64 or x86_64 exec_program(uname ARGS -m OUTPUT_VARIABLE MAC_HOST_SYSTEM) - set(VCPKG_OTEL_TRIPLET ${MAC_HOST_SYSTEM}-osx-otel-matlab) + if(${MAC_HOST_SYSTEM} STREQUAL "arm64") + set(VCPKG_OTEL_TRIPLET "arm64-osx-otel-matlab") + elseif(${MAC_HOST_SYSTEM} STREQUAL "x86_64") + set(VCPKG_OTEL_TRIPLET "x64-osx-otel-matlab") + endif() set(VCPKG_OVERLAY_TRIPLETS ${CMAKE_SOURCE_DIR}/cmake/vcpkg_triplets) set(VCPKG_TARGET_TRIPLET ${VCPKG_OTEL_TRIPLET}) set(TRIPLET_DEFINITIONS -DVCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}) @@ -117,7 +121,7 @@ set(LIBMEXCLASS_FETCH_CONTENT_NAME libmexclass) set(LIBMEXCLASS_FETCH_CONTENT_GIT_REPOSITORY "https://github.com/mathworks/libmexclass.git") -set(LIBMEXCLASS_FETCH_CONTENT_GIT_TAG "bc2b9f2") +set(LIBMEXCLASS_FETCH_CONTENT_GIT_TAG "3a3b581") set(LIBMEXCLASS_FETCH_CONTENT_SOURCE_SUBDIR "libmexclass/cpp") @@ -419,7 +423,7 @@ if(WIN32) if(WITH_OTLP_HTTP) set(OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} - $) + $) endif() if(WITH_OTLP_GRPC) @@ -428,10 +432,8 @@ if(WIN32) set(OPENSSL_DLL libssl-3-x64.dll) set(OPENSSL_CRYPTO_DLL libcrypto-3-x64.dll) set(OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} - $ $/../bin/${OPENSSL_DLL} - $/../bin/${OPENSSL_CRYPTO_DLL} - $) + $/../bin/${OPENSSL_CRYPTO_DLL}) endif() else() set(OTEL_CPP_RUNTIME ${OTEL_CPP_PREFIX}/lib/libopentelemetry_proto${CMAKE_SHARED_LIBRARY_SUFFIX}) @@ -544,3 +546,4 @@ install(FILES ${OPENTELEMETRY_PROXY_RUNTIME_LIBRARIES} DESTINATION ${LIBMEXCLASS if(WITH_EXAMPLES) add_subdirectory(examples) endif() + diff --git a/README.md b/README.md index 23ff4cf..2a0f8e8 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,13 @@ MATLAB® interface to [OpenTelemetry™](https://opentelemetry.io/), based on the [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/). 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 -- Tracing, metrics, and logs are all fully supported. -- Supported and tested on Windows®, Linux®, and macOS. +1. Tracing, metrics, and logs are all fully supported and tested on Windows®, Linux®, and macOS. +2. On Linux, when running MATLAB R2025a or newer releases, the gRPC library used in the gRPC exporter in this package may conflict with the same library in MATLAB, causing MATLAB to crash. To avoid this issue, install otel-matlab-nogrpc.mltbx in [version 1.10.2](https://github.com/mathworks/OpenTelemetry-MATLAB/releases/tag/1.10.2), which excludes the gRPC exporter. +3. If you are using [version 1.10.2](https://github.com/mathworks/OpenTelemetry-MATLAB/releases/tag/1.10.2) on Linux with MATLAB R2024a or older, you may run into an "Invalid MEX file" error due to an incompatible libstdc++ library. Define the environment variable LD_PRELOAD to point to the system libstdc++.so. For example, use the following command in a Bash shell on Ubuntu or Debian Linux. +``` +export LD_PRELOAD="/lib/x86_64-linux-gnu/libstdc++.so.6" +``` +4. OpenTelemetry currently does not support non-ASCII characters. Defining names or attributes with non-ASCII characters will cause the telemetry data to become invalid and fail to export. ### MathWorks Products (https://www.mathworks.com) diff --git a/cmake/vcpkg_triplets/arm64-osx-otel-matlab.cmake b/cmake/vcpkg_triplets/arm64-osx-otel-matlab.cmake index 4e8c1d0..52210d2 100644 --- a/cmake/vcpkg_triplets/arm64-osx-otel-matlab.cmake +++ b/cmake/vcpkg_triplets/arm64-osx-otel-matlab.cmake @@ -5,6 +5,12 @@ if(${PORT} MATCHES "(curl|zlib)") else() set(VCPKG_LIBRARY_LINKAGE static) endif() +# disable the script to fix rpath for curl, which makes an undesirable change to +# the install name from @rpath/libcurl.4.dylib to @rpath/libcurl.4.8.0.dylib +if(${PORT} MATCHES "curl") + set(VCPKG_FIXUP_MACHO_RPATH OFF) +endif() + set(VCPKG_CMAKE_SYSTEM_NAME Darwin) set(VCPKG_OSX_ARCHITECTURES arm64) diff --git a/cmake/vcpkg_triplets/x86_64-osx-otel-matlab.cmake b/cmake/vcpkg_triplets/x64-osx-otel-matlab.cmake similarity index 53% rename from cmake/vcpkg_triplets/x86_64-osx-otel-matlab.cmake rename to cmake/vcpkg_triplets/x64-osx-otel-matlab.cmake index e33a495..c2bedaa 100644 --- a/cmake/vcpkg_triplets/x86_64-osx-otel-matlab.cmake +++ b/cmake/vcpkg_triplets/x64-osx-otel-matlab.cmake @@ -5,6 +5,11 @@ if(${PORT} MATCHES "(curl|zlib)") else() set(VCPKG_LIBRARY_LINKAGE static) endif() +# disable the script to fix rpath for curl, which makes an undesirable change to +# the install name from @rpath/libcurl.4.dylib to @rpath/libcurl.4.8.0.dylib +if(${PORT} MATCHES "curl") + set(VCPKG_FIXUP_MACHO_RPATH OFF) +endif() set(VCPKG_CMAKE_SYSTEM_NAME Darwin) set(VCPKG_OSX_ARCHITECTURES x86_64) diff --git a/cmake/vcpkg_triplets/x64-windows-otel-matlab.cmake b/cmake/vcpkg_triplets/x64-windows-otel-matlab.cmake index 215e1d2..9cb381d 100644 --- a/cmake/vcpkg_triplets/x64-windows-otel-matlab.cmake +++ b/cmake/vcpkg_triplets/x64-windows-otel-matlab.cmake @@ -1,7 +1,8 @@ set(VCPKG_TARGET_ARCHITECTURE x64) set(VCPKG_CRT_LINKAGE dynamic) -# Conflict with abseil_dll.dll used by Simulink. Use static library to avoid conflict. -if(${PORT} MATCHES "abseil") +# Conflict with abseil_dll.dll used by Simulink. cares.dll and re2.dll are also shipped with MATLAB. +# Use static libraries to avoid conflict. +if(${PORT} MATCHES "(abseil|c-ares|re2)") set(VCPKG_LIBRARY_LINKAGE static) else() set(VCPKG_LIBRARY_LINKAGE dynamic) diff --git a/test/callbacks/callbackWithAttributes3.m b/test/callbacks/callbackWithAttributes3.m index 0756373..64ac130 100644 --- a/test/callbacks/callbackWithAttributes3.m +++ b/test/callbacks/callbackWithAttributes3.m @@ -1,7 +1,8 @@ function result = callbackWithAttributes3() % Test callback function for asynchronous instruments that uses attributes +% in a dictionary % -% Copyright 2024 The MathWorks, Inc. +% Copyright 2025 The MathWorks, Inc. value = 30; result = opentelemetry.metrics.ObservableResult; diff --git a/test/tformat.m b/test/tformat.m index f784e6c..ae8b19a 100644 --- a/test/tformat.m +++ b/test/tformat.m @@ -1,7 +1,7 @@ classdef tformat < matlab.unittest.TestCase - % tests for setting format in the exporter + % Tests for setting format in the exporter - % Copyright 2023-2024 The MathWorks, Inc. + % Copyright 2025 The MathWorks, Inc. properties OtelConfigFile diff --git a/test/thttpheaders.m b/test/thttpheaders.m index cb99502..72bb218 100644 --- a/test/thttpheaders.m +++ b/test/thttpheaders.m @@ -1,7 +1,7 @@ classdef thttpheaders < matlab.unittest.TestCase % tests for setting HTTPHeaders in the exporter - % Copyright 2023-2024 The MathWorks, Inc. + % Copyright 2025 The MathWorks, Inc. properties OtelConfigFile diff --git a/test/tjsonbytesmapping.m b/test/tjsonbytesmapping.m index 17ebae6..1532de4 100644 --- a/test/tjsonbytesmapping.m +++ b/test/tjsonbytesmapping.m @@ -1,7 +1,7 @@ classdef tjsonbytesmapping < matlab.unittest.TestCase % tests for setting JsonBytesMapping in the exporter - % Copyright 2023-2024 The MathWorks, Inc. + % Copyright 2025 The MathWorks, Inc. properties OtelConfigFile diff --git a/test/tmetrics.m b/test/tmetrics.m index 018c074..620e89c 100644 --- a/test/tmetrics.m +++ b/test/tmetrics.m @@ -748,9 +748,7 @@ function testAsynchronousInstrumentMultipleCallbacks(testCase, create_async, dat end function testAsynchronousInstrumentDictionaryCallback(testCase, create_async, datapoint_name) - % removeCallback method - %callback = @callbackNoAttributes3; - + % Test for attributes in a dictionary p = opentelemetry.sdk.metrics.MeterProvider(testCase.ShortIntervalReader); mt = p.getMeter("foo"); ct = create_async(mt, @callbackWithAttributes3, "bar", "", "", testCase.CallbackTimeout); diff --git a/test/ttimeout.m b/test/ttimeout.m index 02ac242..d3eacc5 100644 --- a/test/ttimeout.m +++ b/test/ttimeout.m @@ -1,7 +1,7 @@ classdef ttimeout < matlab.unittest.TestCase % tests for setting Timeout in the exporter - % Copyright 2023-2024 The MathWorks, Inc. + % Copyright 2025 The MathWorks, Inc. properties OtelConfigFile diff --git a/test/ttrace_sdk.m b/test/ttrace_sdk.m index 4ee110c..b1cc3b7 100644 --- a/test/ttrace_sdk.m +++ b/test/ttrace_sdk.m @@ -112,7 +112,7 @@ function testAlwaysOnSampler(testCase) end function testParentBasedSampler(testCase) - % testAlwaysOnSampler: should produce all spans + % testParentBasedSampler: should produce all spans tracername = "tracer - AlwaysOnSampler"; spanname = "span - AlwaysOnSampler"; diff --git a/test/tusejsonname.m b/test/tusejsonname.m index 015179b..a382380 100644 --- a/test/tusejsonname.m +++ b/test/tusejsonname.m @@ -1,7 +1,7 @@ classdef tusejsonname < matlab.unittest.TestCase % tests for setting UseJsonName in the exporter - % Copyright 2023-2024 The MathWorks, Inc. + % Copyright 2025 The MathWorks, Inc. properties OtelConfigFile diff --git a/tools/packageMatlabInterface.m b/tools/packageMatlabInterface.m index 3ea1a62..6416c87 100644 --- a/tools/packageMatlabInterface.m +++ b/tools/packageMatlabInterface.m @@ -5,11 +5,16 @@ % the resulting .mltbx file location is taken from environment variable % OTEL_MATLAB_TOOLBOX_OUTPUT_FOLDER. -% Copyright 2024 The MathWorks, Inc. +% Copyright 2024-2025 The MathWorks, Inc. toolboxFolder = string(getenv("OTEL_MATLAB_TOOLBOX_FOLDER")); outputFolder = string(getenv("OTEL_MATLAB_TOOLBOX_OUTPUT_FOLDER")); toolboxVersion = string(getenv("OTEL_MATLAB_TOOLBOX_VERSION")); +workingFolder = string(getenv("WORKING_FOLDER")); +toolboxName = string(getenv("OTEL_MATLAB_TOOLBOX_NAME")); + +% cd to working folder +cd(workingFolder); % Output folder must exist. mkdir(outputFolder); @@ -17,6 +22,7 @@ disp("Toolbox Folder: " + toolboxFolder); disp("Output Folder: " + outputFolder); disp("Toolbox Version:" + toolboxVersion); +disp("Toolbox Name:" + toolboxName); identifier = "dc2cae2f-4f43-4d2c-b6ed-f1a59f0dfcdf"; opts = matlab.addons.toolbox.ToolboxOptions(toolboxFolder, identifier); @@ -33,6 +39,6 @@ opts.MinimumMatlabRelease = "R2022a"; -opts.OutputFile = fullfile(outputFolder, "otel-matlab.mltbx"); +opts.OutputFile = fullfile(outputFolder, toolboxName + ".mltbx"); disp("Output File: " + opts.OutputFile); matlab.addons.toolbox.packageToolbox(opts); diff --git a/vcpkg.json b/vcpkg.json index 22b6db0..a45c1fd 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -20,10 +20,13 @@ "abseil", "c-ares", "re2", - "openssl", + { + "name": "openssl", + "version>=": "3.5.0" + }, "upb" ] } }, - "builtin-baseline": "638b1588be3a265a9c7ad5b212cef72a1cad336a" + "builtin-baseline": "f7423ee180c4b7f40d43402c2feb3859161ef625" } From a06bb448e29730f87cef551c01aa7833229f6058 Mon Sep 17 00:00:00 2001 From: Scott Estabrook Date: Thu, 9 Oct 2025 13:35:08 -0400 Subject: [PATCH 3/3] Address PR Feedback --- test/callbacks/callbackWithAttributes3.m | 2 +- test/tformat.m | 3 +-- test/thttpheaders.m | 3 +-- test/tjsonbytesmapping.m | 3 +-- test/ttimeout.m | 3 +-- test/tusejsonname.m | 3 +-- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/test/callbacks/callbackWithAttributes3.m b/test/callbacks/callbackWithAttributes3.m index 64ac130..3281d1a 100644 --- a/test/callbacks/callbackWithAttributes3.m +++ b/test/callbacks/callbackWithAttributes3.m @@ -6,4 +6,4 @@ value = 30; result = opentelemetry.metrics.ObservableResult; -result = result.observe(value, dictionary({"Level1"}, {"D"},{"Level2"},{"E"})); \ No newline at end of file +result = result.observe(value, dictionary("Level1", "D","Level2","E")); \ No newline at end of file diff --git a/test/tformat.m b/test/tformat.m index ae8b19a..1ffdaec 100644 --- a/test/tformat.m +++ b/test/tformat.m @@ -27,8 +27,7 @@ function setupOnce(testCase) methods (TestMethodSetup) function setup(testCase) - config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); - commonSetup(testCase, config); + commonSetup(testCase); end end diff --git a/test/thttpheaders.m b/test/thttpheaders.m index 72bb218..87bccfe 100644 --- a/test/thttpheaders.m +++ b/test/thttpheaders.m @@ -27,8 +27,7 @@ function setupOnce(testCase) methods (TestMethodSetup) function setup(testCase) - config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); - commonSetup(testCase, config); + commonSetup(testCase); end end diff --git a/test/tjsonbytesmapping.m b/test/tjsonbytesmapping.m index 1532de4..b56f7a6 100644 --- a/test/tjsonbytesmapping.m +++ b/test/tjsonbytesmapping.m @@ -27,8 +27,7 @@ function setupOnce(testCase) methods (TestMethodSetup) function setup(testCase) - config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); - commonSetup(testCase, config); + commonSetup(testCase); end end diff --git a/test/ttimeout.m b/test/ttimeout.m index d3eacc5..d188402 100644 --- a/test/ttimeout.m +++ b/test/ttimeout.m @@ -27,8 +27,7 @@ function setupOnce(testCase) methods (TestMethodSetup) function setup(testCase) - config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); - commonSetup(testCase, config); + commonSetup(testCase); end end diff --git a/test/tusejsonname.m b/test/tusejsonname.m index a382380..b9f6eb9 100644 --- a/test/tusejsonname.m +++ b/test/tusejsonname.m @@ -27,8 +27,7 @@ function setupOnce(testCase) methods (TestMethodSetup) function setup(testCase) - config = fullfile(fileparts(mfilename("fullpath")), "config", "otelcol_config.yml"); - commonSetup(testCase, config); + commonSetup(testCase); end end