Matlab Instrument Opentelemetry Tracing
MathWorksOfficialCreated by MathWorks
- Skill
- Tool integration
- Software development
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a standardized approach to instrumenting MATLAB code with OpenTelemetry spans. It leverages the OpenTelemetry-MATLAB package to enable performance monitoring and debugging through distributed tracing. The implementation follows RAII principles for span lifecycle management, ensuring that spans are correctly created, linked via parent-child context propagation, and terminated without manual intervention in standard function flows. It includes specific patterns for error handling, attribute tagging, and event recording, while adhering to semantic conventions for consistent telemetry data.
What it does
- Creates and manages OpenTelemetry spans within MATLAB functions.
- Establishes parent-child relationships between spans using implicit context propagation.
- Provides patterns for recording exceptions and setting span status.
- Supports adding metadata via attributes and timestamped events.
- Defines standard naming conventions for attributes and span kinds.
- Handles span lifecycle automatically using RAII.
When to use it
- When you need to trace execution flow across multiple MATLAB functions.
- When you want to identify performance bottlenecks in your MATLAB code.
- When you need to capture detailed error information within a distributed trace.
- When you are instrumenting I/O-heavy or external system-dependent MATLAB code.
Advantages
- Ensures consistent telemetry across different MATLAB services.
- Reduces boilerplate by using RAII for span and scope management.
- Provides clear patterns for handling errors and recording exceptions.
- Maintains clean function signatures by avoiding explicit context passing.
Usage examples
- “How do I add OpenTelemetry tracing to my existing MATLAB function?”
- The agent will provide the core pattern, showing how to initialize a tracer, start a span, and use makeCurrent to establish context, ensuring the function remains unchanged in its signature.
- “How should I handle errors when tracing a file read operation in MATLAB?”
- The agent will provide an error-handling pattern using try-catch blocks, recordException, and setStatus to ensure the span correctly reflects the failure in trace viewers.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/matlab/matlab-instrument-opentelemetry-tracing/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-instrument-opentelemetry-tracing.zip -d .agents/skills
Documentation by MathWorks
Instrument MATLAB Code with OpenTelemetry Tracing
Add OpenTelemetry tracing spans to MATLAB functions with correct implicit context propagation, RAII lifecycle, and semantic conventions.
Prerequisites
The OpenTelemetry-MATLAB package (Version 1.11.0 or newer) must be installed. It provides the opentelemetry.trace.* namespace used throughout this skill. Requires MATLAB R2022b or newer.
Key Functions
| Function | Purpose | Toolbox | Available From |
|---|---|---|---|
opentelemetry.trace.getTracer | Obtain a tracer instance by name | OpenTelemetry-MATLAB | R2022b |
startSpan | Create a new span from a tracer | OpenTelemetry-MATLAB | R2022b |
makeCurrent | Set span as current context (enables implicit parent-child) | OpenTelemetry-MATLAB | R2022b |
setAttributes | Attach key-value metadata to a span | OpenTelemetry-MATLAB | R2022b |
setStatus | Mark span as "Ok" or "Error" | OpenTelemetry-MATLAB | R2022b |
recordException | Record an MException on a span | OpenTelemetry-MATLAB | R2022b |
addEvent | Record a timestamped event within a span | OpenTelemetry-MATLAB | R2022b |
endSpan | End a span mid-function (loops only) | OpenTelemetry-MATLAB | R2022b |
MATLAB API Differences
The MATLAB OpenTelemetry API differs slightly from C++, Java, and Python implementations. Do not rely on general OTel knowledge from other languages — when unsure about a function, use help opentelemetry.trace.Span or similar to check what exists. Common traps:
setAttribute(singular) does not exist — usesetAttributes(plural)opentelemetry.trace.StatusCode.Ok/StatusCode.Errorenums do not exist — pass the strings"Ok"or"Error"directly tosetStatus
When to Use
User asks to add tracing or spans to MATLAB code
User asks to instrument code with OpenTelemetry
User asks to add observability and tracing is part of the request
User wants parent-child span relationships across functions
When NOT to Use
User wants metrics only (counters, histograms, gauges) — no tracing skill needed
User wants logging only (structured log records) — no tracing skill needed
User wants to configure the TracerProvider/SDK (exporters, samplers, resource) — that belongs in a separate setup script
User wants to propagate context across network boundaries (inject/extract into HTTP headers)
User wants to instrument inside
parforloops — OTel context is not thread-safe in MATLAB
Absolute Rules
These rules override all other considerations. Violating any of them produces incorrect instrumentation.
1. Never modify function signatures
Instrumentation must be invisible to callers. Never add parameters (tracer, context, span) to a function. Never change return values. Each function obtains its own tracer internally:
tr = opentelemetry.trace.getTracer("tracer_name");This is a cheap lookup from the global provider, not a resource allocation.
2. Spans are RAII — never call endSpan at function exit
The MATLAB Span object holds a C++ shared pointer. When the span variable goes out of scope (function returns, error thrown), the C++ destructor automatically ends the span. Only call endSpan when you need to end a span mid-function before the variable naturally goes out of scope.
3. Scope is RAII — never call clear(scope) at function exit
The Scope object returned by makeCurrent restores the previous context when it goes out of scope. Assign it to a variable to control its lifetime. The %#ok<NASGU> pragma suppresses the "unused variable" warning because the variable's lifetime is its purpose, not its value. Exception: inside loops, clear is required to restore the parent context before the next iteration (see loop pattern).
4. Always call makeCurrent on every span
Every span must be made current so child spans created in called functions automatically become children. Without makeCurrent, child spans become orphaned root spans.
5. Never pass explicit "Context" to startSpan
When makeCurrent is used consistently, startSpan automatically picks up the current span as parent. Passing "Context" explicitly is redundant and error-prone. The only exception is when creating a span that must be a child of a different span than the current one (rare).
6. Always set span status
Every span must have its status set before it ends:
setStatus(span, "Ok")on the success pathsetStatus(span, "Error", message)on the failure path
A span without explicit status is ambiguous in trace viewers.
7. Use getTracer directly
tr = opentelemetry.trace.getTracer("tracer_name");Never use opentelemetry.sdk.trace.TracerProvider() or opentelemetry.trace.Provider.getTracerProvider() followed by getTracer. The one-liner above is the correct entry point.
8. Use snake_case attribute names with meaningful domain nouns
All attribute names must be lowercase, dot-separated, and use snake_case within each component. The namespace (left of the dot) must be a meaningful domain noun — not a generic word like "total", "numeric", or "count". See the Semantic Conventions section below for the full pattern and examples.
Workflow
Step 1: Analyze the code
Identify:
Entry-point function (top-level function the user calls)
Sub-functions and local functions that represent distinct units of work
I/O operations (file read/write, network calls, database)
Error-prone sections (file I/O, external systems, parsing)
Computationally significant operations worth tracking
Step 2: Choose a tracer name
Derive from the file name or containing package:
processSensorData.m→"processSensorData"+mypackage/analyze.m→"mypackage"
All functions in the same workflow should share the same tracer name.
Step 3: Instrument each function
Apply the core pattern to every function that represents a meaningful unit of work. Do not instrument trivial helpers (< 5 lines, no I/O) or functions called thousands of times in tight loops.
Step 4: Add error handling where needed
Add try-catch with recordException only for functions that perform I/O or call external systems. Do not add try-catch around pure computation.
Step 5: Add attributes and events
Add attributes that would help with debugging: input sizes, key parameters, output characteristics. Record events for significant milestones. Follow semantic conventions (see references/semantic-conventions.md).
Step 6: Verify
Run the instrumented code via mcp__matlab__evaluate_matlab_code or mcp__matlab__run_matlab_file to confirm it executes without error.
Core Pattern
This is the one correct pattern for every instrumented function:
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");
endKey points:
getTracerinside the function — never passed as argumentstartSpanwith no"Context"— inherits from current context automaticallyscope = makeCurrent(span)— makes this span the parent for any child spans%#ok<NASGU>— suppresses false warning; scope lifetime is the pointsetStatusbefore the function returnsNo
endSpan— span ends via RAII when function returns
Multi-function example
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");
endEach function independently obtains a tracer and creates its own span. Parent-child relationships are established automatically through makeCurrent.
Error Handling Pattern
Use for functions with I/O or external system calls:
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");
endKey points:
recordExceptioncaptures the full MException (identifier, message, stack trace, cause)setStatus(span, "Error", ...)marks the span as errored in trace viewersrethrow(ME)preserves the original error — span still ends via RAII after rethrowBoth
recordExceptionandsetStatusare needed: one records detail, the other sets the visual indicatorsetStatus(span, "Ok")goes after the try-catch block, not insidetry—rethrowexits the function so "Ok" is only reached on success
Attributes
Set attributes with setAttributes (plural, never singular setAttribute which does not exist):
setAttributes(span, "input.rows", height(data), "input.columns", width(data));Or using a dictionary at span creation:
span = startSpan(tr, "processData", Attributes=dictionary( ...
["input.file", "input.format"], ...
{filepath, "csv"}));Numeric values do not need type casting — the API handles all MATLAB numeric types natively.
Events
Record events for significant milestones within a span:
addEvent(span, "validationComplete", "rows.valid", validCount, "rows.rejected", rejectCount);SpanKind
Set SpanKind only when the operation involves remote communication. Do not set it for internal operations (the default is "internal").
| SpanKind | When | MATLAB examples |
|---|---|---|
"client" | Outgoing request awaiting response | webread, webwrite, ftp, database queries |
"server" | Handling an incoming request | MATLAB Production Server handler |
"producer" | Initiating work without waiting | Publishing to a queue, enqueuing a batch job |
"consumer" | Processing work from a producer | Queue listener, batch worker |
span = startSpan(tr, "fetchData", SpanKind="client");When to Use endSpan
Only use endSpan when ending a span mid-function — before the variable would naturally go out of scope:
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");
endBoth lines at the end of the loop body are mandatory:
endSpan(itemSpan)— ends the span's timing. Without it, the span duration extends until the variable is overwritten on the next iteration (wrong timing).clear("itemScope")— restores the parent span as the current context. Without it,makeCurrenton the next iteration would make the new span a child of the previous iteration's span (nested chain) instead of a sibling under the parent (correct flat list).
Semantic Conventions
Attribute names must follow the {object}.{property} pattern:
Lowercase and dot-separated — never camelCase
snake_case within each component —
total_pathsnottotalPathsNamespace is a meaningful domain noun — the word left of the dot must be a concrete object in the domain (e.g.,
batch,file,result,data), not a generic adjective or quantifier (total,numeric,count)
| Wrong | Right | Why |
|---|---|---|
batch.totalPaths | batch.total_paths | snake_case, not camelCase |
option.S0 | option.initial_price | descriptive, lowercase |
numeric.columns | data.numeric_columns | "data" is meaningful domain noun; "numeric" is not |
total.paths | result.total_paths | "total" is not a domain noun |
See references/semantic-conventions.md for full guidance on code attributes, file I/O attributes, HTTP client attributes, and domain-specific patterns.
Common Mistakes
| Mistake | Why it's wrong | Correct approach |
|---|---|---|
| Pass tracer as function argument | Modifies the function API; breaks existing callers | Call getTracer inside each function |
| Pass context as function argument | Modifies the function API; makes instrumentation visible | Use makeCurrent — child spans inherit automatically |
Call endSpan at function exit | Redundant — span ends via RAII; adds noise | Remove it; only use endSpan mid-function |
Call clear(scope) at function exit | Redundant — scope restores context via RAII when function returns | Remove it; let the variable go out of scope. Inside loops, clear IS required — see loop pattern above |
Pass "Context" to startSpan | Redundant when makeCurrent is used; explicit coupling | Omit it; startSpan uses current context |
Omit makeCurrent in child function | Grandchild spans won't inherit context; breaks deep traces | Always call makeCurrent on every span |
Use setAttribute (singular) | Does not exist in MATLAB OTel API | Use setAttributes (plural) |
Omit setStatus | Span status is ambiguous in trace viewer | Always set "Ok" or "Error" |
Wrap OTel code in if isConfigured | Unnecessary — OTel methods are no-ops when not configured | Remove the guard; instrument unconditionally |
Use TracerProvider() then getTracer | Indirect; extra line of code for no benefit | Use opentelemetry.trace.getTracer(name) directly |
Cast attributes to int64 | Unnecessary — API handles all MATLAB numeric types | Use native types as-is |
What NOT to Instrument
Trivial helper functions (< 5 lines, no I/O)
Functions called thousands of times in tight loops (overhead)
Anonymous functions or callbacks (unless significant work)
Code inside
parforblocks (context is not thread-safe)
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.1 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md15.7 KiB
- manifest.yaml453 B
- references/semantic-conventions.md5.8 KiB
references/semantic-conventions.md
Semantic Conventions for MATLAB Tracing
OpenTelemetry semantic conventions provide standardized attribute names so telemetry data integrates cleanly with backends (Jaeger, Grafana Tempo, etc.) and is consistent across services.
Attribute Naming Rules
Lowercase with dot-separated namespaces:
service.name,http.request.methodsnake_case within components:
http.response.status_code{object}.{property}pattern:file.path,db.systemNever use camelCase or UPPER_CASE for attribute keys
Prefer established namespaces over inventing new ones
General Code Attributes
Use for spans that wrap function calls:
| Attribute | Type | Description | Example |
|---|---|---|---|
code.function.name | string | Function name | "processData" |
code.namespace | string | Package or class | "mypackage" |
code.file.path | string | Source file path | "src/processData.m" |
These are optional — span names already convey the function. Use when extra context helps (e.g., disambiguating overloaded names).
File I/O Attributes
Use for spans wrapping file operations (readtable, writetable, load, save, fopen):
| Attribute | Type | Description | Example |
|---|---|---|---|
file.path | string | Full file path | "/data/input.csv" |
file.name | string | File name only | "input.csv" |
file.size | int | File size in bytes | 1048576 |
file.extension | string | File extension | "csv" |
span = startSpan(tr, "loadData", SpanKind="internal");
scope = makeCurrent(span); %#ok<NASGU>
setAttributes(span, "file.path", filepath, "file.extension", "csv");
data = readtable(filepath);
setAttributes(span, "file.size", dir(filepath).bytes);
setStatus(span, "Ok");HTTP Client Attributes
Use for spans wrapping webread, webwrite, websave, or weboptions-based calls. Set SpanKind="client".
Span naming
Format: {HTTP_METHOD} (e.g., "GET", "POST"). Append a route template if known: "GET /api/sensors".
Required attributes
| Attribute | Type | Description | Example |
|---|---|---|---|
http.request.method | string | HTTP method | "GET", "POST" |
url.full | string | Full request URL (no credentials) | "https://api.example.com/data" |
server.address | string | Server hostname or IP | "api.example.com" |
server.port | int | Server port | 443 |
Conditional attributes
| Attribute | When | Example |
|---|---|---|
http.response.status_code | After response received | 200, 404, 500 |
error.type | On error | "timeout", "404" |
Example
function data = fetchSensorData(baseUrl, sensorId)
tr = opentelemetry.trace.getTracer("sensorService");
url = baseUrl + "/sensors/" + sensorId + "/readings";
span = startSpan(tr, "GET /sensors/{id}/readings", SpanKind="client");
scope = makeCurrent(span); %#ok<NASGU>
setAttributes(span, ...
"http.request.method", "GET", ...
"url.full", url, ...
"server.address", extractHost(baseUrl), ...
"server.port", 443);
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
function host = extractHost(url)
parts = split(extractAfter(url, "://"), "/");
host = parts(1);
endDatabase Attributes
Use for spans wrapping Database Toolbox calls (fetch, sqlread, execute). Set SpanKind="client".
| Attribute | Type | Description | Example |
|---|---|---|---|
db.system | string | Database system | "mysql", "postgresql", "sqlite" |
db.operation.name | string | Operation type | "SELECT", "INSERT" |
db.collection.name | string | Table name | "sensor_readings" |
server.address | string | Database host | "db.example.com" |
server.port | int | Database port | 5432 |
Domain-Specific Attributes
For MATLAB-specific computations where no standard convention exists, use a descriptive namespace:
| Pattern | Example namespace | Example attributes |
|---|---|---|
| Signal processing | signal.* | signal.sample_rate, signal.num_channels |
| Simulation | simulation.* | simulation.time_step, simulation.duration |
| Optimization | optimization.* | optimization.algorithm, optimization.iterations |
| Data processing | data.* | data.input_rows, data.output_rows |
Rules for custom namespaces:
Use a short, descriptive prefix relevant to the domain
Keep attribute names general enough to be reusable
Use the same namespace across all functions in a workflow
Prefer plural nouns for counts:
data.rows_processednotdata.row_processed
Example
function result = filterSignal(data, fs, cutoff)
tr = opentelemetry.trace.getTracer("signalPipeline");
span = startSpan(tr, "filterSignal");
scope = makeCurrent(span); %#ok<NASGU>
setAttributes(span, ...
"signal.sample_rate", fs, ...
"signal.cutoff_frequency", cutoff, ...
"signal.num_samples", height(data));
result = lowpass(data, cutoff, fs);
setAttributes(span, "signal.output_samples", height(result));
setStatus(span, "Ok");
endChoosing Between Standard and Custom Conventions
If the operation maps to a recognized category (HTTP, file I/O, database), use the standard conventions
If the operation is domain-specific (signal processing, simulation, financial modeling), use a descriptive custom namespace
Never mix standard and custom for the same concept — pick one and be consistent within the workflow
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/matlab-software-development/matlab-instrument-opentelemetry-tracing · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-instrument-opentelemetry-tracing.zip