# 数据解析转换 (Transformer)

> 采集任务中的数据加工过程，支持 SPL 模式对数据进行解析、处理、丰富化、实时聚合。

## 配置方式

在采集任务的 `readers` 后添加 `transformers` 列表：

```yaml
readers:
  - tailx:
      path: "/var/log/*.log"

transformers:
  - rex field="raw" "(?P<time>[^ ]+) (?P<level>[^ ]+) (?P<msg>.*)"
  - eval timestamp=strptime(time, "2006-01-02T15:04:05")
  - fields -time

# parallel: 0  # 计算并行度，建议与agent CPU核数一致
```

## 执行条件控制

所有算子支持 `when <eval表达式>` 条件控制，仅当表达式为 true 时执行：

```yaml
transformers:
  - eval c = 3 when a = 1
  - eval d = 4 when b = 1
```

**最佳实践**：多个算子共用条件时，先存到临时字段避免重复计算：

```yaml
transformers:
  - eval condition = match(ip, "127.0.0.1")
  - eval c = 3 when condition
  - eval d = 4 when condition
```

---

## Eval 转换器

`eval`：执行表达式，产生新字段（已存在则覆盖）。

**语法**：`eval <field>=<expression>[, <field>=<expression>]...`

**运算符**：逻辑 `and/or/not` | 四则 `+ - * / ()` | 比较 `< <= = > >=` | 取模 `%`

### 多值字段函数

| 函数 | 描述 | 示例 |
|------|------|------|
| `mvappend(X...)` | 合并值到多值字段 | `eval a = mvappend(1, "2", true)` |
| `mvcount(X)` | 多值字段值数量 | `eval a = mvcount(x)` |
| `mvdedup(X)` | 多值字段去重 | `eval a = mvdedup(x)` |
| `mvindex(X, start, end)` | 按索引取值 [start,end) | `eval a = mvindex(x, 1, 4)` |
| `mvjoin(X, str)` | 多值拼接为字符串 | `eval a = mvjoin(x, ",")` |
| `mvrange(start, end, step)` | 生成数字序列 | `eval a = mvrange(1, 6, 2)` |
| `mvzip(X, Y, str)` | 两个多值字段配对拼接 | `eval a = mvzip(names, ids, ":")` |
| `mvsort(X)` | 多值字典序排序 | `eval a = mvsort(x)` |
| `mvfind(X, REGEX)` | 正则筛选多值 | `eval a = mvfind(x, REGEX)` |
| `mvfilter(X, Y)` | 表达式筛选多值 | `eval a = mvfilter(x, x > 2)` |
| `mvmap(X, Y)` | 对多值每个值执行表达式 | `eval a = mvmap(x, x+2)` |

### 条件判断

| 函数 | 描述 | 示例 |
|------|------|------|
| `case(X, "Y"...)` | 多条件分支，返回第一个匹配 | `case(status==200,"OK", status==404,"Not found")` |
| `if(X, V1, V2)` | 三元判断 | `if(status==200, "OK", "Error")` |

### 比较函数

| 函数 | 描述 | 示例 |
|------|------|------|
| `match(X, Pattern)` | 正则匹配 | `match(f0, "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")` |
| `contains(X, STRING)` | 包含子串 | `contains(_raw, "aaa")` |
| `like(X, Like)` | SQL like 模式 | `like(f6, "%png")` |
| `in(X, V1, V2...)` | 值在集合中 | `in(f0, "2", "4", "5")` |

### JSON 操作

| 函数 | 描述 | 示例 |
|------|------|------|
| `jsonobject(k1,v1...)` | 创建 JSON 对象 | `jsonobject("k1","v1","k2","v2")` |
| `jsonget(X, k1,k2...)` | 获取 JSON 键值 | `jsonget(a, "k1", "k2")` |
| `jsonset(X, k1,v1...)` | 添加/修改 JSON 键值 | `jsonset(a, "k1", "v2")` |
| `jsonvalid(X)` | 校验 JSON 合法性 | `jsonvalid(a)` |
| `jsonkeys(X)` | 获取 JSON 所有键 | `jsonkeys(a)` |
| `jsonvalues(X)` | 获取 JSON 所有值 | `jsonvalues(a)` |
| `jsonpath(X, Y)` | JSONPath 提取 | `jsonpath(a, "$.k1")` |

### 转换函数

| 函数 | 描述 | 示例 |
|------|------|------|
| `printf(template, args...)` | 格式化字符串（Go Sprintf） | `printf("name: %s, %s", first, last)` |
| `tonumber(NUMSTR, BASE)` | 字符串转数字 | `tonumber("100", 2)` → 4 |
| `cast(value, type)` | 类型转换 (long/float/string/bool) | `cast("123.456", "float")` |

### 哈希函数

`md5(X)` | `sha1(X)` | `sha256(X)` | `sha512(X)`

### 类型判断

`isbool(X)` | `isint(X)` | `isnum(X)` | `isstr(X)` | `isnull(X)` | `isnotnull(X)` | `isexist(X)` | `isnotexist(X)` | `typeof(X)`

### 数学函数

`pi()` | `abs(X)` | `ceiling(X)` | `floor(X)` | `exp(X)` | `ln(X)` | `log(X,Y)` | `sqrt(X)` | `pow(X,Y)` | `round(X,N)` | `hypot(X,Y)`

三角函数：`sin/cos/tan/sinh/cosh/tanh/asin/acos/atan/asinh/acosh/atanh(X)`

### 统计函数

`avg(X...)` | `sum(X...)` | `min(X...)` | `max(X...)` | `random()`

### 字符串函数

| 函数 | 描述 | 示例 |
|------|------|------|
| `len(X)` | 字符串长度 | `len(_raw)` |
| `concat(X,Y,...)` | 字符串拼接 | `concat("host: ", host)` |
| `lower/upper(X)` | 大小写转换 | `lower(_raw)` |
| `trim/ltrim/rtrim(X,Y)` | 去除字符 | `trim(_raw, " ")` |
| `substr(X, from, length)` | 子串截取 | `substr(_raw, 1)` |
| `split(X, splitter)` | 分割为数组 | `split(_raw, ",")` |
| `urldecode(X)` | URL 解码 | `urldecode(_raw)` |
| `replace(X, origin, target)` | 文本替换（支持正则） | `replace(_raw, "xx", "yy")` |
| `rex(origin, pattern)` | 正则提取字段 | `rex(raw, "(?P<name>\\w+)")` |
| `base64(x, y)` | base64 编解码 | `base64(raw)` / `base64(raw,"decode")` |
| `index(x, y)` | 子串下标，不存在返回-1 | `index(raw, "test")` |
| `index_byte(x, y)` | 字符下标 | `index_byte(raw, 0)` |
| `decode(X, Y)` | 编码转 UTF-8 | `decode(data, "gbk")` |

**decode 支持编码**：utf8, latin-1, UTF-16LE/BE, ASCII, ISO-8859-*, windows-125*, KOI8-R/U, big5, gbk, gb18030, SJIS, EUC-JP, macintosh

### 时间函数

| 函数 | 描述 | 示例 |
|------|------|------|
| `now()` | 当前毫秒时间戳 | `now()` |
| `strftime(X, Y)` | 时间戳格式化（Go 风格） | `strftime(1650299985000, "2006-01-02 15:04:05")` |
| `strptime(X, Y)` | 字符串解析为时间戳 | `strptime("2022-04-20 15:55:09", "2006-01-02 15:04:05")` |
| `relative_time(X, Y)` | 时间偏移 | `relative_time(ts, "-M@d-2h")` |

### XML 提取

`xmlpath(X, Y)`：根据 XPath 从 XML 字符串 X 提取字段

---

## 字段处理算子

### Grok 字段解析

通过 Grok 模板从字符串提取字段，解析结果打平到根节点。

```
grok [field=<source_field>] [pattern="<pattern>"] [named_patterns="<names>"] [external="<path>"] [new_field="<field>"]
```

| 参数 | 说明 |
|------|------|
| `field` | 源字段名，默认 "raw" |
| `pattern` | 自定义 Grok 模板，支持多个 `pattern_1=...` |
| `named_patterns` | 内置模板名称，逗号分隔（如 `COMMONAPACHELOG`） |
| `external` | 自定义模板文件路径 |
| `new_field` | 结果导出到的字段名（默认打平到根） |

内置模板参考：https://github.com/vjeantet/grok/tree/master/patterns

### ToJSON JSON 序列化

将字段序列化为 JSON 字符串。

```
tojson [field="<field>" new_field="<new_field>"]
```

- `field`：被序列化字段，默认 "root"（整条数据）
- `new_field`：结果字段名，默认 "raw"

### ToMetric 转换指标

将任意数据转为 KetaDB 指标格式。

```
tometric metrics="<指标字段>" tags="<标签字段>" timestamp="<时间戳字段>"
```

- `metrics`：必选，保留的指标字段，逗号分隔
- `tags`：可选，保留的标签字段（默认保留所有字符串字段）
- `timestamp`：可选，时间戳字段

操作：指标字段放入 fields，时间戳放入 timestamp，丢弃非字符串非指标字段。

### ToTrace 转换链路

将数据转为链路追踪格式。

```
totrace trace_id="<field>" span_id="<field>" parent_span_id="<field>" [service_name="<field>"] [operation="<field>"] [start_time="<field>"] [duration="<field>"] [tags="<field>"] [logs="<field>"] [references="<field>"]
```

### Fields 保留/移除字段

```
fields <field1>, <field2>, ...    # 保留指定字段
fields -<field1>, -<field2>, ...  # 移除指定字段
```

### Rename 字段重命名

```
rename <old_name> as <new_name> [, <old_name> as <new_name>]...
```

### Replace 内容替换

```
replace [field=<field>] pattern="<regex>" target="<replacement>"
```

### Split 分割字符串

```
split field=<field> delimiter="<sep>" new_field=<new_field>
```

### Datamask 数据脱敏

```
datamask field=<field> [start=<n>] [end=<n>] [mask_char="*"]
```

### Unpivot 字段拆分

将单字段拆分为多行。

```
unpivot field=<field>
```

### Pivot 字段合并

将多行合并为单行多字段。

```
pivot field=<field>
```

### Mvexpand 多值字段展开

将多值字段展开为多行。

```
mvexpand <field>
```

### Jsonflatten 解析

将嵌套 JSON 打平为单层键值对。

```
jsonflatten [field=<field>] [prefix="<prefix>"] [separator="."]
```

### Xmlflatten 解析

将嵌套 XML 打平为单层键值对。

```
xmlflatten [field=<field>] [prefix="<prefix>"] [separator="."]
```

### Jsonpath 解析

通过 JSONPath 提取字段。

```
jsonpath field=<source> path="<jsonpath>" new_field=<target>
```

### Xmlpath 解析

通过 XPath 提取字段。

```
xmlpath field=<source> path="<xpath>" new_field=<target>
```

### JSON 解析

将 JSON 字符串解析为结构化字段。

```
json field=<field> [prefix="<prefix>"] [mode="object|array"] [keep_field=true|false]
```

- `mode`：`object` 打平字段，`array` 展开为多行
- `keep_field`：是否保留原始 JSON 字段

### CSV 解析

```
csv field=<field> [delimiter=","] [header_line=<n>] [skip_header=<n>]
```

### KV 键值对提取

```
kv [field=<field>] [pair_delim=" "] [kv_delim="="] [prefix="<prefix>"]
```

### Rex 正则提取

```
rex [field=<field>] "<regex_with_named_groups>"
```

使用命名捕获组 `(?P<name>pattern)` 提取字段。

### Syslog 解析

```
syslog [field=<field>] [new_field="<field>"]
```

### UserAgent 解析

```
useragent field=<field> [new_field="<field>"]
```

### Iplocation IP 解析

```
iplocation [field=<field>] [new_field="<field>"]
```

### PrometheusRemoteWrite 解析

```
prometheus_remote_write [field=<field>]
```

### 北冥事件数据解析

```
beiming_event [field=<field>]
```

### K8stag

从 K8s 标签提取字段。

```
k8stag [field=<field>]
```

---

## Stats 转换器

统计分析算子，对数据进行聚合计算。

### 聚合函数

| 函数 | 描述 |
|------|------|
| `count()` | 记录数 |
| `count(<field>)` | 非空记录数 |
| `count(distinct <field>)` | 去重计数 |
| `sum(<field>)` | 求和 |
| `avg(<field>)` | 平均值 |
| `min(<field>)` | 最小值 |
| `max(<field>)` | 最大值 |
| `median(<field>)` | 中位数 |
| `percentile(<field>, p)` | 百分位数 |
| `stddev(<field>)` | 标准差 |
| `variance(<field>)` | 方差 |
| `range(<field>)` | 最大值-最小值 |
| `first(<field>)` | 第一个值 |
| `last(<field>)` | 最后一个值 |
| `values(<field>)` | 所有值（多值字段） |
| `list(<field>)` | 所有值列表 |
| `earliest(<field>)` | 最早时间 |
| `latest(<field>)` | 最晚时间 |
| `dc(<field>)` | 近似去重计数 (HyperLogLog) |
| `estdc(<field>)` | 近似去重计数 |
| `estdc_error(<field>)` | 近似去重误差 |

### 语法

```
stats <agg_func>(<field>) [as <alias>] [, <agg_func>(<field>) [as <alias>]]... [by <field1>, <field2>...]
```

### 示例

```yaml
transformers:
  # 按 host 分组统计
  - stats count() as cnt, avg(cpu) as avg_cpu, max(mem) as max_mem by host
  
  # 全局统计
  - stats count(), sum(bytes) as total_bytes, dc(ip) as unique_ips
  
  # 多字段分组
  - stats count() by host, level
```

### 窗口函数 (stats window)

```
stats <agg_func>(<field>) [as <alias>]... window=<window_size> [by <field>...]
```

按时间窗口进行滚动聚合。

---

## 关联转换器

### lookup_ketadb KetaDB 仓库外部数据源查询

```
lookup_ketadb <repo> <local_field> as <remote_field> [output <output_fields>] [default <default_value>]
```

仅支持 search2 算子。

### lookup_sql 关系型数据库外部数据源查询

```
lookup_sql <connection> <query> <local_field> as <remote_field> [output <output_fields>]
```

### Userinfo 查找

```
userinfo field=<field> [new_field="<field>"]
```

### Where 条件筛选

```
where <eval_expression>
```

仅保留满足条件的记录。

---

## add_metadata 元数据添加

| 算子 | 描述 |
|------|------|
| `add_metadata_env` | 添加环境变量元数据 |
| `add_metadata_agent` | 添加 Agent 信息元数据 |
| `add_metadata_agent_tags` | 添加 Agent 标签元数据 |
| `add_metadata_agent biz_system` | 添加业务系统元数据 |
| `add_metadata_system` | 添加系统元数据 |
| `add_metadata_local_file` | 添加本地文件元数据 |
| `add_metadata_k8s` | 添加 K8s 元数据 |
