jvmScout jvm reliability
Open source · MIT Native JVMTI agent Windows · Linux · macOS

Catch every JVM exception — with the variables that caused it.

jvmScout attaches to the JVM itself as a native JVMTI agent and observes exceptions at the source — capturing the full stack trace, live local-variable values at every frame, the cause chain, and decompiled source. No SDK, no code changes, no recompile.

✓ No SDK ✓ No code changes ✓ Any JVMTI JDK ✓ Adaptive sampling
events/ev_91c4 · NullPointerException
uncaught java.lang.NullPointerException ◂ 8 / 842 ▸
Stack
OrderService.submit
OrderService.java:142
Checkout.post
Checkout.java:68
… 16 more frames
Source decompiled
140  var cust  = lookup(order.id);
141  var total = order.total();
142  return total.amount();
     // total is null → NPE
Locals
order
Order@4c2f
total
null
cardNo ● bci
"***"
live locals
per frame
cause chain
+ JVM metrics
decompiled
source view
0
lines of app code to instrument
3
platforms · .dll · .so · .dylib
Every
throw, with per-frame locals
MIT
fully open source
The problem

A stack trace tells you where. Never why.

Most error monitors need you to wire an SDK into your app, and even then you only see what you remembered to log. By the time a NullPointerException reaches your logs, the local variables that explain it are long gone — and reproducing it in a long-lived production service is its own project.

The solution

An agent that reads the JVM directly.

jvmScout loads into the JVM through the standard JVMTI interface — the same one HotSpot's own tools use — and captures each exception at the moment it's thrown, complete with the live values of every local variable on the stack. It streams them out-of-process to a collector and a live dashboard.

✓No SDK, no recompile, no classpath JAR
✓Local values via JVMTI or BCI shadow
✓Streams out-of-process, async & batched
✓Storm-proof FULL → REDUCED → COUNT
Features

From the throw site to a triaged, alerting dashboard.

Capture happens natively in the JVM; everything else is a multi-tenant collector and a live React dashboard.

Product tour

A reliability console for your JVM fleet.

Environment-scoped reliability score and error-volume trends, a dense events triage table, and per-project alert rules — on top of the three-pane debugger in every exception.

localhost:8080/dashboard
Production Last 24 hours ▾
Total events
128
9 apps in production
Error volume
4.2k
176 / hr
New events
7
3 increasing
Reliability
94
Healthy
Error volume over time
uncaughtcaught
New & increasing errors
NullPointerExceptionNew842
SQLTimeoutException↑ Spike311
IllegalStateException↑ Spike96
Applications health
checkoutAt risk
38
paymentsDegraded
14
catalogHealthy
2
shippingHealthy
1

# Events triage

localhost:8080/events
All New in last deploy Increasing Unresolved
Exception
App
Hits
24h
Status
NullPointerException
OrderService.java:142
checkout
842
Unresolved
SQLTimeoutException
PoolDataSource.java:88
payments
311
Unresolved
IllegalStateException
CartService.java:54
checkout
96
Unresolved
IllegalArgumentException
SkuParser.java:31
catalog
40
Resolved
NumberFormatException
Money.java:12
payments
7
Resolved

# Alert rules

localhost:8080/alerts
◔
New exception class
when an error type is seen for the first time · scope: all apps
◆ Slack · #incidents Enabled
◔
Volume spike
more than 100 occurrences in 5 min · anti-storm cool-down 10 min
⬡ Webhook Enabled
◔
Checkout regressions
a new error introduced by a deployment · scope: checkout
◆ Slack · #checkout Enabled
◔
OutOfMemoryError watch
a specific exception type reoccurs · scope: all apps
⬡ Webhook Paused

Representative UI built from the live dashboard's real layout and design tokens. See the repo for current screenshots.

How it works

One native agent, a collector, and a dashboard.

The JVM hands the agent a JVMTI environment at startup. The agent captures exceptions and streams them out-of-process — your application never blocks on the network.

☕
Target JVM
your-app.jar
application code throws ⚡
JVMTI native agent
Agent_OnLoad · Exception & ClassPrepare callbacks · reads the Local Variable Table
+ BCI transformer (optional)
java.lang.classfile · injected via ClassFileLoadHook for shadow locals when classes lack -g
batched async
HTTP / TLS
⚙
Collector
FastAPI · async
token → project (multi-tenant)
redaction + alert eval (off ingest path)
REST + WebSocket · CFR decompile
🗄SQLite/Postgres
serves SPA
+ live data
▦
Dashboard
React 19 · TanStack
reliability score & trends
events triage & debugger
live WebSocket feed
no app blocking on I/O queues & retries on collector-down FULL → REDUCED → COUNT_ONLY
Install

Running in three steps.

Pull the collector image, attach the agent to a JVM, open the dashboard.

1

Run the collector + dashboard

One image ships the FastAPI collector and the built dashboard.

$ docker run -p 8080:8080 -v jvmscout-data:/data \ ghcr.io/sachin-handiekar/jvmscout-collector:latest # dashboard -> http://localhost:8080
2

Attach the agent to a JVM

Per-OS library; settings inline or via a config file.

$ java -agentpath:./libjvmti-agent.so=\ host=localhost,port=8080,deployment=checkout,\ environment=production,source=true -jar app.jar
3

Open the dashboard

Errors stream in live over WebSocket as they're thrown.

$ open http://localhost:8080 # Events -> click any exception -> # stack + decompiled source + locals

$ Production install — point JVMSCOUT_HOME at a settings file

$ tree /opt/jvmscout /opt/jvmscout |-- jvmscout.yaml # all settings `-- lib/ |-- libjvmti-agent.so # .dll | .dylib `-- bci-transform.jar # auto-found $ export JVMSCOUT_HOME=/opt/jvmscout $ java -agentpath:$JVMSCOUT_HOME/lib/libjvmti-agent.so -jar app.jar
host: collector.internal port: 8080 environment: production deployment: checkout # prefer JVMSCOUT_API_KEY env over secrets on disk api_key: stk_abc123 source: true bci: true bci_packages: [com.acme, com.acme.payments] redact_props: [ssn, cardNumber]

Keeping the agent string to just "load the library" avoids leaking api_key into ps listings. Precedence: defaults → jvmscout.yaml → JVMSCOUT_* env → inline -agentpath overrides.

Configuration

Tunable, with sane defaults.

The same key names work inline on -agentpath, in jvmscout.yaml, or as JVMSCOUT_* env vars.

KeyDefaultPurpose

A selection — see the full configuration reference for every key, including collector env vars.

The "wow" surface

How local-variable capture works.

Two paths recover the live values on the stack at the moment of the throw — whichever your class files support.

debug_info

Compiled with -g

The agent reads locals directly from the JVMTI Local Variable Table — real names, signatures, and values. This is Maven's default, so most apps get it for free.

● bci_shadow

Without debug info

Enable bci=true. The java.lang.classfile transformer injects shadow-capture calls so the agent recovers local values by slot. The dashboard marks these with a ● badge.

Security & multi-tenancy

Built to run on shared, production infrastructure.

Crash data is sensitive. jvmScout isolates tenants, redacts in two places, and ships with security headers on by default.

Use cases

For the moments logs aren't enough.

Open source

Built in the open. Contributions welcome.

MIT licensed across the agent (C++17), collector (Python/FastAPI), and dashboard (React 19).

See the variable that threw — in your own JVM.

Pull the collector image, attach the agent, and throw a test exception. MIT licensed — free to use, modify, and distribute.