# Metrics API - Metric expressions

Metric expressions enable you to use simple arithmetic operations right in the metric selector.

For example, this expression calculates the ratio (as a percentage) of two metrics:

metric1 / metric2 * 100

For the operands of the expression, you can use metrics or numbers.

- You need to use brackets to enforce order of operations.
- All metrics with more than 1 data point involved in a metric expression must be of the same resolution.
- You can use any metric as an operand, including metrics modified by any transformation chain, and you can apply transformations to the result of the expression.

## Limitations

- The selector must contain at least one metric key.
- You can query data points of up to 10 metrics in one query.

For the purposes of this limit, one expression (for example, `metric2 + metric2`

) counts as one metric.

## Precedence

Standard mathematical precedence rules applies:

- Parentheses, metric transformations
- Negation
- Multiplication, division
- Addition, subtraction

## Aggregation

If an aggregation has been applied in a transformation chain, this aggregation is used. If no transformation has been applied, the default aggregation is used. Your metric operands can be of different aggregations. For example, `metric:max - metric:min`

.

## Resolving expressions

Metric expressions are resolved as follows:

- Form tuple pairs for each pair of metrics.
- Align data points in every tuple.
- Apply arithmetic operation to aligned data points.

### Tuples

Arithmetic operations use the data points of tuples (unique combinations of metric—dimension—dimension value) of metrics. Identical tuples of each metric are paired and then their data points are aligned.

If one metric is dimensionless (has just one tuple without dimensions and dimension values), then this single tuple is paired with every tuple of other metrics. The same applies to numbers.

Non-pairable tuples are ignored by the expression and are not presented in the result.

### Data points

Once tuple pairs are formed, the data points are aligned, and then the desired arithmetical operation is applied to the aligned data points.

- If any of the aligned data points is
`null`

, the expression resolves to`null`

. - If a number is involved in the operation, it is aligned with every data point of the metric operand.
- If one metric is a single data point and the other is a series, the single data point is aligned with every data point of the series.
- If both metrics are a single data point, the data points are aligned and the resulting time slot covers both data points.
- If both metrics are series, the data points are aligned by timestamps.

For any unaligned data points, the expression resolves to `null`

.

## Best practices

### Use only when necessary

Use a metric expression only if you cannot accomplish your goal without it. Let's say you want to calculate the average CPU usage of two hosts, `HOST-001`

and `HOST-002`

. You could do it with a metric expression:

(builtin:host.cpu.usage:filter(eq("dt.entity.host","HOST-001")):splitBy()+builtin:host.cpu.usage:filter(eq("dt.entity.host","HOST-002")):splitBy())/2

There are two problems with this approach. First, the expression is hard to read and therefore prone to syntax errors. Second, if one of the hosts is offline, the result of the expression is empty. Even though the second problem could be solved by a **default** transformation, usage of the average aggregation is more effective:

builtin:host.cpu.usage:filter(or(eq("dt.entity.host","HOST-001"),eq("dt.entity.host","HOST-002"))):splitBy():avg

### Do not convert units

Do not use a metric expression to convert the unit of the data. Use the **toUnit** transformation instead. The only exception to this rule is for units that Dynatrace does not support. Use the GET all units request to fetch the list of supported units.

### Limit transformation usage

Always apply the **limit** transformation to the result of a calculation, not to its operands.

Consider the following query, which attempts to add top-10 CPU usage times to top-10 CPU idle times.

builtin:host.cpu.usage:sort(value(avg,descending)):limit(10)+builtin:host.cpu.idle:sort(value(avg,descending)):limit(10)

If you have a large environment with hundreds of hosts, it is unlikely that the 10 hosts with the highest CPU usage are among the 10 hosts with the highest CPU idle time. The operands won't have matching tuples, therefore the result of the expression will be empty. The solution is to apply the limit to the result of the expression instead:

(builtin:host.cpu.usage+builtin:host.cpu.idle):sort(value(auto,descending)):limit(10)

### Cover data gaps with the default transformation

The **default** transformation is particularly valuable for metric expressions. Even though normally the transformation doesn't fill up `null`

data points if a metric doesn't have a single data point in the query timeframe, in the metric expression context its semantic is slightly different. As long as a metric on either side of the expression has at least one data point, the transformation will fill the gaps. However, if all metrics in the expression are missing data, the transformation will return empty results.

Consider this example of a ratio expression, where we calculate the error ratio for key user actions:

builtin:apps.other.keyUserActions.reportedErrorCount.os/builtin:apps.other.keyUserActions.requestCount.os

If there are many requests but not a single error in your timeframe, the result will be empty, though an error ratio of `0`

would be more meaningful. You can achieve that with the `default(0)`

transformation:

builtin:apps.other.keyUserActions.reportedErrorCount.os:default(0)/builtin:apps.other.keyUserActions.requestCount.os

## Examples

With a metric expression, you can build your own ratio metrics. Suppose we start with the following metrics:

**builtin:service.errors.total.count**shows the number of errors of any type in a service**builtin:service.errors.server.successCount**shows the number of calls without server-side errors

From them, we can build an error ratio metric:

builtin:service.errors.total.count:value:default(0)/(builtin:service.errors.total.successCount:value:default(0)+builtin:service.errors.total.count:value:default(0))

The **default** transformation is used to replace the values of the time slots that have the value `null`

with 0.

The **builtin:service.errors.total.count** metric shows the number of errors across your services. The list might be lengthy, and you might be interested in each service's contribution to the error count. A combination of metric transformations and metric expressions can provide this information.

You need these transformations:

- filter transformation to obtain the error count for the service that you're checking.
- split by transformation to merge individual error counts of each service into one.

Then use this expression:

builtin:service.errors.total.count:filter(eq("dt.entity.service","SERVICE-B82BFBCB4E264A98")):value:default(0)/builtin:service.errors.total.count:splitBy():value:default(0) * 100

The **default** transformation is used to replace the values of the time slots that have the value `null`

with 0.

The **builtin:tech.jvm.memory.gc.collectionTime** metric shows the total duration of all garbage collections in a time slot. Information about individual GC times is not available, but we can use the **builtin:tech.jvm.memory.pool.collectionCount** metric showing the number of GCs per time to obtain the average duration of a garbage collection.

Before we start the calculation, we need to align the dimensions of both metrics. To do that, we need to apply the **split by** transformation with the `dt.entity.process_group_instance`

argument to the **builtin:tech.jvm.memory.pool.collectionCount** metric.

Additionally, we can sort the result in descending order by applying the sort transformation. The expression looks like this:

(builtin:tech.jvm.memory.gc.collectionTime/builtin:tech.jvm.memory.pool.collectionCount:splitBy("dt.entity.process_group_instance")):sort(value(max,descending))

For more examples, see the 'Metric Expressions by Example' Github page.

## Introductory video

Note that the syntax used in this video is based on the old syntax, which required parentheses around each metric and number of an expression.