Roon Log Watcher & Memory Display

What started as a beer idea and a simple tail -f command escalated rather quickly into a small hackathon. We asked ourselves a simple question: How much operational insight can actually be extracted from the Roon Server logs?

The result is now the: Roon Log Watcher – Memory Monitor

A lightweight real-time monitoring and diagnostics web app for Roon Server environments. The application is built on top of the Node.js framework and runs entirely as a local browser-based web application. No database, no cloud backend, no Electron dependency required for normal operation. Originally created and optimized for macOS + Roon Nucleus/ROCK SMB mounts (/Volumes/Data), but Linux environments may work as well.

Main Features:

  • Real-time live monitoring of Roon Server logs

  • Browser-based monitoring dashboard

  • Runtime-only monitoring (no historic log imports)

  • Live memory visualization: Physical Memory, Managed Memory, Unmanaged Memory, Virtual Memory, Plausibility Check

  • Intelligent Diagnosis Engine: automatic incident detection, root-cause hints, health impact assessment, incident grouping and correlation.

  • Roon Health Status scoring (still in early beta)

  • Event / Incident timeline

  • Live log output directly inside the web UI

  • Detection of: RAAT disconnects, Audio device issues, Streaming/cache anomalies, Database-related warnings, GC pauses, Memory growth anomalies, Compact dark-mode dashboard UI

  • Export into CSV / JSON export

  • Configurable alerts

One important design principle: The app intentionally ignores historic log content and only evaluates events and metrics collected after startup. The idea is near real-time operational observability rather than postmortem analysis.

Interestingly, many seemingly “critical” log entries turned out to be perfectly normal Roon runtime behavior. A large part of the project therefore evolved into building plausibility logic and reducing false-positive incident detection. The current version (v7.1) already works surprisingly well as a lightweight operational monitoring tool for Roon.

Requirements:

  • Node.js (LTS)

  • npm

  • Access to the Roon log directories

Node.js download:
https://nodejs.org/en/download

Installation:

npm install

Start:

npm run start:all-memory

Dashboard:

http://localhost:17666

This is still a hobby project without warranty or official support, but maybe interesting for some fellow log nerds here. Link to Github

2 Likes

…and the hack goes further. New sections added: Playback Sessions, Zone/Endpoint Monitoring, Playback Performance Monitoring and Incident Lifecycle Monitoring. It’s all there in the logs…

1 Like

New Feature added: Memory Increase Correlation. Every ‘significant’ memory consumption increase is now detected and displaied in the plot. The purple info box gives an indication (based on log data) what cased the increase

1 Like

Hello @Stefan_Mauron ,
I tried your application on Linux. I didn’t get any errors, but nothing is displayed on the dashboard.
I’m available to provide more details, dp.

tail -F started: RoonServer/RoonServer_log.txt
tail streams added: 1
tail -F streams active: 3
Runtime-only memory monitoring: existing log content is ignored.
Memory points detected since watcher start: 0

Ready. To test, trigger Play/Pause, skip a track, or switch zones in Roon.
Show all new log lines with 1 second polling: npm run start:all-fast
Show memory chart window: npm run start:memory
Or directly: node app.js --all --interval 1 --memory-window
Diagnostics: npm run diagnose
Stop: CTRL+C

Memory chart window: http://localhost:17666
Memory data endpoint: http://localhost:17666/api/memory
Memory health endpoint: http://localhost:17666/health
[watcher] running. active tail streams: 3, memory points: 0, waiting for new log lines...
[watcher] running. active tail streams: 3, memory points: 0, waiting for new log lines...
[watcher] running. active tail streams: 3, memory points: 0, waiting for new log lines...
[watcher] running. active tail streams: 3, memory points: 0, waiting for new log lines...

Hi

let us see, if we we can bring it to life on Linux as well. There is a new (more robust) version 8.9 reday to download on Github. Make sure the data directory of your roon server is mounted and the roon server process is running, else, no log entries will be made and hence, nothing to observe in the app. Report back, what the terminal output report.

Rgds, Stefan

1 Like

Thanks for this. I have wanted something like this since I started using Roon in 2017. The ‘real-time only’ is a bit of an issue though. Only the other day playback stopped for no apparent reason and unless I had this permanently running somewhere it wouldn’t be of any use finding out why? Could it be developed to analyse older log entries (Roon keeps 20 sequential log files last time I looked but this may no longer be the case)?

Hi

this is still under investigation. Why? Live logs (RoonServer_log.txt) are written immediately and reflect the real event timeline. Archived diagnostic logs / rotated snapshots are generated later from the live logs and may lag behind. Their file timestamp is therefore not reliable for sequencing events. Visually this causes: timeline jumps backwards or duplicated event windows which leads to misleading causality and incorrect correlation graphs/spikes.

I’m trying to figure out how live streams and rotated snapshots should and could be merged strictly by parsed log timestamps, not by file discovery time or file modification time.

Rgds, Stefan

1 Like

HI @Stefan_Mauron I like the look of this.

However, could you include the command structure and syntax on MacOS to mount the ROCK filestore under /Volumes/Data - my Unix is a little rusty and has been RaspberryPi centric.

Example Nucleus / ROCK mount on macOS:

smb://NUCLEUS/Data

Mounted locally as:

/Volumes/Data

thanks

Hi @Stefan_Mauron ,

I think I’ve found the solution:

the « tail -F » command doesn’t work correctly with SMB1.

I’ve modified your code:

  const script = `
file="${resolved}"
tail -n 10 "$file"
prev=$(wc -l < "$file")
while true; do
    lines=$(wc -l < "$file")
    if [ "$lines" -gt "$prev" ]; then
        tail -n $((lines - prev)) "$file"
        prev=$lines
    fi
    sleep 1
done
`;
  const child = spawn('bash', ['-c', script], {
  // const child = spawn('tail', ['-n', '0', '-F', resolved], {
    stdio: ['ignore', 'pipe', 'pipe']
  });

And I think it works :slightly_smiling_face:

Yeah, thanks for the update. Meanwhile we’ve been working hard on new features including improved Linux support. Check in Github for the v8.40. Let me know, if it works for you without modification. Else I will look into it.

Hi

Since the v8.9 release, the project evolved quite a bit, but more importantly: we’ve learned yet a lot from the logs themselves. What started as a lightweight real-time log watcher slowly turns now into a near real-time runtime diagnostics and observability tool for Roon Server environments.

The latest drop v8.40 can be downloaded from Github.

Many of the newer capabilities were directly driven by patterns repeatedly seen in the logs:

  • memory drift behaviour

  • GC pressure patterns

  • RAAT reconnect storms

  • transient “Unknown zone” noise

  • playback/runtime correlations

  • stream negotiation anomalies

  • endpoint lifecycle instability

  • runtime state transitions

Instead of just showing more logs, the focus gradually shifted towards:

  • reducing diagnostic noise and increasing signal-to-noise ratio

  • correlating incidents over time

  • visualizing runtime behaviour more intelligently

  • making anomalies easier to spot at a glance

v8.40 now includes:

  • Runtime Behaviour Intelligence

  • memory drift detection

  • GC pressure derivation

  • incident lifecycle monitoring

  • RAAT reconnect correlation

  • playback/runtime event correlation

  • intelligent noise suppression

  • persistent UI settings

  • diagnostic summary exports

  • live telemetry timeline

  • runtime capability & release tracking

A lot of effort also went into making the UI feel more like a professional runtime analysis tool:

  • cleaner visual hierarchy

  • strongly differentiated diagnostics colors

  • reduced scrollbar chaos

  • local timeline rendering

  • toggleable overlays

  • compact runtime telemetry

There was also quite a bit of work around Linux compatibility and startup/runtime handling.
Hopefully the Linux support is now significantly better than it was around the v8.9 timeframe:

  • improved log discovery

  • better process/runtime detection

  • cleaner startup handling

  • improved path handling

  • more robust monitoring behaviour on headless servers

Still intentionally: no cloud, no Electron monster, no database, no AI buzzword inflation.

Just near real-time runtime diagnostics for audio nerds, stacktrace enjoyers and people who trust the timeline more than “have you tried rebooting it?”

Software made with love for audio nerds, because rebooting is not debugging!

No, sorry… I think I need to redo the edit.

Edit: @Stefan_Mauron OK with previous modification and :

"logDirectories": [ "/Volumes/Data/RAATServer/Logs", "/Volumes/Data/RoonGoer/Logs", "/Volumes/Data/RoonServer/Logs" ],

Thanks for the update. It’s now on my backlog.

Rgds, Stefan

Hi

the first Release Candidates is now available on Github. This RC is mainly a housekeeping and foundation release after the rapid evolution from v8.40.

Under the hood:
• major internal refactoring and modularization
• general codebase cleanup and reduced technical debt
• cleaned up and properly restructured the GitHub repository
• initial Linux-related improvements and cleanup work

New UI feature:
• added contextual tooltips throughout the dashboard to better explain metrics and runtime indicators directly inside the UI.

Linux support has been slightly touched in this RC, but this area still needs much deeper attention and will be addressed in more detail in upcoming releases.

The full release package can be downloaded from the GitHub repository under “Releases” on the right-hand side of the project page.

Rgds, Stefan

3 Likes

Thanks for sharing this very interesting project. Just told my Hermes agent to install it on my Roon Extension LXC on my Proxmox server. Up and running in less than 5 minutes :nerd_face:

1 Like

I installed this project on my Mac, and pointed baseDirectory at a file share containing my Roon Server’s data. npm run diagnose seems happy enough.

~/Projects/roon-log-watcher|main⚡*?
 ➤npm run diagnose

> roon-log-watcher@8.82.0 diagnose
> node app.js --diagnose

[startup 01] Node.js v26.3.1 on darwin/arm64
[startup 02] Runtime profile: macOS
[startup 03] Loading configuration from /Users/kalkwarf/Projects/roon-log-watcher/config.json
[startup 04] Configuration loaded. Starting log directory discovery...
[startup 05] Base directory: /Volumes/docker/roon
[startup 06] Known direct Roon log paths found: 0
[startup 07] Deep discovery enabled. Scanning for additional Logs directories...
[startup 08] Deep discovery finished: 3 directories found.
[startup 09] Log directory discovery finished: 3 candidate directories.
Roon Log Watcher v8.82 starting (dashboard-only edition)...
Mode: show warnings/anomalies only
Log output: browser window only
Log read backend: tail -F event stream
Polling: 0.25 seconds
Memory window: disabled
Process memory polling: disabled
Log directories:
 - /Volumes/docker/roon/database/RAATServer/Logs
 - /Volumes/docker/roon/database/RoonGoer/Logs
 - /Volumes/docker/roon/database/RoonServer/Logs


================ DIAGNOSE ================
Config file: /Users/kalkwarf/Projects/roon-log-watcher/config.json
Platform: darwin/arm64 (macOS)
Configured baseDirectory: /Volumes/docker/roon
Discovery strategy: explicit paths + platform-specific Roon log discovery + optional deep scan
Mounted /Volumes entries:
 - .timemachine
 - Macintosh HD
 - docker

Roon processes:
 - PID 41362: /Applications/Roon.app/Contents/MacOS/Roon
 - PID 41365: /Applications/Roon.app/Contents/MacOS/RAATServer
Selected process for memory: PID 41362
Memory source: Roon [stats] log lines

Active log directories:
 - /Volumes/docker/roon/database/RAATServer/Logs: 7 files
    watching: RAATServer_log.txt (5 KB, modified 6/20/2026, 8:01:25 AM)
    watching: RAATServer_log.01.txt (5 KB, modified 6/20/2026, 1:06:22 AM)
    watching: RAATServer_log.02.txt (7 KB, modified 6/19/2026, 2:07:25 PM)
    watching: RAATServer_log.03.txt (38 KB, modified 6/19/2026, 11:08:01 AM)
    watching: RAATServer_log.04.txt (11 KB, modified 6/10/2026, 4:54:08 PM)
    watching: RAATServer_log.05.txt (11 KB, modified 6/10/2026, 1:41:43 PM)
    watching: RAATServer_log.06.txt (4 KB, modified 6/9/2026, 10:23:22 PM)
 - /Volumes/docker/roon/database/RoonGoer/Logs: 6 files
    watching: RoonGoer_log.txt (0 KB, modified 6/20/2026, 2:01:12 AM)
    watching: RoonGoer_log.01.txt (0 KB, modified 6/19/2026, 3:06:11 PM)
    watching: RoonGoer_log.02.txt (0 KB, modified 6/19/2026, 1:54:08 PM)
    watching: RoonGoer_log.03.txt (0 KB, modified 6/10/2026, 5:07:46 PM)
    watching: RoonGoer_log.04.txt (0 KB, modified 6/10/2026, 3:43:56 PM)
    watching: RoonGoer_log.05.txt (1 KB, modified 6/9/2026, 10:49:27 PM)
 - /Volumes/docker/roon/database/RoonServer/Logs: 21 files
    watching: RoonServer_log.txt (1744 KB, modified 6/20/2026, 11:23:43 AM)
    watching: RoonServer_log.01.txt (5282 KB, modified 6/20/2026, 2:10:37 AM)
    watching: RoonServer_log.02.txt (2944 KB, modified 6/20/2026, 1:59:59 AM)
    watching: RoonServer_log.03.txt (7517 KB, modified 6/20/2026, 1:31:08 AM)
    watching: RoonServer_log.04.txt (699 KB, modified 6/19/2026, 3:05:01 PM)
    watching: RoonServer_log.05.txt (6798 KB, modified 6/19/2026, 1:53:46 PM)
    watching: RoonServer_log.06.txt (8227 KB, modified 6/19/2026, 2:05:38 AM)
    watching: RoonServer_log.07.txt (8235 KB, modified 6/18/2026, 9:13:24 PM)
    watching: RoonServer_log.08.txt (8230 KB, modified 6/18/2026, 1:46:22 AM)
    watching: RoonServer_log.09.txt (8230 KB, modified 6/17/2026, 12:01:53 PM)
    watching: RoonServer_log.10.txt (8244 KB, modified 6/17/2026, 1:22:46 AM)
    watching: RoonServer_log.11.txt (8223 KB, modified 6/16/2026, 1:07:43 PM)
    watching: RoonServer_log.12.txt (8235 KB, modified 6/16/2026, 1:16:50 AM)
    watching: RoonServer_log.13.txt (8231 KB, modified 6/15/2026, 1:13:19 AM)
    watching: RoonServer_log.14.txt (8229 KB, modified 6/14/2026, 1:31:27 AM)
    watching: RoonServer_log.15.txt (8240 KB, modified 6/14/2026, 1:04:17 AM)
    watching: RoonServer_log.16.txt (8223 KB, modified 6/12/2026, 4:26:26 PM)
    watching: RoonServer_log.17.txt (8238 KB, modified 6/12/2026, 1:18:46 AM)
    watching: RoonServer_log.18.txt (4721 KB, modified 6/11/2026, 1:20:04 AM)
    watching: RoonServer_log.19.txt (3496 KB, modified 6/10/2026, 5:03:20 PM)
    watching: RoonServer_log.20.txt (1916 KB, modified 6/10/2026, 3:09:53 PM)

RoonServer logs detected: yes
==========================================

~/Projects/roon-log-watcher|main⚡*?
 ➤

but when I run npm run start:all-memory Safari eventually opens, but nothing loads:

I’ve also tried that URL in Edge.

Any debugging suggestions? (I can also open a GitHub issue, if that’s your preference)

I decided to give the idea a go and created a native app for macOS in Swift. I hope that was OK. The initial results are already very promising. It’s really interesting to see what the log files reveal:

2 Likes

If anyone fancies giving it a go, I’ve published the first version: RoonLogWatcher_Swifted

2 Likes

Wow! It looks really good, well done!

…but, it completely escalated: We’ve refactored the application from the ground up with a significantly improved architecture and user interface. The idea: Roon Observability Center - turns low-level Roon runtime logs into user-facing understanding through a controlled interpretation pipeline.

The flow starts with raw Roon logs from the local runtime. These logs are treated as factual observations, not yet as product meaning. The system first parses them into structured technical events, then normalizes those events into a consistent internal language so different Roon log patterns can be compared and understood reliably.

Next, Roon Observability correlates related events over time. Individual observations become runtime events, then broader runtime episodes such as playback activity, connectivity problems, recovery signals, memory behavior, or source freshness changes. From there, the Observability Core layer determines higher-level product concepts such as active incidents, health assessments, recommendations, supporting evidence, and runtime story context.

The important architectural point is that meaning is created before the GUI. The Presentation layer prepares a stable, user-readable view model: cards, labels, statuses, drawer content, evidence references, recommendation wording, confidence text, and navigation targets. This layer decides what the user should understand from the runtime facts.

The GUI layer then renders that prepared meaning. It owns only interaction state such as selected cards, open drawers, local focus, or temporary local actions like acknowledging or snoozing a recommendation. It does not parse logs, infer health, create incidents, or decide whether Roon is healthy or problematic.

In management terms:

Raw Roon logs
-> parsed technical events
-> normalized observations
-> correlated runtime events
-> runtime episodes
-> incidents, health, recommendations and evidence
-> Presentation-owned user understanding
-> GUI rendering and local interaction

This separation keeps the product trustworthy: the interface is not inventing conclusions. It is showing a governed interpretation of Roon’s runtime behavior, with traceable evidence behind every visible assessment.

Here’s a first preview:

2 Likes

try: npm run start:all-memory

what does ‘lsof -nP -iTCP:17666 -sTCP:LISTEN’ report?