Menu

Important: This documentation is about an older version. It's relevant only to the release noted, many of the features and functions have been updated or replaced. Please view the current version.

Open source

Java

How to add Java profiling to your application

Java integration is distributed as a single jar file: pyroscope.jar. It contains native async-profiler libraries for:

  • Linux on x64;
  • Linux on ARM64;
  • MacOS on x64.
  • MacOS on ARM64.

Visit our GitHub releases page to download the latest version of pyroscope.jar.

The latest release is also available on Maven Central.

Profiling Java applications

You can start Pyroscope either from your apps’s Java code or attach it as javaagent.

Start Pyroscope from app’s Java code

First, add Pyroscope dependency

maven
<dependency>
  <groupId>io.pyroscope</groupId>
  <artifactId>agent</artifactId>
  <version>0.12.1</version>
</dependency>
gradle
implementation("io.pyroscope:agent:0.12.1")

Then add the following code to your application:

java
PyroscopeAgent.start(
  new Config.Builder()
    .setApplicationName("ride-sharing-app-java")
    .setProfilingEvent(EventType.ITIMER)
    .setFormat(Format.JFR)
    .setServerAddress("http://pyroscope-server:4040")
    .build()
);
spring
import io.pyroscope.javaagent.PyroscopeAgent;
import io.pyroscope.javaagent.config.Config;
import io.pyroscope.javaagent.EventType;
import io.pyroscope.http.Format;

@PostConstruct
public void init() {

    PyroscopeAgent.start(
    new Config.Builder()
        .setApplicationName("ride-sharing-app-java")
        .setProfilingEvent(EventType.ITIMER)
        .setFormat(Format.JFR)
        .setServerAddress("http://pyroscope-server:4040")
        // Optionally, if authentication is enabled, specify the API key.
        // .setAuthToken(System.getenv("PYROSCOPE_AUTH_TOKEN"))
        // Optionally, if you'd like to set allocation threshold to register events, in bytes. '0' registers all events
        // .setProfilingAlloc("0")
        .build()
    );
}

You can also optionally replace some Pyroscope components:

java
PyroscopeAgent.start(
  new PyroscopeAgent.Options.Builder(config)
    .setExporter(snapshot -> {
      // Your custom export/upload logic may go here
      // It is invoked every 10 seconds by default with snapshot of
      // profiling data
    })
    .setLogger((l, msg, args) -> {
      // Your custom logging may go here
      // Pyroscope does not depend on any logging library
      System.out.printf((msg) + "%n", args);
    })
    .setScheduler(profiler -> {
      // Your custom profiling schedule logic may go here
    })
    .build()
);

Start Pyroscope as javaagent

To start profiling a Java application, run your application with pyroscope.jar javaagent:

shell
export PYROSCOPE_APPLICATION_NAME=my.java.app
export PYROSCOPE_SERVER_ADDRESS=http://pyroscope-server:4040

java -javaagent:pyroscope.jar -jar app.jar

How to add profiling labels to Java applications

It is possible to add dynamic tags (labels) to the profiling data. These tags can be used to filter the data in the UI.

Add labels dynamically:

java
Pyroscope.LabelsWrapper.run(new LabelsSet("controller", "slow_controller"), () -> {
  slowCode();
});

It is also possible to possible to add static tags (labels) to the profiling data:

java
Pyroscope.setStaticLabels(Map.of("region", System.getenv("REGION")));
// or with Config.Builder if you start pyroscope with PyroscopeAgent.start
PyroscopeAgent.start(new Config.Builder()
    .setLabels(mapOf("region", System.getenv("REGION")))
    // ...
    .build()
);

Java client configuration options

When you start Pyroscope as javaagent or obtain configuration by Config.build() Pyroscope searches for configuration in multiple sources: system properties, environment variables, and pyroscope.properties. Property keys have same name as environment variables, but are lowercased and replace _ with .. For example, PYROSCOPE_FORMAT becomes pyroscope.format

The Java integration supports JFR format to be able to support multiple events (JFR is the only output format that supports multiple events in async-profiler). There are several environment variables that define how multiple event configuration works:

FlagDescription
PYROSCOPE_FORMATsets the profiler output format. The default is collapsed, but in order to support multiple formats it must be set to jfr.
PYROSCOPE_PROFILER_EVENTsets the profiler event. With JFR format enabled, this event refers to one of the possible CPU profiling events: itimer, cpu, wall. The default is itimer.
PYROSCOPE_PROFILER_ALLOCsets the allocation threshold to register the events, in bytes (equivalent to --alloc= in async-profiler). The default value is "" - empty string, which means that allocation profiling is disabled. Setting it to 0 will register every event, causing significant CPU and network overhead, making it not suitable production environments. We recommend setting a starting value of 512k and adjusting it as needed.
PYROSCOPE_PROFILER_LOCKsets the lock threshold to register the events, in nanoseconds (equivalent to --lock= in async-profiler). The default value is "" - empty string, which means that lock profiling is disabled. Setting it to 0 will register every event, causing significant CPU and network overhead, making it not suitable production environments. We recommend setting a starting value of 10ms and adjusting it as needed.
PYROSCOPE_CONFIGURATION_FILEsets an additional properties configuration file. The default value is pyroscope.properties.
PYROSCOPE_BASIC_AUTH_USERHTTP Basic authentication username. The default value is "" - empty string, no authentication.
PYROSCOPE_BASIC_AUTH_PASSWORDHTTP Basic authentication password. The default value is "" - empty string, no authentication.
PYROSCOPE_TENANT_IDpyroscope tenant ID, passed as X-Scope-OrgID http header. The default value is "" - empty string, no tenant ID.
PYROSCOPE_HTTP_HEADERSextra http headers in json format, for example: {"X-Header": "Value"}. The default value is {} - no extra headers.
PYROSCOPE_LABELSsets static labels in the form of comma separated key=value pairs. The default value is "" - empty string, no labels.
PYROSCOPE_LOG_LEVELdetermines the level of verbosity for Pyroscope’s logger. Available options include debug, info, warn, and error. The default value is set to info.
PYROSCOPE_PUSH_QUEUE_CAPACITYspecifies the size of the ingestion queue that temporarily stores profiling data in memory during network outages. The default value is set to 8.
PYROSCOPE_INGEST_MAX_TRIESsets the maximum number of times to retry an ingestion API call in the event of failure. A value of -1 indicates that the retries will continue indefinitely. The default value is set to 8.
PYROSCOPE_EXPORT_COMPRESSION_LEVEL_JFRsets the level of GZIP compression applied to uploaded JFR files. This option accepts values of NO_COMPRESSION, BEST_SPEED, BEST_COMPRESSION, and DEFAULT_COMPRESSION.
PYROSCOPE_EXPORT_COMPRESSION_LEVEL_LABELSoperates similarly to PYROSCOPE_EXPORT_COMPRESSION_LEVEL_JFR, but applies to the dynamic labels part. The default value is set to BEST_SPEED.
PYROSCOPE_GC_BEFORE_DUMPis a boolean value that executes a System.gc() command before dumping the profile when set to true. This option may be useful for live profiling, but is disabled by default.

Sending data to Pyroscope OSS or Grafana Cloud Profiles with Pyroscope java SDK

Add the following code to your application:

java
PyroscopeAgent.start(
    new Config.Builder()
        .setApplicationName("test-java-app")
        .setProfilingEvent(EventType.ITIMER)
        .setFormat(Format.JFR)
        .setServerAddress("<URL>")
        .setBasicAuthUser("<User>")
        .setBasicAuthPassword("<Password>")
        // Optional Pyroscope tenant ID (only needed if using multi-tenancy). Not needed for Grafana cloud.
        // .setTenantID("<TenantID>")
        .build()
);

To configure the Java SDK to send data to Pyroscope, replace the <URL> placeholder with the appropriate server URL. This could be the Grafana Cloud URL or your own custom Pyroscope server URL.

If you need to send data to Grafana Cloud, you’ll have to configure HTTP Basic authentication. Replace <User> with your Grafana Cloud stack user and <Password> with your Grafana Cloud API key.

If your Pyroscope server has multi-tenancy enabled, you’ll need to configure a tenant ID. Replace <TenantID> with your Pyroscope tenant ID.

Java profiling examples

Check out the following resources to learn more about Java profiling: