SPL概述
云日志服务提供SPL语句(Semi-structured Processing Language),对半结构化的日志数据做结构化处理,比如信息提取、字段操作、数据过滤等操作。SPL提供类Linux Shell脚本的管道级联功能,其中第一级管道前的表达式是数据源(可以是索引过滤条件或其他SPL语句的命名引用),后面的多级管道前后都是SPL命令表达式。
SPL语法说明
一个SPL语句由多个表达式组成,各表达式之间通过英文管道符 (|) 连接;一个SPL语句以英文分号 (;) 作为语句结束符。SPL语法结构如下:
语法
source-expr | cmd-expr | cmd-expr;参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
source-expr | 数据源。包括日志单元数据和SPL命名引用 | · *· $src |
| | 管道符:与Linux Shell中管道符作用类似,即管道前的指令的输出会作为管道后的指令的输入 | |
cmd-expr | 指令表达式,即SPL处理日志数据的各种表达式。 详情参考指令表达式语法。 | parse -type=csv __raw__ as time,ip,level,msg |
数据源
| 分类 | 场景 | 数据来源 | 说明 |
|---|---|---|---|
| 非命名引用 | 采集 | 采集器采集数据 | 使用星号(*)表示将采集器采集的全部原始数据作为输入 |
| 数据加工 | 日志单元数据 | 使用索引过滤结果作为输入。 · 目前索引过滤条件仅支持 * | |
| 命名引用 | SPL | 通过let指令定义的命名引用 | 前提:其他SPL表达式已通过let指令定义了命名引用; 通过 $+命名引用名称,即可解引用并将其当作数据源 |
指令表达式语法
语法
cmd -option=<option> -option ... <expression>, ... as <output>, ...参数说明
| 参数 | 说明 |
|---|---|
| cmd | 指令名称;已支持的指令请参考 SPL指令列表 |
| option | 指令执行参数。参数名以中划线(-)开头,参数顺序无要求支持如下2种参数类别: · 键值参数 -option=<option>,使用时以键值对的形式输入· 开关参数 -option,默认均为关闭状态,添加该参数即表示打开开关 |
| expression | 对数据源执行处理的表达式参数(列表);如有多个表达式则以逗号(,)分隔,参数顺序必须与指令的定义保持一致表达式类型: · 字段名:比如 "topic#1"· 字符串表达式:比如 '__tag__*'· 键值表达式:比如,对字段赋值 topic='process';计算SQL表达式并赋值给字段 host=regexp_extract("data#refer", '\w+.\w+')· SQL表达式:如 cast(status as integer)!=200 |
| output | 输出字段名(列表);如有多个字段则以逗号(,)分隔 |
数据类型
| 类别 | 类型名称 | 说明 |
|---|---|---|
| 基础数据类型 | boolean | 布尔类型, true或false |
| tinyint | 8位整数类型 | |
| smallint | 16位整数类型 | |
| integer | 32位整数类型 | |
| bigint | 64位整数类型 | |
| float | 32位浮点类型 | |
| double | 64位浮点类型 | |
| timestamp | UNIX时间戳类型 | |
| date | 日期数据类型,格式为YYYY-MM-DD | |
| datetime | 日期时间数据类型,格式为YYYY-MM-DD HH:MM:SS.SSS | |
| varchar | 可变长度字符数据类型 |
参数类型说明
| 参数类型 | 说明 |
|---|---|
| bool | 布尔类型参数;SPL指令中为选项开关参数,通常表现形式是选项存在为true,不存在为false |
| char | ASCII字符类型参数,需使用单引号('')包裹;比如','表示英文逗号,'\t'表示制表符 |
| integer | 整数类型参数 |
| string | 字符串类型参数,需使用单引号('')包裹;比如'this is an expression option' |
| Regexp | 正则表达式类型参数,需使用单引号('')包裹,比如 '\w+' |
| JSONPath | JSON路径类型参数,需使用单引号('')包裹,比如 '$.response.docs[0]' |
| Field | 字段名类型参数,比如* \| rename status="data#status":· 如果字段名以ASCII字母或下划线( _)开头,后续跟随ASCII字母、数字或下划线,则不需要用引号包裹;· 如果字段名中包含ASCII字母、数字、下划线以外的特殊字符,需使用双引号( "")或反引号(``)包裹; |
| Pattern | 字段名和通配符组合的字符串或字段名类型参数。通配符号*,表示匹配0个或多个任意字符。需使用单引号('')包裹,如fields -drop -wildcard '__tag__*' |
| SQLExpr | SQL表达式类型参数 |
| SPLExpr | SPL表达式类型参数 |
| NameRef | 命名数据的名称,包含ASCII字母、数字和下划线,并且以字母开头,区分大小写 |
SPL指令列表
| 指令类别 | 指令名称 | 说明 | 采集场景 | 加工场景 |
|---|---|---|---|---|
| 字段操作 | fields | 保留或排除指定模式相匹配的字段 | ✓ | ✓ |
| rename | 重命名指定字段,并保留其他所有字段原样 | ✓ | ✓ | |
| expand | 展开指定字段的第一层JSON对象,生成多条结果 | × | ✓ | |
| SQL计算 | eval | 通过SQL表达式计算结果产生新字段 | ✓ | ✓ |
| where | 根据SQL表达式过滤数据,保留满足SQL表达式的数据条目 | ✓ | ✓ | |
| 数据解析提取 | parse | 解析并提取指定字段中的信息;支持正则/CSV/JSON/键值对 格式 | ✓ | ✓ |
| 数据加工 | pack | 打包多个日志字段,并以JSON序列化输出到新字段 | × | ✓ |
| 其他 | let | 将本SPL语句结果定义为命名数据,以提供给其他SPL语句作为源来引用 | × | ✓ |
fields指令
用于保留或排除指定模式相匹配的字段。
语法
保留字段
fields -wildcard -mode='overwrite' <pattern>, ... , <field>, ... , <output>=<field>排除字段
fields -wildcard -drop <pattern>, ... , <field>, ...参数说明
保留字段
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| wildcard | bool | 否 | 是否开启通配模式。默认为字段精确匹配。若要开启通配模式,则需添加该参数 |
| mode | string | 否 | 重命名时,名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| pattern | Pattern | 否 | 需要保留的字段和通配符组合(将处理匹配到的所有字段) 使用此参数时,比如带上 wildcard参数 |
| field | Field | 否 | 需要保留的字段,或待重命名的原字段(即=右边)。对于重命名的原字段:1. 若该字段在输入数据中不存在,则不执行重命名。 2. 不支持同一字段被多次重命名。 |
| output | Field | 否 | 重命名后的新字段名称,不支持多次重命名至相同的目标字段 字段名冲突时,由 mode参数决定取值策略 |
排除字段
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| drop | bool | 否 | 是否为排除模式。此选项出现时,表示执行丢弃字段操作;默认此选项关闭,即执行保留字段操作 |
| wildcard | bool | 否 | 是否开启通配模式。默认为字段精确匹配。若要开启通配模式,则需添加该参数 |
| pattern | Pattern | 否 | 需要排除的字段和通配符组合(将处理匹配到的所有字段) 使用此参数时,比如带上 wildcard参数 |
| field | Field | 否 | 需要排除的字段 |
注:
保留字段时:
<pattern>、<field>、<output>=<field>三类参数至少有一个可在同一条指令中重命名字段;指令执行过程中,会先进行字段保留,再进行字段重命名。
排除字段时:
<pattern>、<field>此两类参数至少有一个排除字段时,不可在同一条指令中同时进行字段重命名;
时间字段(
__ts__)不可排除、重命名与覆盖。详情请参见时间字段
示例
示例1:保留特定字段
* | fields level, msg示例2:丢弃符合指定前缀的字段
* | fields -drop -wildcard '__tag__*'示例3:重命名字段
* | fields ip=__tag__hostip, msgrename指令
重命名指定字段,并原样保留其他字段。
语法
rename -mode='overwrite' <output>=<field>参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| mode | string | 否 | 重命名时,名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| field | Field | 否 | 待重命名的原字段。 1. 若该字段在输入数据中不存在,则不执行重命名。 2. 不支持同一字段被多次重命名。 |
| output | Field | 否 | 重命名后的新字段名称,不支持多次重命名至相同的目标字段 字段名冲突时,由 mode参数决定取值策略 |
注:时间字段(__ts__)不可重命名与覆盖。详情请参见时间字段。
示例
重命名指定字段
* | rename ip=__tag__hostip, pod=__tag__podnameexpand指令
展开指定字段的第一层JSON对象,生成多条结果。
语法
expand -mode='overwrite' -path=<path> -limit=<limit> -keep <field> as <output>参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| mode | string | 否 | 名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| path | JSONPath | 否 | 指定字段内容中的JSON路径,用于定位需要展开的内容位置。默认值为空,表示直接展开指定字段的完整内容 |
| limit | integer | 否 | 每条原始数据可展开的最大条目数,值为1至10之间的整数。默认值为10 |
| keep | bool | 否 | 展开后是否保留原字段。默认不保留,需要保留时,打开此开关 |
| field | Field | 否 | 需要展开的原字段名称,支持字段值的类型为varchar。如果指定字段不存在,则不执行展开操作 |
| output | Field | 否 | 展开的目标字段名称。如果不指定,则默认输出结果至输入字段。 针对原始内容的展开逻辑为: 1. JSON数组:根据数组的元素逐个展开。 2. JSON字典:根据字典键值对逐个展开。 3. 其他JSON类型:返回原值。 4. 非法JSON:返回null |
注:
时间字段(
__ts__)不可重命名与覆盖。详情请参见时间字段。展开结果的数据类型为varchar
示例
示例1:展开数组,输出多条结果数据
SPL语句
* | expand fruits输入数据
owner: 'Alice' fruits: '["apple", "pear", "banana"]'输出结果
# 数据1 owner: 'Alice' fruits: 'apple' # 数据2 owner: 'Alice' fruits: 'pear' # 数据3 owner: 'Alice' fruits: 'banana'示例2:按JSONPath展开指定内容,输出至新字段
SPL语句
* | expand -path='$.data' -keep http_body as rsp输入数据
http_body: '{"data": {"alice":1, "bob":0}}'输出结果
# 数据1 http_body: '{"data": {"alice":1, "bob":0}}' rsp: '{"alice":1}' # 数据2 http_body: '{"data": {"alice":1, "bob":0}}' rsp: '{"bob":0}'
eval指令
通过SQL表达式计算结果产生新字段。
语法
eval <output>=<sql-expr>, ...参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| output | Field | 是 | 添加的目标字段名称。不支持多个表达式结果输出至相同的目标字段 |
| sql-expr | SQLExpr | 是 | 数据处理表达式 |
示例
示例1:使用数值运算表达式,将计算结果赋值给新字段
* | eval result=a*b+c*d示例2:使用正则表达式提取值给新字段
* | eval tid=regexp_extract(msg, '\w+-\w+')where指令
根据SQL表达式过滤数据(只保留满足SQL表达式的数据)。
语法
where <sql-expr>参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| sql-expr | SQLExpr | 是 | SQL表达式(类似SQL中紧随where关键字的布尔表达式) |
示例
示例1:根据字段内容过滤数据
* | where level='ERROR'示例2:提取字段内的数字并转换类型后,比较数值并过滤数据
* | where cast(regexp_extract(http_code, '\d+') as integer)>=300parse指令
用指定解析器解析并提取指定字段中的信息。
语法
正则提取
parse -type=regexp -mode=<mode> -pattern=<pattern> <field> as <output>, ...CSV提取
parse -type=csv -mode=<mode> -delim=<delim> -quote=<quote> -strict <field> as <output>, ...JSON提取
parse -type=json -mode=<mode> -path=<path> -prefix=<prefix> <field>键值分隔符提取
parse -type=regexp -mode=<mode> -prefix=<prefix> -greedy -delim=<delim> -kv-sep=<kv-sep> <field>键值正则提取
parse -type=kv_reg -mode=<mode> -prefix=<prefix> -pattern=<pattern> <field>参数说明
正则提取
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 提取器类型,正则提取为regexp |
| mode | string | 否 | 名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| pattern | Regexp | 是 | 正则表达式 |
| field | Field | 是 | 需要提取信息的原字段名称。 输入数据中要包含该字段,类型为varchar,且其值为非null。否则,不执行提取操作。 |
| output | Field | 否 | 用于存储提取结果的字段名称 |
CSV提取
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 提取器类型,CSV提取为csv |
| mode | string | 否 | 名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| delim | string | 否 | CSV分隔符。 允许1至3个有效ASCII字符。可使用转义符表示特殊字符,比如 \t表示制表符默认值为英文逗号( ,) |
| quote | char | 否 | CSV引用符。 允许1个有效ASCII字符,比如双引号( ")、单引号(')等默认不使用引用符。 该参数仅在delim参数为单个字符时生效,且取值不能与delim相同。 |
| strict | bool | 否 | 当输入数据中,值的数量与output中指定字段不一致时,是否开启严格配对:· false:非严格配对,当值的数量多于字段时,多余值不输出;当字段数多于值时,多余字段输出为空字符串· true:严格配对,值与字段数量不一致时,不输出任何字段 |
| field | Field | 是 | 需要提取信息的原字段名称。 输入数据中要包含该字段,类型为varchar,且其值为非null。否则,不执行提取操作。 |
| output | Field | 是 | 用于存储提取结果的字段名称 |
JSON提取
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 提取器类型,JSON提取为json |
| mode | string | 否 | 名字冲突的结果取值策略,详情参考新旧值保留与覆盖 |
| path | JSONPath | 是 | 指定字段内容中的JSON路径,用于定位需要提取的内容位置。默认值为空,表示直接展开指定字段的完整内容 |
| prefix | string | 否 | JSON结构展开的结果字段前缀,默认为空字符串 |
| field | Field | 是 | 需要提取信息的原字段名称。 输入数据中要包含该字段,类型为varchar,且其值为非null,且为合法JSON字符串。否则,不执行提取操作。 |
分隔符键值提取
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 提取器类型,键值分隔符提取为kv |
| mode | string | 否 | 如果提取目标字段已存在于原数据中,可选择数据覆盖模式,默认值为overwrite。 详情参考字段提取与覆盖模式 |
| prefix | string | 否 | 提取结果输出字段名前缀,默认为空字符串。 |
| greedy | bool | 否 | 是否开启贪婪匹配字段值:false:遇到delim即停止匹配字段值true:完整匹配下一个键值对前的内容作为字段值 |
| delim | string | 是 | 多个键值对之间的分隔符。允许1至5个有效ASCII字符。 不支持指定kv-sep的子字符串。 |
| kv-sep | string | 是 | 键值对内部,键与值之间的分隔符。允许1至5个有效ASCII字符。 不支持指定delim的子字符串。 |
| field | Field | 是 | 需要提取信息的原字段名称。 1. 如果输入数据中不包含该字段,或类型不为varchar,或值为null,则不执行提取操作。 2. 如果数据内容未匹配到任何键值对,则不对该条目做任何处理 |
正则键值提取
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 提取器类型,正则分隔符提取为kv_reg |
| mode | string | 否 | 如果提取目标字段已存在于原数据中,可选择数据覆盖模式,默认值为overwrite。 详情参考字段提取与覆盖模式 |
| prefix | string | 否 | 提取结果输出字段名前缀,默认为空字符串。 |
| pattern | Regexp | 是 | 包含2个正则捕获组的正则表达式,第1个捕获组提取字段名,第2个捕获组提取字段值。 |
| field | Field | 是 | 需要提取信息的原字段名称。 输入数据中要包含该字段,类型为varchar,且其值为非null。否则,不执行提取操作。 |
示例
示例1:正则分组提取
SPL语句
* | parse -type=regexp -pattern='(\S+)\s+\w+\s+(\S+)' msg as ip, uri输入数据
msg: '192.168.0.1 POST /v1/content/fetch 32 0.128'输出结果
msg: '192.168.0.1 POST /v1/content/fetch 32 0.128' ip: '192.168.0.1' uri: '/v1/content/fetch'示例2:CSV提取
SPL语句
* | parse -type=csv msg as a,b,c输入数据
msg: 'alice,bob,carol'输出结果
msg: 'alice,bob,carol' a: 'alice' b: 'bob' c: 'carol'示例3:JSON提取
SPL语句
* | parse -type=json -path='$.data' msg输入数据
msg: '{"data": {"alice":1, "bob":0}}'输出结果
msg: '{"data": {"alice":1, "bob":0}}' alice: 1 bob: 0示例4:分隔符键值提取
SPL语句
* | parse -type=kv -delim='&' -kv-sep='=' msg输入数据
msg: 'thread=23921715548&rule=cable_1&dancer=alice'输出结果
msg: 'thread=23921715548&rule=cable_1&dancer=alice' thread: '23921715548' rule: 'cable_1' dancer: 'alice'
pack指令
打包多个日志字段,并以JSON序列化输出到新字段
语法
pack -include=<include> -exclude=<exclude> -keep -ltrim as <output>参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| include | Regexp | 否 | 白名单配置,符合正则表达式的字段会被打包。默认为".*" ,表示全部匹配 |
| exclude | Regexp | 否 | 黑名单配置(优先于白名单),符合正则表达式的字段不会被打包。默认为空,表示不进行匹配判断 |
| keep | bool | 否 | 打包数据后是否保留被打包的原数据,默认为false:true:输出结果中保留被打包的原数据false:输出结果中不保留被打包的原数据 |
| ltrim | string | 否 | 在输出字段名称中,去掉指定前缀 |
| output | Field | 是 | 指定打包后输出的字段名称。字段值格式为JSON字符串 |
示例
示例1:打包k1和k2字段到msg字段,不删除被打包的原始字段
SPL语句
* | pack -keep -include='^\w+$' as msg输入数据
key#val:'test' k1: 'v1' k2: 123输出结果
key#val:'test' k1: 'v1' k2: 123 msg: '{"k1":"v1", "k2":123}'
let指令
定义数据引用,作为后续SPL表达式的数据源。
没有被引用到的命名引用定义将会被忽略
语法
定义数据引用
let <name>=<spl-expr>解引用
$<name>; -- 直接输出
$<name> | ...; -- 作为其他SPL语句的数据源参数说明
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| name | NameRef | 是 | 数据引用的名称,包含ASCII字母、数字和下划线,并且以字母开头,区分大小写 |
| spl-expr | SPLExpr | 是 | 被命名引用的SPL表达式 |
示例
将访问日志基于状态码进行过滤和分类处理,然后将其输出
SPL语句
-- 转换字段格式后,定义数据引用,名称为src
let src=* | eval status=cast(status as integer);
-- 通过$+引用名称,解引用,并分别对不同范围的状态码增加错误详情并输出
$src | where status>=200 and status<300 | eval msg='success';
$src | where status>=400 and status<500 | eval msg='request error';
$src | where status>=500 | eval msg='server error';输入数据
# 数据1
status: '200'
data: 'test ok'
# 数据2
status: '404'
data: 'page not found'
# 数据3
status: '504'
data: 'gateway timeout'输出结果
# 结果1
status: 200
data: 'test ok'
msg: 'success'
# 结果2
status: 404
data: 'page not found'
msg: 'request error'
# 结果3
status: 504
data: 'gateway timeout'
msg: 'server error'特殊字段处理
时间字段
在SPL执行过程中,云日志服务的日志时间字段(__ts__)类型始终保持为datetime。
若要更新数据时间,需使用eval指令操作,且确保新值类型为datetime。
其他指令均不可操作时间字段,其行为如下:
fields、rename:默认保留时间字段,不可将其重命名,也不可将其覆盖。parse:如果提取结果包含时间字段,则将其忽略。
字段提取与覆盖模式
在SPL指令执行提取过程中,提取模式mode参数的取值说明如下
| 参数值 | 说明 |
|---|---|
preserve | 提取出的字段名与现有字段重名时,保留现有字段 |
overwrite | 覆盖目标字段 |
新旧值保留与覆盖
在SPL指令执行过程中,其输出的目标字段与输入数据中已有字段重名时,该字段的取值策略如下
新旧值类型不一致
直接保留输入字段原始值
示例
SPL语句
* | eval tmp=regexp_extract(msg, '\w+-\w+') -- 表达式计算结果为varchar类型,赋值给tmp
| rename tid=tmp -- tid旧值integer类型,tmp为varchar类型;tid保留旧值输入数据
msg: '127.0.0.1 ed06b28a-9de1006f INFO unknown method, discard request'
tid: 3817568输出结果
msg: '127.0.0.1 ed06b28a-9de1006f INFO unknown method, discard request'
tmp: 'ed06b28a-9de1006f'
tid: 3817568新旧值类型一致
由指令中指定的mode参数确定。如果指令没有定义mode参数,则其默认值为overwrite
| 模式 | 说明 |
|---|---|
overwrite | 使用新值覆盖旧值作为字段值 |
preserve | 保留旧值作为字段值,舍弃新值 |
示例
SPL语句
* | eval tmp=regexp_extract(msg, '\w+-\w+') -- 表达式计算结果为varchar类型,赋值给tmp
| rename -mode='overwrite' code=tmp -- code旧值varchar类型,tmp为varchar类型;tid被新值覆盖输入数据
msg: '127.0.0.1 ed06b28a-9de1006f INFO unknown method, discard request'
code: '405 Method Not Allowed'输出结果
msg: '127.0.0.1 ed06b28a-9de1006f INFO unknown method, discard request'
code: 'ed06b28a-9de1006f'