Skip to content

Commit 4ccf8ce

Browse files
committed
Add AutoTrace source code
1 parent e2ace2e commit 4ccf8ce

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ set(TRACE_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/trace/+opentelemetr
476476
set(METRICS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/metrics/+opentelemetry)
477477
set(LOGS_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/logs/+opentelemetry)
478478
set(COMMON_SDK_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sdk/common/+opentelemetry)
479+
set(AUTO_INSTRUMENTATION_MATLAB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/auto-instrumentation/+opentelemetry)
479480
set(EXPORTER_MATLAB_SOURCES
480481
${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultSpanExporter.m
481482
${CMAKE_CURRENT_SOURCE_DIR}/exporters/otlp/+opentelemetry/+exporters/+otlp/defaultMetricExporter.m
@@ -510,6 +511,7 @@ install(DIRECTORY ${TRACE_SDK_MATLAB_SOURCES} DESTINATION .)
510511
install(DIRECTORY ${METRICS_SDK_MATLAB_SOURCES} DESTINATION .)
511512
install(DIRECTORY ${LOGS_SDK_MATLAB_SOURCES} DESTINATION .)
512513
install(DIRECTORY ${COMMON_SDK_MATLAB_SOURCES} DESTINATION .)
514+
install(DIRECTORY ${AUTO_INSTRUMENTATION_MATLAB_SOURCES} DESTINATION .)
513515
install(FILES ${EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR})
514516
if(WITH_OTLP_HTTP)
515517
install(FILES ${OTLP_HTTP_EXPORTER_MATLAB_SOURCES} DESTINATION ${OTLP_EXPORTERS_DIR})
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
classdef AutoTrace < handle
2+
% Automatic instrumentation with OpenTelemetry tracing.
3+
4+
% Copyright 2024 The MathWorks, Inc.
5+
6+
properties (SetAccess=private)
7+
StartFunction function_handle % entry function
8+
InstrumentedFiles string % list of M-files that are auto-instrumented
9+
end
10+
11+
properties (Access=private)
12+
Instrumentor (1,1) opentelemetry.autoinstrument.AutoTraceInstrumentor % helper object
13+
end
14+
15+
methods
16+
function obj = AutoTrace(startfun, options)
17+
% AutoTrace Automatic instrumentation with OpenTelemetry tracing
18+
% AT = OPENTELEMETRY.AUTOINSTRUMENT.AUTOTRACE(FUN) where FUN
19+
% is a function handle, automatically instruments the function
20+
% and all the functions in the same file, as well as their dependencies.
21+
% For each function, a span is automatically started and made
22+
% current at the beginning, and ended at the end. Returns an
23+
% object AT. When AT is cleared or goes out-of-scope, automatic
24+
% instrumentation will stop and the functions will no longer
25+
% be instrumented.
26+
%
27+
% AT = OPENTELEMETRY.AUTOINSTRUMENT.AUTOTRACE(FUN, NAME1, VALUE1,
28+
% NAME2, VALUE2, ...) specifies optional name-value pairs.
29+
% Supported options are:
30+
% "AdditionalFiles" - List of additional file names to
31+
% include. Specifying additional files
32+
% are useful in cases when automatic
33+
% dependency detection failed to include them.
34+
% For example, MATLAB Toolbox functions
35+
% authored by MathWorks are excluded by default.
36+
% "ExcludeFiles" - List of file names to exclude
37+
% "AutoDetectFiles" - Whether to automatically include dependencies
38+
% of FUN, specified as a logical scalar.
39+
% Default value is true.
40+
% "TracerName" - Specifies the name of the tracer
41+
% the automatic spans are generated from
42+
% "TracerVersion" - The tracer version
43+
% "TracerSchema" - The tracer schema
44+
% "Attributes" - Add attributes to all the automatic spans.
45+
% Attributes must be specified as a dictionary.
46+
% "SpanKind" - Span kind of the automatic spans
47+
arguments
48+
startfun (1,1) function_handle
49+
options.TracerName {mustBeTextScalar} = "AutoTrace"
50+
options.TracerVersion {mustBeTextScalar} = ""
51+
options.TracerSchema {mustBeTextScalar} = ""
52+
options.SpanKind {mustBeTextScalar}
53+
options.Attributes {mustBeA(options.Attributes, "dictionary")}
54+
options.ExcludeFiles {mustBeText}
55+
options.AdditionalFiles {mustBeText}
56+
options.AutoDetectFiles (1,1) {mustBeNumericOrLogical} = true
57+
end
58+
obj.StartFunction = startfun;
59+
startfunname = func2str(startfun);
60+
processFileInput(startfunname); % validate startfun
61+
if options.AutoDetectFiles
62+
if isdeployed
63+
% matlab.codetools.requiredFilesAndProducts is not
64+
% deployable. Instead instrument all files under CTFROOT
65+
fileinfo = dir(fullfile(ctfroot, "**", "*.m"));
66+
files = fullfile(string({fileinfo.folder}), string({fileinfo.name}));
67+
68+
% filter out internal files in the toolbox directory
69+
files = files(~startsWith(files, fullfile(ctfroot, "toolbox")));
70+
else
71+
%#exclude matlab.codetools.requiredFilesAndProducts
72+
files = string(matlab.codetools.requiredFilesAndProducts(startfunname));
73+
end
74+
else
75+
% only include the input file, not its dependencies
76+
files = string(which(startfunname));
77+
end
78+
% add extra files, this is intended for files
79+
% matlab.codetools.requiredFilesAndProducts somehow missed
80+
if isfield(options, "AdditionalFiles")
81+
incfiles = string(options.AdditionalFiles);
82+
for i = 1:numel(incfiles)
83+
incfiles(i) = which(incfiles(i)); % get the full path
84+
processFileInput(incfiles(i)); % validate additional file
85+
end
86+
files = union(files, incfiles);
87+
end
88+
89+
% make sure files are unique
90+
files = unique(files);
91+
92+
% filter out excluded files
93+
if isfield(options, "ExcludeFiles")
94+
excfiles = string(options.ExcludeFiles);
95+
for i = 1:numel(excfiles)
96+
excfiles(i) = which(excfiles(i)); % get the full path
97+
end
98+
files = setdiff(files, excfiles);
99+
end
100+
% filter out OpenTelemetry files, in case manual
101+
% instrumentation is also used
102+
files = files(~contains(files, ["+opentelemetry" "+libmexclass"]));
103+
104+
for i = 1:length(files)
105+
currfile = files(i);
106+
if currfile =="" % ignore empties
107+
continue
108+
end
109+
obj.Instrumentor.instrument(currfile, options);
110+
obj.InstrumentedFiles(end+1,1) = currfile;
111+
end
112+
end
113+
114+
function delete(obj)
115+
obj.Instrumentor.cleanup(obj.InstrumentedFiles);
116+
end
117+
118+
function varargout = beginTrace(obj, varargin)
119+
% beginTrace Run the auto-instrumented function
120+
% [OUT1, OUT2, ...] = BEGINTRACE(AT, IN1, IN2, ...) calls the
121+
% instrumented function with error handling. In case of
122+
% error, all running spans will end and the last span will
123+
% set to an "Error" status. The instrumented function is
124+
% called with the synax [OUT1, OUT2, ...] = FUN(IN1, IN2, ...)
125+
%
126+
% See also OPENTELEMETRY.AUTOINSTRUMENT.AUTOTRACE/HANDLEERROR
127+
try
128+
varargout = cell(1,nargout);
129+
[varargout{:}] = feval(obj.StartFunction, varargin{:});
130+
catch ME
131+
handleError(obj, ME);
132+
end
133+
end
134+
135+
function handleError(obj, ME)
136+
% handleError Perform cleanup in case of an error
137+
% HANDLEERROR(AT, ME) performs cleanup by ending all running
138+
% spans and their corresponding scopes. Rethrow the
139+
% exception ME.
140+
if ~isempty(obj.Instrumentor.Spans)
141+
setStatus(obj.Instrumentor.Spans(end), "Error");
142+
for i = length(obj.Instrumentor.Spans):-1:1
143+
obj.Instrumentor.Spans(i) = [];
144+
obj.Instrumentor.Scopes(i) = [];
145+
end
146+
end
147+
rethrow(ME);
148+
end
149+
end
150+
151+
152+
end
153+
154+
% check input file is valid
155+
function processFileInput(f)
156+
f = string(f); % force into a string
157+
if startsWith(f, '@') % check for anonymous function
158+
error(f + " is an anonymous function and is not supported.");
159+
end
160+
if exist(f, "file") ~= 2
161+
error(f + " is not a valid MATLAB file with a .m extension and is not supported.")
162+
end
163+
end
Binary file not shown.

0 commit comments

Comments
 (0)