# Observability 过滤规则使用说明

本文说明 `pi_logger::observability` 中 local、remote 和 trace 过滤规则的配置方式、
执行顺序、边界行为及推荐用法。

## 1. 规则结构

每个过滤作用域都使用相同的规则模型：

```text
default_level + overrides
```

基础结构：

```toml
[log.filter]
default_level = "info"

[[log.filter.overrides]]
name = "decoder_debug"
enabled = true
level = "debug"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::decoder" },
]
field_rules = [
    { field = "id", op = "eq", value = 100 },
]
```

- `default_level`：没有命中任何 override 时使用的等级。
- `overrides`：针对部分 target 或调用链覆盖默认等级。
- `fuzzy_rules`：选择 override 适用的 target 或 span。
- `field_rules`：对已经选中的 override 继续执行 event 字段过滤。

## 2. 完整执行流程

一条日志事件按以下顺序判断：

```text
1. 按 priority 从高到低检查已启用的 overrides。
2. 使用 fuzzy_rules 匹配 event target 或当前调用链中的 span 名称。
3. 选择第一条 fuzzy_rules 命中的 override。
4. 没有 override 命中时，使用 default_level 判断日志等级。
5. 有 override 命中时，使用该 override.level 判断日志等级。
6. 等级通过后，检查该 override 的全部 field_rules。
7. 等级与字段规则全部通过，事件才会输出。
```

必须注意：

- override 的选择只看 `fuzzy_rules` 和 `priority`，不看 `field_rules`。
- 最高优先级 override 一旦被选中，不会继续尝试较低优先级 override。
- 已选 override 的字段不匹配时，不会回退到 `default_level`。
- `field_rules` 只影响所属 override，不影响未命中该 override 的其他模块。

## 3. 默认等级 default_level

```toml
[log.filter]
default_level = "info"
```

支持的等级：

| 配置等级 | 放行的日志 |
|---|---|
| `trace` | trace、debug、info、warn、error |
| `debug` | debug、info、warn、error |
| `info` | info、warn、error |
| `warn` | warn、error |
| `error` | error |
| `off` | 全部关闭 |

例如：

```toml
[log.filter]
default_level = "info"
```

```rust
tracing::debug!(target: "my_app::user", "不会输出");
tracing::info!(target: "my_app::user", "会输出");
tracing::error!(target: "my_app::user", "会输出");
```

生产环境通常使用 `info` 作为默认等级，出现问题时再通过 override 临时提升部分模块。

## 4. Override 覆盖规则

Override 用于为一组 target 或调用链设置独立等级和可选字段过滤。

```toml
[[log.filter.overrides]]
name = "rpc_debug"
enabled = true
level = "debug"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::rpc" },
]
```

字段含义：

| 字段 | 含义 |
|---|---|
| `name` | 规则唯一名称，用于诊断、reload 和动态开关，不参与匹配 |
| `enabled` | 是否启用，缺省为 `true` |
| `level` | 命中后使用的最低日志等级 |
| `priority` | 多条规则同时匹配时，数值更大的规则优先 |
| `fuzzy_rules` | target/span 选择规则，同一 override 内为 OR |
| `field_rules` | event 字段过滤规则，同一 override 内为 AND |

规则约束：

- 同一个过滤作用域内，override `name` 必须唯一。
- `enabled = false` 的规则保留在配置中，但不参与匹配。
- 没有 `fuzzy_rules` 的 override 匹配所有事件，应只用于明确的全局覆盖。
- 建议让可能同时命中的规则使用不同 `priority`，避免配置意图不清晰。

### 4.1 优先级选择

```toml
[[log.filter.overrides]]
name = "decoder_debug"
level = "debug"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::decoder" },
]

[[log.filter.overrides]]
name = "ton_decoder_trace"
level = "trace"
priority = 200
fuzzy_rules = [
    { kind = "target", match_type = "exact", pattern = "my_app::decoder::ton" },
]
```

`my_app::decoder::ton` 同时命中两条规则，但最终使用优先级更高的
`ton_decoder_trace`。

### 4.2 使用 off 关闭部分日志

```toml
[[log.filter.overrides]]
name = "trace_test_logs_off"
level = "off"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "contains", pattern = "trace_test" },
]
```

命中该规则的日志事件全部关闭，其他模块继续使用默认等级或各自 override。

## 5. Fuzzy Rules

`fuzzy_rules` 用于选择一批 target 或当前调用链中的 span。

```toml
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::decoder" },
    { kind = "span", match_type = "contains", pattern = "decode_transaction" },
]
```

同一个 override 中，多条 fuzzy rule 是 OR 关系，任意一条命中即可选择该 override。

### 5.1 kind

| kind | 匹配对象 |
|---|---|
| `target` | `tracing` event 的 target |
| `span` | event 当前调用链中的 span 名称 |

Target 示例：

```rust
tracing::info!(
    target: "my_app::decoder",
    "target 是 my_app::decoder"
);
```

Span 示例：

```rust
#[tracing::instrument(name = "decode_transaction")]
fn decode_transaction() {
    tracing::info!("当前调用链包含 decode_transaction span");
}
```

`span` 规则匹配 span 名称，不匹配 span fields。

### 5.2 match_type

| match_type | 行为 |
|---|---|
| `exact` | 完全相等 |
| `prefix` | 前缀匹配 |
| `contains` | 子字符串匹配 |
| `glob` | 使用 `*` 和 `?` 通配符 |
| `regex` | 正则表达式匹配 |

建议优先使用 `exact` 和 `prefix`。它们可以走 target 索引路径，适合高频日志。
`contains`、`glob` 和 `regex` 更灵活，但运行时开销更高。

每条 override 最多允许 32 条 fuzzy regex/glob 规则，单个正则模式最长 256 字节。

`FuzzyRuleConfig.level` 当前只保留用于诊断和未来扩展，不参与等级判断。等级必须配置在
所属 override 的 `level` 字段。

## 6. Field Rules

`field_rules` 对已经选中的 override 继续执行 event 字段过滤。

```toml
field_rules = [
    { field = "id", op = "eq", value = 100 },
    { field = "status", op = "in", value = ["ok", "retry"] },
    { field = "retry_count", op = "lte", value = 3 },
    { field = "sampled", op = "eq", value = true },
]
```

同一个 override 中，多条 field rule 是 AND 关系，必须全部匹配。

对应日志：

```rust
tracing::debug!(
    target: "my_app::decoder",
    id = 100_u64,
    status = "ok",
    retry_count = 1_u64,
    sampled = true,
    "字段全部匹配"
);
```

### 6.1 支持的操作

| op | value 要求 | 含义 |
|---|---|---|
| `eq` | 标量 | 相等 |
| `ne` | 标量 | 不相等 |
| `contains` | 字符串 | 包含子字符串 |
| `starts_with` | 字符串 | 以指定字符串开头 |
| `ends_with` | 字符串 | 以指定字符串结尾 |
| `regex` | 字符串 | 正则匹配 |
| `in` | 数组 | 实际值等于数组中的任意值 |
| `exists` | 不需要 | 字段存在 |
| `not_exists` | 不需要 | 字段不存在 |
| `gt` | 标量 | 大于 |
| `gte` | 标量 | 大于或等于 |
| `lt` | 标量 | 小于 |
| `lte` | 标量 | 小于或等于 |

`value` 支持 number、string、bool 和数组。数值比较按数值语义处理，例如配置
`value = 100` 可以匹配日志中的 `id = 100_u64`。

字段不存在时：

- `exists` 返回 false。
- `not_exists` 返回 true。
- 其他操作返回 false，包括 `ne`。

每条 override 最多允许 32 条 field regex 规则，单个正则模式最长 256 字节。

### 6.2 Field Rules 只读取 event 字段

可以过滤：

```rust
tracing::info!(id = 100_u64, status = "ok", "event fields");
```

不能作为 event field 过滤：

```rust
#[tracing::instrument(fields(id = id))]
fn decode(id: u64) {
    tracing::info!("id 只记录在 span 中");
}
```

`#[tracing::instrument(fields(...))]` 创建的是 span fields。当前 `field_rules` 不读取
span fields。如果需要按调用链过滤，应使用 `kind = "span"` 的 fuzzy rule。

通过兼容桥接进入 observability 的普通 `log::...!` 记录支持等级、target 和 message，
但不提供 `tracing::...!` 的结构化业务字段。建议对 `log::...!` 使用等级和 target
过滤；需要按业务字段过滤时使用 `tracing::...!`。

桥接事件格式化输出中的顶层 tracing target 为 `log`，原始 target 位于 `log.target`
字段。过滤器识别这类事件后会自动使用原始 `log.target` 执行 fuzzy target 规则。

## 7. 字段不匹配不会回退

配置：

```toml
[log.filter]
default_level = "info"

[[log.filter.overrides]]
name = "decoder_id_100"
level = "debug"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::decoder" },
]
field_rules = [
    { field = "id", op = "eq", value = 100 },
]
```

结果：

| target | 日志等级 | id | 结果 | 原因 |
|---|---|---:|---|---|
| `my_app::user` | info | 200 | 输出 | 未命中 override，使用默认 info |
| `my_app::user` | debug | 100 | 不输出 | 未命中 override，默认 info 不允许 debug |
| `my_app::decoder` | debug | 100 | 输出 | override 等级和字段均通过 |
| `my_app::decoder` | debug | 200 | 不输出 | 已选 override，但字段不匹配 |
| `my_app::decoder` | info | 200 | 不输出 | 已选 override 后字段不匹配，不回退默认 info |

如果希望 decoder 的普通 info 日志继续输出，而只对 `id = 100` 开启 debug，当前单条
override 无法表达“字段不匹配时回退默认等级”。可采用以下方式之一：

- 为需要诊断的日志使用更具体的 target。
- 临时开启整个 decoder 模块 debug，不配置 field rules。
- 接受字段过滤会同时过滤该 override 命中的 info/warn/error 事件。

## 8. local、remote、trace 作用域

三个过滤作用域相互独立：

| 作用域 | 配置位置 | 影响范围 |
|---|---|---|
| local | `[log.filter]` | console 和本地文件 |
| remote | `[log.remote.filter]` | 远端 OpenTelemetry logs |
| trace | `[trace.filter]` | 远端 OpenTelemetry traces |

远端日志未配置 `[log.remote.filter]` 时，会复用 `[log.filter]`。

```toml
[log.filter]
default_level = "info"

[log.remote.filter]
default_level = "off"

[[log.remote.filter.overrides]]
name = "upload_remote_test"
level = "info"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "contains", pattern = "remote_test" },
]
```

该配置表示：

- console 和本地文件默认记录 info 及以上日志。
- 远端日志默认关闭。
- 只有命中 `upload_remote_test` 的日志上传远端。

日志等级为 `trace` 与 OpenTelemetry traces 是两套概念。将日志模块调整为 trace
等级，不会自动开启 trace/span 上传；开启 trace/span 上传也不会自动放行 trace 级别日志。

## 9. 常见配置场景

### 9.1 临时开启一个模块的 debug

```toml
[log.filter]
default_level = "info"

[[log.filter.overrides]]
name = "payment_debug"
level = "debug"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "my_app::payment" },
]
```

### 9.2 快速开启一批 decoder 模块

```toml
[[log.filter.overrides]]
name = "all_decoders_trace"
level = "trace"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "contains", pattern = "decoder" },
]
```

### 9.3 远端只上传特定业务日志

```toml
[log.remote.filter]
default_level = "off"

[[log.remote.filter.overrides]]
name = "upload_failed_orders"
level = "info"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "exact", pattern = "my_app::order" },
]
field_rules = [
    { field = "status", op = "eq", value = "failed" },
]
```

### 9.4 关闭高噪声模块

```toml
[[log.filter.overrides]]
name = "disable_noisy_dependency"
level = "off"
priority = 100
fuzzy_rules = [
    { kind = "target", match_type = "prefix", pattern = "noisy_dependency" },
]
```

## 10. 热更新

支持运行时更新：

- local 过滤规则。
- remote 过滤规则。
- trace 过滤规则。
- `default_level`。
- overrides。
- 按 override `name` 动态启用或禁用规则。

不支持热更新：

- console/local/remote/trace/metrics 的 `enabled`。
- OTLP endpoint。
- 输出格式。
- 本地文件目录和文件名。
- metrics 配置。
- remote filter 在“复用 local”和“独立配置”之间切换。

新规则会先完成校验和正则预编译。更新失败时，旧规则继续生效，错误信息会尽量包含
失败的 override `name`。

## 11. 性能建议

- 默认使用 `default_level = "info"`，只在排查问题时临时提升部分模块。
- 优先使用 target `exact` 和 `prefix`，它们可以使用索引快速选择规则。
- 避免大量 `contains`、`glob`、`regex` 和 span 规则。
- 仅在日志量仍然过大时增加 `field_rules`。
- 不需要字段过滤时不要配置空占位字段；无 field rules 的事件无需读取 event fields。
- 使用明确且唯一的 override `name`，并为可能重叠的规则设置清晰的不同优先级。

## 12. 配置检查

文件配置可以在初始化前检查：

```rust
use pi_logger::observability::validate_observability_config_path;

let config = validate_observability_config_path("observability.toml")?;
```

使用 `pi_config` 时：

```rust
use pi_logger::observability::observability_config_from_pi_config;

let config = observability_config_from_pi_config(&mut application_config, "observability")?;
```

检查会验证：

- 日志等级是否合法。
- override `name` 是否唯一。
- fuzzy glob/regex 是否有效。
- field regex 和 value 类型是否有效。
- 已启用 exporter 的必要配置是否完整。
