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.
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:
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.
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:
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:
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.
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: