mcpField
M
matlab-instrument-opentelemetry-tracing

matlab-instrument-opentelemetry-tracing

github.com/matlab开发1.2k43 次安装1 天前更新
代码仓库

关于 matlab-instrument-opentelemetry-tracing

这项技能用于为 MATLAB 函数添加 OpenTelemetry 跟踪,面向需要监测 MATLAB 工作流的开发者,重点说明 Span 生命周期、隐式父子上下文、异常记录和语义属性。

主要功能

  1. 01为有意义的函数创建 Span
  2. 02隐式传递父子上下文
  3. 03记录异常与 Span 状态
  4. 04添加属性和关键事件
  5. 05遵循 OpenTelemetry 语义约定

适用场景

  1. 01跟踪多函数 MATLAB 工作流
  2. 02观测 MATLAB 文件或网络 I/O
  3. 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-MATLABR2022b
startSpan从追踪器创建新的 SpanOpenTelemetry-MATLABR2022b
makeCurrent将 Span 设为当前上下文(启用隐式父子关系)OpenTelemetry-MATLABR2022b
setAttributes为 Span 附加键值元数据OpenTelemetry-MATLABR2022b
setStatus将 Span 标记为“Ok”或“Error”OpenTelemetry-MATLABR2022b
recordException在 Span 上记录一个 MExceptionOpenTelemetry-MATLABR2022b
addEvent在 Span 中记录带时间戳的事件OpenTelemetry-MATLABR2022b
endSpan在函数中途结束 Span(仅限循环)OpenTelemetry-MATLABR2022b

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.totalPathsbatch.total_paths使用 snake_case,而非驼峰命名法
option.S0option.initial_price描述性名称,使用小写
numeric.columnsdata.numeric_columns"data" 是有意义的领域名词;"numeric" 不是
total.pathsresult.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.


评论

支持 Markdown