Skip to content

run --local: attach a file log subscriber so application logs are captured - #39

Merged
ako merged 1 commit into
mainfrom
fix/runtime-log-subscriber
Jul 26, 2026
Merged

run --local: attach a file log subscriber so application logs are captured#39
ako merged 1 commit into
mainfrom
fix/runtime-log-subscriber

Conversation

@ako

@ako ako commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

Round 2 of findings #25. PR #38 tee'd the runtime JVM's stdout/stderr to .mxcli/runtime.log, but a re-test on the Sudoku app showed the file stays nearly empty: microflow LOG output and server-side exception stack traces never appear, so a page-action error still can't be diagnosed (the browser only shows the generic Mendix dialog).

Root cause: a standalone runtime launched via runtimelauncher.jar attaches no log subscriber by default — unlike a Studio Pro / m2ee run, which calls create_log_subscriber after start. Mendix application logs flow to log subscribers, not stdout, so with none attached they go nowhere.

Fix

After a successful start (and on every restart's Start, since each fresh JVM has no subscriber), attach a file log subscriber via the create_log_subscriber admin action, mirroring m2ee-tools' post-start _configure_logging:

{ type: "file", name: "mxcli-run-local", autosubscribe: "INFO",
  filename: <abs runtime.log>, max_size: 1 GiB, max_rotate: 0 }
  • max_rotate: 0 is load-bearing — the JVM stdout tee (PR run --local: tee the Mendix runtime log to a file for debugging #38) holds an open fd on the same file, and a rotate-rename would detach it. Both writers append.
  • The path is passed absolute (the runtime's cwd is <install>/runtime, not mxcli's).
  • Best-effort: a logging failure warns to stdout rather than failing an otherwise-healthy start. Disabling the file with --runtime-log - disables the subscriber too.

Tests

  • TestStart_AttachesLogSubscriber — asserts the params (type/filename/autosubscribe/name/max_rotate:0) after a successful start.
  • TestStart_NoLogSubscriberWhenUnset — no subscriber when LogSubscriberFile is empty.
  • TestStart_LogSubscriberFailureNonFatal — a failing create_log_subscriber does not fail Start; it warns instead.

All docker unit tests pass; the only failure is the pre-existing environmental TestServeIntegration (test app 11.6.3 vs MxBuild cache 11.12.1), unrelated to this change.

Docs

  • run help text, run-local skill, and docs-site "Debugging a server-side error" now explain that two sources feed runtime.log (JVM tee + file log subscriber) and why.
  • fix-issue.md symptom table extended with the round-2 entry.

🤖 Generated with Claude Code


Generated by Claude Code

…tured

The runtime-log tee added earlier writes the runtime JVM's stdout/stderr to
.mxcli/runtime.log, but a standalone runtime (launched via runtimelauncher.jar)
attaches no log subscriber by default — unlike a Studio Pro / m2ee run, which
calls create_log_subscriber after start. Mendix application logs flow to log
subscribers, not stdout, so microflow LOG output and server-side exception
stack traces never reached the file: a page-action error still showed only the
generic Mendix dialog with nothing to correlate against.

After a successful start (and on every restart's Start, since each fresh JVM
starts with no subscriber), attach a "file" log subscriber pointing at the same
runtime.log with autosubscribe INFO. max_rotate is 0 so the runtime never
renames the file out from under the JVM tee's open handle; the path is passed
absolute because the runtime's cwd is <install>/runtime, not mxcli's. The
attach is best-effort — a logging failure warns to stdout rather than failing
an otherwise-healthy start.

Tests: TestStart_AttachesLogSubscriber (params), TestStart_NoLogSubscriberWhenUnset,
TestStart_LogSubscriberFailureNonFatal. Docs: run-local skill, docs-site, and the
fix-issue symptom table (findings #25, round 2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit a2f8d82 into main Jul 26, 2026
3 of 5 checks passed
ako pushed a commit that referenced this pull request Jul 29, 2026
Two entity-correctness fixes from the ledger project, both verified end-to-end on
Mendix 11.12.1.

#41 (CE6771 check gap): `create association from/to <ViewEntity>` passed
`mxcli check --references` and exec, but `mx check` rejects it — associations
to/from view entities are impossible. The reference checker now resolves both
endpoints and rejects when either `isViewEntity` (new helper), naming it and
pointing at a non-persistent alternative. Both directions caught.

#39 (mx check crash): a `create or modify entity` dropping an indexed attribute
left the index orphaned (its column referenced a removed GUID), and `mx check`
CRASHED loading the project with an unhandled AggregateException. Two parts:
- executor `reconcileDroppedIndexes`: when the statement lists no indexes, carry
  existing ones forward, pruning columns for dropped attributes (attr IDs survive
  by name) and dropping empty indexes — fixes partial drops (index(A,B)→index(A)).
- backend `UpdateEntity`: an untouched empty Indexes PartList is "clean", so the
  codec passes the raw orphan through; force it dirty (add+remove) so the codec
  re-emits an empty list, clearing the raw — fixes the all-removed case.
Verified: `mx check` → 0 errors (previously crashed).

Tests: TestIsViewEntity, TestReconcileDroppedIndexes. Examples + symptom table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 29, 2026
… ledger cases

Wave 1 (MDL-WF01/02/03, MDL-BUTTON01 — cases 1/2/4/5) is shipped. Record the
~10 ledger-driven checks added on the same pattern (MDL045-049, MDL-WIDGET13/14/15,
MDL031 pass-through, view-entity association CE6771) plus the two write-path parity
fixes (#24 navlist names, #39 orphaned index), and note the two originally-
cataloged gaps that remain: case 3 (CE7412) and the standalone case-6 warn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 29, 2026
A `break` nested inside an if/case within a loop serializes a dangling
sequence-flow reference — `mx check` then CRASHES loading the project with an
unhandled System.AggregateException ("key … not present in the dictionary"), an
unrecoverable failure (same severity class as #39). A break placed directly in
the loop body serializes fine; only the useful `if <cond> then break` form is
affected.

The underlying flow-graph serialization bug is still open (a write-path fix).
MDL051 is the interim guard: on a LoopStmt, flag a break nested in an
if/case/inheritance-split (not descending into nested loops, which trap their own
break). Since the pattern currently CRASHES, a check-time rejection is a strict
improvement. Fix for the user: a guard variable (declare $Done Boolean = false; …
set $Done = true) — verified clean on mx check (11.12.1).

Test: TestValidateMicroflow_ConditionalBreak. Repro:
ledger-52-break-in-conditional.fail.mdl. Symptom table notes the real fix is the
write-path serialization; MDL051 is the interim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 29, 2026
….9 (FINDINGS #39)

A workflow whose flow contains a "call microflow" activity was written with the
on-disk $Type `Workflows$CallMicroflowTask`. Both checkers passed (mxcli check ✓,
mx check → 0 errors), but on an 11.9+ project the runtime refused to load the
ENTIRE model at boot:

    Failed to load model: ... Class 'Workflows$CallMicroflowTask' could not be found

Mendix 11.9 (WOR-2802) split MicroflowBasedActivity into CallMicroflowActivity +
AIAgentTaskActivity, renaming the on-disk $Type. Evidence from the cached
toolchains: the 11.6.3 modeler knows only CallMicroflowTask; the 11.10 modeler
carries both (the old one marked "Removed due to code refactoring ... WOR-2802")
plus a conversion routine; the 11.10+ runtime metamodel jars know only
CallMicroflowActivity. The 11.9 boundary matches the existing HasOwner→HasOwnerAttr
domain-model gate.

Fix: emit CallMicroflowActivity for projects >= 11.9 and keep CallMicroflowTask for
older ones. The semantic model is unchanged — only the emitted $Type differs — so
the tree is built with the legacy name and rewritten when targeting 11.9+.

- modelsdk (default engine): applyCallMicroflowStorageName walks the built element
  tree; useCallMicroflowActivityName() gates on pv.IsAtLeast(11,9). Codec
  TypeDefaults + list-marker registered under both $Type names. Wired into
  CreateWorkflow/UpdateWorkflow and the ALTER-workflow activity serializer.
- legacy engine: renameCallMicroflowTypeBSON rewrites the serialized BSON, gated the
  same way in serializeWorkflow and SerializeWorkflowActivity.
- read path already folds both gen types into the one semantic CallMicroflowTask.

Tests: rename walk (both directions) + encode-validity under the new name; version
gate against the vendored 11.6.6 fixture; legacy BSON-rewrite unit test. Repro
mdl-examples/bug-tests/263-workflow-callmicroflow-storage-name.mdl; 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
ako pushed a commit that referenced this pull request Jul 29, 2026
…ize context var (FINDINGS #39 regression)

The 11.9 storage-name fix (CallMicroflowTask→CallMicroflowActivity) let the runtime
load the model again, but the retest found the new class also structures outcomes and
the parameter-mapping expression differently — so a call-microflow with a parameter
or a non-void return now failed `mx check` with two errors that the tolerant old
class had passed:

- CE6686 "outcomes do not match the configured microflow": autoBindCallMicroflow
  injected a single VoidConditionOutcome regardless of return type. The 11.9+ class
  requires outcomes that match the microflow — a Boolean return needs true/false
  BooleanConditionOutcomes. defaultCallMicroflowOutcomes now generates them from the
  target microflow's ReturnType (Boolean → two branches, else → single default).
- CE0117 "Error(s) in expression": the context parameter is named "WorkflowContext"
  and 11.9+ expressions are case-sensitive, so a user-written `$workflowContext`
  (the form in every example) is an undefined variable. normalizeWorkflowContextExpr
  rewrites it to `$WorkflowContext`.

Verified against real mxbuild on BOTH classes:
- Mendix 11.12.1 (CallMicroflowActivity): `call microflow ACT with (Ctx='$workflowContext')`
  on a Boolean-returning microflow → 0 errors (was 2).
- Mendix 11.6.3 (CallMicroflowTask): same script → 0 errors (no regression).

Unit tests for both helpers; bug-test 263 updated to the param+Boolean-outcome case.

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