Simple Log Service provides multiple APIs to query time series metrics or write metric data to a MetricStore. These APIs are compatible with the open source Prometheus protocol. This topic describes these APIs in detail.
Overview
The APIs provided by Prometheus are located in the /api/v1/ directory. The MetricStore APIs follow the same convention. The complete URL is https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/.
Variable | Required | Description |
{sls-endpoint} | Yes | The endpoint of Simple Log Service. The endpoint is the domain name used to access the service. The endpoint varies by the region where the project is located. For more information, see Endpoints. |
{project} | Yes | Project Name: A Project is the resource management unit of Simple Log Service. It is the primary boundary for multi-user isolation and access control. For more information, see Manage Projects. |
{metricstore} | Yes | The MetricStore that you created. For more information, see Create a MetricStore. |
When you call the APIs, you must use BasicAuth authentication. Set Username to your AccessKey ID and Password to your AccessKey secret. We recommend that you use the AccessKey of a Resource Access Management (RAM) user. You must grant the RAM user the permissions to query the specified project. For more information, see Configure a permission assistant.
The APIs also support Security Token Service (STS) authentication. In this case, the Password for BasicAuth is in the format {AccessKey Secret}${STS Token}. For more information, see What is STS?.
Time series metric query APIs
The time series metric query APIs include the Instant Queries API and the Range Queries API.
Instant Queries API
You can use the Instant Queries API to query metric data at a specific point in time.
GET https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/query
POST https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/queryThe following table describes the parameters.
Variable | Required | Description |
query | Yes | The Prometheus Query Language (PromQL) statement. For more information, see PromQL syntax. |
time | No | The point in time to execute the query. The value is a UNIX timestamp, accurate to the second. The default value is the current time. |
timeout | No | The query execution timeout period, in seconds. Formats such as 1s, 2m, 3h, and 4d are also supported. For example, timeout=10s. For more information, see Time Durations. |
lookback_delta | No | Customizes the `query.lookback-delta` flag in Prometheus for the current query only. The value must follow the Time Durations format. For example, `lookback_delta=1m`. For more information, see Time Durations. This parameter specifies the maximum lookback interval for finding data points in PromQL calculations. The default value in an SLS Metricstore is `3m`. |
Example
curl -X GET 'https://haoqi-sls-metric-test.pub-cn-hangzhou.log.aliyuncs.com/prometheus/haoqi-sls-metric-test/prometheus-metrics/api/v1/query?query=up&time=1676700699' \ -u username:password \ -H 'Content-Type: application/x-www-form-urlencoded' # Set username and password to your Alibaba Cloud AccessKey.Query Results
{ "status": "success", "data": { "resultType": "vector", "result": [ { "metric": { "__name__": "up", "instance": "demo.promlabs.com:10001", "job": "demo" }, "value": [ 1676700550.696, "1" ] }, { "metric": { "__name__": "up", "instance": "demo.promlabs.com:10000", "job": "demo" }, "value": [ 1676700550.696, "1" ] } ] } }
Range Queries API
You can use the Range Queries API to query metric data at multiple points in time within a specified time range.
GET https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/query_range
POST https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/query_rangeThe following table describes the parameters.
Variable | Required | Description |
query | Yes | The PromQL statement. For more information, see PromQL syntax. |
start | No | The start time of the query. The value is a UNIX timestamp, accurate to the second. |
end | No | The end time of the query. The value is a UNIX timestamp, accurate to the second. |
step | No | The interval between query executions, in seconds. Formats such as 1s, 2m, 3h, and 4d are also supported. For example, `step=2m`. For more information, see Time Durations. |
timeout | No | The query execution timeout period, in seconds. Formats such as 1s, 2m, 3h, and 4d are also supported. For example, timeout=10s. For more information, see Time Durations. |
lookback_delta | No | Customizes the `query.lookback-delta` flag in Prometheus for the current query only. The value must follow the Time Durations format. For example, `lookback_delta=1m`. For more information, see Time Durations. This parameter specifies the maximum lookback interval for finding data points in PromQL calculations. The default value in an SLS Metricstore is `3m`. |
Example
This example queries metric data from 14:09:59 on 2023-02-18 to 14:16:39 on 2023-02-18 with a step of 60 s.
curl -X GET 'https://haoqi-sls-metric-test.pub-cn-hangzhou.log.aliyuncs.com/prometheus/haoqi-sls-metric-test/prometheus-metrics/api/v1/query_range?query=up&start=1676700599&end=1676700999&step=60s' \ -u username:password \ -H 'Content-Type: application/x-www-form-urlencoded' # Set username and password to your Alibaba Cloud AccessKey.Query results
{ "status": "success", "data": { "resultType": "matrix", "result": [ { "metric": { "__name__": "up", "instance": "demo.promlabs.com:10000", "job": "demo" }, "values": [ [ 1676700599, "1" ], [ 1676700659, "1" ], [ 1676700719, "0" ], [ 1676700779, "0" ], [ 1676700839, "1" ], [ 1676700899, "0" ], [ 1676700959, "1" ] ] }, { "metric": { "__name__": "up", "instance": "demo.promlabs.com:10001", "job": "demo" }, "values": [ [ 1676700599, "1" ], [ 1676700659, "1" ], [ 1676700719, "0" ], [ 1676700779, "0" ], [ 1676700839, "1" ], [ 1676700899, "1" ], [ 1676700959, "1" ] ] } ] } }
Metadata query APIs
Simple Log Service also supports querying metadata, such as labels and label values. The SLS metadata query APIs are compatible with the Querying metadata APIs in Prometheus. You can use these APIs to retrieve all metrics, labels, and label values within a specific time period. The response does not include timestamps or numeric values.
Query Series API
You can use the Query Series API to query all metric names and their corresponding label-value pairs that match specific conditions within a specified time period.
GET https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/series
POST https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/seriesThe following table describes the parameters.
Variable | Required | Description |
match[] | Yes | The filter condition. Example: match[]=up{instance="demo.*"}. You can set one or more values. |
start | No | The start time of the query. The value is a UNIX timestamp, accurate to the second. The default value is 5 minutes before the current time. |
end | No | The end time of the query. The value is a UNIX timestamp, accurate to the second. The default value is the current time. Important If you specify custom values for both the start and end parameters, the API only supports querying data within the 5 minutes before the end time. The query range is `(end - 5 minutes, end)`. |
Example
curl -g -X GET 'https://haoqi-sls-metric-test.pub-cn-hangzhou.log.aliyuncs.com/prometheus/haoqi-sls-metric-test/prometheus-metrics/api/v1/series?match[]=up{instance="demo.promlabs.com:10000"}&match[]=go_sched_latencies_seconds_bucket&start=1676700599&end=1676700999' \ -u username:password \ -H 'Content-Type: application/x-www-form-urlencoded' # Set username and password to your Alibaba Cloud AccessKey.Results
{ "status": "success", "data": [ { "__name__": "go_gc_duration_seconds_count", "instance": "demo.promlabs.com:10000", "job": "demo" }, { "__name__": "go_gc_duration_seconds_count", "instance": "demo.promlabs.com:10001", "job": "demo" }, { "__name__": "up", "instance": "demo.promlabs.com:10000", "job": "demo" } ] }
Query Label Names API
You can use this API to query all label names that match specific conditions within a specified time period.
GET https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/labels
POST https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/labelsThe following table describes the parameters.
Variable | Required | Description |
match[] | Yes | The filter condition. For example, `match[]=up{instance="demo.*"}`. You can specify zero, one, or more values. |
start | No | The start time of the query range. The value is a UNIX timestamp, accurate to the second. The default value is 5 minutes before the current time. |
end | No | The end time of the query range. The value is a UNIX timestamp, accurate to the second. The default value is the current time. Important If you specify custom values for both the start and end parameters, the API only supports querying data within the 5 minutes before the end time. The query range is `(end - 5 minutes, end)`. |
Example
This example queries the label names of all metrics within a specified time period.
curl -X GET 'https://haoqi-sls-metric-test.pub-cn-hangzhou.log.aliyuncs.com/prometheus/haoqi-sls-metric-test/prometheus-metrics/api/v1/labels?start=1676700599&end=1676700999' \ -u username:password \ -H 'Content-Type: application/x-www-form-urlencoded' # Set username and password to your Alibaba Cloud AccessKey.Query results
{ "status": "success", "data": [ "code", "instance", "job", "le", "method", "mode", "path", "quantile", "status", "type", "version", "__name__" ] }
Query Label Values API
You can use the Query Label Values API to query all label values for a specific label name that match specific conditions within a specified time period.
In the API URL, replace <label_name> with a specific label name.
GET https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/label/<label_name>/valuesThe following table describes the parameters.
Variable | Required | Description |
match[] | Yes | The filter condition. For example, `match[]=up{instance="demo.*"}`. You can specify one or more values. |
start | No | The start time of the query. The value is a UNIX timestamp, accurate to the second. The default value is 5 minutes before the current time. |
end | No | The end time of the query range. The value is a UNIX timestamp, accurate to the second. The default value is the current time. Important If you specify custom values for both the start and end parameters, the API only supports querying data within the 5 minutes before the end time. The query range is `(end - 5 minutes, end)`. |
Example
This example queries all label values for the instance label of the up metric within a specified time period.
curl -X GET 'https://haoqi-sls-metric-test.pub-cn-hangzhou.log.aliyuncs.com/prometheus/haoqi-sls-metric-test/prometheus-metrics/api/v1/label/instance/values?match[]=up&start=1676700599&end=1676700999' \ -u username:password \ -H 'Content-Type: application/x-www-form-urlencoded' # Set username and password to your Alibaba Cloud AccessKey.Query results
{ "status": "success", "data": [ "demo.promlabs.com:10000", "demo.promlabs.com:10001", "demo.promlabs.com:10002" ] }
Data write API
You can ingest time series data into a MetricStore by configuring the `remote_write` parameter in the configuration file of a Prometheus process. For more information, see Ingest Prometheus monitoring data using the remote write protocol. Because MetricStore is compatible with the Prometheus remote write protocol, you can also write data to a MetricStore by directly calling the `remote_write` API over HTTP without using a Prometheus process.
MetricStore provides the following API, which is compatible with the remote write protocol. This API parses time series data and writes the data to backend storage.
If time series data is written to an SLS MetricStore using the remote write protocol, SLS uses `MetricName` and `Labels` as the hash key by default. This routes time series data from different time series to specific shards to improve data locality in storage.
POST https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}/api/v1/writeThe following code provides an example.
import (
"bytes"
"flag"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
"github.com/prometheus/prometheus/prompb"
"io/ioutil"
"net/http"
"time"
)
func MockRemoteWrite() {
project := flag.String("project", "xxxx", "")
metricStore := flag.String("metricstore", "xxxx", "")
endpoint := flag.String("endpoint", "xxxx", "")
akId := flag.String("akid", "xxxx", "") // AccessKey information.
akKey := flag.String("aksecret", "xxxx", "")
flag.Parse()
Url := fmt.Sprintf("https://%s.%s/prometheus/%s/%s/api/v1/write", *project, *endpoint, *project, *metricStore)
timestamp := time.Now().UnixNano()
timeSeries := []prompb.TimeSeries{
{
Labels: []prompb.Label{
{Name: "__name__", Value: "test_metric"},
{Name: "app", Value: "HOST"},
{Name: "device", Value: "vda"},
},
Samples: []prompb.Sample{
{Timestamp: timestamp / 1000000, Value: 100},
{Timestamp: timestamp/1000000 + 10000, Value: 200},
{Timestamp: timestamp/1000000 + 20000, Value: 400},
{Timestamp: timestamp/1000000 + 30000, Value: 300},
},
},
{
Labels: []prompb.Label{
{Name: "__name__", Value: "test_metric"},
{Name: "app", Value: "HOST"},
{Name: "device", Value: "vda"},
{Name: "uid", Value: "123456"},
},
Samples: []prompb.Sample{
{Timestamp: timestamp / 1000000, Value: 100},
{Timestamp: timestamp/1000000 + 10000, Value: 200},
{Timestamp: timestamp/1000000 + 20000, Value: 400},
{Timestamp: timestamp/1000000 + 30000, Value: 600},
},
},
}
data, _ := proto.Marshal(&prompb.WriteRequest{Timeseries: timeSeries})
bufBody := snappy.Encode(nil, data)
rwR, err := http.NewRequest("POST", Url, ioutil.NopCloser(bytes.NewReader(bufBody)))
rwR.Header.Add("Content-Encoding", "snappy")
rwR.Header.Set("Content-Type", "application/x-protobuf")
rwR.SetBasicAuth(*akId, *akKey) // Set the basic auth information.
if err != nil {
fmt.Println(err.Error())
return
}
start := time.Now().UnixNano() / 1000000 // ms
do, err := client.Do(rwR)
end := time.Now().UnixNano() / 1000000 // ms
if err != nil {
panic(err)
}
status, result := parseResp(do)
fmt.Println("status:", status, "result:", result, "duration:", end-start)
}
func parseResp(resp *http.Response) (status, data string) {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) // The body content must be read completely.
if err != nil {
panic(err)
}
return resp.Status, string(body)
}SDK examples
Access the query API over HTTP
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
const separator = "#"
func http_main() {
project := flag.String("project", "xxxx", "")
metricStore := flag.String("metricstore", "xxxx", "")
endpoint := flag.String("endpoint", "xxxx", "")
akId := flag.String("akid", "xxxx", "")
akKey := flag.String("aksecret", "xxxx", "")
query := flag.String("query", "avg(up)", "")
queryType := flag.String("type", "values", "range or query or labels or values or series")
matches := flag.String("match", "up", "") // Use the # symbol to concatenate multiple match[] parameters.
labelName := flag.String("label", "instance", "")
step := flag.String("step", "1m", "")
fromtime := flag.String("from", "2023-02-15T00:00:00Z", "time 2006-01-02T15:04:05Z07:00")
totime := flag.String("to", "2023-02-15T00:15:00Z", "time 2006-01-02T15:04:05Z07:00")
flag.Parse()
timeFrom, err := time.Parse(time.RFC3339, *fromtime)
if err != nil {
panic(err)
}
timeTo, err := time.Parse(time.RFC3339, *totime)
if err != nil {
panic(err)
}
// URL: https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}
prometheusEndpoint := fmt.Sprintf("https://%s/prometheus/%s/%s", *project+"."+*endpoint, *project, *metricStore)
var uri string
urlVal := url.Values{}
urlVal.Add("start", strconv.FormatInt(timeFrom.Unix(), 10))
urlVal.Add("end", strconv.FormatInt(timeTo.Unix(), 10))
switch *queryType {
case "range":
urlVal.Add("query", *query)
urlVal.Add("step", *step)
uri = fmt.Sprintf("%s/api/v1/query_range?%v", prometheusEndpoint, urlVal.Encode())
case "query":
urlVal.Add("query", *query)
urlVal.Add("time", strconv.FormatInt(timeTo.Unix(), 10))
uri = fmt.Sprintf("%s/api/v1/query?%v", prometheusEndpoint, urlVal.Encode())
case "labels":
extractAddMatches(*matches, urlVal)
uri = fmt.Sprintf("%s/api/v1/labels?%v", prometheusEndpoint, urlVal.Encode())
case "values":
extractAddMatches(*matches, urlVal)
uri = fmt.Sprintf("%s/api/v1/label/%s/values?%v", prometheusEndpoint, *labelName, urlVal.Encode())
case "series":
extractAddMatches(*matches, urlVal)
uri = fmt.Sprintf("%s/api/v1/series?%v", prometheusEndpoint, urlVal.Encode())
}
req, _ := http.NewRequest(http.MethodGet, uri, nil)
req.SetBasicAuth(*akId, *akKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
buf, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
panic(err)
}
fmt.Println(string(buf))
}
func extractAddMatches(matches string, uVal url.Values) {
splits := strings.Split(matches, separator)
for _, match := range splits {
uVal.Add("match[]", match)
}
}Access the query API using the Prometheus SDK
This example is based on Prometheus client_golang v1.14.0.
import (
"context"
"flag"
"fmt"
"github.com/prometheus/client_golang/api"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"net"
"net/http"
"net/url"
"time"
)
func main() {
project := flag.String("project", "xxxx", "")
metricStore := flag.String("metricstore", "xxxx", "")
endpoint := flag.String("endpoint", "xxxx", "")
akId := flag.String("akid", "xxxx", "")
akKey := flag.String("aksecret", "xxxx", "")
flag.Parse()
// URL: https://{project}.{sls-endpoint}/prometheus/{project}/{metricstore}
prometheusEndpoint := fmt.Sprintf("https://%s.%s/prometheus/%s/%s", *project, *endpoint, *project, *metricStore)
client, err := api.NewClient(api.Config{
Address: prometheusEndpoint,
RoundTripper: &http.Transport{
// set basic auth
Proxy: func(req *http.Request) (*url.URL, error) {
req.SetBasicAuth(*akId, *akKey)
return nil, nil
},
DialContext: (&net.Dialer{
Timeout: 60 * time.Second,
KeepAlive: 60 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 10 * time.Second,
},
})
if err != nil {
panic(err)
}
v1api := v1.NewAPI(client)
ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
r := v1.Range{
Start: time.Now().Add(-15 * time.Minute),
End: time.Now(),
Step: time.Minute,
}
// query range
result, warnings, err := v1api.QueryRange(ctx, "avg(up)", r)
if err != nil {
panic(err)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v %v\n", warnings, result)
}
fmt.Println(result)
// query
result, warnings, err = v1api.Query(ctx, "avg(up)", time.Now())
if err != nil {
panic(err)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v %v\n", warnings, result)
}
fmt.Println(result)
// series
series, warnings, err := v1api.Series(ctx, []string{"up"}, time.Now().Add(-15*time.Minute), time.Now())
if err != nil {
panic(err)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v %v\n", warnings, result)
}
fmt.Println(series)
// labels
names, warnings, err := v1api.LabelNames(ctx, []string{"up"}, time.Now().Add(-15*time.Minute), time.Now())
if err != nil {
panic(err)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v %v\n", warnings, result)
}
fmt.Println(names)
// labelValues
values, warnings, err := v1api.LabelValues(ctx, "instance", []string{"up"}, time.Now().Add(-15*time.Minute), time.Now())
if err != nil {
panic(err)
}
if len(warnings) > 0 {
fmt.Printf("Warnings: %v %v\n", warnings, result)
}
fmt.Println(values)
}Response structure
The response structure for the query and write APIs is as follows:
{
"status": "success" | "error",
"data": <data>,
// The following two items are returned when an error occurs during query analysis.
"errorType": "<string>",
"error": "<string>",
// A warning message is returned, usually for an incomplete query.
"warnings": ["<string>"]
}Error handling
This section describes common errors and their solutions.
Authentication failed
If the following information is returned, authentication has failed. Check and modify your AccessKey.
{ "status": "error", "code": "401", "errorType": "unauthorized", "error": "get query instance error: {\n \"httpCode\": 401,\n \"errorCode\": \"Unauthorized\",\n \"errorMessage\": \"AccessKeyId not found: xxxx\",\n \"requestID\": \"xxxx\"\n}" }If the following information is returned, the source IP address is not in the VPC CIDR block whitelist. Add the IP address to the VPC CIDR block whitelist.
{ "status": "error", "code": "401", "errorType": "unauthorized", "error": "get query instance error: {\n \"httpCode\": 401,\n \"errorCode\": \"Unauthorized\",\n \"errorMessage\": \"AccessKeyId not found: xxxx\",\n \"requestID\": \"xxxx\"\n}" }
Error in the PromQL statement
If the following information is returned, the PromQL statement contains an error. Modify the query statement in the query parameter.
--> /api/v1/query_range?query=up[2m]&start=1676700599&end=1676700999&step=60s
{
"status": "error",
"errorType": "bad_data",
"error": "invalid expression type \"range vector\" for range query, must be Scalar or instant Vector"
}Timeout error
If the following information is returned, a timeout error occurred. Increase the value of the timeout parameter.
{
"status": "error",
"errorType": "timeout",
"error": "query timed out in expression evaluation"
}Incomplete query
If the following information is returned, the query is incomplete. We recommend that you narrow the query time range and try again.
{
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {},
"values": [
[
1673798460,
"11111111"
],
[
1673799060,
"22222222"
],
[
1673799660,
"33333333"
]
]
}
]
},
"warnings": [
"Request to Sls partial incompleted, incomplete task count : 11, total : 108"
]
}