From 6014966c51f240a5a6a52ef96f08b827a1ef3e07 Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Mon, 12 Jan 2026 13:26:53 -0500 Subject: [PATCH 1/9] Initial plugin draft --- .../+plugins/OpenTelemetryPluginService.m | 15 + .../+buildtool/+plugins/OpenTelemetryPlugin.m | 198 +++++++ test/tbuildtoolplugin.m | 533 ++++++++++++++++++ 3 files changed, 746 insertions(+) create mode 100644 instrumentation/buildtool/+matlab/+buildtool/+internal/+services/+plugins/OpenTelemetryPluginService.m create mode 100644 instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m create mode 100644 test/tbuildtoolplugin.m diff --git a/instrumentation/buildtool/+matlab/+buildtool/+internal/+services/+plugins/OpenTelemetryPluginService.m b/instrumentation/buildtool/+matlab/+buildtool/+internal/+services/+plugins/OpenTelemetryPluginService.m new file mode 100644 index 0000000..1a36bce --- /dev/null +++ b/instrumentation/buildtool/+matlab/+buildtool/+internal/+services/+plugins/OpenTelemetryPluginService.m @@ -0,0 +1,15 @@ +classdef OpenTelemetryPluginService < matlab.buildtool.internal.services.plugins.BuildRunnerPluginService + % This class is unsupported and might change or be removed without notice + % in a future version. + + % Copyright 2026 The MathWorks, Inc. + + methods + function plugins = providePlugins(~, ~) + plugins = matlab.buildtool.plugins.BuildRunnerPlugin.empty(1,0); + if ~isMATLABReleaseOlderThan("R2026a") + plugins = matlab.buildtool.plugins.OpenTelemetryPlugin(); + end + end + end +end \ No newline at end of file diff --git a/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m new file mode 100644 index 0000000..5ce8976 --- /dev/null +++ b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m @@ -0,0 +1,198 @@ +classdef OpenTelemetryPlugin < matlab.buildtool.plugins.BuildRunnerPlugin + + % Copyright 2026 The MathWorks, Inc. + + methods(Access = protected) + function runBuild(plugin, pluginData) + % Configure by attaching to span if passed in via environment + % variable, and propagating baggage + configureOTel(); + + % Attributes + otelAttributes = dictionary( ... + [ ... + "otel.library.name", ... + "span.kind", ... + "internal.span.format", ... + ], ... + [ ... + "buildtool", ... + "internal", ... + "proto", ... + ] ... + ); + + tr = opentelemetry.trace.getTracer("buildtool"); + sp = tr.startSpan("buildtool", Attributes=otelAttributes); + scope = makeCurrent(sp); %#ok + + % Run build + runBuild@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); + + % Update status + if pluginData.BuildResult.Failed + sp.setStatus("Error", "Build completed, results not successful"); + else + sp.setStatus("Ok"); + end + + % Results-based attributes + taskResults = pluginData.BuildResult.TaskResults; + successful = [taskResults([taskResults.Successful]).Name]; + failed = [taskResults([taskResults.Failed]).Name]; + skipped = [taskResults([taskResults.Skipped]).Name]; + + sp.setAttributes( ... + "buildtool.tasks", numel(pluginData.BuildResult.TaskResults), ... + "buildtool.tasks.successful", successful, ... + "buildtool.tasks.failed", failed, ... + "buildtool.tasks.skipped", skipped, ... + "buildtool.build.successes", numel(successful), ... + "buildtool.build.failures", numel(failed), ... + "buildtool.build.skips", numel(skipped) ... + ); + + % Update metrics + meter = opentelemetry.metrics.getMeter("buildtool"); + successes = meter.createCounter("buildtool.tasks.successful"); + failures = meter.createCounter("buildtool.tasks.failed"); + skips = meter.createCounter("buildtool.tasks.skipped"); + buildSuccesses = meter.createCounter("buildtool.build.successes"); + buildFailures = meter.createCounter("buildtool.build.failures"); + + successes.add(numel(successful)); + failures.add(numel(failed)); + skips.add(numel(skipped)); + buildSuccesses.add(double(~pluginData.BuildResult.Failed)); + buildFailures.add(double(pluginData.BuildResult.Failed)); + + cleanupOTel(sp); + end + + function runTask(plugin, pluginData) + % TODO: + % - buildtool.task.outputs + % - buildtool.task.inputs + + % Definitions + taskName = pluginData.Name; + taskDescription = pluginData.Tasks.Description; + + % Attributes + otelAttributes = dictionary( ... + [ ... + "otel.library.name", ... + "span.kind", ... + "internal.span.format", ... + "buildtool.task.name", ... + "buildtool.task.description", ... + ], ... + [ ... + taskName, ... + "internal", ... + "proto", ... + taskName, ... + taskDescription ... + ] ... + ); + + tr = opentelemetry.trace.getTracer(taskName); + sp = tr.startSpan(taskName, Attributes=otelAttributes); + scope = makeCurrent(sp); %#ok + + % Run task + runTask@matlab.buildtool.plugins.BuildRunnerPlugin(plugin, pluginData); + + % Set results-based attributes + resultAttributes = dictionary( ... + [ ... + "buildtool.task.successful", ... + "buildtool.task.failed", ... + "buildtool.task.skipped" ... + ], ... + [ ... + pluginData.TaskResults.Successful, ... + pluginData.TaskResults.Failed, ... + pluginData.TaskResults.Skipped ... + ] ... + ); + sp.setAttributes(resultAttributes); + + % Update span status + if pluginData.TaskResults.Successful + sp.setStatus("Ok"); + else + sp.setStatus("Error", "Task completed, results not successful"); + end + + sp.endSpan(); + end + end +end + +% Use the same configuration as PADV +function extcontextscope = configureOTel() +% populate resource attributes +otelservicename = "buildtool"; +otelresource = dictionary("service.name", otelservicename); + +% baggage propagation +otelbaggage = getenv("BAGGAGE"); +if ~isempty(otelbaggage) + otelbaggage = split(split(string(otelbaggage),','), "="); + otelresource = insert(otelresource, otelbaggage(:,1), otelbaggage(:,2)); +end + +% check for passed in external context +extcontextscope = []; +traceid = getenv("TRACE_ID"); +spanid = getenv("SPAN_ID"); +if ~isempty(traceid) && ~isempty(spanid) + spcontext = opentelemetry.trace.SpanContext(traceid, spanid); + extcontextscope = makeCurrent(spcontext); +end + +% tracer provider +otelspexp = opentelemetry.exporters.otlp.OtlpGrpcSpanExporter; % use gRPC because Otel plugin for Jenkins only use gRPC +otelspproc = opentelemetry.sdk.trace.BatchSpanProcessor(otelspexp); +oteltp = opentelemetry.sdk.trace.TracerProvider(otelspproc, Resource=otelresource); +setTracerProvider(oteltp); + +% meter provider +otelmexp = opentelemetry.exporters.otlp.OtlpGrpcMetricExporter; % use gRPC because Otel plugin for Jenkins only use gRPC +otelmread = opentelemetry.sdk.metrics.PeriodicExportingMetricReader(otelmexp); +otelmp = opentelemetry.sdk.metrics.MeterProvider(otelmread, Resource=otelresource); +setMeterProvider(otelmp); + +% logger provider +otellgexp = opentelemetry.exporters.otlp.OtlpGrpcLogRecordExporter; % use gRPC because Otel plugin for Jenkins only use gRPC +otellgproc = opentelemetry.sdk.logs.BatchLogRecordProcessor(otellgexp); +otellp = opentelemetry.sdk.logs.LoggerProvider(otellgproc, Resource=otelresource); +setLoggerProvider(otellp); +end + +% Use the same cleanup as PADV +function cleanupOTel(span) + +timeout = 5; + +% end the input span before cleaning up +if nargin > 0 + endSpan(span); +end + +% tracer provider +oteltp = opentelemetry.trace.Provider.getTracerProvider; +opentelemetry.sdk.common.Cleanup.forceFlush(oteltp, timeout); +opentelemetry.sdk.common.Cleanup.shutdown(oteltp); + +% meter provider +otelmp = opentelemetry.metrics.Provider.getMeterProvider; +opentelemetry.sdk.common.Cleanup.forceFlush(otelmp, timeout); +opentelemetry.sdk.common.Cleanup.shutdown(otelmp); + +% logger provider +otellp = opentelemetry.logs.Provider.getLoggerProvider; +opentelemetry.sdk.common.Cleanup.forceFlush(otellp, timeout); +opentelemetry.sdk.common.Cleanup.shutdown(otellp); +end \ No newline at end of file diff --git a/test/tbuildtoolplugin.m b/test/tbuildtoolplugin.m new file mode 100644 index 0000000..c15a8d6 --- /dev/null +++ b/test/tbuildtoolplugin.m @@ -0,0 +1,533 @@ +classdef tbuildtoolplugin < matlab.unittest.TestCase + properties + OtelConfigFile + JsonFile + PidFile + ListPid + ReadPidList + ExtractPid + Sigint + Sigterm + OtelcolName + Otelcol + + BuildRunner + end + + methods (TestClassSetup) + function setupOnce(testCase) + % add the utils, callbacks, and fixtures folders to the path + folders = fullfile(fileparts(mfilename('fullpath')), ["utils" "callbacks" "fixtures"]); + testCase.applyFixture(matlab.unittest.fixtures.PathFixture(folders)); + commonSetupOnce(testCase); + end + end + + methods (TestMethodSetup) + function setup(testCase) + commonSetup(testCase); + end + + function createBuildRunner(testCase) + plugin = matlab.buildtool.plugins.OpenTelemetryPlugin(); + testCase.BuildRunner = matlab.buildtool.BuildRunner.withNoPlugins(); + testCase.BuildRunner.addPlugin(plugin); + end + end + + methods (TestMethodTeardown) + function teardown(testCase) + commonTeardown(testCase); + end + end + + methods (Test) + function pluginIsAddedAsADefaultPluginInNewReleases(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + runner = matlab.buildtool.BuildRunner.withDefaultPlugins(); + tf = arrayfun(@(x)isa(x, "matlab.buildtool.plugins.OpenTelemetryPlugin"), runner.Plugins); + testCase.verifyTrue(any(tf)); + end + + function pluginIsNotAddedAsADefaultPluginInOlderReleases(testCase) + testCase.assumeTrue(isMATLABReleaseOlderThan("R2026a")); + + runner = matlab.buildtool.BuildRunner.withDefaultPlugins(); + tf = arrayfun(@(x)isa(x, "matlab.buildtool.plugins.OpenTelemetryPlugin"), runner.Plugins); + testCase.verifyFalse(any(tf)); + end + + function runOneTaskHasCorrectSpans(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + % Create plan with 1 successful task + plan = buildplan(); + plan("task") = matlab.buildtool.Task(); + + % Run build + testCase.BuildRunner.run(plan, "task"); + + % Get results + results = readJsonResults(testCase); + spanData = results{1}.resourceSpans; + + % Verify spans + spans = spanData.scopeSpans; + testCase.verifyEqual(numel(spans), 2); + + % First span is overall build span + buildSpan = spanData.scopeSpans(1).spans; + testCase.verifyEqual(string(buildSpan.name), "buildtool"); + testCase.verifyEqual(buildSpan.status.code, 1); + + % Build span attributes + att1.key = 'otel.library.name'; + att1.value.stringValue = 'buildtool'; + + att2.key = 'span.kind'; + att2.value.stringValue = 'internal'; + + att3.key = 'internal.span.format'; + att3.value.stringValue = 'proto'; + + att4.key = 'buildtool.tasks'; + att4.value.doubleValue = 1; + + att5.key = 'buildtool.tasks.successful'; + att5.value.stringValue = 'task'; + + att6.key = 'buildtool.tasks.failed'; + att6.value.arrayValue = struct(); + + sizeZero.doubleValue = 0; + att7.key = 'buildtool.tasks.failed.size'; + att7.value.arrayValue.values = [sizeZero; sizeZero]; + + att8.key = 'buildtool.tasks.skipped'; + att8.value.arrayValue = struct(); + + att9.key = 'buildtool.tasks.skipped.size'; + att9.value.arrayValue.values = [sizeZero; sizeZero]; + + att10.key = 'buildtool.build.successes'; + att10.value.doubleValue = 1; + + att11.key = 'buildtool.build.failures'; + att11.value.doubleValue = 0; + + att12.key = 'buildtool.build.skips'; + att12.value.doubleValue = 0; + + expected = [ ... + att1, ... + att2, ... + att3, ... + att4, ... + att5, ... + att6, ... + att7, ... + att8, ... + att9, ... + att10, ... + att11, ... + att12, ... + ]'; + + testCase.verifyEqual(buildSpan.attributes, expected); + + % Second span is "task" span + taskSpan = spanData.scopeSpans(2).spans; + + testCase.verifyEqual(string(taskSpan.name), "task"); + testCase.verifyEqual(taskSpan.status.code, 1); + testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); + + % Task span attributes + tAtt1.key = 'otel.library.name'; + tAtt1.value.stringValue = 'task'; + + tAtt2.key = 'span.kind'; + tAtt2.value.stringValue = 'internal'; + + tAtt3.key = 'internal.span.format'; + tAtt3.value.stringValue = 'proto'; + + tAtt4.key = 'buildtool.task.name'; + tAtt4.value.stringValue = 'task'; + + tAtt5.key = 'buildtool.task.description'; + tAtt5.value.stringValue = ''; + + tAtt6.key = 'buildtool.task.successful'; + tAtt6.value.boolValue = true; + + tAtt7.key = 'buildtool.task.failed'; + tAtt7.value.boolValue = false; + + tAtt8.key = 'buildtool.task.skipped'; + tAtt8.value.boolValue = false; + + expected = [ ... + tAtt1, ... + tAtt2, ... + tAtt3, ... + tAtt4, ... + tAtt5, ... + tAtt6, ... + tAtt7, ... + tAtt8, ... + ]'; + + testCase.verifyEqual(taskSpan.attributes, expected); + end + + function runOneTaskHasCorrectMetrics(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + % Create plan with 1 successful task + plan = buildplan(); + plan("task") = matlab.buildtool.Task(); + + % Run build + testCase.BuildRunner.run(plan, "task"); + + % Get results + results = readJsonResults(testCase); + metricData = results{2}.resourceMetrics; + + metrics = metricData.scopeMetrics.metrics; + + % Order appears to be deterministic on my machine. + testCase.verifyEqual(metrics(1).name, 'buildtool.build.failures'); + testCase.verifyEqual(metrics(1).sum.dataPoints.asDouble, 0); + + testCase.verifyEqual(metrics(2).name, 'buildtool.build.successes'); + testCase.verifyEqual(metrics(2).sum.dataPoints.asDouble, 1); + + testCase.verifyEqual(metrics(3).name, 'buildtool.tasks.skipped'); + testCase.verifyEqual(metrics(3).sum.dataPoints.asDouble, 0); + + testCase.verifyEqual(metrics(4).name, 'buildtool.tasks.failed'); + testCase.verifyEqual(metrics(4).sum.dataPoints.asDouble, 0); + + testCase.verifyEqual(metrics(5).name, 'buildtool.tasks.successful'); + testCase.verifyEqual(metrics(5).sum.dataPoints.asDouble, 1); + end + + function runningSeveralTasksProducesCorrectSpans(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + % Create plan with 3 successful tasks and a failing task + plan = buildplan(); + plan("t1") = matlab.buildtool.Task(); + plan("t2") = matlab.buildtool.Task(Dependencies="t1"); + plan("t3") = matlab.buildtool.Task(Dependencies="t2"); + plan("error") = matlab.buildtool.Task(Actions=@(~)error("bam"), Dependencies="t3"); + + % Run build + testCase.BuildRunner.run(plan, "error"); + + % Get results + results = readJsonResults(testCase); + spanData = results{1}.resourceSpans; + + % Verify spans + spans = spanData.scopeSpans; + testCase.verifyEqual(numel(spans), 5); + + % First span is overall build span + buildSpan = findSpan("buildtool", spans); + testCase.verifyEqual(string(buildSpan.name), "buildtool"); + testCase.verifyEqual(buildSpan.status.code, 2); % Build should fail + + % Build span attributes + att1.key = 'otel.library.name'; + att1.value.stringValue = 'buildtool'; + + att2.key = 'span.kind'; + att2.value.stringValue = 'internal'; + + att3.key = 'internal.span.format'; + att3.value.stringValue = 'proto'; + + att4.key = 'buildtool.tasks'; + att4.value.doubleValue = 4; + + val1.stringValue = 't1'; + val2.stringValue = 't2'; + val3.stringValue = 't3'; + att5.key = 'buildtool.tasks.successful'; + att5.value.arrayValue.values = [val1; val2; val3]; + + size1.doubleValue = 1; + size3.doubleValue = 3; + att6.key = 'buildtool.tasks.successful.size'; + att6.value.arrayValue.values = [size1; size3]; + + att7.key = 'buildtool.tasks.failed'; + att7.value.stringValue = 'error'; + + att8.key = 'buildtool.tasks.skipped'; + att8.value.arrayValue = struct(); + + sizeZero.doubleValue = 0; + att9.key = 'buildtool.tasks.skipped.size'; + att9.value.arrayValue.values = [sizeZero; sizeZero]; + + att10.key = 'buildtool.build.successes'; + att10.value.doubleValue = 3; + + att11.key = 'buildtool.build.failures'; + att11.value.doubleValue = 1; + + att12.key = 'buildtool.build.skips'; + att12.value.doubleValue = 0; + + expected = [ ... + att1, ... + att2, ... + att3, ... + att4, ... + att5, ... + att6, ... + att7, ... + att8, ... + att9, ... + att10, ... + att11, ... + att12, ... + ]'; + + testCase.verifyEqual(buildSpan.attributes, expected); + + % "t1" span + taskSpan = findSpan("t1", spans); + + testCase.verifyEqual(string(taskSpan.name), "t1"); + testCase.verifyEqual(taskSpan.status.code, 1); + testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); + + % Task span attributes + tAtt1.key = 'otel.library.name'; + tAtt1.value.stringValue = 't1'; + + tAtt2.key = 'span.kind'; + tAtt2.value.stringValue = 'internal'; + + tAtt3.key = 'internal.span.format'; + tAtt3.value.stringValue = 'proto'; + + tAtt4.key = 'buildtool.task.name'; + tAtt4.value.stringValue = 't1'; + + tAtt5.key = 'buildtool.task.description'; + tAtt5.value.stringValue = ''; + + tAtt6.key = 'buildtool.task.successful'; + tAtt6.value.boolValue = true; + + tAtt7.key = 'buildtool.task.failed'; + tAtt7.value.boolValue = false; + + tAtt8.key = 'buildtool.task.skipped'; + tAtt8.value.boolValue = false; + + expected = [ ... + tAtt1, ... + tAtt2, ... + tAtt3, ... + tAtt4, ... + tAtt5, ... + tAtt6, ... + tAtt7, ... + tAtt8, ... + ]'; + + testCase.verifyEqual(taskSpan.attributes, expected); + + % "t2" span + taskSpan = findSpan("t2", spans); + + testCase.verifyEqual(string(taskSpan.name), "t2"); + testCase.verifyEqual(taskSpan.status.code, 1); + testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); + + % Task span attributes + tAtt1.key = 'otel.library.name'; + tAtt1.value.stringValue = 't2'; + + tAtt2.key = 'span.kind'; + tAtt2.value.stringValue = 'internal'; + + tAtt3.key = 'internal.span.format'; + tAtt3.value.stringValue = 'proto'; + + tAtt4.key = 'buildtool.task.name'; + tAtt4.value.stringValue = 't2'; + + tAtt5.key = 'buildtool.task.description'; + tAtt5.value.stringValue = ''; + + tAtt6.key = 'buildtool.task.successful'; + tAtt6.value.boolValue = true; + + tAtt7.key = 'buildtool.task.failed'; + tAtt7.value.boolValue = false; + + tAtt8.key = 'buildtool.task.skipped'; + tAtt8.value.boolValue = false; + + expected = [ ... + tAtt1, ... + tAtt2, ... + tAtt3, ... + tAtt4, ... + tAtt5, ... + tAtt6, ... + tAtt7, ... + tAtt8, ... + ]'; + + testCase.verifyEqual(taskSpan.attributes, expected); + + % "t3" span + taskSpan = findSpan("t3", spans); + + testCase.verifyEqual(string(taskSpan.name), "t3"); + testCase.verifyEqual(taskSpan.status.code, 1); + testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); + + % Task span attributes + tAtt1.key = 'otel.library.name'; + tAtt1.value.stringValue = 't3'; + + tAtt2.key = 'span.kind'; + tAtt2.value.stringValue = 'internal'; + + tAtt3.key = 'internal.span.format'; + tAtt3.value.stringValue = 'proto'; + + tAtt4.key = 'buildtool.task.name'; + tAtt4.value.stringValue = 't3'; + + tAtt5.key = 'buildtool.task.description'; + tAtt5.value.stringValue = ''; + + tAtt6.key = 'buildtool.task.successful'; + tAtt6.value.boolValue = true; + + tAtt7.key = 'buildtool.task.failed'; + tAtt7.value.boolValue = false; + + tAtt8.key = 'buildtool.task.skipped'; + tAtt8.value.boolValue = false; + + expected = [ ... + tAtt1, ... + tAtt2, ... + tAtt3, ... + tAtt4, ... + tAtt5, ... + tAtt6, ... + tAtt7, ... + tAtt8, ... + ]'; + + testCase.verifyEqual(taskSpan.attributes, expected); + + % "error" span + taskSpan = findSpan("error", spans); + + testCase.verifyEqual(string(taskSpan.name), "error"); + testCase.verifyEqual(taskSpan.status.code, 2); % Should error + testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); + + % Task span attributes + tAtt1.key = 'otel.library.name'; + tAtt1.value.stringValue = 'error'; + + tAtt2.key = 'span.kind'; + tAtt2.value.stringValue = 'internal'; + + tAtt3.key = 'internal.span.format'; + tAtt3.value.stringValue = 'proto'; + + tAtt4.key = 'buildtool.task.name'; + tAtt4.value.stringValue = 'error'; + + tAtt5.key = 'buildtool.task.description'; + tAtt5.value.stringValue = ''; + + tAtt6.key = 'buildtool.task.successful'; + tAtt6.value.boolValue = false; + + tAtt7.key = 'buildtool.task.failed'; + tAtt7.value.boolValue = true; + + tAtt8.key = 'buildtool.task.skipped'; + tAtt8.value.boolValue = false; + + expected = [ ... + tAtt1, ... + tAtt2, ... + tAtt3, ... + tAtt4, ... + tAtt5, ... + tAtt6, ... + tAtt7, ... + tAtt8, ... + ]'; + + testCase.verifyEqual(taskSpan.attributes, expected); + end + + function runningSeveralTasksProducesCorrectMetrics(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + % Create plan with 3 successful tasks and a failing task + plan = buildplan(); + plan("t1") = matlab.buildtool.Task(); + plan("t2") = matlab.buildtool.Task(Dependencies="t1"); + plan("t3") = matlab.buildtool.Task(Dependencies="t2"); + plan("error") = matlab.buildtool.Task(Actions=@(~)error("bam"), Dependencies="t3"); + + % Run build + testCase.BuildRunner.run(plan, "error"); + + % Get results + results = readJsonResults(testCase); + metricData = results{2}.resourceMetrics; + + metrics = metricData.scopeMetrics.metrics; + + % Order appears to be deterministic on my machine. + testCase.verifyEqual(metrics(1).name, 'buildtool.build.failures'); + testCase.verifyEqual(metrics(1).sum.dataPoints.asDouble, 1); + + testCase.verifyEqual(metrics(2).name, 'buildtool.build.successes'); + testCase.verifyEqual(metrics(2).sum.dataPoints.asDouble, 0); + + testCase.verifyEqual(metrics(3).name, 'buildtool.tasks.skipped'); + testCase.verifyEqual(metrics(3).sum.dataPoints.asDouble, 0); + + testCase.verifyEqual(metrics(4).name, 'buildtool.tasks.failed'); + testCase.verifyEqual(metrics(4).sum.dataPoints.asDouble, 1); + + testCase.verifyEqual(metrics(5).name, 'buildtool.tasks.successful'); + testCase.verifyEqual(metrics(5).sum.dataPoints.asDouble, 3); + end + end +end + +function span = findSpan(name, spans) + for s = spans' + realSpan = s.spans; + if (strcmp(name, realSpan.name)) + span = realSpan; + return; + end + end + + error("No span found"); +end \ No newline at end of file From 237a0180bc1739b4419152e35b5d58ba0085442b Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 10:21:43 -0500 Subject: [PATCH 2/9] Update CMakeLists.txt to include build tool instrumentation M source code. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index abcd2e1..af2e3ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -599,6 +599,7 @@ set(METRICS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/+opentele set(LOGS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/logs/+opentelemetry) set(COMMON_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/+opentelemetry) set(AUTO_INSTRUMENTATION_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/auto-instrumentation/+opentelemetry) +set(INSTRUMENTATION_MBT_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/instrumentation/buildtool/+matlab) set(EXPORTER_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultSpanExporter.m ${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultMetricExporter.m @@ -634,6 +635,7 @@ install(DIRECTORY ${METRICS_SDK_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${LOGS_SDK_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${COMMON_SDK_MATLAB_SOURCES} DESTINATION .) install(DIRECTORY ${AUTO_INSTRUMENTATION_MATLAB_SOURCES} DESTINATION .) +install(DIRECTORY ${INSTRUMENTATION_MBT_MATLAB_SOURCES} DESTINATION .) install(FILES ${EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) if(WITH_OTLP_HTTP) install(FILES ${OTLP_HTTP_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR}) From 05b894877c378241771bd22db0ff1cd48aa9b118 Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 12:01:37 -0500 Subject: [PATCH 3/9] Run tests on R2026a at least once --- .github/workflows/build_and_test_simple.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test_simple.yml b/.github/workflows/build_and_test_simple.yml index d7c9c78..638a80d 100644 --- a/.github/workflows/build_and_test_simple.yml +++ b/.github/workflows/build_and_test_simple.yml @@ -41,7 +41,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: - release: R2025a + release: latest-including-prerelease products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab From 3d56c45ec483d00acd45c266c3b4c6ee3d48d596 Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 14:03:40 -0500 Subject: [PATCH 4/9] Do not run tests without gRPC --- test/tbuildtoolplugin.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/tbuildtoolplugin.m b/test/tbuildtoolplugin.m index c15a8d6..5c48f0b 100644 --- a/test/tbuildtoolplugin.m +++ b/test/tbuildtoolplugin.m @@ -27,6 +27,11 @@ function setupOnce(testCase) function setup(testCase) commonSetup(testCase); end + + function onlyTestIfgRPCIsInstalled(testCase) + testCase.assumeTrue(logical(exist("opentelemetry.exporters.otlp.OtlpGrpcSpanExporter", "class")), ... + "Otlp gRPC exporter must be installed."); + end function createBuildRunner(testCase) plugin = matlab.buildtool.plugins.OpenTelemetryPlugin(); From e2de93876e385b17c660a99d292aa3a4458e13ac Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 14:14:51 -0500 Subject: [PATCH 5/9] Run full tests on prerelease --- .github/workflows/build_and_test_full.yml | 3 ++- .github/workflows/build_and_test_simple.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_test_full.yml b/.github/workflows/build_and_test_full.yml index 74e04c5..973675a 100644 --- a/.github/workflows/build_and_test_full.yml +++ b/.github/workflows/build_and_test_full.yml @@ -5,6 +5,7 @@ on: push: branches: - main + - mbt-plugin env: MLM_LICENSE_TOKEN: ${{ secrets.MLM_LICENSE_TOKEN }} jobs: @@ -40,7 +41,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: - release: R2025a + release: latest-including-prerelease products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab diff --git a/.github/workflows/build_and_test_simple.yml b/.github/workflows/build_and_test_simple.yml index 638a80d..d7c9c78 100644 --- a/.github/workflows/build_and_test_simple.yml +++ b/.github/workflows/build_and_test_simple.yml @@ -41,7 +41,7 @@ jobs: - name: Install MATLAB uses: matlab-actions/setup-matlab@v2 with: - release: latest-including-prerelease + release: R2025a products: MATLAB_Compiler MATLAB_Compiler_SDK - name: Build OpenTelemetry-Matlab working-directory: opentelemetry-matlab From 20aa4ef8d8df38acae6c46a8c7a5b78dafe361f9 Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 14:38:17 -0500 Subject: [PATCH 6/9] Use environment variable NO_MBT_OTEL_CONFIG to skip configuring providers with the MBT --- .../+buildtool/+plugins/OpenTelemetryPlugin.m | 11 +++++++++ test/tbuildtoolplugin.m | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m index 5ce8976..45207ee 100644 --- a/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m +++ b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m @@ -132,6 +132,12 @@ function runTask(plugin, pluginData) % Use the same configuration as PADV function extcontextscope = configureOTel() + +% Skip configuration if NO_MBT_OTEL_CONFIG set +if (getenv("NO_MBT_OTEL_CONFIG")) + return; +end + % populate resource attributes otelservicename = "buildtool"; otelresource = dictionary("service.name", otelservicename); @@ -174,6 +180,11 @@ function runTask(plugin, pluginData) % Use the same cleanup as PADV function cleanupOTel(span) +% Skip cleanup if NO_MBT_OTEL_CONFIG set +if (getenv("NO_MBT_OTEL_CONFIG")) + return; +end + timeout = 5; % end the input span before cleaning up diff --git a/test/tbuildtoolplugin.m b/test/tbuildtoolplugin.m index 5c48f0b..faaf9d3 100644 --- a/test/tbuildtoolplugin.m +++ b/test/tbuildtoolplugin.m @@ -522,6 +522,29 @@ function runningSeveralTasksProducesCorrectMetrics(testCase) testCase.verifyEqual(metrics(5).name, 'buildtool.tasks.successful'); testCase.verifyEqual(metrics(5).sum.dataPoints.asDouble, 3); end + + function buildToolDoesNotConfigureOTelWhenEnvVariableSet(testCase) + testCase.assumeFalse(isMATLABReleaseOlderThan("R2026a")); + + % Restore environment variable + testCase.addTeardown(@()setenv("NO_MBT_OTEL_CONFIG", "")); + + % Set environment variable + setenv("NO_MBT_OTEL_CONFIG", "1"); + + % Create plan with 1 successful task + plan = buildplan(); + plan("task") = matlab.buildtool.Task(); + + % Run build + testCase.BuildRunner.run(plan, "task"); + + % Get results + results = readJsonResults(testCase); + + % Verify results are empty since we never set up any providers + testCase.verifyEmpty(results); + end end end From df90996b0c0d8b0273ae0d7fad9d308d5894b6ce Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Tue, 13 Jan 2026 16:46:55 -0500 Subject: [PATCH 7/9] Fix issue with PluginData.Tasks, remove unnecessary attributes --- .../+buildtool/+plugins/OpenTelemetryPlugin.m | 25 +- test/tbuildtoolplugin.m | 256 ++++++------------ 2 files changed, 89 insertions(+), 192 deletions(-) diff --git a/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m index 45207ee..f0a3a85 100644 --- a/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m +++ b/instrumentation/buildtool/+matlab/+buildtool/+plugins/OpenTelemetryPlugin.m @@ -8,22 +8,8 @@ function runBuild(plugin, pluginData) % variable, and propagating baggage configureOTel(); - % Attributes - otelAttributes = dictionary( ... - [ ... - "otel.library.name", ... - "span.kind", ... - "internal.span.format", ... - ], ... - [ ... - "buildtool", ... - "internal", ... - "proto", ... - ] ... - ); - tr = opentelemetry.trace.getTracer("buildtool"); - sp = tr.startSpan("buildtool", Attributes=otelAttributes); + sp = tr.startSpan("buildtool"); scope = makeCurrent(sp); %#ok % Run build @@ -75,22 +61,17 @@ function runTask(plugin, pluginData) % - buildtool.task.inputs % Definitions + task = pluginData.TaskGraph.Tasks; taskName = pluginData.Name; - taskDescription = pluginData.Tasks.Description; + taskDescription = task.Description; % Attributes otelAttributes = dictionary( ... [ ... - "otel.library.name", ... - "span.kind", ... - "internal.span.format", ... "buildtool.task.name", ... "buildtool.task.description", ... ], ... [ ... - taskName, ... - "internal", ... - "proto", ... taskName, ... taskDescription ... ] ... diff --git a/test/tbuildtoolplugin.m b/test/tbuildtoolplugin.m index faaf9d3..8f538fc 100644 --- a/test/tbuildtoolplugin.m +++ b/test/tbuildtoolplugin.m @@ -87,42 +87,33 @@ function runOneTaskHasCorrectSpans(testCase) testCase.verifyEqual(buildSpan.status.code, 1); % Build span attributes - att1.key = 'otel.library.name'; - att1.value.stringValue = 'buildtool'; + att1.key = 'buildtool.tasks'; + att1.value.doubleValue = 1; - att2.key = 'span.kind'; - att2.value.stringValue = 'internal'; + att2.key = 'buildtool.tasks.successful'; + att2.value.stringValue = 'task'; - att3.key = 'internal.span.format'; - att3.value.stringValue = 'proto'; - - att4.key = 'buildtool.tasks'; - att4.value.doubleValue = 1; - - att5.key = 'buildtool.tasks.successful'; - att5.value.stringValue = 'task'; - - att6.key = 'buildtool.tasks.failed'; - att6.value.arrayValue = struct(); + att3.key = 'buildtool.tasks.failed'; + att3.value.arrayValue = struct(); sizeZero.doubleValue = 0; - att7.key = 'buildtool.tasks.failed.size'; - att7.value.arrayValue.values = [sizeZero; sizeZero]; + att4.key = 'buildtool.tasks.failed.size'; + att4.value.arrayValue.values = [sizeZero; sizeZero]; - att8.key = 'buildtool.tasks.skipped'; - att8.value.arrayValue = struct(); + att5.key = 'buildtool.tasks.skipped'; + att5.value.arrayValue = struct(); - att9.key = 'buildtool.tasks.skipped.size'; - att9.value.arrayValue.values = [sizeZero; sizeZero]; + att6.key = 'buildtool.tasks.skipped.size'; + att6.value.arrayValue.values = [sizeZero; sizeZero]; - att10.key = 'buildtool.build.successes'; - att10.value.doubleValue = 1; + att7.key = 'buildtool.build.successes'; + att7.value.doubleValue = 1; - att11.key = 'buildtool.build.failures'; - att11.value.doubleValue = 0; + att8.key = 'buildtool.build.failures'; + att8.value.doubleValue = 0; - att12.key = 'buildtool.build.skips'; - att12.value.doubleValue = 0; + att9.key = 'buildtool.build.skips'; + att9.value.doubleValue = 0; expected = [ ... att1, ... @@ -134,9 +125,6 @@ function runOneTaskHasCorrectSpans(testCase) att7, ... att8, ... att9, ... - att10, ... - att11, ... - att12, ... ]'; testCase.verifyEqual(buildSpan.attributes, expected); @@ -149,39 +137,27 @@ function runOneTaskHasCorrectSpans(testCase) testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); % Task span attributes - tAtt1.key = 'otel.library.name'; + tAtt1.key = 'buildtool.task.name'; tAtt1.value.stringValue = 'task'; - tAtt2.key = 'span.kind'; - tAtt2.value.stringValue = 'internal'; - - tAtt3.key = 'internal.span.format'; - tAtt3.value.stringValue = 'proto'; - - tAtt4.key = 'buildtool.task.name'; - tAtt4.value.stringValue = 'task'; - - tAtt5.key = 'buildtool.task.description'; - tAtt5.value.stringValue = ''; + tAtt2.key = 'buildtool.task.description'; + tAtt2.value.stringValue = ''; - tAtt6.key = 'buildtool.task.successful'; - tAtt6.value.boolValue = true; + tAtt3.key = 'buildtool.task.successful'; + tAtt3.value.boolValue = true; - tAtt7.key = 'buildtool.task.failed'; - tAtt7.value.boolValue = false; + tAtt4.key = 'buildtool.task.failed'; + tAtt4.value.boolValue = false; - tAtt8.key = 'buildtool.task.skipped'; - tAtt8.value.boolValue = false; + tAtt5.key = 'buildtool.task.skipped'; + tAtt5.value.boolValue = false; expected = [ ... tAtt1, ... tAtt2, ... tAtt3, ... tAtt4, ... - tAtt5, ... - tAtt6, ... - tAtt7, ... - tAtt8, ... + tAtt5, ... ]'; testCase.verifyEqual(taskSpan.attributes, expected); @@ -247,47 +223,38 @@ function runningSeveralTasksProducesCorrectSpans(testCase) testCase.verifyEqual(buildSpan.status.code, 2); % Build should fail % Build span attributes - att1.key = 'otel.library.name'; - att1.value.stringValue = 'buildtool'; - - att2.key = 'span.kind'; - att2.value.stringValue = 'internal'; - - att3.key = 'internal.span.format'; - att3.value.stringValue = 'proto'; - - att4.key = 'buildtool.tasks'; - att4.value.doubleValue = 4; + att1.key = 'buildtool.tasks'; + att1.value.doubleValue = 4; val1.stringValue = 't1'; val2.stringValue = 't2'; val3.stringValue = 't3'; - att5.key = 'buildtool.tasks.successful'; - att5.value.arrayValue.values = [val1; val2; val3]; + att2.key = 'buildtool.tasks.successful'; + att2.value.arrayValue.values = [val1; val2; val3]; size1.doubleValue = 1; size3.doubleValue = 3; - att6.key = 'buildtool.tasks.successful.size'; - att6.value.arrayValue.values = [size1; size3]; + att3.key = 'buildtool.tasks.successful.size'; + att3.value.arrayValue.values = [size1; size3]; - att7.key = 'buildtool.tasks.failed'; - att7.value.stringValue = 'error'; + att4.key = 'buildtool.tasks.failed'; + att4.value.stringValue = 'error'; - att8.key = 'buildtool.tasks.skipped'; - att8.value.arrayValue = struct(); + att5.key = 'buildtool.tasks.skipped'; + att5.value.arrayValue = struct(); sizeZero.doubleValue = 0; - att9.key = 'buildtool.tasks.skipped.size'; - att9.value.arrayValue.values = [sizeZero; sizeZero]; + att6.key = 'buildtool.tasks.skipped.size'; + att6.value.arrayValue.values = [sizeZero; sizeZero]; - att10.key = 'buildtool.build.successes'; - att10.value.doubleValue = 3; + att7.key = 'buildtool.build.successes'; + att7.value.doubleValue = 3; - att11.key = 'buildtool.build.failures'; - att11.value.doubleValue = 1; + att8.key = 'buildtool.build.failures'; + att8.value.doubleValue = 1; - att12.key = 'buildtool.build.skips'; - att12.value.doubleValue = 0; + att9.key = 'buildtool.build.skips'; + att9.value.doubleValue = 0; expected = [ ... att1, ... @@ -299,9 +266,6 @@ function runningSeveralTasksProducesCorrectSpans(testCase) att7, ... att8, ... att9, ... - att10, ... - att11, ... - att12, ... ]'; testCase.verifyEqual(buildSpan.attributes, expected); @@ -314,39 +278,27 @@ function runningSeveralTasksProducesCorrectSpans(testCase) testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); % Task span attributes - tAtt1.key = 'otel.library.name'; + tAtt1.key = 'buildtool.task.name'; tAtt1.value.stringValue = 't1'; - tAtt2.key = 'span.kind'; - tAtt2.value.stringValue = 'internal'; - - tAtt3.key = 'internal.span.format'; - tAtt3.value.stringValue = 'proto'; - - tAtt4.key = 'buildtool.task.name'; - tAtt4.value.stringValue = 't1'; + tAtt2.key = 'buildtool.task.description'; + tAtt2.value.stringValue = ''; - tAtt5.key = 'buildtool.task.description'; - tAtt5.value.stringValue = ''; + tAtt3.key = 'buildtool.task.successful'; + tAtt3.value.boolValue = true; - tAtt6.key = 'buildtool.task.successful'; - tAtt6.value.boolValue = true; + tAtt4.key = 'buildtool.task.failed'; + tAtt4.value.boolValue = false; - tAtt7.key = 'buildtool.task.failed'; - tAtt7.value.boolValue = false; - - tAtt8.key = 'buildtool.task.skipped'; - tAtt8.value.boolValue = false; + tAtt5.key = 'buildtool.task.skipped'; + tAtt5.value.boolValue = false; expected = [ ... tAtt1, ... tAtt2, ... tAtt3, ... tAtt4, ... - tAtt5, ... - tAtt6, ... - tAtt7, ... - tAtt8, ... + tAtt5, ... ]'; testCase.verifyEqual(taskSpan.attributes, expected); @@ -359,39 +311,27 @@ function runningSeveralTasksProducesCorrectSpans(testCase) testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); % Task span attributes - tAtt1.key = 'otel.library.name'; + tAtt1.key = 'buildtool.task.name'; tAtt1.value.stringValue = 't2'; - tAtt2.key = 'span.kind'; - tAtt2.value.stringValue = 'internal'; - - tAtt3.key = 'internal.span.format'; - tAtt3.value.stringValue = 'proto'; - - tAtt4.key = 'buildtool.task.name'; - tAtt4.value.stringValue = 't2'; + tAtt2.key = 'buildtool.task.description'; + tAtt2.value.stringValue = ''; - tAtt5.key = 'buildtool.task.description'; - tAtt5.value.stringValue = ''; + tAtt3.key = 'buildtool.task.successful'; + tAtt3.value.boolValue = true; - tAtt6.key = 'buildtool.task.successful'; - tAtt6.value.boolValue = true; + tAtt4.key = 'buildtool.task.failed'; + tAtt4.value.boolValue = false; - tAtt7.key = 'buildtool.task.failed'; - tAtt7.value.boolValue = false; - - tAtt8.key = 'buildtool.task.skipped'; - tAtt8.value.boolValue = false; + tAtt5.key = 'buildtool.task.skipped'; + tAtt5.value.boolValue = false; expected = [ ... tAtt1, ... tAtt2, ... tAtt3, ... tAtt4, ... - tAtt5, ... - tAtt6, ... - tAtt7, ... - tAtt8, ... + tAtt5, ... ]'; testCase.verifyEqual(taskSpan.attributes, expected); @@ -404,39 +344,27 @@ function runningSeveralTasksProducesCorrectSpans(testCase) testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); % Task span attributes - tAtt1.key = 'otel.library.name'; + tAtt1.key = 'buildtool.task.name'; tAtt1.value.stringValue = 't3'; - tAtt2.key = 'span.kind'; - tAtt2.value.stringValue = 'internal'; - - tAtt3.key = 'internal.span.format'; - tAtt3.value.stringValue = 'proto'; + tAtt2.key = 'buildtool.task.description'; + tAtt2.value.stringValue = ''; - tAtt4.key = 'buildtool.task.name'; - tAtt4.value.stringValue = 't3'; + tAtt3.key = 'buildtool.task.successful'; + tAtt3.value.boolValue = true; - tAtt5.key = 'buildtool.task.description'; - tAtt5.value.stringValue = ''; + tAtt4.key = 'buildtool.task.failed'; + tAtt4.value.boolValue = false; - tAtt6.key = 'buildtool.task.successful'; - tAtt6.value.boolValue = true; - - tAtt7.key = 'buildtool.task.failed'; - tAtt7.value.boolValue = false; - - tAtt8.key = 'buildtool.task.skipped'; - tAtt8.value.boolValue = false; + tAtt5.key = 'buildtool.task.skipped'; + tAtt5.value.boolValue = false; expected = [ ... tAtt1, ... tAtt2, ... tAtt3, ... tAtt4, ... - tAtt5, ... - tAtt6, ... - tAtt7, ... - tAtt8, ... + tAtt5, ... ]'; testCase.verifyEqual(taskSpan.attributes, expected); @@ -449,39 +377,27 @@ function runningSeveralTasksProducesCorrectSpans(testCase) testCase.verifyEqual(taskSpan.parentSpanId, buildSpan.spanId); % Task span attributes - tAtt1.key = 'otel.library.name'; + tAtt1.key = 'buildtool.task.name'; tAtt1.value.stringValue = 'error'; - tAtt2.key = 'span.kind'; - tAtt2.value.stringValue = 'internal'; - - tAtt3.key = 'internal.span.format'; - tAtt3.value.stringValue = 'proto'; - - tAtt4.key = 'buildtool.task.name'; - tAtt4.value.stringValue = 'error'; - - tAtt5.key = 'buildtool.task.description'; - tAtt5.value.stringValue = ''; + tAtt2.key = 'buildtool.task.description'; + tAtt2.value.stringValue = ''; - tAtt6.key = 'buildtool.task.successful'; - tAtt6.value.boolValue = false; + tAtt3.key = 'buildtool.task.successful'; + tAtt3.value.boolValue = false; - tAtt7.key = 'buildtool.task.failed'; - tAtt7.value.boolValue = true; + tAtt4.key = 'buildtool.task.failed'; + tAtt4.value.boolValue = true; - tAtt8.key = 'buildtool.task.skipped'; - tAtt8.value.boolValue = false; + tAtt5.key = 'buildtool.task.skipped'; + tAtt5.value.boolValue = false; expected = [ ... tAtt1, ... tAtt2, ... tAtt3, ... tAtt4, ... - tAtt5, ... - tAtt6, ... - tAtt7, ... - tAtt8, ... + tAtt5, ... ]'; testCase.verifyEqual(taskSpan.attributes, expected); From a974c14cc267a1bd74ea0c534e015207668f87ca Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Wed, 14 Jan 2026 10:02:30 -0500 Subject: [PATCH 8/9] Attempt to fix codecov paths --- codecov.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index e16f6b0..01fd55b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,4 +11,5 @@ fixes: - "\\+opentelemetry/\\+baggage/::api/baggage/+opentelemetry/+baggage/" - "\\+opentelemetry/\\+exporters/\\+otlp/::exporters/otlp/+opentelemetry/+exporters/+otlp/" - "\\+opentelemetry/\\+sdk/\\+logs::sdk/logs/+opentelemetry/+sdk/+logs/" - - "\\+opentelemetry/\\+logs/::api/logs/+opentelemetry/+logs/" \ No newline at end of file + - "\\+opentelemetry/\\+logs/::api/logs/+opentelemetry/+logs/" + - "\\+matlab/\\+buildtool/::instrumentation/buildtool/+matlab/+buildtool" From b3151994cd3909d0e6eb996ef461c7116d19b1bd Mon Sep 17 00:00:00 2001 From: Sam Eagen Date: Fri, 16 Jan 2026 14:28:46 -0500 Subject: [PATCH 9/9] Add missing trailing slash for codecov --- codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index 01fd55b..f0c1ccd 100644 --- a/codecov.yml +++ b/codecov.yml @@ -12,4 +12,4 @@ fixes: - "\\+opentelemetry/\\+exporters/\\+otlp/::exporters/otlp/+opentelemetry/+exporters/+otlp/" - "\\+opentelemetry/\\+sdk/\\+logs::sdk/logs/+opentelemetry/+sdk/+logs/" - "\\+opentelemetry/\\+logs/::api/logs/+opentelemetry/+logs/" - - "\\+matlab/\\+buildtool/::instrumentation/buildtool/+matlab/+buildtool" + - "\\+matlab/\\+buildtool/::instrumentation/buildtool/+matlab/+buildtool/"