Conditional Expressions
There is no if-then-else
syntax in
LogScale, since the streaming style is not well-suited for
procedural-style conditions. However, there are a few ways to do
conditional evaluation:
Case Expressions
Using case
expressions, you can
describe alternative flows in your queries. It is similar to
case
or
cond
you might know from many
other functional programming languages. It essentially allows you to
write if-then-else
constructs
that work on events streams.
The syntax looks like this:
case {
expression | expression | ...;
expression | expression | ...;
expression | expression | ...;
* | expression | ...
}
You write a sequence of pipeline clauses separated by a semi-colon
(i.e., ;
). LogScale will apply
each clause from top to bottom until one returns a value (matches the
input).
You can add wildcard clause case { ... ; *
=> * }
which matches all events as the "default case",
essentially the else
part of an
if-statement. If you don't add a wildcard clause, any events that don't
match any of the explicit clauses will be dropped.
Example
Let's say we have logs from multiple sources that all have a field
named time
, and we want to get percentiles of the
time fields, but one for each kind of source.
First we try to match some text that distinguishes the different types
of line. Then we can create a new field type
and
assign a value that we can use to group by
time=*
| case { "client-side" | type := "client";
"frontend-server" | ip != 192.168.1.1 | type := "frontend";
Database | type := "db" }
| groupBy(type, function=percentile(time)))
Catch-All Clause
Note that the *
clause captures
any output that has not matched a previous clause, and can be used
either to ignore that information (resulting in no action or output)
or to apply a default operation or value. For example, in the sample
below the client
field is being
used to determine whether the IP address is
localhost
and setting the
local
field accordingly:
case { client = "::1" | local := "true";
client = "127.0.0.1" | local := "true";
* | local := "false"}
The *
clause in this instance
sets local
to
false
for any non-matching
value.
However, the following is also valid:
case { client = "::1" | local := "true";
client = "127.0.0.1" | local := "true";
* }
The above ensures that the non-matching clauses are not processed, but does not create the field unless we've identified a local value.
Match Expressions
Using match
expressions, you can
describe alternative flows in your queries where the conditions all
check the same field. It is similar to match()
or
switch
which you might recognize
from many other programming languages. It essentially enables you to
write if-then-else
constructs
that work on events streams. The matches on the field support the
filters listed in Field Filters.
The syntax looks like this:
field match {
value => expression | expression... ;
/regex/ => expression | ...;
* => expression | ...
}
You write a sequence of filter and pipeline clauses to run when the
filter matches, separated by a semicolon
(;
). LogScale will apply each
clause from top to bottom until one returns a value (matches the input).
You can use some functions as selectors (in addition to string patterns). More specifically, those functions which test a single field (and don't transform the event).
You can add a wildcard clause match { ... ;
* => * }
which matches all events as the "default case",
essentially the else part of an if-statement. If you don't add a
wildcard clause any events that don't match any of the explicit clauses
will be dropped. You cannot use the empty clause — you must
explicitly write *
to match all.
Example
Let's say we have logs from multiple sources that all have a field that holds the time spent on some operation, but in different fields and units. We want to get percentiles of the time fields all in the same unit and in one field.
logtype match {
"accesslog" => time:=response_time ; // Access log is in seconds.
/server_\d+/ => time:=server_time*1000 ; // These servers log in millis
}
| groupBy(logtype, function=percentile(time)))
Setting a Field's Default Value
You can use the function default()
to set the
value of a missing or empty field.