# BTrace: Dynamic Tracing for Running Java JVMs

> BTrace attaches to a live JVM and injects tracing code without a restart or recompilation. This review covers how the instrumentation works, how to run a first trace with JBang, and where the safety model stops helping.

**btraceio/btrace** — Production-safe dynamic tracing and diagnostics for Java applications—attach to live JVMs with no restart or recompilation.

- Repository: https://github.com/btraceio/btrace
- Stars: 5,995 · Forks: 955
- Language: Java
- License: Apache-2.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/btraceio-btrace

## The production JVM that cannot be restarted

Most Java profiling starts with a decision you are not allowed to make. You want method timings from a service that is already serving traffic, and the usual answers are a restart with an agent, a rebuilt artifact, or a heap dump that tells you nothing about call duration. BTrace targets that gap. The README describes it as dynamic instrumentation of running Java applications with "No restarts. No recompilation. Production-safe." The audience is narrow and specific: engineers debugging latency, exceptions or allocation behaviour on a JVM they cannot take down, including containers and CI environments where a restart would destroy the state under investigation. It is not a replacement for an application performance monitoring stack. It is a probe you attach, read, and detach.

## How bytecode injection and the safety verifier fit together

BTrace is delivered as a Java agent. The repository splits the work across modules: btrace-agent holds the instrumentation code, btrace-compiler compiles trace scripts, btrace-runtime provides the runtime support the injected code calls into, and btrace-client is the command-line side that attaches to a target JVM. A trace script is written in Java-like syntax and compiled on the client, then sent to the agent, which rewrites the bytecode of the matched methods to call into the probe.

The design constraint that shapes everything else is the verifier. The README calls scripts "Verified scripts can't crash your application", which is a strong claim and the reason the trace language is restricted rather than plain Java. Arbitrary object allocation, loops and method calls are constrained so an injected probe cannot corrupt the target. That restriction is also the main friction: you are writing in a dialect, not in Java, and the compiler rejects constructs that would be legal in the application itself. A separate btrace-dtrace module and a dtrace topic in the repository suggest DTrace-style usage is part of the intended surface.

## Install BTrace with JBang and run a first trace

The README recommends JBang because it requires no manual installation and manages versions for you. Install JBang first, then register the BTrace catalog once. The catalog URL is the one given in the README.

```bash
curl -Ls https://sh.jbang.dev | bash -s - app setup
jbang catalog add --name btraceio https://raw.githubusercontent.com/btraceio/jbang-catalog/main/jbang-catalog.json
```

After the catalog is registered, you can run BTrace by coordinate. The README shows the pattern with a version placeholder, so substitute the release you want to pin.

```bash
jbang io.btrace:btrace:<version> <PID> <script.java>
```

For a first real trace, the README gives an oneliner that times JDBC statement execution on a running JVM identified by PID. It prints the method name and duration at method return.

```bash
btrace -n 'java.sql.Statement::execute* @return { print method, duration }' <PID>
```

What you should see is one line per matched call with the method and its duration. The README also shows a filter form, `if duration>100ms`, which limits output to slow calls. If you prefer a script over a oneliner, the README's example uses @OnMethod with clazz and method attributes and an @Duration parameter in nanoseconds, then divides by 1_000_000 to print milliseconds.

```java
@BTrace public class Trace {
    @OnMethod(clazz = "com.example.OrderService", method = "checkout")
    public static void onCheckout(@Self Object self, @Duration long ns) {
        println("checkout: " + str(ns / 1_000_000) + "ms");
    }
}
```

## The Java 17 floor and the deprecation warning you will hit

The README states that BTrace 3.0 runs on Java 8 through 25, then immediately narrows that: running against a JVM older than Java 17 is deprecated. It continues to work throughout 3.x but emits a deprecation warning, and support for Java below 17 is scheduled for removal in the next major release, 4.0. That is a real planning constraint. If your fleet still runs Java 11, BTrace will work today and stop working at 4.0, and the README points to a migration guide for the 2.x to 3.0 jump rather than for the 4.0 one.

The second limitation is structural. Because the trace language is restricted to keep the target safe, there are things you simply cannot express in a probe. If your question requires arbitrary logic inside the traced method, BTrace will refuse the script rather than let you write it. That is the trade-off the project made deliberately, and it is the right one for production, but it means BTrace is the wrong tool when you need full expressive freedom and can afford a restart. In that case a conventional agent or a profiler run against a staging instance is the better fit.

A third boundary concerns startup mode. The README warns that BTrace 3.0 does not support an unauthenticated remote prepared-mode endpoint, and directs readers to the startup-mode security boundary in the Getting Started guide. For startup scripts that do not need later client connections, it recommends `noServer=true`. Treat those as configuration decisions to make before deployment, not after.

## BTrace against DTrace and general profilers

The closest conceptual relative is DTrace, and the repository acknowledges it directly: btrace-dtrace is a top-level module and dtrace is one of the project's topics. The difference in approach is the target. DTrace instruments kernel and user-space probes on systems that expose them, with a C-like D language and a broad system view. BTrace is JVM-only and works by rewriting Java bytecode inside the running process, so it sees Java methods, fields and allocations rather than syscalls. If your problem is inside the JVM, BTrace has the finer view; if it is below the JVM, BTrace cannot see it at all.

Against a sampling profiler, the difference is measurement versus observation. A sampler tells you where time is spent across the whole application at low cost. BTrace tells you exactly what a specific method did on a specific call, including arguments and return values, at the price of instrumenting that method. Sampling answers "where is the time going"; BTrace answers "what is this one method doing". The README's oneliner examples, such as attaching to `java.lang.Exception::<init>` to print the stack, are the kind of targeted question a sampler cannot answer.

## Maintenance, licence and upgrade cost

The repository is not archived and the last push was on 2026-09-21, so the project is being worked on. The most recent release listed is v2.2.6 from 2024-11-09, with v2.2.5 and v2.2.4 before it, which means release tags and repository activity are on different clocks. The README and Getting Started material describe BTrace 3.0, so the documentation is ahead of the latest tagged release. Pin an explicit version rather than tracking latest if you need reproducibility, and check the releases page for what is actually tagged.

Licensing is Apache-2.0. That permits commercial and closed-source use, and the repository carries both LICENSE and NOTICE files, which is the normal Apache-2.0 layout. This is an observation about the repository contents, not legal advice; if you redistribute BTrace inside a product image, have your own counsel review the NOTICE obligations.

Upgrade cost is concentrated in one place: the Java baseline. The README's own statement that Java below 17 is deprecated and will be removed in 4.0 means the upgrade from 3.x to 4.0 is really a JDK upgrade. Budget for that. The 2.x to 3.0 migration guide exists in docs, so that hop is documented; the 4.0 hop is announced but not yet described.

## Conclusion

Adopt BTrace when you need method-level timing, exception tracking or allocation probes on a JVM you cannot restart, and when the target runs Java 17 or newer. Skip it if the JVM is older than Java 17 and you cannot upgrade, since that path is deprecated and scheduled for removal in 4.0, or if you need persistent always-on telemetry rather than an interactive session. Before rolling it out, confirm the exact version tag you will pin, check that the target JDK falls inside the Java 17 to 25 range the README describes, and read the startup-mode security boundary in docs/GettingStarted.md if you plan to use -javaagent.

## FAQ

### What is BTrace?

BTrace is a tool for dynamic tracing and diagnostics of Java applications. It attaches to a running JVM and injects tracing code at runtime, so the README describes it as requiring no restarts and no recompilation.

### How do I install BTrace?

The README recommends JBang: install JBang, then add the BTrace catalog with jbang catalog add --name btraceio and the catalog URL. Alternatives listed are SDKMan with sdk install btrace, a manual download of btrace-bin.tar.gz, RPM and DEB packages, and a Docker image.

### Which Java versions does BTrace support?

The README states that BTrace 3.0 runs on Java 8 through 25, but that running against a JVM older than Java 17 is deprecated and emits a warning. Support for Java below 17 is to be removed in the next major release, 4.0.

### Can a BTrace script crash the application it traces?

The README states that verified scripts cannot crash the application, which is why the trace language is restricted instead of being plain Java. The documentation does not describe a rollback mechanism for a script that has already been attached.

## Sources

- [btraceio/btrace on GitHub](https://github.com/btraceio/btrace)
- [Issues](https://github.com/btraceio/btrace/issues)
- [License: Apache-2.0](https://github.com/btraceio/btrace/blob/develop/LICENSE)
- [README](https://github.com/btraceio/btrace/blob/develop/README.md)
- [Releases](https://github.com/btraceio/btrace/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/btraceio-btrace
