Collect useful logs

Enable the right logging categories, capture them safely, and avoid the blanket-debug trap

Collect useful logs

Something misbehaved and you want to send us evidence. This page is about getting useful evidence — which is not the same as getting more of it.

Do not start with *.debug=true

The single most common mistake is turning everything on:

QT_LOGGING_RULES=*.debug=true

That looks thorough and is actively counter-productive. Qt's emoji segmenter emits one line per character at debug level, so a real session produced 974,858 lines and a 136 MB log in a few minutes — and, worse, the logging itself slowed the application down enough that any performance number from that session was meaningless. A report that says "it felt sluggish" gathered under blanket debug logging tells us about the logging.

Every application now ships a curated filter that keeps qDebug output on while silencing the Qt internals that flood it. You get the useful half by default.

Your own rules are added to that filter, not swapped for it, and yours win. Qt applies logging rules in order and the last match decides, so the curated filter goes first and anything you set in QT_LOGGING_RULES goes after it. That means you can re-enable one of the silenced categories without losing the suppression of the other fifteen:

QT_LOGGING_RULES=qt.rhi.debug=true octomy-hub

gives you RHI traffic and still keeps the emoji segmenter quiet.

(Until 2026-08-26 this was not true: setting QT_LOGGING_RULES was silently discarded, because the curated filter overwrote it. If you learned to work around that, you no longer need to.)

Turn on just the area you are reporting

Pick the category that matches the symptom and enable only that one:

Symptom Set
Rendering, viewport, GPU, "sluggish graphics" QT_LOGGING_RULES="qt.rhi.*=true"
Pairing, discovery, peers not appearing OCTOMY_LOG=session
Audio glitches, silence, device selection QT_LOGGING_RULES="qt.multimedia.*=true"
Fonts, text shaping, missing glyphs QT_LOGGING_RULES="qt.text.*=true"

OCTOMY_LOG sets our own category verbosity and is usually the more useful of the two — it is inherited by child processes, so one setting covers a whole constellation.

The categories

Comma-separated, each optionally :off, :summary or :full. No level means full. Names are case-insensitive.

OCTOMY_LOG=Discovery,Pairing:summary,Data:off hub 2> run.log
Category Covers
Handshake Connection handshake between nodes
Scheduling The comms scheduler — what gets sent when
Session Session lifecycle: open, keepalive, teardown
Discovery Finding other nodes on the network
Pairing The pairing/trust ceremony
Data Payload traffic
Blob Per-blob courier and swarm-DHT gossip trace. Off by default — per-object and loud

Each override that takes effect prints a line saying so, and an unrecognised name is reported rather than ignored. If you do not see the confirmation, the setting did not apply — which is worth checking before concluding a subsystem had nothing to say.

Graphics and GPU switches

Four opt-in flags, all off by default because each one distorts what it observes:

Flag Effect
--tick-trace Writes a timeline of the viewport's render ticks: when a tick was requested, when it was served, and how late. Each line carries a monotonic millisecond stamp, so a gap reads directly. Use it when a drag stops moving or a frame arrives late. It is a flag, not an environment variable
--rhi-debug Enables Qt's qt.rhi.debug. Prefer this to setting QT_LOGGING_RULES yourself — the curated filter silences qt.rhi.*, and replacing the filter to get it back also un-silences everything else. (Was OCTOMY_RHI_DEBUG=1 in earlier builds; it is a command-line flag now.)
--rhi-validate Attaches the Khronos validation layer to every Vulkan instance the process creates
--gui-heartbeat Reports every pause of the user interface longer than 250 ms

When the window or the splash stops responding

Start the application with --gui-heartbeat and reproduce the pause. Every pause over 250 ms writes two lines. The first names the phase and, on Windows, the stack of the paused thread. The second gives the length when the pause ends.

[GUI-STALL] GUI thread has not pulsed for 269 ms, phase="StandaloneHarness::exec", stack:
  #0 ntdll.dll+0x164a14 ZwWaitForAlertByThreadId+0x14
  ...
[GUI-STALL] over: 8947 ms, phase="StandaloneHarness::exec"

Attach the whole log. The stack names modules and offsets, and the developer resolves them against the build's symbols. The flag costs one timer and one thread, so a run with it on is still a normal run.

If you enable validation, read the startup lines before trusting a clean result. The program says which branch it took — layer enabled, layer not installed, or no Vulkan platform available at all. The third is not "no errors found": nothing could be enumerated, so the run measured nothing. A silent pass and an absent validator otherwise look identical.

Capture it to a file, not to a terminal

hub > ~/octomy-hub.log 2>&1
# Windows: use cmd.exe redirection rather than PowerShell's
cmd /c "hub.exe > %USERPROFILE%\octomy-hub.log 2>&1"

On Windows this matters more than it looks. The log we write is UTF-8, and a redirect to a file preserves it exactly. A PowerShell redirection re-encodes through the console code page on the way, which mangles any non-ASCII character in the output — so a log captured that way can show corruption that is not in the application at all. If you have already captured one that way and see ? or Ôäó where a symbol should be, that is the capture, not the bug.

What to send

  • The log file.
  • The version, from Help → About or --version on the command line.
  • What you did, in the order you did it. "Opened a scene, added a bounding box, it closed" is worth more than a stack trace with no context.

Let the program gather it for you

If the application is still running, one task collects the mechanical parts into a single zip you can attach:

hub --opal 'system.diagnostics.collect' -c

It writes diagnostics-<timestamp>.zip into the data directory and prints the path. Inside is a MANIFEST.txt that says what it collected and what it looked for and did not find, so a gap in the evidence reads as a gap rather than as nothing. MISSING log will always be there — see Where the log is not — and that is the manifest doing its job, not a failure.

Full detail: OPAL system tasks.

By hand

If the application crashed rather than misbehaved, say so explicitly and include the contents of process-health/ inside your data directory. That is where the crash marker lands, and it records which build the crash came from — which is what makes the symbols resolvable. A program database from a different build does not fail to resolve; it resolves to plausible wrong function names, which is worse than no symbols at all.

Also include whatever Windows left in %LOCALAPPDATA%\CrashDumps. One caution about that folder: Windows keeps at most ten dumps there by default and silently discards new ones once it is full, so an empty-looking result may mean "full" rather than "no crash". Clear it out before reproducing.

Where the log is not

There is no log file unless you redirect one. The applications do not maintain a rotating log of their own, so starting one from a desktop icon and then looking for a .log afterwards will find nothing. Redirect first, reproduce second.

All log output is on stderr, at every severity. Before August 2026 debug and info went to stdout, which is why older instructions say to capture both streams. Capturing both still works and is fine for a bug report. But if you are scripting against a program's output rather than reporting a problem, keep them apart — stdout is data, stderr is diagnostics. See Output streams.

In this section
Topics
howto troubleshooting logging diagnostics
See also