Skip to content

Commit affe34d

Browse files
authored
Add initial docs for the /pyroscope/render API endpoint (#2837)
* Add initial docs for the /pyroscope/render API endpoint * Improve /render endpoint docs following feedback * Add two more links
1 parent 31ec2d0 commit affe34d

File tree

2 files changed

+245
-11
lines changed

2 files changed

+245
-11
lines changed

docs/sources/configure-server/about-server-api.md

Lines changed: 215 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ There is one primary endpoint: POST /ingest. It accepts profile data in the requ
2121

2222
The following query parameters are accepted:
2323

24-
| Name | Description | Notes |
25-
|:-------------------|:----------------------------------------|:--------------------------------|
26-
| `name` | application name | required |
27-
| `from` | UNIX time of when the profiling started | required |
28-
| `until` | UNIX time of when the profiling stopped | required |
29-
| `format` | format of the profiling data | optional (default is `folded`) |
30-
| `sampleRate` | sample rate used in Hz | optional (default is 100 Hz) |
31-
| `spyName` | name of the spy used | optional |
32-
| `units` | name of the profiling data unit | optional (default is `samples` |
33-
| `aggregrationType` | type of aggregration to merge profiles | optional (default is `sum`) |
24+
| Name | Description | Notes |
25+
|:-------------------|:----------------------------------------|:-------------------------------|
26+
| `name` | application name | required |
27+
| `from` | UNIX time of when the profiling started | required |
28+
| `until` | UNIX time of when the profiling stopped | required |
29+
| `format` | format of the profiling data | optional (default is `folded`) |
30+
| `sampleRate` | sample rate used in Hz | optional (default is `100` Hz) |
31+
| `spyName` | name of the spy used | optional |
32+
| `units` | name of the profiling data unit | optional (default is `samples` |
33+
| `aggregrationType` | type of aggregration to merge profiles | optional (default is `sum`) |
3434

3535

3636
`name` specifies application name. For example:
@@ -213,6 +213,210 @@ curl -X POST \
213213

214214
{{< /code >}}
215215

216+
## Querying profile data
217+
218+
There is one primary endpoint for querying profile data: `GET /pyroscope/render`.
219+
220+
The search input is provided via query parameters.
221+
The output is typically a JSON object containing one or more time series and a flamegraph.
222+
223+
### Query parameters
224+
225+
Here is an overview of the accepted query parameters:
226+
227+
| Name | Description | Notes |
228+
|:---------------|:--------------------------------------------------------------------------------------|:-----------------------------------------------------|
229+
| `query` | contains the profile type and label selectors | required |
230+
| `from` | UNIX time for the start of the search window | required |
231+
| `until` | UNIX time for the end of the search window | optional (default is `now`) |
232+
| `format` | format of the profiling data | optional (default is `json`) |
233+
| `maxNodes` | the maximum number of nodes the resulting flamegraph will contain | optional (default is `max_flamegraph_nodes_default`) |
234+
| `groupBy` | one or more label names to group the time series by (doesn't apply to the flamegraph) | optional (default is no grouping) |
235+
236+
#### `query`
237+
238+
The `query` parameter is the only required search input. It carries the profile type and any labels we want to use to narrow down the output.
239+
The format for this parameter is similar to that of a PromQL query and can be defined as:
240+
241+
`<profile_type>{<label_name>="<label_value>", <label_name>="<label_value>", ...}`
242+
243+
Here is a specific example:
244+
245+
`process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name="my_application_name"}`
246+
247+
In a Kubernetes environment, a query could also look like:
248+
249+
`process_cpu:cpu:nanoseconds:cpu:nanoseconds{namespace="dev", container="my_application_name"}`
250+
251+
{{% admonition type="note" %}}
252+
Refer to the [profiling types documentation]({{< relref "../ingest-and-analyze-profile-data/profiling-types" >}}) for more information and [profile-metrics.json](https://github.com/grafana/pyroscope/blob/main/public/app/constants/profile-metrics.json) for a list of valid profile types.
253+
{{% /admonition %}}
254+
255+
#### `from` and `until`
256+
257+
The `from` and `until` parameters determine the start and end of the time period for the query.
258+
They can be provided in absolute and relative form.
259+
260+
**Absolute time**
261+
262+
This table details the options for passing absolute values.
263+
264+
| Option | Example | Notes |
265+
|:-----------------------|:----------------------|:-------------------|
266+
| Date | `20231223` | Format: `YYYYMMDD` |
267+
| Unix Time seconds | `1577836800` | |
268+
| Unix Time milliseconds | `1577836800000` | |
269+
| Unix Time microseconds | `1577836800000000` | |
270+
| Unix Time nanoseconds | `1577836800000000000` | |
271+
272+
**Relative time**
273+
274+
Relative values are always expressed as offsets from `now`.
275+
276+
| Option | Example |
277+
|:---------------|:---------------------|
278+
| 3 hours ago | `now-3h` |
279+
| 30 minutes ago | `now-30m` |
280+
| 2 days ago | `now-2d` |
281+
| 1 week ago | `now-7d` or `now-1w` |
282+
283+
Note that a single offset has to be provided, values such as `now-3h30m` will not work.
284+
285+
**Validation**
286+
287+
The `from` and `until` parameters are subject to validation rules related to `max_query_lookback` and `max_query_length` server parameters.
288+
You can find more details on these parameters in the [limits section]({{< relref "./reference-configuration-parameters#limits" >}}) of the server configuration docs.
289+
290+
- If `max_query_lookback` is configured and`from` is before `now - max_query_lookback`, `from` will be set to `now - max_query_lookback`.
291+
- If `max_query_lookback` is configured and `until` is before `now - max_query_lookback` the query will not be executed.
292+
- If `max_query_length` is configured and the query interval is longer than this configuration, the query will no tbe executed.
293+
294+
#### `format`
295+
296+
The format can either be:
297+
- `json`, in which case the response will contain a JSON object
298+
- `dot`, in which case the response will be text containing a DOT representation of the profile
299+
300+
See the [Query output](#query-output) section for more information on the response structure.
301+
302+
#### `maxNodes`
303+
304+
The `maxNodes` parameter truncates the number of elements in the profile response, to allow tools (for example, a frontend) to render large profiles efficiently.
305+
This is typically used for profiles that are known to have large stack traces.
306+
307+
When no value is provided, the default is taken from the `max_flamegraph_nodes_default` configuration parameter.
308+
When a value is provided, it is capped to the `max_flamegraph_nodes_max` configuration parameter.
309+
310+
#### `groupBy`
311+
312+
The `groupBy` parameter impacts the output for the time series portion of the response.
313+
When a valid label is provided, the response contains as many series as there are label values for the given label.
314+
315+
{{% admonition type="note" %}}
316+
Pyroscope supports a single label for the group by functionality.
317+
{{% /admonition %}}
318+
319+
### Query output
320+
321+
The output of the `/pyroscope/render` endpoint is a JSON object based on the following [schema](https://github.com/grafana/pyroscope/blob/80959aeba2426f3698077fd8d2cd222d25d5a873/pkg/og/structs/flamebearer/flamebearer.go#L28-L43):
322+
323+
```go
324+
type FlamebearerProfileV1 struct {
325+
Flamebearer FlamebearerV1 `json:"flamebearer"`
326+
Metadata FlamebearerMetadataV1 `json:"metadata"`
327+
Timeline *FlamebearerTimelineV1 `json:"timeline"`
328+
Groups map[string]*FlamebearerTimelineV1 `json:"groups"`
329+
}
330+
```
331+
332+
#### `flamebearer`
333+
334+
The `flamebearer` field contains data in a form suitable for rendering a flamegraph.
335+
Data within the flamebearer is organized in separate arrays containing the profile symbols and the sample values.
336+
337+
#### `metadata`
338+
339+
The `metadata` field contains additional information that is helpful to interpret the `flamebearer` data such as the unit (nanoseconds, bytes), sample rate and more.
340+
341+
#### `timeline`
342+
343+
The `timeline` field represents the time series for the profile.
344+
Pyroscope pre-computes the step interval (resolution) of the timeline using the query interval (`from` and `until`). The minimum step interval is 10 seconds.
345+
346+
The raw profile sample data is down-sampled to the step interval (resolution) using an aggregation function. Currently only `sum` is supported.
347+
348+
A timeline contains a start time, a list of sample values and the step interval:
349+
350+
```json
351+
{
352+
"timeline": {
353+
"startTime": 1577836800,
354+
"samples": [
355+
100,
356+
200,
357+
400
358+
],
359+
"durationDelta": 10
360+
}
361+
}
362+
```
363+
364+
#### `groups`
365+
366+
The `groups` field is only populated when grouping is requested by the `groupBy` query parameter.
367+
When this is the case, the `groups` field has an entry for every label value found for the query.
368+
369+
This example groups by a cluster:
370+
371+
```json
372+
{
373+
"groups": {
374+
"eu-west-2": { "startTime": 1577836800, "samples": [ 200, 300, 500 ] },
375+
"us-east-1": { "startTime": 1577836800, "samples": [ 100, 200, 400 ] }
376+
}
377+
}
378+
```
379+
380+
### Alternative Query Output
381+
382+
When the `format` query parameter is `dot`, the endpoint responds with a [DOT format](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) data representing the queried profile.
383+
This can be used to create an alternative visualization of the profile.
384+
385+
### Example queries
386+
387+
This example queries a local Pyroscope server for a CPU profile from the `pyroscope` service for the last hour.
388+
389+
```curl
390+
curl \
391+
'http://localhost:4040/pyroscope/render?query=process_cpu%3Acpu%3Ananoseconds%3Acpu%3Ananoseconds%7Bservice_name%3D%22pyroscope%22%7D&from=now-1h'
392+
```
393+
394+
Here is the same query made more readable:
395+
396+
```curl
397+
curl --get \
398+
--data-urlencode "query=process_cpu:cpu:nanoseconds:cpu:nanoseconds{service_name=\"pyroscope\"}" \
399+
--data-urlencode "from=now-1h" \
400+
http://localhost:4040/pyroscope/render
401+
```
402+
403+
Here is the same example in Python:
404+
405+
```python
406+
import requests
407+
408+
application_name = 'my_application_name'
409+
query = f'process_cpu:cpu:nanoseconds:cpu:nanoseconds{{service_name="{application_name}"}}'
410+
query_from = 'now-1h'
411+
url = f'http://localhost:4040/pyroscope/render?query={query}&from={query_from}'
412+
413+
requests.get(url)
414+
```
415+
416+
See [this Python script](https://github.com/grafana/pyroscope/tree/main/examples/api/query.py) for a complete example.
417+
216418
## Profile CLI
217419

218-
The `profilecli` tool can also be used to interact with the Pyroscope server API. It supports operations such as ingesting profiles, querying for existing profiles and more. Refer to the [Profile CLI]({{< relref "../ingest-and-analyze-profile-data/profile-cli" >}}) page for more information.
420+
The `profilecli` tool can also be used to interact with the Pyroscope server API.
421+
The tool supports operations such as ingesting profiles, querying for existing profiles, and more.
422+
Refer to the [Profile CLI]({{< relref "../ingest-and-analyze-profile-data/profile-cli" >}}) page for more information.

examples/api/query.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import requests
2+
from requests.auth import HTTPBasicAuth
3+
4+
# NOTE: For original docs visit information visit:
5+
# https://grafana.com/docs/pyroscope/next/configure-server/about-server-api/
6+
7+
# Authentication details if using cloud
8+
basic_auth_username = '<username>' # Replace with your Grafana Cloud stack user
9+
basic_auth_password = '<password>' # Replace with your Grafana Cloud API key
10+
pyroscope_server = 'https://profiles-prod-001.grafana.net'
11+
12+
# If not using cloud, use the following:
13+
# pyroscope_server = 'http://localhost:4040' # replace with your server address:port
14+
15+
application_name = 'my_application_name'
16+
query = f'process_cpu:cpu:nanoseconds:cpu:nanoseconds{{service_name="{application_name}"}}'
17+
query_from = 'now-1h'
18+
pyroscope_url = f'{pyroscope_server}/pyroscope/render?query={query}&from={query_from}'
19+
20+
# Sending the request with authentication
21+
response = requests.get(
22+
pyroscope_url,
23+
auth=HTTPBasicAuth(basic_auth_username, basic_auth_password)
24+
)
25+
26+
# Checking the response
27+
if response.status_code == 200:
28+
print(response.text)
29+
else:
30+
print(f"Failed to query data. Status code: {response.status_code}, Message: {response.text}")

0 commit comments

Comments
 (0)