Import Profile Data

Created:2024-03-15 Last Modified:2024-06-24

This document was translated by ChatGPT

#1. Data Flow

#2. Configure Application

Currently, DeepFlow supports Profile data (continuous profiling data) integration based on Pyroscope (opens new window) and Golang pprof, Java Jfr.

#2.1 Based on Pyroscope SDK

DeepFlow currently supports the integration of Profile data sent using the Pyroscope SDK. You can find the supported language SDKs in the Pyroscope SDKs (opens new window) documentation and complete the instrumentation in your code.

After modifying the code, change the target sending address to DeepFlow Agent via environment variables in the application's runtime environment. For example, in a K8S deployment, add the following environment variables to the deployment file:

env:
  - name: PYROSCOPE_SERVER_ADDRESS
    value: http://deepflow-agent.deepflow/api/v1/profile
1
2
3

Additionally, to identify different data sources in DeepFlow, explicitly label the application name by adding the following environment variables:

env:
  - name: PYROSCOPE_APPLICATION_NAME
    value: application-demo # FIXME: your application name
1
2
3

#2.2 Based on Golang pprof

Profile data collected based on Golang pprof can also be sent to DeepFlow, but you need to manually add the code logic for active reporting. You can collect Profile data through "net/http/pprof" (opens new window) and expose the data download service. The client requests the /debug/pprof/profile interface to obtain the target pprof data and then sends it to DeepFlow.

Here is a reference code snippet for building the reporting logic:

func main() {
  // Note, the actual URL used here is `/api/v1/profile/ingest`
  deepflowAgentAddress = "http://deepflow-agent.deepflow/api/v1/profile/ingest"
  var pprof io.Reader // FIXME: This is an example, please first obtain pprof data from `/debug/pprof/profile`
	err = sendProfileData(pprof, deepflowAgentAddress)
	if err != nil {
		fmt.Println(err)
	}
}

// Build the request to send
func sendProfileData(pprof io.Reader, remoteURL string) error {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)
	pprofWriter, err := bodyWriter.CreateFormFile("profile", "profile.pprof")
	if err != nil {
		return err
	}

	_, err = io.Copy(pprofWriter, pprof)
	if err != nil {
		return err
	}

	err = bodyWriter.Close()
	if err != nil {
		return err
	}

	u, err := url.Parse(remoteURL)
	if err != nil {
		return err
	}

	q := u.Query()
	q.Set("spyName", "gospy")                            // hardcode, no need to modify
	q.Set("name", "application-demo")                    // FIXME: your application name
	q.Set("unit", "samples");                            // FIXME: unit of measurement, see explanation below
	q.Set("from", strconv.Itoa(int(time.Now().Unix())))  // FIXME: profile start time
	q.Set("until", strconv.Itoa(int(time.Now().Unix()))) // FIXME: profile end time
	q.Set("sampleRate", "100")                           // FIXME: actual sampling rate of the profile, sampling rate 100=1s/10ms, i.e., sampled every 10ms
	u.RawQuery = q.Encode()

	req, err := http.NewRequest(http.MethodPost, u.String(), bodyBuf)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", bodyWriter.FormDataContentType())

	// Send the request
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("server returned non-OK status: %s", resp.Status)
	}

	return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

#2.3 Based on Java Async Profiler

For Java applications, we support receiving Profile data in JFR (opens new window) format. Use Java's built-in jcmd (opens new window) or async-profiler (opens new window) to collect Profile data and generate Jfr format to send to DeepFlow.

Here is a reference code snippet for building the reporting logic:

import okhttp3.*;
import okio.BufferedSink;
import okio.GzipSink;
import okio.Okio;

import java.io.IOException;

public class Sender {
	// Note, the actual URL used here is `/api/v1/profile/ingest`
    private static final String DEEPFLOW_AGENT_ADDRESS = "http://deepflow-agent.deepflow/api/v1/profile/ingest";

    public static void main(String[] args) throws IOException {
        sendProfileData(DEEPFLOW_AGENT_ADDRESS);
    }

    private static void sendProfileData(String remoteURL) throws IOException {
        OkHttpClient client = new OkHttpClient();

        byte[] data = new byte[] {}; // FIXME: This is an example, please use the profile tool to obtain Jfr content before reporting
        MediaType mediaType = MediaType.parse("application/octet-stream");

        RequestBody requestBody = new RequestBody() {
            @Override
            public MediaType contentType() {
                return mediaType;
            }

			// Use Gzip for compression before transmission
            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                gzipSink.write(data);
                gzipSink.close();
            }
        };

        HttpUrl.Builder urlBuilder = HttpUrl.parse(remoteURL).newBuilder();
        urlBuilder.addQueryParameter("name", "application-demo"); // FIXME: your application name
        urlBuilder.addQueryParameter("spyName", "javaspy"); // hardcode, no need to modify
        urlBuilder.addQueryParameter("format", "jfr"); // hardcode, no need to modify
        urlBuilder.addQueryParameter("unit", "samples"); // FIXME: unit of measurement, see explanation below
        urlBuilder.addQueryParameter("from", String.valueOf(System.currentTimeMillis() / 1000)); // FIXME: profile start time
        urlBuilder.addQueryParameter("until", String.valueOf(System.currentTimeMillis() / 1000));// FIXME: profile end time
        urlBuilder.addQueryParameter("sampleRate", "100"); // FIXME: actual sampling rate of the profile, sampling rate 100=1s/10ms, i.e., sampled every 10ms
        String urlWithQueryParams = urlBuilder.build().toString();

        Request request = new Request.Builder()
                .url(urlWithQueryParams)
                .post(requestBody)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println("Profile data sent successfully.");
            } else {
                System.err.println("server returned non-OK status: " + response.code());
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

#2.4 Reporting Parameter Explanation

Name Type Description
name string Application name, used to identify the reported data. You can add custom tags to mark different deployment specifications of the same application, e.g., application-demo{region="cn",deploy="prod"}
spyName string Used to mark the type of reported data. For Golang applications, it is fixed as gospy, and for Java applications, it is fixed as javaspy
format string Profile data format. For Golang, the collected pprof format is pprof (default), and for Java, the collected jfr format is jfr
unit string Unit of measurement. For different sampling types, there are different units. Refer to here (opens new window) for details. cpu uses samples as the unit, memory uses bytes as the unit, and others are similar
from int Profile start time, Unix timestamp (seconds)
until int Profile end time, Unix timestamp (seconds)
sampleRate int Actual sampling rate of the profile

#3. Configure DeepFlow

Please refer to the Configure DeepFlow section to complete the configuration of DeepFlow Agent and open the data integration port.

#4. Experience Based on Demo

Use the following command to quickly deploy the Demo and experience the continuous profiling capability in DeepFlow:

kubectl apply -f https://raw.githubusercontent.com/deepflowio/deepflow-demo/main/DeepFlow-CP-Demo/deepflow-cp-golang-demo.yaml
1
kubectl apply -f https://raw.githubusercontent.com/deepflowio/deepflow-demo/main/DeepFlow-CP-Demo/deepflow-cp-java-demo.yaml
1

Then, for the community edition, refer to the Continuous Profiling - API section to obtain the data generated by continuous profiling.