Custom probes

A resource document of the JProfiler MCP server. Back to the tool and resource reference.

For agent consumption, this page is also available as raw Markdown.

Content of the MCP resource jprofiler://probes/custom of the JProfiler MCP server, see the tool and resource reference for the full list of tools and resources.

Custom probes are Java classes shipped with the profiled application that JProfiler records like built-in probes: aggregated payload hot spots, call tree splitting, optionally individual events, control objects and telemetries. This server records and queries them as custom:<fully qualified probe class name> subsystems - the probe class name is the probe id in recordingConfig.subsystems, in probeConfig, in the subsystem argument of the query datasets and in the customProbes section of check_status.

When not to write a custom probe

A custom probe is code that has to be compiled and deployed with the profiled application. Cheaper options first:

  • The built-in probes cover common technologies (jdbc, jpa, http_server, http_client, mongo_db, kafka, files, sockets, processes and more) and need no code at all.
  • To split the CPU call tree by a runtime value such as a URL or a tenant id, a recordingConfig.advanced.splitMethods script is enough.
  • To turn selected methods into separately measured groups, use recordingConfig.advanced.complexityMethods.
  • To record payload hot spots and events of application-specific methods without any deployment, define a script probe in recordingConfig.advanced.scriptProbes: the script text is compiled by this server and pushed to the profiled JVM, also on attach.

Write a custom probe when script probes are not enough: they cannot split the call tree, access return values or create control objects, custom event types or telemetries.

Embedded or injected

  • Embedded probe: the application calls the probe API itself, for code you own. Extend PayloadProbe or SplitProbe from com.jprofiler.api.probe.embedded and bracket the measured operations with the static Payload/Split methods. Embedded probes self-register on the first API call and need no further deployment step. API reference: jprofiler://api/probe-embedded.
  • Injected probe: bytecode instrumentation adds the measurement to methods of classes you may not own. Annotate a class with @Probe from com.jprofiler.api.probe.injected and declare interceptions. The agent must be told the probe class names, see deployment below. API reference: jprofiler://api/probe-injected.

Build setup

Both APIs are published as Maven artifacts, com.jprofiler:jprofiler-probe-embedded:<version> and com.jprofiler:jprofiler-probe-injected:<version> where <version> is the JProfiler version - use provided scope, the profiling agent carries the implementation. The same JARs ship in the api directory of a JProfiler installation. The API references show the exact coordinates.

Minimal examples

Embedded payload probe:

public class ImportProbe extends PayloadProbe {
    @Override
    public String getName() { return "Imports"; }

    @Telemetry("Open imports")
    public static int openImports() { return ImportService.openCount(); }
}

// in the application code, around the measured operation:
Payload.execute(ImportProbe.class, "import " + file, () -> doImport(file));

Injected probe intercepting a method without touching its code:

@Probe(name = "Order processing")
public class OrderProbe {
    @PayloadInterception(invokeOn = InvocationType.ENTER, method = @MethodSpec(
        className = "com.example.Shop", methodName = "processOrder",
        parameterTypes = {"int"}, returnType = "void"))
    public static String payload(@Parameter(0) int orderId) { return "order " + orderId; }
}

Deployment

Embedded probes need nothing extra: the probe class is on the application classpath and registers on first use. Injected probes are discovered through the -Djprofiler.customProbes=<probe classes> VM parameter:

  • With prepare_profiling, set "injected": true in the probeConfig entry of every injected probe. The response then contains an additionalJvmParameters array with the discovery parameter - add each element as a separate VM argument to the Java command line, together with the returned jvmParameter.
  • With attach, the server passes the discovery property into the running JVM for probes marked "injected": true, no command line change is needed.

-Djprofiler.customProbes names probe classes that are loaded from the class path of the application, so the JAR with the probe classes must be on it. For probes shipped in a separate JAR that is not on the class path, use -Djprofiler.probeClassPath=<jar or directory> instead: the agent scans those paths itself and registers every probe class it finds, and the probe classes must then not be on the class path.

Recording and analysis

Record a custom probe like a built-in one:

"recordingConfig": {
  "subsystems": ["cpu", "custom:com.example.ImportProbe"],
  "advanced": {"probeConfig": {"custom:com.example.ImportProbe": {"events": true}}}
}

events records individual events in addition to the default aggregated hot spots. Trigger start/stop actions and the startRecording/stopRecording attached actions take the same subsystem name. Starting a probe that is not registered yet is stored by the agent and applied when it registers, attach reports a warning in that case. Application code can also start and stop the probe recording itself with Controller.startProbeRecording(probeClassName, ...), see jprofiler://api/controller.

Recorded custom probes are listed in check_status in the customProbes section with their display name and recording state, and in availableSubsystems. The query datasets serve their data per subsystem: performance.hotspots and performance.callTree (payload nodes appear as custom_split nodes), probe.events, probe.eventTimeline, probe.eventHistogram, probe.controlObjects and telemetry.list (custom probe telemetries with custom:<class>/<telemetry> ids). Custom probes do not exist in JFR recordings.