Conditions

Conditions #

The INFINI Framework provides a conditions system for evaluating events against configurable rules. Conditions are used throughout the framework — most notably in pipeline if/then/else branching — to make runtime decisions based on field values, patterns, numeric ranges, network membership, and logical combinations.

Each operator has its own reference page under this section, kept in a uniform format: what it tests, its parameters, and examples.

Condition Interface #

Every condition implements the Condition interface:

type Condition interface {
    Check(event ValuesMap) bool
    String() string
}
MethodDescription
Check(event ValuesMap) boolEvaluates the condition against an event. Returns true if the event matches.
String() stringReturns a human-readable representation of the condition for logging and debugging.

Events are read through the ValuesMap interface, which provides dot-notation field access:

type ValuesMap interface {
    GetValue(string) (interface{}, error)
}

Evaluation Scope #

The condition’s field lookups resolve against different sources depending on where the if block runs:

  • Per-record sub-chains (e.g. inside for_each): conditions evaluate against the current record, and field names refer to the record’s own attributes (file.path, log_level, …) — dot notation walks nested fields.
  • Pipeline level (no record bound): conditions evaluate against the pipeline context, where fields are referenced through the _ctx. prefix (e.g. _ctx.request.method).

If a field does not exist, GetValue returns an error and the condition typically evaluates to false.

Config Struct #

Conditions are declared in YAML configuration and deserialized into the Config struct (core/conditions/):

type Config struct {
    Equals           *Fields                `config:"equals"`
    Contains         *Fields                `config:"contains"`
    Prefix           map[string]interface{} `config:"prefix"`
    Suffix           map[string]interface{} `config:"suffix"`
    Regexp           *Fields                `config:"regexp"`
    Range            *Fields                `config:"range"`
    Network          map[string]interface{} `config:"network"`
    Exists           []string               `config:"exists"`
    IN               map[string]interface{} `config:"in"`
    LengthEquals     *Fields                `config:"length"`
    OR               []Config               `config:"or"`
    AND              []Config               `config:"and"`
    NOT              *Config                `config:"not"`
    QueueHasLag      []string               `config:"queue_has_lag"`
    ConsumerHasLag   *Fields                `config:"consumer_has_lag"`
    ClusterAvailable []string               `config:"cluster_available"`
}

A Config must contain exactly one top-level operator. To combine multiple operators, use the logical operators and, or, or not.

Operator Reference #

OperatorKindDescription
equalsValueExact value match (string, int, float, bool)
containsValueSubstring match on strings or string arrays
regexpValueRegular expression match
prefixValueString starts-with check
suffixValueString ends-with check
inValueValue membership in a list
rangeValueNumeric range comparison (gt, gte, lt, lte)
existsValueField existence and non-empty check
lengthValueCollection/string length equality
networkValueIP address network membership
andLogicalLogical AND (all must match)
orLogicalLogical OR (any must match)
notLogicalLogical negation
queue_has_lagDomainMessage queue lag detection
consumer_has_lagDomainConsumer group lag detection
cluster_availableDomainElasticsearch cluster availability

Using Conditions in Pipelines #

Conditions power the if/then/else branching in pipeline processor definitions. The if block takes a single condition configuration. When it evaluates to true, the then processors execute; otherwise, the else processors run (if provided).

Basic Branching #

pipeline:
  - name: my_pipeline
    auto_start: true
    keep_running: true
    processor:
      - if:
          equals:
            _ctx.request.method: "POST"
        then:
          - echo:
              message: "POST request received"
        else:
          - echo:
              message: "Non-POST request"

Per-Record Routing #

processor:
  - for_each:
      processor:
        - if:
            contains:
              file.path: "nginx"
          then:
            - mutate:
                add:
                  route: nginx-pipeline
          else:
            - mutate:
                add:
                  route: default-pipeline

Complex Conditions #

pipeline:
  - name: api_filter
    auto_start: true
    keep_running: true
    processor:
      - if:
          and:
            - equals:
                _ctx.request.method: "GET"
            - not:
                contains:
                  _ctx.request.uri: "/health"
            - exists:
                - _ctx.request.header.Authorization
          then:
            - echo:
                message: "Authenticated GET request (non-health)"

Using Conditions Programmatically #

You can create and evaluate conditions directly from Go code:

import "infini.sh/framework/core/conditions"

cfg := &conditions.Config{
    Equals: &conditions.Fields{},
}
// Typically populated by config deserialization, or manually:
cfg.Equals = conditions.MustNewFields(map[string]interface{}{
    "type": "process",
    "proc.pid": 305,
})

cond, err := conditions.NewCondition(cfg)
if err != nil {
    log.Fatal(err)
}

// Check against an event that implements ValuesMap
if cond.Check(event) {
    fmt.Println("Condition matched")
}

Building Compound Conditions #

cfg := &conditions.Config{
    AND: []conditions.Config{
        {
            Equals: conditions.MustNewFields(map[string]interface{}{
                "status": "active",
            }),
        },
        {
            Range: conditions.MustNewFields(map[string]interface{}{
                "age.gte": 18,
            }),
        },
    },
}

cond, err := conditions.NewCondition(cfg)

Using the Context Helper #

The Context type aggregates multiple ValuesMap sources, so a condition can read fields from several data providers. It also supports variable templates with $[[variable]] syntax:

ctx := &conditions.Context{}
ctx.AddContext(primaryData)
ctx.AddContext(fallbackData)

if cond.Check(ctx) {
    // Fields are looked up across all added contexts in order
}
Edit Edit this page