Linux之rsyslog(6)RainerScript
摘要:RainerScript基础语法与功能 RainerScript是rsyslog的核心配置语言,专用于处理网络事件和日志过滤。它支持复杂表达式(含算术、逻辑、字符串运算符),并分为内置函数(如cnum()、dyn_inc())和模块函数(需加载模块,如fmhttp)。控制结构包括if-else条件分支和foreach循环(仅遍历JSON数据),语法类似主流编程语言。无类型设计需注意隐式转换,
Linux之rsyslog(6)RainerScript
Author:Once Day Date:2025年11月12日
全系列文章请查看专栏: Linux Shell基础_Once_day的博客-CSDN博客。
漫漫长路,有人对你微笑过嘛…
参考文档:
文章目录
1. 介绍
RainerScript 是专为处理网络事件和配置事件处理器设计的脚本语言,也是 rsyslog 的主要配置语言。注意,该语言不可缩写为 rscript(此名称为他人商标)。
版本支持历程:
- rsyslog 3.12.0 版本起提供有限支持(仅支持表达式功能)。
- v5 版本新增对 “if … then” 语句的支持。
- v6 版本实现首个完整功能版本。
1.1 数据类型
RainerScript 是一种无类型语言,但这并不意味着无需关注类型相关逻辑。
例如,表达式 "A" + "B" 无法返回有效结果 —— 字母不能直接用 “+” 运算(字符串拼接需使用连接运算符 &)。不过,当场景需要时,脚本解释器会自动完成类型转换。
1.2 表达式(Expressions)
RainerScript 支持任意复杂的表达式,包含所有常用运算符,运算优先级如下(优先级越高越先执行,例如乘法优先于加法):
- 括号内表达式(
()包裹)。 not(逻辑非)、一元减号(-,负号)。*(乘法)、/(除法)、%(取模,同 C 语言)。+(加法)、-(减法)、&(字符串拼接)。==(等于)、!=(不等于)、<>(不等于,与!=功能一致)、<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、contains(字符串包含)、startswith(字符串以… 开头)、endswith(字符串以… 结尾)。and(逻辑与)。or(逻辑或)。
关键注意事项:
- 字符串专用运算符:
contains、startswith、endswith仅适用于字符串类型比较。 not运算符优先级较高:not a == b会先计算not a,再将结果与b比较,并非 “a不等于b”。- 推荐写法:判断 “
a不等于b” 直接用a != b或a <> b(二者完全等价);需组合复杂逻辑时,务必用括号明确优先级(如not (a == b and c > d))。
1.3 函数(Functions)
RainerScript 函数分为两类:内置函数(built-ins)和模块函数(modules),核心差异在于是否需要手动加载。
内置函数(Built-in Functions):无需额外配置,可直接在配置中调用,始终自动加载。
模块函数(Module Functions):使用前必须先加载对应的模块,配置语法如下:
module(load="<模块名称>")
关键规则:
- 函数名冲突处理:多个函数同名时,优先使用先加载模块的函数,会生成错误提示但不终止配置运行。
- 内置函数优先级:内置函数始终比所有模块先加载,无法通过模块函数覆盖内置函数(名称已被占用)。
- 模块命名规范:函数模块的名称以
fm开头(如fmjson)。
RainerScript 函数分为内置函数(直接使用)和模块函数(需加载对应模块),以下是核心功能说明:
(1)内置函数(Built-in Functions),无需加载模块,可直接调用:
- cnum():将值转换为数值类型。
- cstr():将值转换为字符串类型。
- dyn_inc():动态递增计数器(支持自定义名称和初始值)。
- exec_template():执行指定模板并返回结果。
- exists():检查属性、变量或函数是否存在,返回布尔值。
- field():按分隔符提取字符串中的字段(类似属性替换器的字段提取)。
- format_time():将时间值格式化为指定格式(支持 RFC3339、MySQL 等格式)。
- get_property():获取指定消息属性或系统属性的值。
- getenv():获取系统环境变量的值。
- int2hex():将整数转换为十六进制字符串。
- num2ipv4() / ipv42num():IPV4 地址与数值的双向转换(如
192.168.1.1↔数值)。 - is_time():检查输入值是否为有效时间格式,返回布尔值。
- lookup():在预设的键值对集合中查找值(常用于映射表查询)。
- parse_json():解析 JSON 字符串,将其转换为 rsyslog 变量树(如
$!json!field)。 - parse_time():解析自定义格式的时间字符串,返回时间戳。
- percentile_observe():记录数值用于百分位计算(如统计延迟的 95 百分位)。
- previous_action_suspended():检查前一个动作是否被暂停,返回布尔值。
- prifilt():按 syslog 优先级(PRI)过滤消息,返回匹配结果。
- random():生成随机数(支持指定范围)。
- re_extract() / re_extract_i():正则提取字符串内容,
_i后缀表示不区分大小写。 - re_match() / re_match_i():正则匹配字符串,返回布尔值,
_i后缀表示不区分大小写。 - replace():替换字符串中的指定内容(支持普通字符串或正则)。
- script_error():触发脚本错误,可自定义错误信息。
- strlen():返回字符串的长度。
- substring():截取字符串的子串(指定起始和结束位置)。
- tolower() / toupper():将字符串转换为小写 / 大写。
- ltrim() / rtrim():去除字符串左侧 / 右侧的空白字符。
- wrap():按指定长度换行字符串(用于格式化输出)。
(2)模块函数(Module Functions),需先加载对应模块(模块名以fm开头)才能使用:
- Faup:解析 URL、日志中的网络地址等,提取协议、域名、端口等字段(模块:
fmfaup)。 - HashXX:对输入值进行 HashXX 哈希计算,返回哈希结果(模块:
fmhashxx)。 - HashXXmod:对输入值进行 HashXX 哈希计算并取模,返回指定范围内的结果(模块:
fmhashxx)。 - HTTP-Request:发送 HTTP 请求(GET/POST 等),支持传递参数和设置头信息(模块:
fmhttp)。 - Unflatten:将扁平的键值对(如
event.dataset.name)转换为嵌套变量树(模块:fmjson或fmstruct)。
1.4 控制结构(Control Structures)
RainerScript 的控制结构语义与 C、Java、JavaScript、Ruby、Bash 等主流语言相似,以下通过示例说明其用法。
(1)if 语句,用于条件判断,满足条件时执行代码块(可嵌套)。
if ($msg contains "important") then {
# 嵌套 if:若 $.foo 非空,则拼接 $.bar 和 $.baz 到 $.foo
if ( $.foo != "" ) then set $.foo = $.bar & $.baz;
# 满足条件时写入指定日志文件
action(type="omfile" file="/var/log/important.log" template="outfmt")
}
(2)if/else-if/else 语句,多条件分支判断,按顺序匹配首个满足的条件并执行对应代码块。
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")
}
(3)foreach 语句,仅用于遍历 JSON 结构(数组或对象),需先通过 mmjsonparse 等模块解析 JSON 数据。
- 遍历 JSON 数组:按顺序获取元素。
- 遍历 JSON 对象:随机顺序获取键值对(以
{"key": "键名", "value": "值"}形式)。
示例 1:遍历 JSON 数组,假设 $.collection 是解析后的 JSON 数组 [1, "2", {"a": "b"}, 4]:
foreach ($.i in $.collection) do {
# 循环中 $.i 依次为 1、"2"、{"a": "b"}、4
action(type="omfile" file="/var/log/array.log" template="json_elem")
}
示例 2:遍历 JSON 对象,假设 $.collection 是解析后的 JSON 对象 {"a": "b", "c": [1,2,3]}:
foreach ($.i in $.collection) do {
# 循环中 $.i 为 {"key": "a", "value": "b"}、{"key": "c", "value": [1,2,3]}(顺序不定)
set $!key = $.i!key; # 获取键名(如 "a"、"c")
set $!val = $.i!value; # 获取值(如 "b"、[1,2,3])
action(type="omfile" file="/var/log/object.log" template="json_kv")
}
示例 3:嵌套 foreach:
foreach ($.quux in $!foo) do { # 遍历外层 JSON 结构
action(type="omfile" file="./rsyslog.out.log" template="quux")
foreach ($.corge in $.quux!bar) do { # 遍历内层 JSON 结构
reset $.grault = $.corge; # 重置变量并赋值
action(type="omfile" file="./rsyslog.out.log" template="grault")
# 拼接字符串
if ($.garply != "") then set $.garply = $.garply & ", ";
reset $.garply = $.garply & $.grault!baz;
}
}
注意事项:
-
限制:仅支持 JSON 解析后的数组 / 对象,不能遍历手动赋值的数组(如
set $.noarr = ["192.168.1.1"]无法遍历)。 -
异步动作:若循环体内使用异步动作(如队列),需设置
action.copyMsg="on"以避免消息被后续迭代修改:foreach ($.quux in $!foo) do { action(type="omfile" file="./out.log" template="quux" queue.type="linkedlist" action.copyMsg="on") }
(4)call 语句,用于调用自定义的规则集(ruleset),详情参考 rsyslog 官方文档中 “call 语句” 部分。
(5)continue 语句,空操作(NOP),通常用于 if 结构的 then 分支中占位。
1.5 配置对象(Configuration objects)
配置对象的参数不区分大小写。
通用参数(Common Parameters),config.enabled,8.33.0 版本新增,所有配置对象均支持此参数。用于在自动生成的配置中启用或禁用特定配置结构:
- 设为
on或不指定时,配置生效; - 设为其他值时,配置被忽略。
可结合反引号(`)功能,通过文件或环境变量动态控制配置片段的启用状态。
示例:通过环境变量LOAD_IMPTCP条件加载模块:
module(load="imptcp"
config.enabled=`echo $LOAD_IMPTCP`) # 变量为off时不加载模块
配置对象类型(Objects):
- action(),描述 rsyslog 需执行的动作(如写入文件、转发消息等),是定义处理行为的核心对象。
- global(),设置全局配置参数(如默认模板、队列模式等),详情参考 rsyslog 全局配置对象文档。
- input(),定义输入源(如 TCP/UDP 端口、文件监控等),用于收集待处理的消息。
- module(),加载插件模块(如解析器、输出模块等),扩展 rsyslog 功能。
- parser(),定义自定义解析器,用于解析特定格式的消息(如非标准日志格式)。
- timezone(),配置时区设置,影响日志时间戳的解析和显示。
- include(),引入外部配置片段,便于拆分和管理复杂配置。
1.6 Rsyslog 参数字符串常量(Rsyslog Parameter String Constants)
字符串常量的值在 rsyslog 进程启动时解析,且在进程生命周期内保持不变
用途:字符串常量用于比较操作、配置参数值、函数参数等场景。
转义规则:字符串常量中,特殊字符通过前缀反斜杠(\)转义,类似 C 或 PHP 语法。若不确定如何正确转义,可使用 RainerScript 字符串转义在线工具 验证。
类型:rsyslog 提供多种字符串常量类型,借鉴了常见的 shell 语义:
(1)单引号(Single quotes):
值保持原样,仅处理转义序列(如 \' 表示单引号本身)。示例:'Hello \'World\'' 解析为 Hello 'World'。
(2)双引号(Double quotes):
与单引号功能等效,但美元符号($)必须转义为 \$。若 $ 未转义,会触发语法错误,导致 rsyslog 启动失败。示例:"Price: \$100" 解析为 Price: $100。
(3)反引号(Backticks)
版本支持:8.33.0 版本初步支持(有限子集);8.37.0 版本支持多个环境变量扩展及常量文本拼接;8.2508.0 版本支持花括号风格变量(${VAR})、无括号变量的自动终止(遇到非 [A-Za-z0-9_] 字符时),以及变量与静态文本相邻的场景(如键值对)。
功能:实现 shell 类似行为的安全子集,仅支持以下两种形式:
echo ...:解析包含环境变量扩展的简单文本。cat <filename>:包含单个文件的内容,文件不可读时返回空字符串。
其他构造会导致解析错误,且 echo/cat 与后续参数之间必须有且仅有一个空格。
环境变量扩展规则:
- 支持
$VAR和${VAR}两种格式。 - 无括号
$VAR:变量名在遇到首个非[A-Za-z0-9_]字符时终止,该字符原样输出(如$VAR!中!保留)。 - 有括号
${VAR}:扩展在匹配的}处终止,自然支持相邻静态文本(如prefix${VAR}suffix)。 - 未知变量扩展为空字符串(不报错)。
- 不支持命令替换及其他 shell 特性(如
$(pwd))。
1.7 示例
简单环境变量扩展(反引号),已知环境变量 SOMEPATH=/var/log/custompath,通过 echo 拼接路径:
# 配置参数中扩展变量,最终值为 /var/log/custompath/myfile
param = `echo $SOMEPATH/myfile`
花括号变量与相邻文本(反引号),已知环境变量 HOSTNAME=myhost,支持无括号变量自动终止(! 为非变量字符,原样保留):
# 最终值为 Log-myhost!
title = `echo Log-${HOSTNAME}!`
键值对配置(模块参数常用,反引号),已知环境变量 KAFKA_PASSWORD=supersecret(要求 rsyslog ≥ 8.2508.0),用于 Kafka 认证配置:
action(
type="omkafka"
broker=["kafka.example.com:9093"]
confParam=[
"security.protocol=SASL_SSL",
"sasl.mechanism=SCRAM-SHA-512",
"sasl.username=myuser",
`echo sasl.password=${KAFKA_PASSWORD}` # 扩展为 sasl.password=supersecret
]
)
旧版本兼容方案(rsyslog < 8.2508.0),先在外部脚本中拼接完整键值对,再传入 rsyslog:
# Bash/初始化脚本中预拼接
export KAFKA_PASSWORD='supersecret'
export SASL_PWDPARAM="sasl.password=${KAFKA_PASSWORD}"
# rsyslog 配置中直接扩展预拼接变量
action(
type="omkafka"
broker=["kafka.example.com:9093"]
confParam=[
"security.protocol=SASL_SSL",
"sasl.mechanism=SCRAM-SHA-512",
"sasl.username=myuser",
`echo $SASL_PWDPARAM` # 扩展为 sasl.password=supersecret
]
)
基于环境变量的配置对象条件启用(反引号 + config.enabled),通过环境变量 LOAD_IMTCP 控制 imtcp 模块和 TCP 输入的启用状态,仅当 LOAD_IMTCP=on 时生效。
步骤 1:设置环境变量(systemd 系统),创建 systemd 扩展配置文件,注入环境变量:
sudo mkdir -p /etc/systemd/system/rsyslog.service.d
sudo tee /etc/systemd/system/rsyslog.service.d/10-imtcp.conf >/dev/null <<'EOF'
[Service]
Environment=LOAD_IMTCP=on # 启用模块,设为 off 则禁用
EOF
# 重载配置并重启 rsyslog
sudo systemctl daemon-reload
sudo systemctl restart rsyslog
步骤 2:RainerScript 配置:
# 条件加载 imtcp 模块(LOAD_IMTCP 非 on 则不加载)
module(
load="imtcp"
config.enabled=`echo ${LOAD_IMTCP}`
)
# 条件启用 TCP 输入(端口 514,关联规则集 in_tcp)
input(
type="imtcp"
port="514"
ruleset="in_tcp"
config.enabled=`echo ${LOAD_IMTCP}`
)
# 规则集:接收 TCP 日志并写入文件
ruleset(name="in_tcp") {
action(type="omfile" file="/var/log/remote/tcp.log")
}
容器化部署场景,直接通过环境变量传递开关,无需修改配置文件:
docker run --rm \
-e LOAD_IMTCP=on \ # 启用 TCP 输入
-v /path/to/rsyslog.conf:/etc/rsyslog.conf:ro \
my-rsyslog-image
引入文件内容(反引号 + cat),读取文件中的令牌(如证书、密钥)作为配置参数:
# 读取 /etc/rsyslog.d/token.txt 的内容作为 token 值,文件不可读则返回空字符串
token = `cat /etc/rsyslog.d/token.txt`
1.8 变量属性类型
rsyslog 中的变量(属性)主要分为两类:消息 JSON 属性和本地变量,均支持通过 set/unset/reset 语句操作(仅这两类可修改,其他消息属性不可改)。
- 消息 JSON 属性(Message JSON Properties):
- 前缀标识:
$!(感叹号为根节点),路径分隔符统一使用!。 - 作用域:与当前消息绑定,属于消息的 JSON 结构部分(如 CEE/Lumberjack 格式消息)。
- 示例:
$!event!log!message(三级嵌套的消息 JSON 属性)。
- 本地变量(Local Variables):
- 前缀标识:
$.(点号为根节点),路径分隔符统一使用!。 - 作用域:仅当前消息上下文,不属于消息属性(不会包含在
$!JSON 结构中)。 - 示例:
$.temp!count(二级嵌套的本地变量)。
注意:所有变量操作语句末尾必须加英文分号(;),否则会触发配置加载语法错误。
set 语句:设置或合并变量,根据变量现有值的类型,行为不同:
-
现有值和新值均为对象:合并新值到根节点(非指定键下合并)。
set $.x!one = "val_1"; # $. = { "x": { "one": "val_1" } } set $.z!var = $.y; # 新值 $.y 是对象,合并后 $. 根节点新增 "two": "val_2" -
现有值是对象、新值非对象(字符串 / 数字等):忽略新值。
set $.x!one = "val_1"; # $.x 是对象 set $.x = "quux"; # 新值是字符串,被忽略,$.x 仍为 { "one": "val_1" } -
现有值非对象:覆盖原有值。
set $.x!val = "val_1"; # $.x!val 是字符串 set $.x!val = "quux"; # 覆盖为 "quux",结果 $.x = { "val": "quux" }
unset 语句:删除变量键,移除指定路径的变量键,父级结构保留为空对象。
set $.x!val = "val_1"; # $. = { "x": { "val": "val_1" } }
unset $.x!val; # 删除 "val" 键,结果 $. = { "x": { } }
reset 语句:强制覆盖变量,无论变量原有类型或是否存在,均强制设置为新值。
set $.z!var = $.x; # $.z!var = { "one": "val_1" }
reset $.z!var = $.y; # 强制覆盖,$.z!var = { "two": "val_2" }
reset $.x = "quux"; # 强制覆盖对象 $.x 为字符串,结果 $.x = "quux"
1.9 “call” 语句
“call” 语句用于关联多个规则集(ruleset),模仿主流编程语言的 “调用” 语法,可将规则集视为子程序(本质就是子程序)。
支持调用任意类型的规则集,执行方式由规则集的队列配置决定:
- 规则集已分配队列:消息投递至该队列异步处理,调用语句立即返回。
- 无队列规则集:同步执行规则集,执行完成后才返回原流程。
替代废弃的 omruleset 模块,功能一致但效率更高:omruleset 需复制消息(占用内存、增加分配 / 释放开销),而同步调用的 call 语句无额外开销。
关键注意事项:
- “stop” 语句的影响:异步执行时,规则集中的
stop语句不会影响原始消息的后续处理;同步执行时则会终止原始消息流程。 - 兼容性:8.2110.0 版本前存在 bug—— 规则集显式设置
queue="direct"会被当作真实队列处理,可能导致异常行为;8.2110.0 及以上版本已修复,行为一致。若需兼容旧版本或遇到异常,可给规则集添加小型数组队列。
call rulesetname
rulesetname:需在配置中其他位置已定义的规则集名称。- 执行方式(同步 / 异步)由规则集自身参数决定,
call语句无法覆盖。
# 定义被调用的规则集(无队列,同步执行)
ruleset(name="process_debug") {
if ($syslogseverity-text == "debug") then {
action(type="omfile" file="/var/log/debug.log")
stop # 同步执行时,终止当前消息后续处理
}
}
# 主规则集:调用 process_debug 规则集
ruleset(name="main") {
call process_debug # 同步执行,处理完 debug 日志后返回
action(type="omfile" file="/var/log/all.log") # debug 消息不会执行此动作(因 stop)
}
1.10 “call_indirect” 语句
“call_indirect” 语句与 “call” 语句功能等效,核心差异是被调用的规则集名称并非固定常量,而是可在运行时计算的表达式。
核心特性:
- 规则集名称动态计算:通过表达式(如消息变量、字符串拼接)生成规则集名称,灵活适配动态场景。
- 未找到规则集的处理:若表达式计算出的规则集名称不存在,会输出错误日志并忽略该语句,后续执行不受影响。
- 语法要求:语句末尾必须加英文分号(
;),否则会触发配置加载语法错误。
call_indirect expression;
expression:任意有效的 RainerScript 表达式(如变量、字符串拼接等),详情参考表达式文档。
(1)基于消息变量调用规则集:假设规则集名称与 syslog 标签(syslogtag)一致,直接通过变量动态调用:
# 运行时根据 $syslogtag 的值调用对应规则集(如 $syslogtag=sshd 则调用 sshd 规则集)
call_indirect $syslogtag;
(2)带前缀的规则集调用(安全防护):为避免恶意注入无效标签,给规则集名称添加唯一前缀(如 changeme-),通过字符串拼接生成名称:
# 规则集名称格式为 "changeme-+syslogtag"(如 changeme-sshd),降低注入风险
call_indirect "changeme-" & $syslogtag;
(3)常量名称调用(不推荐):虽支持通过表达式传递常量名称,但性能不及 call 语句,建议优先使用 call:
# 等效于 call my_ruleset; 但性能更差,不推荐
call_indirect "my_ruleset";
之所以区分 “call” 和 “call_indirect” 两个语句,是因为 “call” 语句早于 “call_indirect” 存在。若扩展 “call” 支持表达式,会破坏现有配置(如 call ruleset 需改为 call "ruleset"),因此新增 “call_indirect” 专门处理动态规则集调用场景。
更多推荐



所有评论(0)