关于 matlab-instrument-opentelemetry-tracing
这项技能用于为 MATLAB 函数添加 OpenTelemetry 跟踪,面向需要监测 MATLAB 工作流的开发者,重点说明 Span 生命周期、隐式父子上下文、异常记录和语义属性。
主要功能
- 01为有意义的函数创建 Span
- 02隐式传递父子上下文
- 03记录异常与 Span 状态
- 04添加属性和关键事件
- 05遵循 OpenTelemetry 语义约定
适用场景
- 01跟踪多函数 MATLAB 工作流
- 02观测 MATLAB 文件或网络 I/O
- 03记录外部系统调用中的错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-instrument-opentelemetry-tracing安装后在 Claude Code 中输入/matlab-instrument-opentelemetry-tracing确认已加载。
附带文件manifest.yamlreferences/semantic-conventions.md
详细介绍
使用 OpenTelemetry 为 MATLAB 代码添加追踪
为 MATLAB 函数添加 OpenTelemetry 追踪 Span,并确保隐式上下文传播正确、RAII 生命周期管理得当,且遵循语义约定。
前提条件
必须安装 OpenTelemetry-MATLAB 包(版本 1.11.0 或更高)。该包提供本技能中使用的 opentelemetry.trace.* 命名空间。需要 MATLAB R2022b 或更高版本。
关键函数
| 函数 | 用途 | 工具箱 | 起始版本 |
|---|---|---|---|
opentelemetry.trace.getTracer | 按名称获取追踪器实例 | OpenTelemetry-MATLAB | R2022b |
startSpan | 从追踪器创建新的 Span | OpenTelemetry-MATLAB | R2022b |
makeCurrent | 将 Span 设为当前上下文(启用隐式父子关系) | OpenTelemetry-MATLAB | R2022b |
setAttributes | 为 Span 附加键值元数据 | OpenTelemetry-MATLAB | R2022b |
setStatus | 将 Span 标记为“Ok”或“Error” | OpenTelemetry-MATLAB | R2022b |
recordException | 在 Span 上记录一个 MException | OpenTelemetry-MATLAB | R2022b |
addEvent | 在 Span 中记录带时间戳的事件 | OpenTelemetry-MATLAB | R2022b |
endSpan | 在函数中途结束 Span(仅限循环) | OpenTelemetry-MATLAB | R2022b |
MATLAB API 差异
MATLAB OpenTelemetry API 与 C++、Java 和 Python 实现略有不同。不要依赖其他语言中的通用 OTel 知识;不确定某个函数时,请使用 help opentelemetry.trace.Span 或类似命令检查其是否存在。常见陷阱:
- 不存在
setAttribute(单数);请使用setAttributes(复数) - 不存在
opentelemetry.trace.StatusCode.Ok/StatusCode.Error枚举;请直接将字符串"Ok"或"Error"传给setStatus
适用场景
- 用户要求为 MATLAB 代码添加追踪或 Span
- 用户要求使用 OpenTelemetry 为代码添加检测
- 用户要求添加可观测性,且请求中包含追踪
- 用户希望在不同函数之间建立父子 Span 关系
不适用场景
- 用户只需要指标(计数器、直方图、仪表);无需追踪技能
- 用户只需要日志(结构化日志记录);无需追踪技能
- 用户希望配置 TracerProvider/SDK(导出器、采样器、资源);这应放在单独的设置脚本中
- 用户希望跨网络边界传播上下文(在 HTTP 标头中注入/提取)
- 用户希望在
parfor循环中添加检测;OTel 上下文在 MATLAB 中不是线程安全的
绝对规则
这些规则优先于所有其他考虑因素。违反任何一条都会导致检测不正确。
1. 绝不修改函数签名
检测必须对调用方不可见。绝不要向函数添加参数(追踪器、上下文、Span)。绝不要更改返回值。每个函数都在内部获取自己的追踪器:
tr = opentelemetry.trace.getTracer("tracer_name");
这是从全局提供程序进行的低开销查找,不会分配资源。
2. Span 遵循 RAII;绝不要在函数退出时调用 endSpan
MATLAB Span 对象持有一个 C++ 共享指针。当 Span 变量超出作用域时(函数返回或抛出错误),C++ 析构函数会自动结束 Span。只有在需要于变量自然超出作用域之前、在函数中途结束 Span 时,才调用 endSpan。
3. Scope 遵循 RAII;绝不要在函数退出时调用 clear(scope)
makeCurrent 返回的 Scope 对象会在超出作用域时恢复先前的上下文。将它赋给变量以控制其生命周期。%#ok<NASGU> 编译指示会抑制“未使用变量”警告,因为该变量的作用是控制生命周期,而不是提供值。例外:在循环中,需要调用 clear,以便在下一次迭代前恢复父上下文(参见循环模式)。
4. 始终对每个 span 调用 makeCurrent
必须将每个 span 设为当前 span,这样被调用函数中创建的子 span 才会自动成为其子 span。如果不调用 makeCurrent,子 span 就会成为孤立的根 span。
5. 切勿向 startSpan 显式传递 "Context"
只要始终使用 makeCurrent,startSpan 就会自动将当前 span 设为父 span。显式传递 "Context" 是多余的,也容易出错。唯一的例外是:创建的 span 必须成为与当前 span 不同的另一个 span 的子 span(这种情况很少见)。
6. 始终设置 span 状态
每个 span 在结束前都必须设置状态:
- 在成功路径上调用
setStatus(span, "Ok") - 在失败路径上调用
setStatus(span, "Error", message)
如果没有显式状态,跟踪查看器中的 span 状态就会含糊不清。
7. 直接使用 getTracer
tr = opentelemetry.trace.getTracer("tracer_name");
切勿使用 opentelemetry.sdk.trace.TracerProvider() 或先调用 opentelemetry.trace.Provider.getTracerProvider() 再调用 getTracer。上面的一行代码才是正确的入口。
8. 使用包含有意义领域名词的 snake_case 属性名称
所有属性名称都必须为小写,使用点分隔,并在每个组成部分中采用 snake_case。命名空间(点左侧)必须是有意义的领域名词,不能使用“total”“numeric”或“count”这类通用词。完整模式和示例请参见下文的语义约定部分。
工作流
第 1 步:分析代码
识别:
- 入口函数(用户调用的顶层函数)
- 表示不同工作单元的子函数和局部函数
- I/O 操作(文件读写、网络调用、数据库)
- 容易出错的部分(文件 I/O、外部系统、解析)
- 值得跟踪的重要计算操作
第 2 步:选择 tracer 名称
根据文件名或所属包确定:
processSensorData.m→"processSensorData"+mypackage/analyze.m→"mypackage"
同一工作流中的所有函数都应使用相同的 tracer 名称。
第 3 步:为每个函数添加埋点
对每个表示有意义工作单元的函数应用核心模式。不要为琐碎的辅助函数(少于 5 行且不涉及 I/O)或在紧密循环中调用数千次的函数添加埋点。
第 4 步:按需添加错误处理
仅对执行 I/O 或调用外部系统的函数添加包含 recordException 的 try-catch。不要对纯计算添加 try-catch。
第 5 步:添加属性和事件
添加有助于调试的属性:输入大小、关键参数、输出特征。为重要里程碑记录事件。遵循语义约定(参见 references/semantic-conventions.md)。
第 6 步:验证
通过 mcp__matlab__evaluate_matlab_code 或 mcp__matlab__run_matlab_file 运行添加埋点后的代码,确认其运行无误。
核心模式
这是每个添加埋点的函数唯一正确的模式:
function result = processData(inputData, method)
tr = opentelemetry.trace.getTracer("myService");
span = startSpan(tr, "processData");
scope = makeCurrent(span); %#ok<NASGU>
% --- original function body (unchanged) ---
result = transform(inputData, method);
setStatus(span, "Ok");
end
要点:
- 在函数内部调用
getTracer,绝不将其作为参数传递 - 调用
startSpan时不传入"Context",会自动继承当前上下文 scope = makeCurrent(span):将此 span 设为所有子 span 的父 span%#ok<NASGU>:抑制无效警告;关键在于 scope 的生命周期- 在函数返回前调用
setStatus - 不调用
endSpan,函数返回时 span 会通过 RAII 结束
多函数示例
function results = runPipeline(inputFile)
tr = opentelemetry.trace.getTracer("pipeline");
span = startSpan(tr, "runPipeline");
scope = makeCurrent(span); %#ok<NASGU>
data = loadData(inputFile);
cleaned = cleanData(data);
results = analyze(cleaned);
setStatus(span, "Ok");
end
function data = loadData(filepath)
tr = opentelemetry.trace.getTracer("pipeline");
span = startSpan(tr, "loadData");
scope = makeCurrent(span); %#ok<NASGU>
data = readtable(filepath);
setStatus(span, "Ok");
end
function cleaned = cleanData(data)
tr = opentelemetry.trace.getTracer("pipeline");
span = startSpan(tr, "cleanData");
scope = makeCurrent(span); %#ok<NASGU>
cleaned = rmmissing(data);
setStatus(span, "Ok");
end
function results = analyze(data)
tr = opentelemetry.trace.getTracer("pipeline");
span = startSpan(tr, "analyze");
scope = makeCurrent(span); %#ok<NASGU>
results = summary(data);
setStatus(span, "Ok");
end
每个函数都会独立获取 tracer 并创建自己的 span。通过 makeCurrent 自动建立父子关系。
错误处理模式
用于包含 I/O 或外部系统调用的函数:
function data = fetchData(url)
tr = opentelemetry.trace.getTracer("myService");
span = startSpan(tr, "fetchData", SpanKind="client");
scope = makeCurrent(span); %#ok<NASGU>
setAttributes(span, "url.full", url);
try
data = webread(url);
setAttributes(span, "http.response.status_code", 200);
catch ME
recordException(span, ME);
setStatus(span, "Error", ME.message);
rethrow(ME);
end
setStatus(span, "Ok");
end
要点:
recordException会捕获完整的 MException(标识符、消息、堆栈跟踪、原因)setStatus(span, "Error", ...)会在跟踪查看器中将 span 标记为出错rethrow(ME)会保留原始错误 — 即使重新抛出错误,span 仍会通过 RAII 结束recordException和setStatus都不可或缺:前者记录详细信息,后者设置可视指示标记setStatus(span, "Ok")应放在 try-catch 块之后,而不是try内部 —rethrow会退出函数,因此只有成功时才会执行到 "Ok"
属性
使用 setAttributes(复数形式;不要使用不存在的单数形式 setAttribute)设置属性:
setAttributes(span, "input.rows", height(data), "input.columns", width(data));
也可以在创建 span 时使用字典:
span = startSpan(tr, "processData", Attributes=dictionary( ...
["input.file", "input.format"], ...
{filepath, "csv"}));
数值无需进行类型转换 — API 原生支持所有 MATLAB 数值类型。
事件
为 span 中的重要里程碑记录事件:
addEvent(span, "validationComplete", "rows.valid", validCount, "rows.rejected", rejectCount);
SpanKind
仅当操作涉及远程通信时才设置 SpanKind。内部操作不要设置(默认值为 "internal")。
| SpanKind | 适用场景 | MATLAB 示例 |
|---|---|---|
"client" | 发出请求并等待响应 | webread、webwrite、ftp、数据库查询 |
"server" | 处理传入请求 | MATLAB Production Server 处理程序 |
"producer" | 发起工作但不等待 | 发布到队列、将批处理作业加入队列 |
"consumer" | 处理来自生产者的工作 | 队列侦听器、批处理工作线程 |
span = startSpan(tr, "fetchData", SpanKind="client");
何时使用 endSpan
只有在函数中途结束 span 时才使用 endSpan — 即变量尚未自然超出作用域时:
function results = batchProcess(items)
tr = opentelemetry.trace.getTracer("myService");
span = startSpan(tr, "batchProcess");
scope = makeCurrent(span); %#ok<NASGU>
results = cell(numel(items), 1);
for i = 1:numel(items)
itemSpan = startSpan(tr, "processItem");
itemScope = makeCurrent(itemSpan); %#ok<NASGU>
setAttributes(itemSpan, "item.index", i);
results{i} = processOne(items{i});
setStatus(itemSpan, "Ok");
endSpan(itemSpan); % Needed: itemSpan must end each iteration
clear("itemScope"); % Needed: restore parent context before next iteration
end
setStatus(span, "Ok");
end
循环体末尾的两行都必不可少:
endSpan(itemSpan)— 结束 span 的计时。若省略,span 持续时间会延长到下一次迭代覆盖该变量时(计时错误)。clear("itemScope")— 将父 span 恢复为当前上下文。若省略,下一次迭代中的makeCurrent会使新 span 成为上一次迭代的 span 的子 span(嵌套链),而非父 span 下的同级 span(正确的平铺列表)。
语义约定
属性名称必须遵循 {object}.{property} 模式:
- 小写并以点分隔 — 不要使用驼峰命名法
- 每个组件内部使用 snake_case — 使用
total_paths,而不是totalPaths - 命名空间应为有意义的领域名词 — 点号左侧必须是领域中的具体对象(例如
batch、file、result、data),而不是通用形容词或量词(total、numeric、count)
| 错误 | 正确 | 原因 |
|---|---|---|
batch.totalPaths | batch.total_paths | 使用 snake_case,而非驼峰命名法 |
option.S0 | option.initial_price | 描述性名称,使用小写 |
numeric.columns | data.numeric_columns | "data" 是有意义的领域名词;"numeric" 不是 |
total.paths | result.total_paths | "total" 不是领域名词 |
请参阅 references/semantic-conventions.md,了解代码属性、文件 I/O 属性、HTTP 客户端属性和特定领域模式的完整指南。
常见错误
| 错误做法 | 错误原因 | 正确做法 |
|---|---|---|
| 将 tracer 作为函数参数传入 | 修改了函数 API;会破坏现有调用方 | 在每个函数内部调用 getTracer |
| 将 context 作为函数参数传入 | 修改了函数 API;会暴露插桩细节 | 使用 makeCurrent——子 span 会自动继承 |
在函数退出时调用 endSpan | 多余——span 会通过 RAII 结束;还会增加噪声 | 删除它;仅在函数中途使用 endSpan |
在函数退出时调用 clear(scope) | 多余——函数返回时,scope 会通过 RAII 恢复 context | 删除它;让变量超出作用域。在循环内部,必须使用 clear——参见上面的循环模式 |
将 "Context" 传给 startSpan | 使用 makeCurrent 时多余;会造成显式耦合 | 省略它;startSpan 会使用当前 context |
在子函数中省略 makeCurrent | 孙级 span 不会继承 context;会破坏深层追踪 | 始终对每个 span 调用 makeCurrent |
使用 setAttribute(单数) | MATLAB OTel API 中不存在 | 使用 setAttributes(复数) |
省略 setStatus | 追踪查看器中的 span 状态不明确 | 始终设置为 "Ok" 或 "Error" |
用 if isConfigured 包装 OTel 代码 | 没有必要——未配置时,OTel 方法会执行空操作 | 删除条件判断;始终进行插桩 |
先使用 TracerProvider() 再调用 getTracer | 方式间接;多写一行代码,却没有好处 | 直接使用 opentelemetry.trace.getTracer(name) |
将属性强制转换为 int64 | 没有必要——API 支持所有 MATLAB 数值类型 | 直接使用原生类型 |
不要插桩的内容
- 简单的辅助函数(少于 5 行且无 I/O)
- 在紧密循环中调用数千次的函数(会产生开销)
- 匿名函数或回调(除非包含重要工作)
parfor代码块中的代码(context 不是线程安全的)
版权所有 2026 The MathWorks, Inc.