Skip to content

run --local: --trace-otlp to export traces to an OTLP collector - #49

Merged
ako merged 4 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h
Jul 27, 2026
Merged

run --local: --trace-otlp to export traces to an OTLP collector#49
ako merged 4 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h

Conversation

@ako

@ako ako commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Follow-up to the OpenTelemetry tracing work (PR #46), addressing the one open item the sudoku project surfaced while verifying it.

Problem

mxcli run --local --trace attaches the OpenTelemetry agent with the console exporter (spans land in the runtime log). But the console exporter omits start/end timestamps and parent span IDs, so call trees and durations can't be reconstructed — no flame charts. Getting usable traces meant hand-setting three env vars before invoking mxcli:

export OTEL_TRACES_EXPORTER=otlp \
       OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
       OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
mxcli run --local -p app.mpr --trace

Change

Add --trace-otlp <endpoint> (implies --trace):

mxcli run --local -p app.mpr --trace-otlp http://127.0.0.1:4318

It switches the traces exporter to OTLP and wires the protocol (http/protobuf) + endpoint for you. User-set OTEL_* variables still take precedence, so a fully hand-rolled env keeps working; without the flag the console default is unchanged.

Wiring: cmd_run.go (flag + --trace implication) → runlocal.go (LocalRunOptions.TraceOTLP, boot message) → withTraceEnv in localboot.go (the actual OTEL_* env). The boot line now reports the OTLP endpoint, and the console-only message points at --trace-otlp.

Testing

  • TestWithTraceEnv extended: an OTLP endpoint sets exporter+protocol+endpoint; a user-pinned exporter overrides --trace-otlp; the console default is untouched.
  • go build ./... clean; gofmt clean on changed files; go test ./cmd/mxcli/docker/ trace/runtime-config tests pass (the pre-existing environmental TestServeIntegration — needs a matching mxbuild cache — is unrelated).
  • Rebased onto the latest main (synced from mendixlabs); --trace-otlp verified present in mxcli run --help.

Docs updated: run-local skill, docs-site run-local page, --trace/--trace-otlp help, and the CLAUDE.md OpenTelemetry entry.

Reported from the sudoku project (OpenTelemetry tracing follow-up — the console-exporter limitation on finding #29).

🤖 Generated with Claude Code


Generated by Claude Code

claude added 4 commits July 27, 2026 06:13
The `--trace` console exporter omits start/end timestamps and parent span
IDs, so call trees and durations can't be reconstructed from it — for
flame charts you had to hand-set three OTEL_* env vars
(OTEL_TRACES_EXPORTER=otlp, OTEL_EXPORTER_OTLP_PROTOCOL,
OTEL_EXPORTER_OTLP_ENDPOINT).

Add `mxcli run --local --trace-otlp <endpoint>` (implies --trace): it
switches the traces exporter to OTLP and points it at the collector
(protocol http/protobuf). User-set OTEL_* still take precedence, so a
fully hand-rolled env keeps working; without the flag the console
default is unchanged.

Reported from the sudoku project (OpenTelemetry tracing follow-up on the
console-exporter limitation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Plain `refresh catalog` leaves the analytic tables empty (0 rows, no
error); a query then silently returns nothing. `warnIfCatalogModeInsufficient`
already warns for the FULL-only views, but `xpath_expressions` was missing
from `fullOnlyTables` — so `SELECT … FROM CATALOG.XPATH_EXPRESSIONS` in
fast mode returned empty with no hint (activities/refs were covered, but
xpath was not).

Add `xpath_expressions` to `fullOnlyTables` so a fast-mode query against
it warns "requires refresh catalog full" like the sibling analytic tables.
TestFullOnlyTablesCoverage guards that every FULL-gated view
(activities/refs/xpath_expressions/widgets) is flagged and a fast-mode
table (entities) is not.

Reported from the sudoku "app warehouse" finding (#30) — the
REFRESH-CATALOG-FULL silent-empty papercut.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
The catalog is a plain SQLite database at <projectDir>/.mxcli/catalog.db —
exporting it to JSON first (as one might reach for) is strictly worse: it
loses most of the tables, adds human-readable chatter, and goes stale.
Document attaching it directly, plus an external DuckDB "app warehouse"
recipe that joins the catalog to the app's Postgres and the OTLP trace
dump read-only for cross-source questions no single source can answer
(mxcli does not embed DuckDB — this is a dev-container recipe).

Also flag, in both the use-cases page and the REFRESH CATALOG page, that
FULL mode is what populates the analytic tables (activities/refs/
xpath_expressions/widgets) — plain refresh leaves them empty.

Reported from the sudoku "app warehouse" finding (#30).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…alog)

Analyzing an app's runtime behavior is a recurring procedure spanning four
signals — the runtime log, Prometheus metrics, OpenTelemetry traces, and
the catalog (model shape) — so it belongs in a skill, not scattered across
feature docs.

Add `.claude/skills/mendix/analyze-runtime.md`: when to reach for each
signal, how to collect it (`run --local` + `--metrics` / `--trace` /
`--trace-otlp`, `refresh catalog full`), the cross-source "app warehouse"
join (external DuckDB — mxcli does not embed it), the logs↔traces gap, and
a decision guide mapping questions to tools. Registered in the skills
README (Specialized Skills + loading guide); synced into user projects by
`mxcli init`.

Trim the docs-site catalog "app warehouse" block to the catalog-attach
feature note plus a pointer to the skill, so the full procedure has a
single canonical home.

Follow-up to the sudoku "app warehouse" finding (#30).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit e4c1f65 into main Jul 27, 2026
5 checks passed
ako pushed a commit that referenced this pull request Jul 29, 2026
…tly dropping them

A `create page X { ...widgets... }` with no `Layout:` clause reported "Created page"
but produced an EMPTY page: buildPageV3 only builds a LayoutCall when Layout: is
present, and the widget tree is built into that LayoutCall's placeholder arguments —
so with no LayoutCall the widgets have nowhere to attach and were silently dropped.
Mendix then rejects the layout-less page at build with CE1613 ("layout
'dummyModule.dummyName' no longer exists" — its internal placeholder for a missing
layout). mxcli check passed; the widgets were simply gone — the same silent
data-loss pattern the TimeRegistration findings are about.

buildPageV3 now returns an actionable error when a page has body widgets (or
placeholder blocks) but no LayoutCall, distinguishing "no Layout: clause" from
"layout not found". Empty layout-less pages are unaffected (nothing to drop), and
snippets (buildSnippetV3) are layout-less by design and untouched.

Discovered while reproducing FINDINGS #49 (which no longer reproduces) against a
real Mendix 11.12.1 project + mx check. Unit tests (widgets→error, empty→ok);
repro mdl-examples/bug-tests/266; fixed the pre-existing no-layout page in the
ce0148 example. Symptom row added to fix-issue.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants