prometheusgrafanapromqlgrafana-variable

PromQL if/else-like expression based on value of Grafana query variable


How do I incorporate if/else logic in PromQL based on the value of a Grafana query variable?

if($query_variable == "ALL")
    <a modified query>
else
    <the primary query>

For individual values of $query_variable, the value will filter the results of <the primary query>. However, <the primary query> does not work with more than one value of $query_variable, so <a modified query> is needed for viewing unfiltered results.


Solution

  • There is no straightforward way of implementing if-else statement based on arbitrary outside variable in promQL.

    But employing some tricky usage of function absent, vector matching and boolean logic, one can construct what is needed.


    Let's start with query that will return value if variable is equal to ALL.
    Since you cannot directly compare strings in promQL, we need to place them into label of some pseudo metric. This can be done with function absent, because

    absent() tries to be smart about deriving labels of the 1-element output vector from the input vector.

    Then to compare if two generated labels are equal we can use and, that make our query return result only if labels in both generated metrics are equal.

    So query, that returns a pseudo metric only if variable is equal to predefined value ALL will be

    absent(non_existent{pseudo_input="ALL"}) and absent(non_existent{pseudo_input="$query_variable"})
    

    or since actual names of non existing metrics don't matter (as long as they are guaranteed to not exist) and label names too, we can shorthand this to

    absent(_{_="ALL"}) and absent(_{_="$query_variable"})
    

    After that all you need is to combine your two queries in the manner, that filters results based on if the previous query returned value or not.

    So in this case we can use and on() and unless on() to simply check existence of the result in the RHS, ignoring it's labels.

    (
      <a modified query>
      and on()
      (absent(_{_="ALL"}) and absent(_{_="$query_variable"}))
    ) or on() (
      <the primary query>
      unless on()
      (absent(_{_="ALL"}) and absent(_{_="$query_variable"}))
    )
    

    Theoretically, if your <a modified query> is guarantied to have some result, you can omit unless on() absent(_{_="ALL"}) and absent(_{_="$query_variable"}) in the second part. But for general purpose I find it's better to be preserved as a fail safe, and more clear indicator of intentions.