EDXSample

Learn about the EDXSample Edge Delta OTTL extension function.

Minimum Agent Version: v2.25.0

Overview

EDXSample returns true for a share of the items it evaluates, so you can sample inside an OTTL statement instead of adding a separate Sample processor.

It has two forms. Without a key, each evaluation is an independent random draw. With a key, the decision is a stable hash of that value, so every item sharing the key is kept or dropped together: a trace ID keeps whole traces, a user ID keeps whole sessions, a pod name keeps whole pods.

The keyed form uses the same hash and the same decision rule as the Sample processor, so a condition keyed on a field agrees with that processor at the same percentage.

Syntax

EDXSample(percentage)
EDXSample(percentage, key)
  • percentage: The share of items for which the function returns true, from 0 to 100. Fractions are allowed, so 0.1 keeps one item in a thousand. It can be an expression over the item, such as attributes["priority"] * 10. A value outside 0 to 100, or NaN, fails the statement.
  • key: Optional. When set, the decision is a stable hash of this value instead of a random draw. A null or empty key fails the statement with EDXSample: key is empty.

The function also accepts a third seed argument, which fixes the random sequence of the keyless form. OTTL fills optional arguments in order, so a seed can only be passed alongside a key, and a key makes the seed inert. Use the two forms above.

A percentage of 0 always returns false and 100 always returns true, in both forms. Neither boundary reads the key, so they are safe on items that do not carry it.

Keyed Sampling

The key is hashed to a position between 0 and 100, and the item is kept when that position is below the percentage. The position depends only on the key, so it is the same on every agent and on every run.

KeyPositionEDXSample(10, key)EDXSample(25, key)EDXSample(50, key)
00f067aa0ba902b71.31truetruetrue
user-441713.14falsetruetrue
user-882148.67falsefalsetrue
4bf92f3577b34da6a3ce929d0e0e473658.82falsefalsefalse

Raising the percentage only ever adds keys, so a key kept at 10 percent is still kept at 25 percent.

Examples

Marking a sampled session

Input

{
  "_type": "log",
  "timestamp": 1735789600000,
  "body": "checkout completed",
  "resource": {...},
  "attributes": {
    "user_id": "user-4417",
    "priority": 5
  }
}

Statement

set(attributes["sampled_10"], EDXSample(10, attributes["user_id"]))
set(attributes["sampled_25"], EDXSample(25, attributes["user_id"]))
set(attributes["always"], EDXSample(100))
set(attributes["never"], EDXSample(0))

Output

{
  "_type": "log",
  "timestamp": 1735789630000,
  "body": "checkout completed",
  "resource": {...},
  "attributes": {
    "user_id": "user-4417",
    "priority": 5,
    "sampled_10": false,
    "sampled_25": true,
    "always": true,
    "never": false
  }
}

Every log carrying user-4417 produces the same two decisions, so a session is either wholly in the 25 percent sample or wholly out of it.

Keeping whole traces

Key on the trace ID so that a sampled trace keeps all of its spans, rather than a random tenth of them:

nodes:
- name: keep_whole_traces
  type: route_ottl
  paths:
  - path: sampled
    condition: EDXSample(10, trace.id)
    exit_if_matched: true

Taking the rate from the item

Because percentage is an expression, the rate can come from the data. This keeps a tenth of priority 1, half of priority 5, and all of priority 10:

nodes:
- name: sample_by_priority
  type: route_ottl
  paths:
  - path: sampled
    condition: EDXSample(attributes["priority"] * 10)
    exit_if_matched: true

A priority above 10 puts the percentage over 100 and fails the statement, so clamp the field upstream if it is unbounded.

Testing a sampling rule

The keyless form draws at random, so a dry run over the same input gives different results each time. To check a rule in Live Capture or a dry run, key it on a field instead. The decision is then fixed per key and repeats across runs and agents:

set(attributes["sampled"], EDXSample(10, attributes["user_id"]))

The boundaries are also useful while testing: EDXSample(100) passes everything and EDXSample(0) passes nothing, so you can confirm the rest of the statement independently of the sampling.