Metric selector conversion guide

Latest Dynatrace

Grail metrics are queried with DQL, while classic metrics are queried using metric selectors. This guide shows how to convert classic metric selector expressions to DQL so you can smoothly and efficiently transition to Grail.

Automatically convert metric selectors to DQL

To make converting queries easier, Data Explorer and Dashboards Classic can now help you transform your existing metric selector queries into DQL.

Because there isn't an exact, one-to-one mapping between Classic metric selectors and DQL, some queries will require manual adjustments during the conversion. The troubleshooting section on this page can help you in these scenarios.

In this section, you'll learn how to use Dynatrace's conversion tool to product DQL from existing Classic metric selectors.

From Data Explorer to Notebooks or Dashboards

To automatically convert the metric selector to a DQL query in Notebooks or Dashboards

  1. Go to Data Explorer to create and run your query.

    Data Explorer: standard view

    The metric selector for this query in the advanced mode reads as:

    builtin:host.cpu.usage:splitBy("dt.entity.host"):sort(value(auto,descending)):limit(20)
  2. Select Open with… in the upper-right corner of the Data Explorer Result section.

    Data Explorer: Open with

  3. Follow the on-screen instructions to:

    • Select a destination application (Notebooks or Dashboards)
    • Select a destination document (a new or existing notebook or dashboard, depending on the destination application you selected)
  4. When the destination document is displayed, verify the resulting query.

    Notebook DQL

    In our example, the resulting query is

    timeseries usage = avg(dt.host.cpu.usage), by: { dt.entity.host }
    | sort arrayAvg(usage) desc
    | fieldsAdd entityName(dt.entity.host)
    | limit 20

    If you have questions, see the troubleshooting section.

Manually convert metric selectors to DQL

If your metric selector is not automatically convertable, in this section you'll learn how to manually convert your metric selector to DQL.

  1. Verify whether your metric can be converted by searching Built-in Metrics on Grail to find some metrics that have focused migration guides.
  2. Check that all Classic transformations in your metric selector are supported.
  3. Begin converting your metric selector with a focus on the DQL timeseries command. Several aggregations and transformations, including filter and splitBy, can be included in this initial command.
  4. Remaining arithmetic and transformations that do not belong in the timeseries command can be added by additional commands as illustrated below.

Troubleshooting Converted DQL

A list of Classic metrics and their equivalents in Grail can be found in the Built-in Metrics on Grail. Bear in mind that not all Classic metrics have Grail equivalents migration guides, and some metrics are not yet supported on Grail.

You should use Grail metrics when querying DQL. A list of Classic metrics and their equivalents in Grail can be found in Built-in Metrics on Grail.

Some metrics, such as those prefixed with builtin:apps, are not supported on Grail and cannot be queried with DQL. The full list is available on Built-in Metrics on Grail.

Not all Classic metric selectors have equivalent DQL. If you're not sure whether your Classic metric is supported on Grail, look it up on Built-in Metrics on Grail.

In the process of transitioning from classic queries to Grail in Dynatrace, you might notice some differences in your query results. This is expected, as not all classic metrics have direct equivalents in Grail. However, if you encounter differences where you expect the queries to match, the following scenarios might help explain why.

It's possible that none of the following scenarios describe your query if you converted your Metrics Classic selector yourself. Try creating the DQL using the transpiler and comparing the results.

Scenario
Guide

The automatically converted DQL query returns no data on Grail.

Not all classic metrics have equivalents in Grail, and some metrics are not yet supported on Grail. For a list classic metrics and their equivalents in Grail, see Built-in Metrics on Grail.

The charts look similar but the numbers are slightly different.

To reduce complexity, several metrics have been refactored and have no direct equivalents in Grail. The runtime metrics migration guide will help you migrate these queries to Grail.

The percentile function returns a different result than in Metrics Classic.

Grail uses a more accurate algorithm to calculate percentiles. Learn more in Metric commands.

The count function returns a different result than in Metrics Classic.

DQL uses the count function to calculate metric cardinality. You may need to use sum(..., rollup:total). To learn more, see the count aggregation section.

The chart looks similar, but the numbers are on a different scale.

Scale factor changes may indicate a bug in the metric calculation. In this case, please report the issue to the Dynatrace support team.

Aggregations

In DQL, there are equivalent aggregations for avg, sum, min, max, count, and percentile.

builtin:host.cpu.usage:splitby():avg

For example, the Classic metric selector query above can be converted to DQL as follows:

timeseries avg(dt.host.cpu.usage)

Time aggregation

Specifying a time aggregation is possible with both the Metrics Classic metric selector and DQL.

builtin:host.cpu.usage:max:splitby():avg

The example above calculates the average maximum CPU usage across all hosts. It can be converted to DQL with the rollup parameter as follows:

timeseries avg(dt.host.cpu.usage, rollup:max)

Median aggregation

You can reproduce the Metrics Classic median aggregation with the parametrized function percentile(..., 50).

builtin:host.cpu.usage:avg:splitby():median

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries median=percentile(dt.host.cpu.usage, 50, rollup:avg)

Value aggregation

The Metrics Classic value aggregation performs a sum for count metrics, and is therefore equivalent to the timeseries sum aggregation.

builtin:service.requestCount.total:splitBy():value

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries sum(dt.service.request.count)

Auto aggregation

The Metrics Classic metric selector includes a pseudo-aggregation called auto. Based on the metric's metadata, auto selects the default aggregation for that metric. DQL doesn't have an equivalent pseudo-aggregation, however, so you need to explicitly specify the aggregation.

builtin:host.cpu.usage:splitby():auto

For example, the Metrics Classic metric builtin:host.cpu.usage defaults to the avg aggregation. Therefore, the Metrics Classic metric selector above can be converted to DQL as follows:

timeseries avg(dt.host.cpu.usage)

Custom metrics can be determined from the metric key directly. A Metrics Classic metric.key with the .count suffix uses the value aggregation by default (and therefore will use a sum in the converted DQL). All other custom metrics use the avg aggregation by default.

For non-custom metrics (generally metric keys that contain a colon :), there are two options:

  1. Use the automatic converter to convert a simple metric selector such as

    builtin:host.cpu.usage:splitby():auto
  2. Look up the defaultAggregation property of your metric's metadata via the Metrics Classic Metrics browser or the metric metadata API.

Because Metrics Classic metric selectors don't require an explicit aggregation, it's possible to omit the aggregation entirely. The auto transformation is used implicitly in these cases.

builtin:host.cpu.usage:splitby()

Consequently, this metric selector is equivalent to the examples above.

Count aggregation

The vast majority of Metrics Classic count aggregations return cardinality–the number of series represented by a metric. The DQL count function returns cardinality as well.

builtin:host.cpu.usage:splitBy():count

For example, many Dynatrace users want to count the number of hosts. This selector is converted to DQL as follows:

timeseries num_hosts = count(dt.host.cpu.usage)

Variation: count observations

The Metrics Classic count transformation does ultimately depend on the metric metadata. In some scenarios, count returns the number of observations reported to a metric. This behavior is uncommon but nevertheless useful in these scenarios.

record_processing_time:splitBy("host.name"):count

The above metric selector queries a hypothetical custom metric. As a custom metric without the .count suffix (see below), the count transformation here calculates the count of records processed. The converted DQL uses sum(..., rollup:total):

timeseries records_processed = sum(record_processing_time, rollup:total)

The final behavior of count ultimately depends on a number of conditions. The automatic converter performs all these checks for you. You can provide a simple metric selector and determine from the resulting DQL which count is performed. For example:

builtin:host.cpu.usage:splitBy():count

Group by

The Metrics Classic splitBy transformation is replaced by the timeseries by parameter.

builtin:host.cpu.usage:splitby():avg
builtin:service.requestCount.total:splitBy("dt.entity.service"):sum

For example, the Metrics Classic metric selector queries above can be converted to DQL as follows:

timeseries avg(dt.host.cpu.usage)
timeseries sum(dt.service.request.count), by:{dt.entity.service}

Merge

The Metrics Classic merge transformation is a counterpart to the splitBy transformation. There is no equivalent to merge in DQL, as it can be expressed equivalently with splitBy.

builtin:host.net.nic.bytesRx:merge("dt.entity.network_interface"):avg

Because the Metrics Classic metric builtin:host.net.nic.bytesRx has two dimensions, dt.entity.host and dt.entity.network_interface, the above merge on dt.entity.network_interface is equivalent to a split on dt.entity.host:

builtin:host.net.nic.bytesRx:splitBy("dt.entity.host"):avg

Consequently, the metric selector can be converted to DQL as described in the split by transformation section:

timeseries bytes_rx=avg(dt.host.net.nic.bytes_rx), by:{dt.entity.host}

Filter

The Metrics Classic filter transformation is equivalent to the timeseries filter parameter.

builtin:cloud.aws.dynamo.tables
:filter(
in(
"dt.entity.aws_availability_zone",
entitySelector("type(aws_availability_zone),entityName.equals(~"us-east-1a~")")
)
):splitBy():sort(value(auto,descending)):limit(20)

As Metrics Classic filter transformations rely on entity selectors, the same syntax is readily supported in DQL with the classicEntitySelector function.

timeseries tables=avg(dt.cloud.aws.dynamo.tables),
by:{dt.entity.aws_availability_zone},
filter:{
in(
dt.entity.aws_availability_zone,
classicEntitySelector("type(aws_availability_zone),entityName.equals(\"us-east-1a\")")
)
}

Filter conditions

The following table shows Metrics Classic filter conditions and their equivalent DQL.

Classic filter condition
DQL function
suffix
contains
in
series
Constructed from comparisons to array functions
existsKey
Not supported on Grail
remainder
Not supported on Grail

Entity selector existsKey and remainder have no equivalent in DQL and are ignored at conversion.

The Metrics Classic series condition consists of an aggregation and a comparison operator, which can be reconstructed from array functions and comparison operators respectively.

builtin:host.disk.usedPct
:splitBy("dt.entity.host")
:filter(series(avg,gt(20)))

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries percent = avg(dt.host.disk.used.percent),
by:{dt.entity.host}
| filter arrayAvg(percent) > 20

In this example, avg is replaced by arrayAvg, and gt is replaced by the > (greater than) operator.

Arithmetic

DQL uses variable assignment and sequential commands to improve on the arithmetic in Metrics Classic metric queries. You can learn more in DQL operators.

(
100 * builtin:kubernetes.resourcequota.limits_memory_used
/ builtin:kubernetes.resourcequota.limits_memory
)
:splitBy("dt.entity.cloud_application_namespace","k8s.resourcequota.name")

The above metric selector query is formatted to highlight difference with the following converted DQL.

timeseries
limits_memory_used=avg(dt.kubernetes.resourcequota.limits_memory_used),
limits_memory=avg(dt.kubernetes.resourcequota.limits_memory),
by:{dt.entity.cloud_application_namespace, k8s.resourcequota.name}
| fieldsAdd
// use square brackets [] to perform timeseries arithmetic
limits_memory_usage = 100 * limits_memory_used[] / limits_memory[]

There is a clear difference in the approach taken with DQL. Metrics Classic metric selectors mix arithmetic with queries, whereas DQL separates these steps: first you assign the inputs, and then you perform the calculation within a fieldsadd command.

This approach is both easier to read and easier to write:

  • Operations are shorter and clearer by using descriptive variables instead of long expressions.
  • Variable assignment replaces repeated expressions—you need to write an expression only once.
  • Complex expressions can be broken down into separate steps and further commented.
  • Series operations are generic. The same functions and operators can be performed with fetch logs or the makeTimeseries command, for example.

Transformations

The Metrics Classic transformations in this section can be converted to DQL.

Default transformation

The Metrics Classic default transformation is equivalent to the timeseries default parameter.

builtin:host.availability.state:splitBy("dt.entity.host"):sum():default(0)

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries availability = sum(dt.host.availability, default:0),
by:dt.entity.host

Default always transformation

The Metrics Classic default(x, always) transformation is a special case. The behavior of always is mapped to the timeseries nonempty parameter.

builtin:host.availability.state:splitBy("dt.entity.host"):sum():default(0,always)

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries availability = sum(dt.host.availability, default:0),
by:{dt.entity.host},
nonempty:true

Delta transformation

The Metrics Classic delta transformation is equivalent to the arrayDelta function in DQL.

builtin:service.errors.fivexx.rate:splitBy():sum:delta

For example, the Metrics Classic metric selector query above can be converted to DQL as follows:

timeseries rate=sum(dt.service.errors.fivexx.rate)
| fieldsAdd rate=arrayDelta(rate)

Fold transformation

The following Metrics Classic fold transformations have equivalent array functions in DQL:

Fold transformation
DQL array function
fold(avg)
fold(max)
fold(min)
fold(sum)
fold(value)
fold(count)
fold(median)
fold(percentile(X))

For example, this Metrics Classic metric selector query:

builtin:host.cpu.usage:avg:fold(avg)

can be converted to DQL as follows:

timeseries usage=avg(dt.host.cpu.usage), by:{dt.entity.host}
| fieldsAdd usage=arrayAvg(usage)

There are two ways to determine the equivalent DQL:

  1. Use the automatic converter to detect the correct fold transformation.
  2. Replace the empty fold with fold(sum) or fold(avg) and compare the new query results to the original.

An empty fold transformation resolves to either the arraySum or arrayAvg function in DQL, depending on a combination of metric metadata and the sequence of operations before the fold.

Single value visualization

The fold transformation has some implicit uses to be aware of in Data Explorer and Dashboards Classic. The Single value visualization, shown below, converts a line chart to a single value.

Single value example

DQL does not allow implicit single value charts. Instead, the same result can be achieved by adding a fold transformation, which can be converted to DQL as documented above.

builtin:kubernetes.container.restarts:splitby():sum

Using the above example, you can reproduce the Data Explorer's Single value by appending a fold transformation.

builtin:kubernetes.container.restarts:splitby():sum:fold

In turn, the new selector can be converted to DQL.

timeseries restarts = sum(kubernetes.container.restarts)
| fieldsAdd restarts = arraySum(restarts)

Last transformation

The Metrics Classic last transformation is equivalent to the arrayLast function in DQL.

For example, this Metrics Classic metric selector query:

builtin:service.response.time:splitBy("dt.entity.service"):percentile(99):last

Can be converted to DQL as follows:

timeseries time=percentile(dt.service.response.time, 99),
by:{dt.entity.service}
| fieldsAdd time=arrayLast(time)

The Metrics Classic lastReal transformation has no equivalent in DQL. It also uses arrayLast at conversion.

Limit transformation

The Metrics Classic limit transformation is equivalent to the limit command in DQL.

For example, this Metrics Classic metric selector query (formatted for clarity):

builtin:host.cpu.usage:splitBy("dt.entity.host"):avg
:sort(value(avg,descending))
:limit(3)

can be converted to DQL as follows:

timeseries usage=avg(dt.host.cpu.usage), by:{dt.entity.host}
| sort arrayAvg(usage) desc
| limit 3

Names transformation

The Metrics Classic names transformation is equivalent to the entityName DQL function.

For example, this Metrics Classic metric selector query:

builtin:dt.host.disk.avail:splitBy("dt.entity.host", "dt.entity.disk"):max:names

can be converted to DQL as follows:

timeseries avail = max(dt.host.cpu.usage),
by:{dt.entity.host, dt.entity.disk}
| fieldsadd entityName(dt.entity.host), entityName(dt.entity.disk)

Parents transformation

The Metrics Classic parents transformation is not directly supported in DQL, but in many cases it can be reproduced.

For example, this Classic metric selector query (formatted for clarity):

builtin:tech.jvm.threads.count:avg
:splitBy("dt.entity.process_group_instance")
:parents:splitBy("dt.entity.host"):avg

uses the parents transformation, so it can split on the parent dt.entity.host dimension. Because data is already enriched in Grail, you can instead split by the host dimension directly.

timeseries avg(dt.runtime.jvm.threads.count),
by:{dt.entity.host}

Missing enriched dimensions may indicate a bug in the metric calculation. In this case, please report the issue to the Dynatrace support team.

Partition transformation

The Metrics Classic partition transformation is unique in that it creates new series from existing ones. The equivalent DQL makes use of iterative expressions.

For example, this Metrics Classic metric selector query (formatted for clarity):

builtin:host.disk.avail:fold(avg):limit(3)
:partition("disk_usage",
value("underused",gt(450000000000)),
value("optimal",otherwise))

can be converted to DQL as follows:

timeseries avail=avg(dt.host.disk.avail), by: { dt.entity.disk, dt.entity.host }
| fieldsAdd avail=arrayAvg(avail)
| fieldsAdd disk_usage=if(avail>450000000000, "underused", else: "optimal")
| limit 3

This example Metrics Classic metric selector query (formatted for clarity):

builtin:host.disk.avail:auto:limit(3)
:partition("disk_usage",
value("underused",gt(450000000000)),
value("optimal",otherwise))

can be converted to DQL as follows:

timeseries avail=avg(dt.host.disk.avail), by: { dt.entity.disk, dt.entity.host }
| expand disk_usage=array("underused", "optimal")
| fieldsAdd avail=if(disk_usage=="underused",
if(avail[]>450000000000, avail[]),
else:if(disk_usage=="optimal", if(NOT(avail[]>450000000000), avail[])))
| filterOut isNull(arrayMin(avail))
| limit 3
| filterOut isNull(arrayMin(avail)) // arrayMin returns null if all elements are null

Rate transformation

The Metrics Classic rate transformation is equivalent to the timeseries rate parameter.

For example, this Metrics Classic metric selector query:

builtin:service.requestCount.total:splitBy("dt.entity.service"):value:rate(1s)

can be converted to DQL as follows:

timeseries total=sum(dt.service.request.count, rate:1s),
by:{dt.entity.service}

Rollup transformation

The Metrics Classic rollup transformations have equivalent array functions in DQL:

Rollup transformation
DQL array function
rollup(avg)
rollup(max)
rollup(min)
rollup(sum)
rollup(value)
rollup(count)
Not supported
rollup(median)
Not supported
rollup(percentile(X))
Not supported

For example, this Metrics Classic metric selector query:

builtin:host.cpu.usage:splitBy():avg:rollup(avg,5m)

can be converted to DQL as follows:

timeseries usage=avg(dt.host.cpu.usage), interval:1m
| fieldsadd usage=arrayMovingAvg(usage, 5)

The moving window is time-agnostic and so the interval parameter has been fixed to 1m to ensure a 5-minute moving average.

DQL does not directly support the Metrics Classic rollup(median, ...) and rollup(percentile(N), ...) transformations.

Instead, you can use a larger starting interval to calculate the percentile.

For example, this Classic metric selector query:

builtin:host.cpu.usage:splitBy():avg:rollup(percentile(95),5m)

can be converted to DQL as follows:

timeseries usage=percentile(dt.host.cpu.usage, 95, rollup:avg),
interval:5m

This result is not a moving average, but a sequence of 5-minute time intervals. Consequently, a 2-hour timeframe has only 24 data points, not 120.

DQL does not directly support the Metrics Classic rollup(count, ...) transformation.

Instead, you can calculate a rolling sum of non-null values within the timeframe.

For example, this Metrics Classic metric selector query:

builtin:kubernetes.container.restarts
:splitBy("dt.entity.cloud_application")
:sum:rollup(count, 5m)

can be converted to DQL as follows:

timeseries usage = avg(dt.kubernetes.container.restarts),
by:{dt.entity.cloud_application}
| fieldsAdd zero_if_null = if(isNull(usage[]), 0, else:1)
| fieldsAdd usage_rollup_count = arrayMovingSum(zero_if_null, 5)

Only observations within the query timeframe are counted. Consequently, the first five minutes of the result do not represent a full 5-minute rolling count.

Smooth transformation

The Metrics Classic smooth transformation has no equivalent DQL.

Sort transformation

The Metrics Classic sort transformation is equivalent to the sort command in DQL.

For example, this Metrics Classic metric selector query (formatted for clarity):

builtin:host.cpu.usage:splitBy("dt.entity.host"):avg
:sort(value(avg,descending))
:limit(3)

can be converted to DQL as follows:

timeseries usage=avg(dt.host.cpu.usage), by:{dt.entity.host}
| sort arrayAvg(usage) desc
| limit 3

Timeshift transformation

The Metrics Classic timeshift transformation is equivalent to the timeseries shift: parameter.

For example, this MetricsClassic metric selector query:

builtin:host.cpu.usage:splitBy():avg:timeshift(-7d)

can be converted to DQL as follows:

timeseries usage=avg(dt.host.cpu.usage), shift:-7d

Unit transformation

The Metrics Classic setUnit and toUnit transformations are not supported directly in DQL.

Instead, you can use the "Units and formats" section of any chart.

Metric percent example