#22139: Fix(ui): improve log formatting for JSON payloads
app: web-ui
size: S
Cluster:
Hooks and UI Fixes
## Summary
- **Problem:** JSONL logs often contain nested JSON strings or objects in keys "0", "1", and "2", which were previously rendered as flat, unreadable strings or generic `[object Object]` placeholders.
- **Why it matters:** Developers could not effectively parse complex system event payloads (like heartbeat diagnostics or provider responses) without manually copying them out to a formatter.
- **What changed:** Implemented a recursive JSON parser and formatter in the log controller to detect nested structures and pretty-print them with 2-space indentation; added max-height and overflow scrolling to log message containers.
- **What did NOT change (scope boundary):** No changes were made to the server-side logging implementation or the WebSocket log-tailing protocol.
### Change Type (select all)
- [ ] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
### Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [x] UI / DX
- [ ] CI/CD / infra
### Linked Issue/PR
- Closes #
- Related #
### User-visible / Behavior Changes
- **Logs Tab:** Nested JSON strings and objects are now automatically detected and pretty-printed.
- **Logs Tab:** Subsystem detection is more robust, pulling from nested module or subsystem keys within the JSON payload.
- **Logs Tab:** Long log entries (especially formatted JSON) now have a max-height of 300px and vertical scrollbars to maintain list usability.
### Security Impact (required)
- New permissions/capabilities? (**No**)
- Secrets/tokens handling changed? (**No**)
- New/changed network calls? (**No**)
- Command/tool execution surface changed? (**No**)
- Data access scope changed? (**No**)
### Repro + Verification
#### Environment
- **OS:** Linux
- **Runtime/container:** Node.js 22
- **Model/provider:** N/A (UI focused)
#### Steps
1. Run the project: `npm start -- --port 3001`.
2. Navigate to the Logs tab.
3. Observe entries from subsystems like `gateway/heartbeat` or `diagnostic` which contain structured objects.
#### Expected
- JSON objects are indented and readable.
- The UI remains responsive even with large log payloads.
#### Actual
- Structured logs are correctly parsed and pretty-printed.
- Large logs are constrained to the 300px height limit.
### Evidence
- [x] **Trace/log snippets:** Verified against `refer/openclaw-2026-02-20.log` structures.
### Human Verification (required)
- **Verified scenarios:** Parsing of mixed string/JSON fields, recursive indentation, and scrollbar behavior for tall messages.
- **Edge cases checked:** Invalid JSON strings (fallback to raw text), null/undefined fields, and very long single-line strings.
- **What you did not verify:** Browser performance with >5,000 active DOM elements in the log list.
### Compatibility / Migration
- Backward compatible? (**Yes**)
- Config/env changes? (**No**)
- Migration needed? (**No**)
### Failure Recovery (if this breaks)
- **How to disable/revert:** Revert `ui/src/ui/controllers/logs.ts` to restore default string/object rendering.
- **Known bad symptoms:** If a log line is extremely deep, the recursive formatter could theoretically hit a stack limit (though unlikely for these system logs).
### Risks and Mitigations
- **Risk:** Heavily indented JSON might be cut off on small mobile screens.
- **Mitigation:** The log message container allows horizontal scrolling if needed, and the overall app remains mobile-first.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds recursive JSON parsing and pretty-printing to the logs UI for better readability of nested payloads in keys "0", "1", and "2". Also adds max-height scrolling (300px) to long log messages.
## Key Changes
- New `formatLogValue()` function recursively detects and formats nested JSON strings/objects with 2-space indentation
- Enhanced subsystem detection to check nested `module` and `subsystem` keys
- Changed message parsing to concatenate obj["1"] and obj["2"] instead of selecting just one
- CSS: added `max-height: 300px` + `overflow-y: auto` to `.log-message` for vertical scrolling
## Issues Found
- **Critical:** Infinite recursion risk in `formatLogValue()` (line 55) - no depth limit or circular reference protection when processing untrusted log data
<h3>Confidence Score: 2/5</h3>
- Has a critical infinite recursion vulnerability that could crash the UI
- The recursive `formatLogValue()` function lacks depth limiting or circular reference protection, creating a crash risk when processing untrusted log data with deep nesting or circular structures. While the feature improves UX, the logic bug needs fixing before merge.
- Pay close attention to `ui/src/ui/controllers/logs.ts` - the `formatLogValue()` function needs safeguards against infinite recursion
<sub>Last reviewed commit: 92eb056</sub>
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#14719: UI: fix debug event log layout and health history toggle
by detecti1 · 2026-02-12
78.2%
#11281: fix(logging): prevent subsystem loggers from bypassing file log lev...
by janckerchen · 2026-02-07
76.6%
#8342: Fix debug interface CSS layout issues
by Klopib · 2026-02-03
76.4%
#7316: fix: /chat dashboard performance
by felipcsousa · 2026-02-02
76.1%
#15253: Adding structured log content
by emailhxn · 2026-02-13
75.7%
#23669: refactor(logging): migrate node-host and tailscale console calls to...
by kevinWangSheng · 2026-02-22
75.2%
#13838: UI: fix config panel CI failures and modularize rendering
by fresed05 · 2026-02-11
74.4%
#23672: fix(resilience): guard JSON.parse of external process output with t...
by kevinWangSheng · 2026-02-22
74.2%
#11549: lint: add no-console rule and migrate 5 files to structured logger
by vaibhavtupe · 2026-02-08
73.9%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
73.8%