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

JVM

In March 2023, Grafana Labs acquired Pyroscope, the company behind the eponymous open source continuous profiling project. As a result, the Pyroscope and Grafana Phlare projects will be merged under the new name Grafana Pyroscope. To learn more, read our recent blog post about the news.

JVM and Java don’t support pprof endpoints natively, but we are building a library (jpprof) to make it possible.

The library currently is available on the maven central repository under the com.grafana group. It currently only supports CPU endpoints, but we are working on adding more.

Adding the dependency

If you’re using maven, add the following dependency to your pom.xml:

<dependency>
  <groupId>com.grafana</groupId>
  <artifactId>jpprof</artifactId>
  <version>0.1.4</version>
</dependency>

If you’re using gradle, add the following dependency to your build.gradle:

dependencies {
// ....
    implementation 'com.grafana:jpprof:0.1.4'
//...
}

Integrating with Spring Boot

If you’re using Spring Boot, you can add the following Controller to your application:

package com.example.springboot;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;

import jpprof.CPUProfiler;

@RestController
public class PprofController {

  @GetMapping("/debug/pprof/profile")
  @ResponseBody
  public void profile(@RequestParam(required = false) String seconds, HttpServletResponse response) {
    try {
      Duration d = Duration.ofSeconds(Integer.parseInt(seconds));
      CPUProfiler.start(d, response.getOutputStream());
      response.flushBuffer();
    } catch (Exception e) {
      System.out.println("exception: " + e.getMessage());
    }
  }
}

Integrating with a standalone server

If you’re using another framework than Spring Boot, you can still use the library by adding the following lines to your code:

package com.example;

import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpServer;
import jpprof.PprofHttpHandler;

public class Main {

    public static void main(String[] args) throws Exception {
        //.... your main code ...
        var server = HttpServer.create(new InetSocketAddress(8080), 0);
        server.createContext("/", new PprofHttpHandler());
        server.start();
    }

}

This will expose the pprof endpoints on the root path using a new server listening on port 8080.

Docker Considerations

jpprof uses behind the scenes async-profiler which takes advantage of perf_event_open to collect the CPU samples.

This means that you need to run your container with the SYS_ADMIN capability and/or the --privileged flag.

async-profiler also requires JVM symbols to be available, use an appropriate docker base image for your JVM version.

Make sure to check out our docker-compose example.

Scrape Target Configuration

Because the support is currently only for CPU endpoints, you need to add the following configuration to your scrape target:

  - job_name: "java"
    scrape_interval: "15s"
    static_configs:
      - targets: ["my-java-app:8080"]
    profiling_config:
      pprof_config:
        block: { enabled: false }
        goroutine: { enabled: false }
        memory: { enabled: false }
        mutex: { enabled: false }

This way, the agent will only scrape the CPU endpoint.