Skip to main content

Java Agent

The Levo Java Agent captures TLS-encrypted traffic directly from the JVM by instrumenting the SSL/TLS layer at the bytecode level. It works alongside the Levo eBPF Sensor, which reconstructs HTTP requests and responses from the captured bytes.

Prerequisites

  • Java 8 or later
  • Levo eBPF Sensor installed and running — see Install eBPF Sensor
  • libnative_ioctl.so accessible on the host (included in the agent download)

Installation

1. Download and extract

Download the agent package from the Downloads repo and extract it on the application server:

levo-agent-0.9.0-all.jar        # main agent JAR
levo-agent-bootstrap-0.9.0.jar # bootstrap JAR
libnative_ioctl.so # native JNI library (amd64)

2. Set the native library path

export LIB_NATIVE_IOCTL_FILE_PATH=/path/to/libnative_ioctl.so

3. Start the application with the agent

java -javaagent:/path/to/levo-agent-0.9.0-all.jar \
-Dbootstrap.jar=/path/to/levo-agent-bootstrap-0.9.0.jar \
-jar your-application.jar

For JRuby applications:

JRUBY_OPTS="-J-javaagent:/path/to/levo-agent-0.9.0-all.jar -J-Dbootstrap.jar=/path/to/levo-agent-bootstrap-0.9.0.jar" ruby app.rb

Quick Start Examples

Standard server-side capture (most applications):

java -javaagent:/path/to/levo-agent-0.9.0-all.jar=LevoSatelliteUrl=localhost:4317,LevoSamplingPercent=100 \
-Dbootstrap.jar=/path/to/levo-agent-bootstrap-0.9.0.jar \
-jar your-application.jar

Full capture with debug logging:

java -javaagent:/path/to/levo-agent-0.9.0-all.jar=LevoSatelliteUrl=localhost:4317,LevoSamplingPercent=100,LevoDebug=true,LevoLogFile=/tmp/levo-agent.log \
-Dbootstrap.jar=/path/to/levo-agent-bootstrap-0.9.0.jar \
-jar your-application.jar

Client-side only (outbound third-party API discovery):

java -javaagent:/path/to/levo-agent-0.9.0-all.jar=TraceClientTrafficOnly=true \
-Dbootstrap.jar=/path/to/levo-agent-bootstrap-0.9.0.jar \
-jar your-application.jar

Configuration Reference

Agent arguments are passed as key=value pairs on the -javaagent: flag, separated by commas. Most settings also accept a JVM system property (-Dlevo.xxx=value) as an alternative.

Precedence order (highest to lowest):

  1. Agent argument on -javaagent: flag
  2. JVM system property (-D...)
  3. Environment variable (logging settings only)
  4. Built-in default

Instrumentation Toggles

Controls which SSL/TLS interception paths are active. Defaults are tuned for modern Java stacks.

Agent ArgDefaultCoversWhen to Change
SunSecuritySslInstrumentationtrueBlocking JDK SSLSocket (sun.security.ssl.AppInputStream/AppOutputStream). Covers Kafka clients, HttpsURLConnection, Apache HttpClient, JDBC SSL, JRuby OpenSSL bridge.Rarely needed to disable.
JavaxSSLEngineInstrumentationtrueNon-blocking JDK SSLEngine (sun.security.ssl.SSLEngineImpl, io.netty.handler.ssl.JdkSslEngine). Covers Vert.x, Quarkus, Netty with SslProvider.JDK, gRPC JDK provider.Disable only when isolating instrumentation paths.
NettyOpenSslInstrumentationtrueNetty BoringSSL/OpenSSL (ReferenceCountedOpenSslEngine). Covers Netty/Vert.x with SslProvider.OPENSSL via netty-tcnative.Safe to leave on — no-op if netty-tcnative is not on the classpath.
JavaxSslSocketStreamInstrumentationfalseSSLSocketImpl.getInputStream()/getOutputStream() wrapping.Enable for specific raw-SSLSocket use cases.
JRubyOpensslInstrumentationfalseJRuby OpenSSL bridge (org.jruby.ext.openssl.SSLSocket).Enable for JRuby applications.
NettySslHandlerProbeInstrumentationtrueLogs the active SSLEngine implementation once at startup.Disable to suppress the startup INFO line.
NettySslHandlerChannelBindInstrumentationtrue5-tuple recovery for Netty/Vert.x/Spring WebFlux/Reactor-Netty.Disable only for diagnostic purposes.
TomcatSecureChannelBindInstrumentationtrue5-tuple recovery for Tomcat NIO/NIO2. Covers Tomcat 8.5/9/10/11.Disable only for diagnostic purposes.
JettySslConnectionBindInstrumentationtrue5-tuple recovery for Jetty. Covers Jetty 9.4/10/11/12.Disable only for diagnostic purposes.
SSLContextCreateEngineInstrumentationtrue5-tuple recovery for JCE stacks. Covers JDK HttpsServer, BouncyCastle server, Conscrypt server.Disable only for diagnostic purposes.

Capture Filters

Agent ArgSystem PropertyDefaultDescription
TraceClientTrafficOnlylevo.trace.client.traffic.onlyfalseCapture only outbound (client-side) traffic. Use for third-party API discovery.
LevoCapturePortslevo.capture.portsunsetComma-separated port allowlist (e.g. 8443,9000). Unset = all ports.
LevoExcludePortslevo.exclude.portsunsetComma-separated port denylist. Wins over the allowlist.
LevoCaptureHostslevo.capture.hostsunsetHost allowlist with wildcard support (*.foo.com, api.*).
LevoExcludeHostslevo.exclude.hostsunsetHost denylist. Use to exclude health-check and metrics endpoints.
LevoSuppressHttpProbeslevo.suppress.http.probesonSuppress HEAD / keepalive probes from being captured.
LevoHttpProbePathslevo.suppress.http.probe.paths/Comma-separated paths suppressed when LevoSuppressHttpProbes=on.

Filter precedence (evaluated in order):

Port allowlist → Port denylist → Host allowlist → Host denylist
→ Connection sampling → Rate cap → Dispatcher

Denylist always wins over allowlist.


Sampling and Queue

Agent ArgSystem PropertyDefaultDescription
LevoSamplingPercentlevo.sampling.percent100Per-connection sticky sampling (0–100). Decision is made once per TCP connection and held for its lifetime — guarantees fully reconstructable HTTP messages.
LevoSamplingRatePerSeclevo.sampling.rate.per.sec0Token-bucket cap on captured events/sec. 0 disables.
LevoMaxQueueSizelevo.dispatch.queue.size8192Bounded queue capacity (rounds up to next power of two).
LevoDispatcherThreadslevo.dispatch.threads1Consumer threads draining the queue. Clamped to 1–8.
LevoMaxPayloadByteslevo.max.payload.bytes32768Truncate captured payloads above this size (bytes). 0 disables truncation.
LevoDropPolicylevo.drop.policynewestQueue overflow behavior: newest drops incoming events; oldest evicts the queue head.
LevoMetricsLogIntervalSeclevo.metrics.log.interval.sec60Interval for the periodic dispatch summary log line (seconds). 0 disables.
Connection-level sampling

LevoSamplingPercent applies once per connection, not per chunk. A sampled-in connection is fully captured for its lifetime, guaranteeing fully reconstructable HTTP messages.


Debug and Logging

Agent ArgSystem PropertyEnv VarDefaultDescription
LevoDebuglevo.debugLEVO_DEBUGfalseEnable debug mode. Forces LevoSamplingPercent=100 and LevoSamplingRatePerSec=0 unless explicitly overridden.
LevoLogLevellevo.log.levelLEVO_LOG_LEVELDEBUGLog level: DEBUG, INFO, WARN, ERROR, or OFF.
LevoLogFilelevo.log.fileLEVO_LOG_FILE/tmp/levo-agent-debug.logPath for agent log output.
LevoLogMaxSizeMblevo.log.max.size.mbLEVO_LOG_MAX_SIZE_MB10Max log file size (MB) before rotation.
LevoLogMaxFileslevo.log.max.filesLEVO_LOG_MAX_FILES3Number of rotated log files to retain.
LevoSatelliteUrllevo.satellite.urlHost and port of the Levo collector (e.g. localhost:4317).

Native Library

SettingTypeDefaultDescription
LIB_NATIVE_IOCTL_FILE_PATHEnvironment variable/usr/lib/libnative_ioctl.soAbsolute path to the shared JNI library. The agent logs the resolved path at startup.

Understanding the Startup Banner

At startup the agent logs a full configuration banner showing every effective value and its source (agentArg, default, sysProp):

LEVO JAVA AGENT STARTUP
Agent version : 0.9.0
LevoMaxPayloadBytes : 32768 (default)
LevoSamplingPercent : 100 (set: agentArg)
SunSecuritySslInstrumentation: true (default)
JavaxSSLEngineInstrumentation: true (default)
...

Each instrumentation reports its transform result at install time:

INFO  JavaxSSLEngineInstrumentation: at install — transformed 1 class(es), 0 error(s)

A deferred health check fires 60 seconds after startup and logs a summary of active instrumentation.


Understanding the Dispatch Summary

Every LevoMetricsLogIntervalSec seconds (default 60s), the agent logs a dispatch summary:

Dispatch: events[r/w/a/c]=842/388/8/4  bytes[r/w]=1.2MB/0.6MB
drops[sampled/full/oversize/empty]=0/0/0/0
dropReasons={http_probe_suppressed=12}
tuple[netty/tomcat/jetty/peer/reflect/fallback]=388/0/0/0/0/0
ioctl[nonzero/err/ackAgoSec]=1230/0/never
topHosts={localhost:8443=842}
FieldWhat it means
events[r/w/a/c]READ / WRITE / ACCEPT / CLOSE events captured
drops[sampled/full/oversize/empty]Events filtered by sampling / queue capacity / payload size / empty buffer
dropReasonsNamed drop reasons including filter hits and probe suppression
tuple[netty/tomcat/jetty/peer/reflect/fallback]5-tuple resolution breakdown — shows which framework paths are active
ioctl[nonzero/err/ackAgoSec]ioctl delivery counters
topHostsMost active captured hosts

JMX Metrics

The agent exposes live metrics at ai.levo:type=Metrics:

AttributeDescription
EventsOfferedTotal events submitted to the dispatcher
EventsAcceptedEvents that passed sampling and entered the queue
EventsDroppedQueueFullEvents dropped due to queue capacity
EventsDroppedOversizeEvents dropped for exceeding LevoMaxPayloadBytes
EventsProcessedEvents successfully delivered to the sensor
BytesProcessedTotal payload bytes delivered to the sensor
QueueDepthCurrent queue depth (live)
IoctlNonZeroReturnsioctl delivery counter

Coexistence with Other Java Agents

The Levo agent is compatible with premain-attach APM agents including OpenTelemetry, DataDog, and New Relic. When running alongside dynamic-attach APM agents (Riverbed AppInternals, AppDynamics, Dynatrace OneAgent), configure both agents to instrument non-overlapping class sets.

Contact support@levo.ai for the full compatibility matrix and configuration guidance.


Support

For assistance, contact Levo support at support@levo.ai or via the support portal.

When filing a support request, include:

  1. Agent version (grep "Agent version" /tmp/levo-agent-debug.log | head -1)
  2. The full startup banner from the agent log
  3. Three consecutive Dispatch: summary lines from the agent log
  4. Output of bpftool prog show | grep -iE "levo|kprobe" from the sensor host
Was this page helpful?