This example-oriented document will teach you techniques with metric selectors that will enable you to implement advanced use cases quickly.
If you query a metric, you'll get only the id of the entity dimensions in the result like "HOST-0123456789ABCDEF"
. Using the names
transformation, you can add the pretty name of the entity in your result:
builtin:host.cpu.user:names
The query result will then contain dt.entity.host
and the dt.entity.host.name
dimensions.
Let's say you want to keep only the timeslots with a value greater than zero. You can use the partition
transformation for that:
my_metric
:partition(
"status",
value("keep", gt(0))
)
:merge("status")
The partition operator discards all timeslots not matching its conditions. Afterward, we remove the dimension status
added in the partitioning step using the merge
transformation.
Whereas you can use the partition
transformation to filter single values, you can sift the whole series using the series
condition of the filter
transformation. For example, if you want to get only the series that were available over zero percent in the query timeframe, you can use the following query:
builtin:pgi.availability
:filter(
series(max,gt(0))
)
To get more insights about the difference between the partition transformation and the series condition, see https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/metric-selector#conditions.
Consider you have the following values for the dimension url
of your metric web_requests
:
- app.dynatrace.co.jp
- app.dynatrace.com
- www.dynatrace.com
- app.dynatrace.de
If you want to include only URLS starting with app.
and filter out all URLs ending with .co.jp
, you can use the following query:
web_requests
:filter(
and(
prefix(url, "app."),
not(
suffix(url, ".co.jp")
)
)
)
The url
dimensions returned for this query are "app.dynatrace.com" and "app.dynatrace.de". To get all dimensions, you can leverage the contains
condition:
web_requests
:filter(
contains(url, ".dynatrace.")
)
You can use the embedded entity selector to filter dt.entity.*
dimensions. For example,
builtin:host.cpu.user
:filter(
in("dt.entity.host",
entitySelector("type(~"HOST~"),not(entityName.startsWith(~"dc1~"))")
)
)
returns only host entities with names that don't start with "dc1".
As no out-of-the-box metric gives you the number of hosts, you can use
builtin:host.cpu.user
:splitBy("dt.entity.host")
:auto
:splitBy()
:count
to figure out the number of hosts. Similarly, you can get the number of distinct disks by the following query:
builtin:host.disk.avail
:splitBy("dt.entity.disk")
:auto
:splitBy()
:count
The approach is always:
- Reduce the dimensions by
splitBy(<dimension>)
, so only the dimension for which you want to count the distinct values remains - Extract a single value for each series and timeslot using
auto
- Merge all dimensions using
splitBy()
- call the
count
aggregation
To get the total value of distinct values for the query timeframe, insert a :fold
after the first step. For example:
builtin:host.cpu.user
:splitBy("dt.entity.host")
:fold
:auto
:splitBy()
:count
The query
builtin:service.response.time
:avg
:partition("latency",value("good",lt(10000)))
:splitBy()
:count
:default(0)
/
builtin:service.response.time
:avg
:splitBy()
:count
* 100
returns the percentage of the series that had an average response time over 10,000 microseconds for a timeslot. This number is not equal to the percentage of requests with a response time of over 10k microseconds as the metric data is aggregated to 1-minute buckets. To get the exact percentage of requests running longer than 10k microseconds, you'd need to create a Calculated service metric that only captures requests matching this criterion and use it as the dividend in the metric expression.
Consider you have the metric number_errors
that is only incremented when an error occurs. To fill up the empty timeslots with zeros even if there were no booking in the whole query timeframe, use default(0,always)
:
number_errors:
:splitBy()
:sum
:default(0,always)
For instance, when determining the store's revenue percentage within a city relative to the entire country's revenue, we can use the following expression:
revenue_city
:filter("city", "Berlin")
:splitBy("city")
/
revenue_country
:filter("country", "Germany")
:splitBy()
* 100
It is essential to use splitBy()
without any dimension key for the revenue_country
metric. Without this, the series can't be properly joined. For a successful metric merge in a metric expression, the dimension tuples of both metrics must be identical, or at least one of the metrics needs to be dimensionless. See the public documentation for details.
For some entity dimensions like PROCESS_GROUP_INSTANCE
and SERVICE_METHOD
, you can enrich the result with the "parent" of these dimensions. For example, the metric builtin:tech.generic.processCount
per default only provides the dt.entity.process_group_instance
dimension. To get also the host dimension in the result, use the query:
builtin:tech.generic.processCount:parents
Moreover, you can combine the names
and the parents
transformations.
builtin:tech.generic.processCount:parents:names
will return both the PROCESS_GROUP_INSTANCE
and HOST
dimensions and the pretty names for both.
Besides enriching the metric dimensionality with the display name and the pre-configured parent of the entity dimensions, adding additional dimensions using the partition
transformation is possible. For example:
builtin:service.response.time
:avg
:partition(
"latency",
value("slow", gt(10000)),
value("good", otherwise)
)
Adds the dimension "latency" to the series of the result. The dimension value depends on the response time. Data points greater than 10,000 microseconds are categorized as slow and the others as good.
If you want to add a new dimension based on existing dimensions, you can also use the partition
transformation. For example, consider a metric with the HTTP status code as its dimension, and you want to map all requests to "success" unless their status code begins with 4 or 5 (that is, they have 400 or 500 as their status code).
You can achieve that by the following query:
http_request
:partition("status",
dimension("success", and(not(prefix("status_code", "4")), not(prefix("status_code", "5")))),
otherwise("error"))
:splitBy("status")
Using the metric selector query language, there is no further way to enrich the series dimensionality. You'd need to leverage the DQL for more advanced use cases.