Use TSL
TSL overview
TSL overview
Last updated 23rd August, 2019
Our mission is to provide you a great experience and we thought current languages was either too limited or too complicated to reach this goal. That's why we wanted to propose a take : Time Series Language (or TSL). It's build to be easier to read, back-end agnostic and most of all oriented to simplify servers and applications monitoring. We will describe here its syntax and how to query data with it.
First things first, let's decode a TSL query.
select("sys.cpu.nice").where("host=web01").from(1346846400000,to=1346847000005)
This is a first TSL query, each keywords have its own meaning:
Now that you see how simple it's to query time-series data, let's see how it works with a real example.
If you do not have your own data we will reuse the example previously explained in our getting started guide and push multiple datapoints into a single OpenTSDB query. As a reminder create a file called opentsdb.json
and write this content to it:
[
{
"metric": "sys.cpu.nice",
"timestamp": 1346846400000,
"value": 18,
"tags": {
"host": "web01",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346846800000,
"value": 19,
"tags": {
"host": "web01",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346847000000,
"value": 20,
"tags": {
"host": "web01",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346847000005,
"value": 19,
"tags": {
"host": "web01",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346846400000,
"value": 9,
"tags": {
"host": "web02",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346846800000,
"value": 8,
"tags": {
"host": "web02",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346847000000,
"value": 7,
"tags": {
"host": "web02",
"dc": "lga"
}
},
{
"metric": "sys.cpu.nice",
"timestamp": 1346847000005,
"value": 10,
"tags": {
"host": "web02",
"dc": "lga"
}
}
]
Using the curl
command below, replace REGION per your own one : gra1 or bhs1. As the user doesn't matter on the metrics backend, all the information are stored in our cryptographical token, you can replace or let test as if.
curl -v -X POST -d @opentsdb.json \
'https://test:WRITE_TOKEN@opentsdb.REGION.metrics.ovh.net/api/put'
If everyting happens correctly, the cURL would exit with a 200 code status and the data are available using your metrics account.
Now that we pre-load some data points, let's execute the following query:
select("sys.cpu.nice").where("host=web01").from(1346846400000,to=1346847000005)
We can send this request through an HTTP Post with the cURL Command line or any other HTTP tools like Insomnia or PostMan.
Using a cURL command, we can write a new TSL file containing the previous select
$ curl -v --data-binary @select.TSL 'https://DESC:READ_TOKEN@TSL.gra1.metrics.ovh.net/v0/query'
You should get the result as a time-series JSON list.
To retrieve data, TSL have a select method to choose the metrics name to retrieve, a where clauses to select specific labels and a from or last method to select the temporal period to get.
Once the data are available, with TSL you can apply a lot of functions: from sampling, to grouping, to metrics operation or to operation between metrics.
As with TSL the goal is to simplify metrics queries, inside a query a user can store variables that will re-used, we will see how to use it. TSL offers the possibilities to fetch data from different backend and to dynamically execute queries on them from a same script using the connect method. Besides we will see how to update metrics meta-data (name and labels) and storing the request result in the specified backend
The first instructions used to select data is the method select.
Select contains only one parameter :
Example:
-- Will load the last points of all sys.cpu.nice
select("sys.cpu.nice")
-- Will load the last points of all series of this application (only on a Warp 10 backend)
select(*)
TSL supports native backend. At current time, for Prometheus you need to specify the exact classname of the metrics to load. When for Warp 10, you can use native regexp. As example "~sys.*" is a working Warp 10 REGEXP to select all series starting with sys.
The where method allow the user to filter the metrics to load matching labels clauses.
Where can contains one to n parameter(s):
With the "key=label" string, we use an equals label matcher. The TSL valid matcher are =, ~, != and !~. The first one encounters in the string will be used.
Example:
-- Will load the last points of sys.cpu.nice series where 'dc' equals 'lga'
select("sys.cpu.nice").where("dc=lga")
-- Will load the last points of sys.cpu.nice series where 'dc' equals 'lga' and have labels 'web'
select("sys.cpu.nice").where("dc=lga", "web~.*")
You can chain as many where clauses as wanted in a TSL query, example: select(...).where(...).where(...) as long as you are defining the data to retrieve.
The last methods to define the data to retrieve are last and from. They are used to set the time limits to retrieve the data.
The from method is used to select physical time limits.
From can contains one or two parameters:
A valid timestamp for Warp 10 is a long in time unit (on our Metrics platform it's in micro-seconds: 1346846400000 is valid), when a valid timestamp for Prometheus may be provided as a Unix timestamp in seconds, with optional decimal places for sub-second precision (on our Metrics platform, you can have timestamp in ms: 1524376786.878 is valid).
A valid date string for Warp 10 are ISO 8601 dates string and for Prometheus are date in RFC3339 format: "2018-04-22T00:57:00-05:00" is valid for both backends.
By default, if only one parameter is set, it considers that it corresponds to the from parameter and will load all data from the current date. Be careful as it can retrieve a lot of data.
When using the from method, you can prefix the parameter with from or to prefix.
Example:
-- Will load all values set after timestamp 0 of sys.cpu.nice series.
select("sys.cpu.nice")
.from(0)
-- Will load all values between two timestamps of sys.cpu.nice series.
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
-- Will load all values between two timestamps of sys.cpu.nice series.
select("sys.cpu.nice")
.from(from=1346846400000000, to=1346847000006000)
-- Will load all values between two timestamps of sys.cpu.nice series.
select("sys.cpu.nice")
.from(to=1346847000006000, from=1346846400000000)
-- Will load all values between two dates of sys.cpu.nice series.
select("sys.cpu.nice")
.from("2018-04-22T00:57:00-05:00",to="2018-04-22T01:00:00-05:00")
-- From a Prometheus backend
select("sys.cpu.nice")
.from(to=1346847000.005, from=1346846400.000)
To resume from valid parameters are listed below. A parameter can be optional or mandatory. When a prefix is indicated, it means that this parameter can be set using a prefix name.
name | type | mandatory | prefix | Complementary information |
---|---|---|---|---|
from | Integer,String | from | Only on Warp 10, ISO 8601 dates string | |
from | Integer, Double, String | from | Only on Prometheus, RFC3339 format | |
to | Integer,String | to | Only on Warp 10, ISO 8601 dates string | |
to | Integer, Double, String | to | Only on Prometheus, RFC3339 format |
The last method is used to select the last recorded datapoints after a valid date.
Last can contains one or three parameters:
Example:
-- Will load last point before the current date of sys.cpu.nice series.
select("sys.cpu.nice")
.last(1)
-- Will load last minute before the current date of sys.cpu.nice series.
select("sys.cpu.nice")
.last(1m)
-- Will load 10 points before 1528240512000000 of sys.cpu.nice series.
select("sys.cpu.nice")
.last(10, timestamp=1528240512000000)
-- Will load last minute before "2018-04-22T01:00:00-05:00" of sys.cpu.nice series.
select("sys.cpu.nice")
.last(2m, date="2018-04-22T01:00:00-05:00")
-- Will load last minute one hour before NOW of sys.cpu.nice series.
select("sys.cpu.nice")
.last(2m, shift=1h)
To resume last valid parameters are listed below. A parameter can be optional or mandatory. When a prefix is indicated, it means that this parameter can be set using a prefix name.
name | type | mandatory | prefix | Complementary information |
---|---|---|---|---|
span | Duration value | None | first parameter | |
count | Integer | None | Only on Warp 10, first parameter | |
date | String | date | On Prometheus, RFC3339 format and on Warp 10, ISO 8601 dates string | |
timestamp | Integer | timestamp | Only on Warp 10 | |
timestamp | Integer, Double | timestamp | Only on Prometheus | |
shift | Duration value | shift |
When collecting servers or application metrics, the data stored are often unsynchronised. To start processing our stored metrics, it's often mandatory to sample the data. Sampling the data corresponds to split metrics data points per time window. All values in this time window are send as parameter to a function that will provide one value as result.
This can be done using the TSL sampleBy method.
The sampleBy method expects as first parameter (mandatory):
The sampleBy method expects as second parameter (mandatory):
The sampleBy method takes also two optionals parameters:
The duration format is a number followed by one of w for week(s), d for day(s), h for hour(s), m for minute(s), s for second(s), ms for milli-second(s), us for micro-second(s), ns for nano-second(s) and ps for pico-second(s)
With a Prometheus back-end, we use the step query parameter to sample the data. It's handled a bit differently as by default Prometheus will sample by the last value recorded (until last 5 minutes).
When using sampleBy in TSL on Prometheus you can only set a span and an aggregator equals to last as parameters.
Example:
-- Will load all values between of sys.cpu.nice series with 1 minute samples (one point per minute), aggegrated using a mean function.
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, mean)
-- Will load all values between of sys.cpu.nice series with 1 minute samples (one point per minute), aggegrated using a max function.
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(30, max)
-- Will load all values between of sys.cpu.nice series with 1 minute samples aggegrated using a mean function. One point per minute when at least one point exists in each minute.
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, mean, "none")
-- Will load all values between of sys.cpu.nice series with 1 minute samples aggegrated using a mean function, filling intermediary missing point by a values interpolation and not using a relative sampling.
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, mean, "interpolate", false)
-- Valid parameters prefixes
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(span=1m, aggregator=mean, fill="interpolate", relative=false)
-- Using a list as fill policy
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(span=1m, aggregator="mean", fill=["interpolate", "next", "previous"], relative=false)
To resume sampleBy valid parameters are listed below. A parameter can be optional or mandatory. When a prefix is indicated, it means that this parameter can be set using a prefix name.
name | type | mandatory | prefix | Complementary information |
---|---|---|---|---|
span | Duration value | span | ||
count | Number | count | ||
aggregator | Operator | aggregator | Operator value can be one of: max, mean, min, first, last, sum, join, median, count, and, or | |
fill | String | fill | Fill value can be one of auto, none, interpolate, next, previous | |
fill | List of string | fill | Each values of the list can be one of interpolate, next, previous | |
relative | Boolean | relative |
When building a metrics data flow, once we sampled the data, we may want to regroup similar metrics. This is what the group and groupBy methods are build to. The user defines the aggregation function and custom rules to applied to reduce to a single value all metrics values occuring at the same time.
The group method will generate a single series using the specified aggregator.
The group method takes one parameter:
-- Valid parameters prefix
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "mean")
.group(sum)
The groupBy method allow to specify labels to limit the aggegator on series that have the same values for each labels key specified. For example, when using our example, if we specify dc and host, it will reduce the data into two series: both with "dc=lga" and one with host equals to "web01" and the second with "web02".
The groupBy method takes two to n parameters:
Example:
-- Valid parameters prefix
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "mean")
.groupBy("dc", mean)
-- Valid parameters prefix
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "mean")
.groupBy(["host","dc"],mean)
To resume groupBy valid parameters are listed below. A parameter can be optional or mandatory. When a prefix is indicated, it means that this parameter can be set using a prefix name.
name | type | mandatory | prefix | Complementary information |
---|---|---|---|---|
label | String | None | a label key as first parameter | |
labels | List of string | None | a label key list as first parameter | |
aggregator | Operator | None | Operator value can be one of: max, mean, min, sum, join, median, count, and or or as second parameter |
Sometimes, we just want to update our series values (adding 2, checking the values with a threshold, rounded the value, compute a rate, and so on). In TSL, we have a large variety of Time series operator available than can be applied directly on a series result.
This can be done using the TSL window method.
The window method expects
As Warp 10 is more flexible, you can either specify a duration or a number of points to apply on with the pre and/or post parameter.
Example:
-- With only a duration (Prometheus or Warp10)
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "last")
.window(mean, 1m)
-- With pre and post as durations (Warp10)
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "last")
.window(sum, 2m, 1m)
-- With pre and post as integer (Warp10)
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.window(sum, 2, 10)
The following TSL methods can be used to apply arithmetic operators on metrics:
The logN and cumulativeSum operators are not available on Prometheus.
The following TSL methods can be used to apply equality operators on metrics:
The following TSL methods can be used to apply limit operators on metrics:
The following TSL methods can be used to apply time related operators on metrics:
TSL includ a method to apply an operation on a selected time window. This represents time window mapper in Warp 10 and over_time operators in PromQL. This methods apply a function to all values of a time window of each metrics and replace the current value by the result of this function.
The method applied can be one of
TSL introduces some methods to sort metrics by their samples values.
The sortBy, sortDescBy, topNBy and bottomNBy operators are not available for Prometheus.
When we load several set of data, we may want to apply operation on metrics sets. TSL allow us to apply operators on metrics.
A metrics operators will apply an operation on several set of metrics.
For example: we can add the values of a first series with a second one. Value will be added when ticks have an exact match, this is why it's important to sample the data before executing such an operation.
Example:
-- Valid parameters prefix
add(
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.where("host=web01")
.sampleBy(1m, "mean"),
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.where("host=web02")
.sampleBy(1m, "mean")
)
By default, on Warp 10 only one metrics will be computed as result except if we use the on and/or ignoring method explained below. By default, on Prometheus the minimal equivalence class matching a maximum of labels will be computed as result except if we use the on and/or ignoring method explained below.
To limit operation on specific labels, the method on can be-used post a metrics operator one.
For example the following TSL query will return two series one where all values of the "web01" host series are summed and a second one for the "web02" host series.
-- Add on label "host"
add(
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "mean"),
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.sampleBy(1m, "mean")
).on("host")
The Ignoring method will remove the selected labels of the equivalence class for the operator method.
Example:
-- Compute an add on all series
add(
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.where("host=web01")
.sampleBy(1m, "mean"),
select("sys.cpu.nice")
.from(1346846400000000,1346847000006000)
.where("host=web02")
.sampleBy(1m, "mean")
).ignoring("host")
TSL allow the user to set it's own variable. Just set a name followed by an "=" sign.
To reuse a variable, just use it's name. To execute a query store in a variable, just write the variable name in a new line.
Example:
-- Store a two minutes duration
customDuration = 2m
-- Store a series name
seriesName = "sys.cpu.nice"
-- Store a label name
labelName = "host=web02"
-- Store a number
myNumber = 100
-- Store a select instruction and it's operation
mySelect = select(seriesName)
.from(1346846400000000,1346847000006000)
.where(labelName)
.sample(30s)
.add(100)
-- Get it's result
mySelect
-- Store a label name
labelName = "host=web02"
-- Store a single select
mySelect = select("sys.cpu.nice").from(1346846400000000,1346847000006000)
-- Apply post select operation and get result
mySelect.where(labelName).ln()
mySelect.where(labelName).add(100)
-- Store a label name
labelName = "host=web02"
-- Use variables in operation
mySelect = select("sys.cpu.nice").from(1346846400000000,1346847000006000)
add(mySelect.where(labelName).ln(),mySelect
.where("host=web01"))
-- Store multiples series operation result in variable
addSave = add(mySelect.where(labelName).ln(),mySelect
.where("host=web01"))
-- Get multiples series operation result from addSave variable
addSave.on("host").add(100)
In TSL, we can directly use the Connect method to update the set the backend on which queries are processed.
connect("http://localhost:9090", "TOKEN")
The back-end have to be declared in TSL configuration, for OVH the valid backends are: "https://p:READ_TOKEN@prometheus.gra1.metrics.ovh.net/api/v1/query_range" and "https://warp10.gra1.metrics.ovh.net". Do not replace the "READ_TOKEN" key to use our emulated Prometheus backend.
The update metrics meta-data in TSL you can use one of the following function:
None of those methods are currently available for Prometheus.
TSL can also be used to store query result back on the backend. This can be done using the store method. Store expects a token as unique parameter. Use example: .store("WRITE_TOKEN").
store is only avaible on a Warp 10 backend.
To resets counters values the method resets can be applied in TSL. Use example: .resets("host").
TSL goals are to simplify the process to query Time series data and to provide a unified language syntax to apply on different backend.
The Warp 10 language, WarpScript, gives a lot of possibilities to query metrics data. However it's not the easiest language to learn. That's where TSL can used to abstract the WarpSCript complexity but still use its power.
To execute TSL on the Metrics platform using a Warp 10 backend you can use the connect method on top of your TSL query.
connect("https://warp10.gra1.metrics.ovh.net")
The Prometheus back-end is widely use, this is why TSL queries can also be executed on Prometheus.
To execute TSL on the Metrics platform using an emulated Prometheus backend, you can use the connect method on top of your TSL query.
connect("https://p:READ_TOKEN@prometheus.gra1.metrics.ovh.net/api/v1/query_range")
N’hésitez pas à nous proposer des suggestions d’amélioration afin de faire évoluer cette documentation.
Images, contenu, structure… N’hésitez pas à nous dire pourquoi afin de la faire évoluer ensemble !
Vos demandes d’assistance ne seront pas traitées par ce formulaire. Pour cela, utilisez le formulaire "Créer un ticket" .
Merci beaucoup pour votre aide ! Vos retours seront étudiés au plus vite par nos équipes..
Accedez à votre espace communautaire. Posez des questions, recherchez des informations, publiez du contenu et interagissez avec d’autres membres d'OVHcloud Community.
Echanger sur OVHcloud Community