rsyslog学习8 -- RainerScript

RainerScript

RainerScript是一种处理网络事件、配置、过程的脚本语言,是rsyslog的主要配置语言。不能简写成rscript,因为已经被注册了。

v6后支持。

Data Types 数据类型

RainerScript是一种typeless语言,不需要关心数据类型。不能使用 “A” + “B” ,可以使用 & 连接。有需要的话脚本会自动转换类型

Expressions 表达式

支持任意复杂表达式。下面按优先级列出各表达式

  • expressions in parenthesis
  • not, unary minus
  • *, /, % (modulus, as in C)
  • +, -, & (string concatenation)
  • ==, !=, <>, <, >, <=, >=, contains (strings!), startswith (strings!)
  • and
  • or

比如,“not a == b” 的返回很有可能不是你想要的,因为脚本处理器会运算“not a”等到一个布尔值再与b比较,而你可能想要的是 “not (a == b)”。

如果要使用不等于,建议使用 “!=” or “<>”,两者是一样的。“not” 一般用在需要使用布尔值的场合。

Functions 函数

有两种函数,build-ins和modules。Built-in可以随时被调用 ,而modules需要事先被载入。

1
module(load="<name of module>")

如果有多于一个的函数使用了相同的名字,第一个载入的有效,同时,会产生一个错误消息,但不会中止。build-in函数总是预先被载入,所以会占据这些名字。

Control Structures 控制结构

if

1
2
3
4
if ($msg contains "important") then {
if ( $.foo != "" ) then set $.foo = $.bar & $.baz;
action(type="omfile" file="/var/log/important.log" template="outfmt")
}

if/else-if/else

1
2
3
4
5
6
7
8
9
if ($msg contains "important") then {
set $.foo = $.bar & $.baz;
action(type="omfile" file="/var/log/important.log" template="outfmt")
} else if ($msg startswith "slow-query:") then {
action(type="omfile" file="/var/log/slow_log.log" template="outfmt")
} else {
set $.foo = $.quux;
action(type="omfile" file="/var/log/general.log" template="outfmt")
}

foreach

要说明的是,一般对于foreach会存在误解,这个只能工作于json结构,事实上,我们当时应该拒绝 foreach的,只是太迟了。

要记住,在这种脚本语言中,没有array的概念,因为我们不想把事情搞得太复杂。只能在一些配置对象和一组选择比较时使用。

如果你在解析json,foreach可以迭代json array和json objects。array是有序的,object是kay-value,无序。

For the foreach invocation below:

1
2
3
foreach ($.i in $.collection) do {
...
}

Say $.collection holds an array [1, "2", {"a": "b"}, 4], value of $.i across invocations would be 1, "2", {"a" : "b"} and 4.

$.collection 必须来源于 JSON (via mmjsonparse).

When $.collection holds an object {"a": "b", "c" : [1, 2, 3], "d" : {"foo": "bar"}}, value of $.i across invocations would be {"key" : "a", "value" : "b"}, {"key" : "c", "value" : [1, 2, 3]} and {"key" : "d", "value" : {"foo" : "bar"}} (not necessarily in the that order). In this case key and value will need to be accessed as $.i!key and $.i!value respectively.

Here is an example of a nested foreach statement:

1
2
3
4
5
6
7
8
9
10
foreach ($.quux in $!foo) do {
action(type="omfile" file="./rsyslog.out.log" template="quux")
foreach ($.corge in $.quux!bar) do {
reset $.grault = $.corge;
action(type="omfile" file="./rsyslog.out.log" template="grault")
if ($.garply != "") then
set $.garply = $.garply & ", ";
reset $.garply = $.garply & $.grault!baz;
}
}

Again, the itereted items must have been created by parsing JSON.

Please note that asynchronous-action calls in foreach-statement body should almost always set action.copyMsg to on. This is because action calls within foreach usually want to work with the variable loop populates (in the above example, $.quux and $.corge) which causes message-mutation and async-action must see message as it was in a certain invocation of loop-body, so they must make a copy to keep it safe from further modification as iteration continues. For instance, an async-action invocation with linked-list based queue would look like:

1
2
3
4
foreach ($.quux in $!foo) do {
action(type="omfile" file="./rsyslog.out.log" template="quux
queue.type="linkedlist" action.copyMsg="on")
}
1
2
3
4
5
# 这种代码是无效的,因为不是从json创建的对象
set $.noarr = ["192.168.1.1", "192.168.2."];
foreach ($.elt in $.noarr) do {
...
}

call

Details here: The rsyslog “call” statement

continue

A NOP, useful e.g. inside the then part of an if-structure.

configuration objects 配置对象

Common Parameters

config.enabled

New in version 8.33.0.

所有的配置对象都有 config.enabled 参数 ,用来禁用它们。如果设置为on或留空,配置会被启用,如果是其它值,值忽略。这个可以从环境变量或文件中,利用反引号`来调整。

比如,将环境变量LOAD_MPTCP设为off,然后构建

1
2
module(load="imptcp"
config.enabled=`echo $LOAD_IMPTCP`)

该模块不会被载入

1
2
module(load="imptcp"
config.enabled=`echo $LOAD_IMPTCP`)
1
2
module(load="imptcp"
config.enabled=`echo $LOAD_IMPTCP`)

Objects

action()

The action object is the primary means of describing actions to be carried out.

global()

This is used to set global configuration parameters. For details, please see the rsyslog global configuration object.

input()

The input object is the primary means of describing inputs, which are used to gather messages for rsyslog processing.

module()

The module object is used to load plugins.

parser()

The parser object is used to define custom parser objects.

timezone()

The timezone object is used to define timezone settings.

include()

The include object is use to include configuration snippets stored elsewhere into the configuration.

String Constants 字串常量

字串常量是所有的脚本语言所必须,提供了程序开始时的一些值。

Uses

比较,配置参数,函数参数等地方都要用到。

在字串常量中,特殊字符需要用反斜杠去注明。想要知道如何去正确的escape,使用工具 RainerScript String Escape Online Tool.

Types

Rsyslog 提供了多种字串常量,与shell类似:

  • 单引号

    可以被escape

  • 双引号

    在单引号的基础上,对$进行escape,如果无法被escape,将会产生一个语法错误,导致启动失败

  • 反引号

    This was added in 8.33.0. 与shell中类似,only the following is supported:

    • echo $VARNAME - It will evaluate the environment variable and use it as string constant. If the variable is not found, an empty string is generated (this is not an error).

      从8.37开始,echo被加强了,支持环境变量和字串常量混合

      An example:

      • env SOMEPATH is set to “/var/log/custompath”
      • config is: param=echo $SOMEPATH/myfile
      • param than is expanded to “/var/log/custompath/myfile”

      不支持${VAR},一个环境变量只能被whitespace 或 / 终结。此功能的目的并不是会了模仿bash,而是为了使用自定义外界的参数

    • cat filename - It will evaluate to the content of the given file. 只支持读取一个文件名。如果无法读取,返回空值。

    echo和cat后只能跟一个空格

    Backticks are especially useful for configuration files that are auto-generated but need to contain a small set of special functionality.

    更多例子参考 https://github.com/rsyslog/rsyslog-docker/tree/master/appliance/alpine.

Variable (Property) types 变量类型

所有的属性都能被RainerScript所调用,用$表示

1
set $.x!host = $hostname;

也支持本地变量,只作用于当前消息,但并不是消息属性。(e.g. the “$!” all JSON property does not contain them)

只有 消息的json(CEE/Lumberjack) 属性可以被 set, unset and reset 修改, 本地变量也可以。

消息的 JSON 属性命名以 “$!” 打头,where the bang character represents the root.

本地变量以 “$.” 打头, whJSere the dot denotes the root.

JSON 属性和本地变量都支持任意长度的深度,! 常用来作为路径分割,无论是消息属性还是本地变量。比如 “$!path1!path2!varname” 和 “$.path1!path2!varname” 都是用!分割的,但$后的!和.区分是消息属性还是本地变量。

表达式最后要加上”;”,否则语法错误

参考以下例子

set

sets the value of a local-variable or json property, 如果要定义的变量已经存在内容,将会有以下几种行为方式:

merges 如果新值为object, 但新的值会附加到root而不是指定的key下,比如

1
2
3
4
5
6
7
8
9
10
11
set $.x!one = "val_1";
# results in $. = { "x": { "one": "val_1" } }
set $.y!two = "val_2";
# results in $. = { "x": { "one": "val_1" }, "y": { "two": "val_2" } }

set $.z!var = $.x;
# results in $. = { "x": { "one": "val_1" }, "y": { "two": "val_2" }, "z": { "var": { "one": "val_1" } } }

set $.z!var = $.y;
# results in $. = { "x": { "one": "val_1" }, "y": { "two": "val_2" }, "z": { "var": { "one": "val_1" } }, "two": "val_2" }
# note that the key *two* is at root level and not under *$.z!var*.

ignores 如果原内容是object,而新值为string或num等非object. Eg:

1
2
3
4
set $.x!one = "val_1";
set $.x = "quux";
# results in $. = { "x": { "one": "val_1" } }
# note that "quux" was ignored

resets 如果旧值为非object.

1
2
3
set $.x!val = "val_1";
set $.x!val = "quux";
# results in $. = { "x": { "val": "quux" } }

unset

removes the key. Eg:

1
2
3
set $.x!val = "val_1";
unset $.x!val;
# results in $. = { "x": { } }

reset

force sets the new value regardless of what the variable originally contained or if it was even set. Eg.

1
2
3
4
5
6
7
8
9
10
11
12
13
# to contrast with the set example above, here is how results would look with reset
set $.x!one = "val_1";
set $.y!two = "val_2";
set $.z!var = $.x;
# results in $. = { "x": { "one": "val_1" }, "y": { "two": "val_2" }, "z": { "var": { "one": "val_1" } } }
# 'set' or 'reset' can be used interchangeably above(3 lines), they both have the same behaviour, as variable doesn't have an existing value

reset $.z!var = $.y;
# results in $. = { "x": { "one": "val_1" }, "y": { "two": "val_2" }, "z": { "var": { "two": "val_2" } } }
# note how the value of $.z!var was replaced

reset $.x = "quux";
# results in $. = { "x": "quux", "y": { "two": "val_2" }, "z": { "var": { "two": "val_2" } } }

Lookup Tables 查找表

Lookup tables are a powerful construct to obtain “class” information based on message content (e.g. to build log file names for different server types, departments or remote offices).

General Queue Parameters 通用队列参数

Usage

Queue parameters can be used together with the following statements:

Queues need to be configured in the action or ruleset it should affect. If nothing is configured, default values will be used. Thus, the default ruleset has only the default main queue. Specific Action queues are not set up by default.

To fully understand queue parameters and how they interact, be sure to read the queues documentation.

参考 https://www.rsyslog.com/doc/master/rainerscript/queue_parameters.html

Examples

Example 1

The following is a sample of a TCP forwarding action with its own queue.

1
2
3
action(type="omfwd" target="192.168.2.11" port="10514" protocol="tcp"
queue.filename="forwarding" queue.size="1000000" queue.type="LinkedList"
)

The rsyslog “call” statement

Call与ruleset联系,可以把rulesets看成是一个子程序,就容易理解call了。

Call可以调用任何rulsests,如果在ruleset中存在queue,由消息发送给queue异步处理,否则会同步执行,结束后返回控制

在注意同步和异步的区别

Call是用来取代已经废弃的 omruleset模块,使用了新引擎,更有效率,特别是对于那些同步操作,几乎是0开销。而omrulset需要复制消息,这至少要消耗250字节的内存和一些计算性能

syntax

1
call rulesetname

Where “rulesetname” is the name of a ruleset that is defined elsewhere inside the configuration. If the call is synchronous or asynchronous depends on the ruleset parameters. This cannot be overridden by the “call” statement.

The rsyslog “call_indirect” statement

The rsyslog “call_indirect” 类亿于 “call”,区别在于被call的ruleset不是常量,还是一个实时计算的表达式

如果ruleset不存在,会产生一个错误消息,然后被跳过,继续执行下一语句

syntax

1
call_indirect expression;

Where “expression” is any valid expression. See expressions for more information.

结尾要有分号

examples

“call_indirect” 可以根据消息变量来调用ruleset,例如,rulesets以syslog tag命名,就可以这么写

1
call_indirect $syslogtag;

使用时要小心被注入,最好在ruleset前加上前缀,比如 “changeme-” :

1
call_indirect "changeme-" & $syslogtag;

call_indirect也可以调用常量名,比如

1
call_indirect "my_ruleset";

不过常量名的调用最好还是用call,效率会高很多。

additional information

We need to have two different statements, “call” and “call_indirect” because “call” already existed at the time “call_indirect” was added. We could not extend “call” to support expressions, as that would have broken existing configs. In that case call ruleset would have become invalid and call "ruleset" would have to be used instead. Thus we decided to add the additional “call_indirect” statement for this use case.

global() configuration object

允许设置全局变量,但每个变量只能设置一次,之后不能被reset。

参考 https://www.rsyslog.com/doc/master/rainerscript/global.html

The rsyslog include() object

include() 可以调用 配置片断,New in version 8.33.0.

How it Works

如果不熟悉include,可以认为是一种复制粘贴。rsyslog到到要include的对象,复制内容到指定位置,然后删除include文句

include时要注意位置,不同的位置可能会影响执行结果。

Parameters

参数名大小写不敏感,每个include中file和text只能选其一

file

Name of file to be included. May include wildcards, in which case all matching files are included (in order of file name sort order).

text

Text to be included. This is most useful when using backtick string constants.

mode

Affects how missing files are to be handled:

  • abort-if-missing, with rsyslog aborting when the file is not present
  • required (default), with rsyslog emitting an error message but otherwise continuing when the file is not present
  • optional, which means non-present files will be skipped without notice

Examples

Include a required file

1
include(file="/path/to/include.conf")

Note

Unless otherwise specified, files referenced by an include() object must be present, otherwise an error will be generated.

Include an optional file

The referenced file will be used if found, otherwise no errors or warnings will be generated regarding its absence.

1
2
3
4
include(
file="/path/to/include.conf"
mode="optional"
)

Include multiple files

1
include(file="/etc/rsyslog.d/*.conf")

Note

Unless otherwise specified, files referenced by an include() object must be present, otherwise an error will be generated.

Include an environment variable as configuration

1
include(text=`echo $ENV_VAR`)

Include a file specified via an environment variable

1
include(file=`echo $ENV_VAR`)

Note

Unless otherwise specified, files referenced by an include() object must be present, otherwise an error will be generated.

Include an optional file specified via an environment variable

1
2
3
4
include(
file=`echo $ENV_VAR`
mode="optional"
)
0%