Skip to content

Commit 6fd6aa8

Browse files
committed
AutoTrace to capture exception in catch block
1 parent 33ce33c commit 6fd6aa8

File tree

2 files changed

+74
-18
lines changed

2 files changed

+74
-18
lines changed

auto-instrumentation/+opentelemetry/+autoinstrument/AutoTrace.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
classdef AutoTrace < handle
22
% Automatic instrumentation with OpenTelemetry tracing.
33

4-
% Copyright 2024-2025 The MathWorks, Inc.
4+
% Copyright 2024-2026 The MathWorks, Inc.
55

66
properties (SetAccess=private)
77
StartFunction function_handle % entry function
@@ -155,7 +155,9 @@ function handleError(obj, ME)
155155
% spans and their corresponding scopes. Rethrow the
156156
% exception ME.
157157
if ~isempty(obj.Instrumentor.Spans)
158-
setStatus(obj.Instrumentor.Spans(end), "Error", ME.message);
158+
errorspan = obj.Instrumentor.Spans(end);
159+
setStatus(errorspan, "Error", ME.message);
160+
recordException(errorspan, ME);
159161
for i = length(obj.Instrumentor.Spans):-1:1
160162
obj.Instrumentor.Spans(i) = [];
161163
obj.Instrumentor.Scopes(i) = [];

test/tautotrace.m

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,24 +289,51 @@ function testError(testCase)
289289
at = opentelemetry.autoinstrument.AutoTrace(@linearfit_example);
290290

291291
% run the example with an invalid input, check for error
292-
verifyError(testCase, @()beginTrace(at, "invalid"), "autotrace_examples:linearfit_example:generate_data:InvalidN");
292+
errorid_expected = "autotrace_examples:linearfit_example:generate_data:InvalidN";
293+
errormsg_expected = "Input must be a numeric scalar";
294+
verifyError(testCase, @()beginTrace(at, "invalid"), errorid_expected);
293295

294296
% perform test comparisons
295297
results = readJsonResults(testCase);
296298
verifyNumElements(testCase, results, 2);
297299

298300
% check span names
299-
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.name), "generate_data");
300-
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.name), "linearfit_example");
301+
spannames = cellfun(@(x)string(x.resourceSpans.scopeSpans.spans.name), results);
302+
spannames_expected = ["generate_data" "linearfit_example"];
303+
[lia, locb] = ismember(spannames_expected, spannames);
304+
verifyTrue(testCase, all(lia));
305+
generatedata = results{locb(1)};
306+
linearfitexample = results{locb(2)};
301307

302308
% check parent children relationship
303-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.parentSpanId, results{2}.resourceSpans.scopeSpans.spans.spanId);
309+
verifyEqual(testCase, generatedata.resourceSpans.scopeSpans.spans.parentSpanId, linearfitexample.resourceSpans.scopeSpans.spans.spanId);
304310

305311
% check error status
306-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.status.code, 2); % error
307-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.status.message, ...
308-
'Input must be a numeric scalar');
309-
verifyEmpty(testCase, fieldnames(results{2}.resourceSpans.scopeSpans.spans.status)); % ok, no error
312+
verifyEqual(testCase, generatedata.resourceSpans.scopeSpans.spans.status.code, 2); % error
313+
verifyEqual(testCase, string(generatedata.resourceSpans.scopeSpans.spans.status.message), ...
314+
errormsg_expected);
315+
verifyEmpty(testCase, fieldnames(linearfitexample.resourceSpans.scopeSpans.spans.status)); % ok, no error
316+
317+
% check exception event
318+
verifyTrue(testCase, isfield(generatedata.resourceSpans.scopeSpans.spans, "events")); % exception event in "generate_data" span
319+
exception = generatedata.resourceSpans.scopeSpans.spans.events;
320+
verifyEqual(testCase, string(exception.name), "exception");
321+
% exception attributes
322+
exception_attrkeys = string({exception.attributes.key});
323+
identifieridx = find(exception_attrkeys == "exception.identifier");
324+
verifyNotEmpty(testCase, identifieridx);
325+
verifyEqual(testCase, string(exception.attributes(identifieridx).value.stringValue), errorid_expected);
326+
messageidx = find(exception_attrkeys == "exception.message");
327+
verifyNotEmpty(testCase, messageidx);
328+
verifyEqual(testCase, string(exception.attributes(messageidx).value.stringValue), errormsg_expected);
329+
causeidx = find(exception_attrkeys == "exception.cause");
330+
verifyNotEmpty(testCase, causeidx);
331+
verifyEqual(testCase, string(exception.attributes(causeidx).value.stringValue), "[]");
332+
stacktraceidx = find(exception_attrkeys == "exception.stacktrace");
333+
verifyNotEmpty(testCase, stacktraceidx);
334+
verifyTrue(testCase, contains(string(exception.attributes(stacktraceidx).value.stringValue), "generate_data.m"));
335+
336+
verifyFalse(testCase, isfield(linearfitexample.resourceSpans.scopeSpans.spans, "events")); % no exception event in "linearfit_example" span
310337
end
311338

312339
function testHandleError(testCase)
@@ -324,25 +351,52 @@ function testHandleError(testCase)
324351

325352
% call example directly instead of calling beginTrace, and pass
326353
% in an invalid input
354+
errorid_expected = "autotrace_examples:linearfit_example:generate_data:InvalidN";
355+
errormsg_expected = "Input must be a numeric scalar";
327356
verifyError(testCase, @()linearfit_example_trycatch(at, "invalid"), ...
328-
"autotrace_examples:linearfit_example:generate_data:InvalidN");
357+
errorid_expected);
329358

330359
% perform test comparisons
331360
results = readJsonResults(testCase);
332361
verifyNumElements(testCase, results, 2);
333362

334363
% check span names
335-
verifyEqual(testCase, string(results{1}.resourceSpans.scopeSpans.spans.name), "generate_data");
336-
verifyEqual(testCase, string(results{2}.resourceSpans.scopeSpans.spans.name), "linearfit_example_trycatch");
364+
spannames = cellfun(@(x)string(x.resourceSpans.scopeSpans.spans.name), results);
365+
spannames_expected = ["generate_data" "linearfit_example_trycatch"];
366+
[lia, locb] = ismember(spannames_expected, spannames);
367+
verifyTrue(testCase, all(lia));
368+
generatedata = results{locb(1)};
369+
linearfitexample = results{locb(2)};
337370

338371
% check parent children relationship
339-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.parentSpanId, results{2}.resourceSpans.scopeSpans.spans.spanId);
372+
verifyEqual(testCase, generatedata.resourceSpans.scopeSpans.spans.parentSpanId, linearfitexample.resourceSpans.scopeSpans.spans.spanId);
340373

341374
% check error status
342-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.status.code, 2); % error
343-
verifyEqual(testCase, results{1}.resourceSpans.scopeSpans.spans.status.message, ...
344-
'Input must be a numeric scalar');
345-
verifyEmpty(testCase, fieldnames(results{2}.resourceSpans.scopeSpans.spans.status)); % ok, no error
375+
verifyEqual(testCase, generatedata.resourceSpans.scopeSpans.spans.status.code, 2); % error
376+
verifyEqual(testCase, string(generatedata.resourceSpans.scopeSpans.spans.status.message), ...
377+
errormsg_expected);
378+
verifyEmpty(testCase, fieldnames(linearfitexample.resourceSpans.scopeSpans.spans.status)); % ok, no error
379+
380+
% check exception event
381+
verifyTrue(testCase, isfield(generatedata.resourceSpans.scopeSpans.spans, "events")); % exception event in "generate_data" span
382+
exception = generatedata.resourceSpans.scopeSpans.spans.events;
383+
verifyEqual(testCase, string(exception.name), "exception");
384+
% exception attributes
385+
exception_attrkeys = string({exception.attributes.key});
386+
identifieridx = find(exception_attrkeys == "exception.identifier");
387+
verifyNotEmpty(testCase, identifieridx);
388+
verifyEqual(testCase, string(exception.attributes(identifieridx).value.stringValue), errorid_expected);
389+
messageidx = find(exception_attrkeys == "exception.message");
390+
verifyNotEmpty(testCase, messageidx);
391+
verifyEqual(testCase, string(exception.attributes(messageidx).value.stringValue), errormsg_expected);
392+
causeidx = find(exception_attrkeys == "exception.cause");
393+
verifyNotEmpty(testCase, causeidx);
394+
verifyEqual(testCase, string(exception.attributes(causeidx).value.stringValue), "[]");
395+
stacktraceidx = find(exception_attrkeys == "exception.stacktrace");
396+
verifyNotEmpty(testCase, stacktraceidx);
397+
verifyTrue(testCase, contains(string(exception.attributes(stacktraceidx).value.stringValue), "generate_data.m"));
398+
399+
verifyFalse(testCase, isfield(linearfitexample.resourceSpans.scopeSpans.spans, "events")); % no exception event in "linearfit_example_trycatch" span
346400
end
347401

348402
function testMultipleInstances(testCase)

0 commit comments

Comments
 (0)