Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Analyze Runtime

ASecurity

Find out what a running Mendix app actually does — logs, Prometheus metrics, OpenTelemetry traces and the model catalog, joined across sources. Use when profiling a slow page or microflow, finding what hits the database, chasing an error that shows only a generic dialog, or correlating runtime cost with model shape.

128 stars
0 votes
0 copies
0 views
Added 9/26/2026
ai-agentsgojavabashsqlnodeexpressdebuggingapidatabasebackend

Works with

cliapi

Security Analysis

A96/100
mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned 9/26/2026

$npx -y skills add mendixlabs/mxcli --skill analyze-runtime --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Analyze Runtime?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Analyze Runtime
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mendixlabs-analyze-runtime/badge)](https://www.skillsdirectory.com/skills/mendixlabs-analyze-runtime)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: analyze-runtime
description: "Find out what a running Mendix app actually does — logs, Prometheus metrics, OpenTelemetry traces and the model catalog, joined across sources. Use when profiling a slow page or microflow, finding what hits the database, chasing an error that shows only a generic dialog, or correlating runtime cost with model shape."
---

# Analyze an App's Runtime Behavior — Logs, Metrics, Traces, Catalog

## Overview

When you need to understand what an app *actually does* at runtime — why a page is
slow, which microflow dominates cost, what hits the database, whether an error is
network or logic — the signals live in four places. This skill is the procedure for
collecting them and, crucially, **joining them**, because the useful questions cross
sources that no single tool answers alone.

| Signal | Where | How you get it |
|--------|-------|----------------|
| **Logs** (server stack traces + your `LOG` output) | `<projectDir>/.mxcli/runtime.log` | `mxcli run --local` tees it automatically |
| **Metrics** (throughput, DB counts, sessions, queues) | `/prometheus` on the admin port | `mxcli run --local --metrics` |
| **Traces** (per-microflow / per-activity spans + timings) | console→`runtime.log`, or an OTLP collector | `mxcli run --local --trace` / `--trace-otlp` |
| **Model shape** (activities, complexity, refs, XPath) | `.mxcli/catalog.db` (SQLite) | `mxcli … "refresh catalog full"` then `SELECT … FROM CATALOG.*` |

## When to Use This Skill

- A page/microflow is slow and you need to find *where* the time goes.
- You want to know which entities/queries the app actually hits, and how often.
- A server-side error shows only a generic dialog in the browser.
- You're profiling and need a flame chart, or want cost correlated with model shape.

Prerequisite: run the app with the fast local loop — see `run-local`. Everything
below assumes `mxcli run --local` (add the flags noted per signal).

## 1. Logs — the first stop for errors

`run --local` writes the runtime log to `<projectDir>/.mxcli/runtime.log` (override
`--runtime-log`, `-` disables). It carries JVM stdout/stderr **and** the application
log — server stack traces, your microflow/nanoflow `LOG` output, and the DB
synchronization counts at startup.

```bash
mxcli run --local -p app.mpr
tail -f .mxcli/runtime.log
```

Gotchas:
- **Nanoflow `LOG` lands under the `Client_Nanoflow` node**, not the node name you
  declared — a filter built around microflow node names silently drops it. `LOG DEBUG`
  from a nanoflow is dropped server-side (browser console only). See `write-nanoflows`.
- A spike in "Executing N database synchronization command(s)" on an *unchanged* model
  is a red flag (see the `create or modify` data-loss class of bug).

### Turning up one subsystem: `mxcli log`

Everything logs at `INFO` by default, so the detail you need usually is not in the
file at all — and raising the whole runtime to `TRACE` is unusable. Logging is
publish/subscribe: code publishes to a named **LogNode**, and each node has its
own level.

```bash
mxcli log list                          # every node and its level (57 on a blank 11.12 app)
mxcli log list --filter connectionbus   # narrow it — nobody remembers the exact names
mxcli log set ConnectionBus_Queries TRACE
mxcli log set ConnectionBus_Queries=TRACE Connector=DEBUG   # one admin call
mxcli log set ConnectionBus_Queries INFO                    # put it back
```

Levels: `NONE CRITICAL ERROR WARNING INFO DEBUG TRACE`.

This needs a **running** app (it goes through the M2EE admin API), and the change
lasts as long as the process — it is a debugging knob, not project configuration.

Nodes worth knowing:

| Question | Node |
|---|---|
| What SQL is being run | `ConnectionBus_Queries` (and `_Retrieve`, `_Update`) |
| Database sync at startup | `ConnectionBus_Synchronize` |
| Consumed OData / REST calls | `ODataConsume`, `REST Consume` |
| **Published** OData requests (the incoming URI) | `OData Publish` — note the space; only exists if the project publishes a service |
| Microflow execution | `MicroflowEngine`, `ActionManager` |
| Scheduled events / queues | `SystemTask`, `TaskQueue` |
| Java/JS action wiring | `Connector` |

**`--force` creates the node, permanently.** Without it an unknown node is refused,
which is what you want — a typo should be an error. With it the name is registered
for the life of the process, so a typo becomes a real (empty) node that shows up in
`log list` from then on. Use it only to pre-register a node that has not published
yet.

**Nodes appear only once something registers them**, so the list is a property of
*this* app, not of Mendix. A blank 11.12 app reports 57; adding one published
OData service makes it 58. This is why `log list` is the first step rather than a
remembered name — and why `--force` exists for a node that has not registered yet.

### Seeing what a published OData resource is asked

`OData Publish` — **note the space** — is the node, and it exists only when the
project publishes a service. At TRACE it logs the full incoming URI, which is the
question `$filter`/`$top`/key-lookup bugs turn on:

```bash
mxcli log set "OData Publish" TRACE
# GET /odata/f1/Rows?$top=5&$filter=rowKey eq 'abc'
```
```
TRACE - OData Publish: Incoming request from 127.0.0.1: GET .../Rows?$top=5&$filter=rowKey eq 'abc'
DEBUG - OData Publish: Responding to client with status code 400.
```

`ODataConsume` is the *client* side — a different node for a different direction.

That same probe showed Mendix rejecting `$filter` on a property not declared
`Filterable`, with a **400 "Property 'rowKey' is non-filterable"**, before the read
microflow ran. So the platform does enforce the filterability you declare in
`expose (…)`; what it does *not* do is apply `$top`/`$skip`/`$orderby` for a
read-microflow resource (see `odata-data-sharing`).

## 2. Metrics — throughput and database pressure

`--metrics` registers a Prometheus registry, served at
`http://127.0.0.1:<admin-port>/prometheus` (loopback).

```bash
mxcli run --local -p app.mpr --metrics
curl -s http://127.0.0.1:8090/prometheus | grep -E 'connectionbus_|handler_requests|sessions_|taskqueue_'
```

Useful families: `connectionbus_{selects,inserts,updates,deletes,transactions}_total`
(database pressure), `handler_requests_total` (throughput), `sessions_*`,
`taskqueue_*` (background work). Merge any extra registry (otlp/influx/statsd) with
`--runtime-setting 'Metrics.Registries=[…]'`.

## 3. Traces — where the time goes

`--trace` attaches the bundled OpenTelemetry agent. **Default span filters ship with
it** (`OpenTelemetry._RuntimeSpanFilters`) because unfiltered per-activity tracing is
**~10× slower** and produces ~110k spans for one busy transaction — that's a
flow-*shape* debugging mode, not a timing mode.

```bash
# console exporter → runtime.log (span names/attrs only)
mxcli run --local -p app.mpr --trace
# flame charts: export to a collector (console can't reconstruct call trees/durations)
mxcli run --local -p app.mpr --trace-otlp http://127.0.0.1:4318
```

- The **console exporter omits start/end timestamps and parent span IDs** — you get
  span names + attributes but no call tree and no durations. For real timing/flame
  charts use `--trace-otlp <endpoint>` (implies `--trace`), which sets the OTLP
  exporter for you; user-set `OTEL_*` env still wins.
- `--trace-service NAME` sets `OTEL_SERVICE_NAME` (default the `.mpr` name); use
  distinct names per app for multi-app correlation. Trace context (W3C `traceparent`)
  crosses app boundaries automatically over `rest call`.
- To examine flow *shape* on a small flow, temporarily disable the filters with
  `--runtime-setting 'OpenTelemetry._RuntimeSpanFilters=[]'`.

## 4. Model shape — the catalog

The catalog is a SQLite database at `.mxcli/catalog.db` describing the model. **Run
`refresh catalog full`** — plain `refresh catalog` (fast mode) leaves the analytic
tables (`CATALOG.ACTIVITIES`, `CATALOG.REFS`, `CATALOG.XPATH_EXPRESSIONS`,
`CATALOG.WIDGETS`) **empty** (a fast-mode query warns "requires refresh catalog full").

```bash
mxcli -p app.mpr -c "refresh catalog full"
mxcli -p app.mpr -c "SELECT MicroflowQualifiedName, COUNT(*) activities
                     FROM CATALOG.ACTIVITIES GROUP BY 1 ORDER BY 2 DESC LIMIT 10"
```

`CATALOG.ACTIVITIES.Id` is the model GUID the debugger breaks on (see
`debug-microflows`), with the action name and its sequence — a named, ordered
activity list per microflow. See `catalog-search` / `graph-analysis` for the
richer queries.

## 5. The app warehouse — join the signals (external DuckDB)

Each signal alone answers little; the useful questions cross them. Because the catalog
is a plain database and the app's dev data is Postgres, one engine can join model
shape + live data + telemetry with **no ETL**. mxcli does **not** embed DuckDB — this
is a dev-container recipe (dev data, dev telemetry, everything read-only):

```sql
-- in duckdb, from the project dir, after `refresh catalog full` + a --trace-otlp run
ATTACH '.mxcli/catalog.db' AS cat (TYPE sqlite, READ_ONLY);
ATTACH 'dbname=app host=127.0.0.1 user=mendix' AS app (TYPE postgres, READ_ONLY);
CREATE VIEW spans AS SELECT * FROM read_json_auto('spans.jsonl');
```

Two joins that are impossible in any single source:

- **Runtime cost × model shape** — span durations per microflow joined to
  `cat.activities_data` (count) and complexity. Complexity does *not* predict cost: a
  2-activity flow that delegates can cost more than a 14-activity one. A lint rule
  can't see that; this join can.
- **Query time × entity × live rows** — span DB timings joined to `cat` (which entity)
  and `app` (row counts). This is how you find that the task-queue poller
  (`system$queuedtask`, owned by no microflow) is the app's largest DB consumer —
  invisible from any per-microflow view.

Caveats: keep every attachment **read-only** (never a production DB; even locally make
it explicit), and filter/sample traces first — unfiltered span volume is large.

## Known gap: logs ↔ traces

Runtime log lines carry **no trace id**, so joining logs to traces is a fuzzy
timestamp join (worst exactly under concurrency). The OTel agent populates the trace
id in the MDC, but Mendix's log pattern doesn't print it — closing the file-log side
needs an upstream `%X{trace_id}` change, not mxcli. For collector-side correlation,
export traces (and logs) via OTLP with `--trace-otlp` so the backend joins them.

## Decision guide

| Question | Reach for |
|----------|-----------|
| "Why did this error?" (server-side) | **Logs** (`runtime.log`) |
| "How much DB / throughput / queue work?" | **Metrics** (`--metrics`) |
| "Where does the time go in this flow?" | **Traces** (`--trace-otlp` for a flame chart) |
| "What's the flow's shape / activity list?" | **Catalog** (`refresh catalog full`) or the debugger |
| "Which cost belongs to which entity/model construct?" | **Warehouse** (catalog × spans × app DB) |

## See Also

- `run-local` — the local loop these flags hang off (`--metrics` / `--trace` / `--trace-otlp` / `--runtime-setting` reference).
- `debug-microflows` — interactive breakpoints/stepping when a trace isn't enough.
- `catalog-search`, `graph-analysis` — catalog query patterns and dependency analysis.
- `verify-with-oql` — query the running app's data directly.

Attribution

mendixlabsmendixlabs
View sourceSee grades on GitHubMore from mendixlabs →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Terse caveman voice: answer first, fluff gone, every technical fact kept. Use for /caveman, "caveman mode", "talk like caveman", "be brief", "less tokens". Stays on until "stop caveman" or "normal mode".

1100021 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

698621 votes

Writing Skills

Create and manage Claude Code skills in HASH repository following Anthropic best practices. Use when creating new skills, modifying skill-rules.json, understanding trigger patterns, working with hooks, debugging skill activation, or implementing progressive disclosure. Covers skill structure, YAML frontmatter, trigger types (keywords, intent patterns), UserPromptSubmit hook, and the 500-line rule. Includes validation and debugging with SKILL_DEBUG. Examples include rust-error-stack, cargo-dep...

3931 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3421 votes

catchup

Recovers the conversation and failed tool calls of a previous Codex, Amp, Claude Code, Antigravity, Cline, Copilot CLI, Cursor, DeepSeek Harness, Grok Build, Kimi, OpenCode, Pi Agent, or ZCode session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", asks to recover/summarize a previous session before continuing, or asks to diagnose or report a catchup failure. Do NOT use for the current conversation, git history, or any non-agent log.

741 votes
View all in ai-agents →