Skip to content

Attribute Value Expressions for Metrics, Logs & Traces

Manpreet edited this page Jul 22, 2023 · 5 revisions

For metrics, logs and spans data, OpenTelemetry schema includes attributes for each of those. In our telemetry generator tool, the attributes generation for all these types works in the same way.
As we generate and post the data with some frequency, a metric, log or span will be posted multiple times. We may want to post varying values for these attributes in each of these packets. For example, consider a span representing a request on an auth server which has an attribute auth.type. In subsequent packets for this span we may want changing values like SAML, OAuth, OIDC etc.
To handle this in a succinct manner, instead of just providing hardcoded values for each telemetry data type (metric/log/span), we use expressions that are evaluated at runtime. These expressions leverage a variety of methods we have defined and listed below and use the Jakarta Expression Language for evaluation. Each of the following methods available are deterministic, i.e. every time you trigger them in a new JVM process, they will result in the same values.

Method Name Return Type Description
counter(String INPUT) String Returns the INPUT string prefixed to a counter value. The counter is stateful and is linked to the input string. For example, counter("cluster-name-") would return cluster-name-1, cluster-name-2, cluster-name-3 and so on for subsequent calls.
UUIDFromStringCounter(String INPUT) String Returns a type 3 UUID based on the counter of the input string. So a call to this method with "log" means UUID is generated with the value "log1" and a subsequent call would result in the UUID being generated using "log2" and so on. This ensures that the UUIDs are generated in sequence and are deterministic.
roundRobin([String VAL1, String VAL2, ...]) String Returns one of the strings provided in the input list in a sequential and stateful manner. Returns VAL1, VAL2, VAL3 and so on for each subsequent call.
alphanumericSequence(String INPUT) String Returns the next alphanumeric string derived from the INPUT string in a stateful manner. The next alphanumeric string means the rightmost character is incremented to the next character in the sequence. For example, alphanumericSequence("abc8") → "abc8" and next calls return "abc9", "abca", "abcb" and so on.
alphanumericSequenceFromEnv() String There might be cases where you want to set the starting string for the alphanumeric sequence yourself. To handle this use case, this expression method obtains the string stored in environment variable/system property for the ENV_ALPHANUMERIC key. This value is used as the seed value for the alphanumericSequence call.
IPv4Sequence(String INPUT_IP) String Returns the next IPv4 address derived from the INPUT_IP in a stateful manner. The next IPv4 address means the rightmost possible octet is incremented. For example, IPv4Sequence("128.10.114.254") → "128.10.114.254" and next calls return "128.10.114.255", "128.10.115.1", "128.10.115.2"
getLong(String EXPRESSION) Long An arithmetic expression is specified along with the count() method call, which always returns the value from a counter in the context of a specific attribute. For example, getLong("count() * 3 + 1") → 4 and next calls return 7, 10, 13 and so on. Obviously, you may omit the arithmetic expression and just use count() inside the getLong() call.
getDouble(String EXPRESSION) Double An arithmetic expression is specified along with the count() method call, which always returns the value from a counter in the context of a specific attribute. Works exactly like getLong method except that it returns double values. For example, getDouble("count() / 4") → 0.25 and next calls return 0.5, 0.75, 1.0 and so on.
getBoolean(String EXPRESSION) Boolean An arithmetic expression is specified along with count() method call, which whenever the expression evaluates to 0 returns false and true in all other cases. For example, getBoolean("count() % 2") → false and next calls return true, false, true and so on.

For attributes values of List and Map types, you can use the same expressions and specify literal attribute value. For example:

  • List → '[ counter("abc"), IPv4Sequence("10.10.111.1"), "xyz", getLong("count() * 2") ]'
    The lists generated on subsequent calls are: ["abc1", "10.10.111.1", "xyz", 2 ], ["abc2", "10.10.111.2", "xyz", 4], ["abc3", "10.10.111.3", "xyz", 6] and so on.
  • Map → '{"app": alphanumericSequence("svc-vodka"), "ip": IPv4Sequence("10.10.111.1"), "version": roundRobin(["latest", "22.5.0-142"])}'
    The maps generated on subsequent calls are: {"app": "svc-vodka", "ip": "10.10.111.1", "version": "latest"}, {"app": "svc-vodkb", "ip": "10.10.111.2", "version": "22.5.0-142"}, {"app": "svc-vodkc", "ip": "10.10.111.3", "version": "latest"} and so on.

Since the ELProcessor used to evaluate these expressions is not thread safe, we do not recommend concatenation of these attributes. For example: "instance-".concat(alphanumericSequence("abc")).

It is possible to implement and provide your own expression methods for generating attribute values. See User Defined Expressions.

Clone this wiki locally