Setup JVMXRay To Monitor Your Java Application Security

JVMXRay is a free, open-source Java agent that gives you runtime visibility into what your application is actually doing — file access, network connections, process execution, class loading — without changing a single line of your code. This guide walks you through cloning, building, and attaching JVMXRay to a Java application in about ten minutes.

Prerequisites

Before you begin, make sure the following are installed and available on your PATH:

  • JDK 17 or later, confirm with java -version
  • Maven 3.9+, confirm with mvn -version
  • Git , confirm with git --version

That is everything you need to build and run the agent.

Step 1: Clone the Repo

Pull the latest source from GitHub:

git clone https://github.com/spoofzu/jvmxray.git
cd jvmxray

The repository is a single-module Maven project. The agent source lives under src/main/java/org/jvmxray/agent/ and the Turtle integration tests are in src/test/java/.

Step 2: Build and Verify

From the project root, run:

mvn clean install

This compiles the project, produces the agent JAR in target/ (look for jvmxray-0.0.1-agent.jar), and automatically runs the Turtle integration tests. Turtle is a test harness bundled with JVMXRay that deliberately exercises the kinds of operations the agent is designed to detect — file reads, network connections, process execution, and more. It confirms the agent fires, sensors register, and events flow correctly.

When the build finishes, check the Maven output for test results. A passing Turtle run means the agent is healthy and ready to attach to a real application. If any tests fail, the build log will tell you exactly which sensor did not fire as expected.

Step 3: Attach to Your Application

Attaching to your own application is a one-line change to your startup command:

java -javaagent:target/jvmxray-0.0.1-agent.jar \
     -jar yourapp.jar

Replace yourapp.jar with your application. That is it. No code changes. No recompile. The -javaagent flag tells the JVM to load JVMXRay’s bytecode instrumentation before your application’s main() method runs, so sensors are in place from the very first operation.

By default, the agent creates a .jvmxray/ directory in the current working directory where it extracts its logging configuration and writes logs. If you want to control where this goes, set the -Djvmxray.home property:

java -javaagent:target/jvmxray-0.0.1-agent.jar \
     -Djvmxray.home=/opt/jvmxray \
     -jar yourapp.jar

Set this to wherever makes sense for your environment — /opt/jvmxray, a path under your project directory, or anywhere you prefer to keep operational data.

Step 4: Observe Raw Events

With the agent attached, check the logs/ folder inside the .jvmxray/ directory (or your custom jvmxray.home path). Events are split across separate log files by category — agent-IO-events.log, agent-NET-events.log, agent-SYSTEM-events.log, and so on. Each line is a pipe-delimited record with key-value pairs. Here are examples of the kinds of events you will see:

# File I/O — application reading a config file (agent-IO-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | main | INFO | org.jvmxray.events.io.fileio |
operation=read_write|file=/etc/myapp/application.yml|is_new_file=false|
is_sensitive=false|bytes_read=1048576|bytes_written=0|read_operations=15|
write_operations=0|duration_ms=125

# File creation (agent-IO-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | main | INFO | org.jvmxray.events.io.fileio |
operation=CREATE|file=/tmp/data/output.txt|status=created

# Library static load — JAR on the classpath (agent-SYSTEM-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | jvmxray.libsensor-1 | INFO | org.jvmxray.events.system.lib |
load_type=static|jar_path=/home/user/.m2/repository/org/springframework/spring-core/6.1.0/spring-core-6.1.0.jar|
sha256=a1b2c3d4...|groupId=org.springframework|artifactId=spring-core|version=6.1.0|
packages=org.springframework.core,org.springframework.util

# Library dynamic load — plugin loaded at runtime (agent-SYSTEM-events.log)
C:AP | 2024.09.15 at 14:30:28 EDT | jvmxray.libsensor-1 | INFO | org.jvmxray.events.system.lib |
load_type=dynamic|jar_path=/opt/app/plugins/custom-plugin.jar|
sha256=f0e1d2c3...|packages=com.example.plugin

# Process execution — shelling out to an external command (agent-SYSTEM-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | main | INFO | org.jvmxray.events.system.process |
operation=EXECUTE|command=/bin/sh|args=-c whoami|working_dir=/opt/app|
execution_time_ms=45|status=started

# Network socket connect — outbound connection (agent-NET-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | main | INFO | org.jvmxray.events.net.socket.connect |
bind_src=192.168.1.10:54321|dst=93.184.216.34:443|status=connected

# Network socket bind — listening on a port (agent-NET-events.log)
C:AP | 2024.09.15 at 14:30:25 EDT | main | INFO | org.jvmxray.events.net.socket.bind |
bind_src=0.0.0.0:8080|status=accepted

Each record includes a timestamp, the thread that triggered the event, a log level, and a namespace identifying the sensor. The remaining fields are pipe-separated key-value pairs specific to the event type, file paths, JAR coordinates, process commands, socket addresses, and more.

Every event is attributed to the originating class in your application’s call stack. This is what makes JVMXRay different from generic system-level monitoring, you do not just see that a file was opened, you see which class in your application opened it. That attribution is critical for understanding whether a behavior is expected or suspicious.

What’s Next

At this point you have a working JVMXRay installation producing raw security events. From here you can fire up Splunk or however you explore your haystacks of log files in your centralized logging scheme. Those are topics for future posts. For now, the agent is running, events are flowing, and you have runtime visibility into your Java application that you did not have ten minutes ago.

JVMXRay is an open-source project on GitHub. Issues, pull requests, and stars are always welcome.