RainerScript ABNF

This is the formal definition of RainerScript, as supported by rsyslog configuration. Please note that this currently is working document and the actual implementation may be quite different.

The first glimpse of RainerScript will be available as part of rsyslog 3.12.0 expression support. However, the 3.12. series of rsyslog will have a partial script implementaiton, which will not necessariy be compatible with the later full implementation. So if you use it, be prepared for some config file changes as RainerScript evolves.

C-like comments (/* some comment */) are supported in all pure RainerScript lines. However, legacy-mapped lines do not support them. All lines support the hash mark "#" as a comment initiator. Everything between the hash and the end of line is a comment (just like // in C++ and many other languages).

Formal Definition

Below is the formal language definitionin ABNF (RFC 2234) format:

; all of this is a working document and may change! -- rgerhards, 2008-02-24

script := *stmt
stmt := (if_stmt / block / vardef / run_s / load_s)
vardef := "var" ["scope" = ("global" / "event")]
block := "begin" stmt "end"
load_s := "load" constraint ("module") modpath params ; load mod only if expr is true
run_s := "run" constraint ("input") name
constraint:= "if" expr ; constrains some one-time commands
modpath := expr
params := ["params" *1param *("," param) "endparams"]
param := paramname) "=" expr
paramname := [*(obqualifier ".") name]
modpath:= ; path to module
?line? := cfsysline / cfli
cfsysline:= BOL "$" *char EOL ; how to handle the first line? (no EOL in front!)
BOL := ; Begin of Line - implicitely set on file beginning and after each EOL
EOL := 0x0a ;LF
if_stmt := "if" expr "then"
old_filter:= BOL facility "." severity ; no whitespace allowed between BOL and facility!
facility := "*" / "auth" / "authpriv" / "cron" / "daemon" / "kern" / "lpr" /
"mail" / "mark" / "news" / "security" / "syslog" / "user" / "uucp" /
"local0" .. "local7" / "mark"
; The keyword security should not be used anymore
; mark is just internal
severity := TBD ; not really relevant in this context

; and now the actual expression
expr := e_and *("or" e_and)
e_and := e_cmp *("and" e_cmp)
e_cmp := val 0*1(cmp_op val)
val := term *(("+" / "-" / "&") term)
term := factor *(("*" / "/" / "%") factor)
factor := ["not"] ["-"] terminal
terminal := var / constant / function / ( "(" expr ")" )
function := name "(" *("," expr) ")"
var := "$" varname
varname := msgvar / sysvar
msgvar := name
sysvar := "$" name
name := alpha *(alnum)
constant := string / number
string := simpstr / tplstr ; tplstr will be implemented in next phase
simpstr := "'" *char "'" ; use your imagination for char ;)
tplstr := '"' template '"' ; not initially implemented
number := ["-"] 1*digit ; 0nn = octal, 0xnn = hex, nn = decimal
cmp_op := "==" / "!=" / "<>" / "<" / ">" / "<=" / ">=" / "contains" / "contains_i" / "startswith" / "startswith_i"
digit := %x30-39
alpha := "a" ... "z" # all letters
alnum :* alpha / digit / "_" /"-" # "-" necessary to cover currently-existing message properties

Samples

Some samples of RainerScript:

define function IsLinux
begin
    if $environ contains "linux" then return true else return false
end

load if IsLinux() 'imklog.so' params name='klog' endparams /* load klog under linux only */
run if IsLinux() input 'klog'
load 'ommysql.so'

if $message contains "error" then
  action
    type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,
    action.dbname='events', action.dbuser='uid',
    [?action.template='templatename'?] or [?action.sql='insert into table... values('&$facility&','&$severity&...?]
  endaction

... or ...

define action writeMySQL
    type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,
    action.dbname='events', action.dbuser='uid',
    [?action.template='templatename'?] or [?action.sql='insert into table... values(' & $facility & ','  & $severity &...?]
   endaction

if $message contains "error" then action writeMySQL

ALTERNATE APPROACH

define function IsLinux(
    if $environ contains "linux" then return true else return false
)

load if IsLinux() 'imklog.so' params name='klog' endparams /* load klog under linux only */
run if IsLinux() input 'klog'
load 'ommysql.so'

if $message contains "error" then
  action(
    type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,
    action.dbname='events', action.dbuser='uid',
    [?action.template='templatename'?] or [?action.sql='insert into table... values('&$facility&','&$severity&...?]
  )

... or ...

define action writeMySQL(
    type='ommysql.so', queue.mode='disk', queue.highwatermark = 300,
    action.dbname='events', action.dbuser='uid',
    [?action.template='templatename'?] or [?action.sql='insert into table... values('&$facility&','&$severity&...?]
   )

if $message contains "error" then action writeMySQL(action.dbname='differentDB')

[rsyslog.conf overview]

Implementation

RainerScript will be implemented via a hand-crafted LL(1) parser. I was tempted to use yacc, but it turned out the resulting code was not thread-safe and as such did not fit within the context of rsyslog. Also, limited error handling is not a real problem for us: if there is a problem in parsing the configuration file, we stop processing. Guessing what was meant and trying to recover would IMHO not be good choices for something like a syslogd. [manual index] [rsyslog site]

This documentation is part of the rsyslog project.
Copyright © 2008 by Rainer Gerhards and Adiscon. Released under the GNU GPL version 3 or higher.