Skip to content

Filtering And Expressions

Chris Michael edited this page Sep 29, 2026 · 1 revision

Pudu

Filtering And Expressions

A filter decides whether an event continues; every filter must keep it. PuduLangLog.Filter offers byExcluding, byIncludingOnly, and predicates: fromSource, withProperty, withPropertyValue, withPropertyWhere, atLeast, withFailure, allOf, anyOf, and not.

The expression language

Expressions.compile reads an expression once; evaluate runs it against an event and answers a value, or none when it is undefined. predicate is true only for the boolean true; computed adds a property; template builds a formatter.

Kind Syntax
Names properties, Cart.Items[0].Price, Meta['region'], and built-ins @t, @m, @mt, @l, @x, @p, @i, @r, @tr, @sp
Operators and, or, not, =, <>, <, <=, >, >=, like, not like, in, not in, is null, is not null, +, -, *, /, %, ^
Case ci after a comparison or call ignores case
Wildcards Items[?] any element, Items[*] every element
Literals numbers, 0xFF, 'text', true, false, null, [1, ..rest], {a: 1, ..other}
Conditionals if … then … else …
Functions Coalesce, Concat, Contains, ElementAt, EndsWith, IndexOf, IndexOfMatch, Inspect, IsDefined, IsMatch, LastIndexOf, Length, Nest, Now, Replace, Rest, Round, StartsWith, Substring, TagOf, ToString, TypeOf, Undefined, UtcDateTime
module Ex05Filtering

import Std.Io as Io
import PuduLangLog.Configuration as Configuration
import PuduLangLog.Event as Event
import PuduLangLog.Expressions as Expressions
import PuduLangLog.Filter as Filter
import PuduLangLog as Log
import PuduLangLog.Logger as Logger
import PuduLangLog.Sinks.Memory as Memory
import PuduLangLog.Value as Value

export fn main() -> Int {
  let health = match Expressions.predicate("RequestPath like '/health%'") {
    case Ok(test) => test
    case Err(problem) => panic(problem)
  }
  let memory = Memory.create()
  let logger = Configuration.create()
    .filterExcluding(health)
    .filterExcluding(Filter.allOf([Filter.fromSource("Shop.Cache"), Filter.not(Filter.atLeast(Log.Warning))]))
    .writeTo(Memory.sink(&memory))
    .createLogger()
  logger.information("GET \{RequestPath\}", [Value.text("/health/ready")])
  logger.information("GET \{RequestPath\}", [Value.text("/orders")])
  logger.forSource("Shop.Cache").information("cache hit", [])
  logger.forSource("Shop.Cache").warning("cache full", [])
  let _kept = Io.writeLine("kept: " + Memory.messages(&memory).join(" | "))
  let order = Event.create(Log.Timestamp{millis: 0, offset: 0}, Log.Information, "Order \{Id\}", [
      Value.property("Id", Value.int(7)),
      Value.property("Items", Value.list([Value.object([("Sku", Value.text("tea")), ("Price", Value.int(4))]), Value.object([("Sku", Value.text("pot")), ("Price", Value.int(30))])])),
      Value.property("SourceContext", Value.text("Shop.Orders.Api"))
    ])
  for text in ["Items[?].Price > 20", "Items[*].Price > 20", "Length(Items)", "IsDefined(Sum)", "Sum + 1", "if Id > 5 then 'big' else 'small'", "Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)", "ElementAt(Items, 1).Sku", "@l = 'information' ci", "Frob(Id)"] {
    let answer = match Expressions.compile(text) {
      case Ok(compiled) => match Expressions.evaluate(&compiled, &order) {
        case Some(held) => Value.render(&held)
        case None => "undefined"
      }
      case Err(problem) => "refused: " + problem
    }
    let _line = Io.writeLine(text + "  =>  " + answer)
  }
  Logger.close(&logger)
  0
}

Check it and run it:

pudu check src/Ex05Filtering.pudu
pudu run src/Ex05Filtering.pudu

Output:

kept: GET "/orders" | cache full
Items[?].Price > 20  =>  true
Items[*].Price > 20  =>  false
Length(Items)  =>  2
IsDefined(Sum)  =>  false
Sum + 1  =>  undefined
if Id > 5 then 'big' else 'small'  =>  "big"
Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)  =>  "Api"
ElementAt(Items, 1).Sku  =>  "pot"
@l = 'information' ci  =>  true
Frob(Id)  =>  refused: unknown function `frob`

An undefined operand makes most expressions undefined rather than failing. A call to an unknown function is refused when compiling, so a typo fails at startup.

Related

Clone this wiki locally