目录
第一部分 Logs
第二部分 操作
第三部分 AI 要点(只建框架)
第一部分 Logs
1. 一条日志本质上是什么
一条日志 = 带时间戳的事件记录。最小形态:时间 + 谁说的 + 严重程度 + 说了什么
可观测性里还希望有:服务名、实例、请求 ID、错误码、耗时,这样才能检索、聚合、和 Trace 关联。
日志回答的是「发生了什么」,包括:
- 业务事件:订单创建、库存不足
- 错误与堆栈
- 审计:谁在何时改了什么(你们有独立流
cht_log_aws_audit)- 调试:开发期的细节(生产要慎打)
必看文档
- 编辑OTel Logs
- OpenObserve:编辑Logs 概述
- 编辑第一次搜日志
- 编辑SQL 示例
- 编辑SQL Reference(先看
match_all、str_match)
2. 三种形态(最重要的原理之一)
非结构化(纯文本):
2026-08-20 17:01:02 ERROR order failed timeout
人能读,机器很难稳定解析。全文搜索可以,但按「服务 + 错误码」聚合很痛。
半结构化:
time=2026-08-20T17:01:02Z level=error service=order msg="timeout" trace=abc
有键值,但格式各写各的,字段名容易漂。
结构化(推荐,JSON 等):
{ "_timestamp": "2026-08-20T17:01:02Z", "level": "error", "service": "order", "trace": "4bf9e929d0e0e4736", "error": "upstream timeout", "http_code": "504" }每个键都是列。好处:过滤准、聚合快、告警稳、和 Trace 对齐容易。
JSON 同时满足几件事:
- 字段就是键。
level、service、trace、purchase_ordersn不用靠正则从句子里抠。 - 跨语言。 Java、Go、PHP、前端都能直接
json.Marshal/json_encode。 - 能嵌套。 采购单、SKU 列表、错误对象可以放在一个对象里,不像纯文本只能摊成一长串。
- 采集端好拆。 OpenObserve 看到 JSON,就能把键变成
cht_log_aws里的列,于是能写:
SELECT * FROM cht_log_aws WHERE trace = '4bf9e929d0e0e4736'反面教材:如cht_log_aws目前大约 192 个字段。同一类意思出现了多套名字,例如error/err/errcode/error_msg/errormsg/errmessage/http_code/code。这就是长期半结构化、各服务各打各的结果:schema 爆炸,查询要写一堆OR,索引也变贵。
3. 级别(level)
常见级别从低到高:
| 级别 | 何时用 | 生产建议 |
|---|---|---|
TRACE / DEBUG | 开发细节 | 默认关,按需打开 |
INFO | 关键业务里程碑 | 克制,不要每个循环打 |
WARN | 可恢复异常、降级 | 要能说明风险 |
ERROR | 这次操作失败 | 必须能定位 |
FATAL | 进程要死 | 极少 |
原则:级别表达「对运维的紧急程度」,不要把 INFO 当 DEBUG 用。
告警一般盯 ERROR+,但「ERROR 刷屏」也可能是下游抖动,要结合数量和service,不能一条 ERROR 就叫人。
4. 日志数据模型(OTEL 视角)
日志数据模型就是约定「一条日志长什么样」:必须有哪些字段、每个字段什么含义。不是指磁盘怎么存,而是双方怎么理解这一条记录。
有了模型,采集端、OpenObserve、查询 SQL 才说的是同一种东西。没有模型,就只是一堆字。
OTEL 里一条 Log Record 大致包括:
- Timestamp:事件时间(注意:采集时间 ≠ 事件时间)
- ObservedTimestamp:被采集到的时间
- Severity:级别
- Body:正文(字符串或结构化体)
- Attributes:字段(
http.status_code等) - Resource:谁在打(
service.name、主机) - TraceContext:
trace_id、span_id(有则能关联链路)
即使现在很多日志不是 OTLP 进来的,模型仍应往这套靠:时间、级别、资源、属性、trace 上下文。
OpenObserve 里常见还会有_timestamp(平台时间列)。查询时间范围用的就是它。
5. 采集路径
应用 logger.info(...) / 写文件 / stdout
│
▼
Agent / 采集器(Fluent Bit、Vector、OTEL Collector、云 sidecar)
│ 解析、加字段、脱敏、丢弃 DEBUG
▼
管道 / 消息队列(可选,削峰)
│
▼
存储(OpenObserve / Elasticsearch / Loki)
│
▼
查询 UI、告警、看板
每一跳都可能丢数据或改字段。排障时要问:
- 日志是应用直接打 OTLP,还是先写文件再采集?
- 解析规则会不会把 JSON 拆错(你们流里有
_badkey,往往就是解析失败的痕迹)? - 时钟是否准?(容器时区、多机会导致同一请求时间对不齐)
6. 采样、脱敏、保留(成本与合规)
量:日志是三类里通常最贵的。1 亿 QPS 级别的 DEBUG 能把集群打满。手段:
- 生产默认 INFO+
- 成功请求抽样(例如只留 1%),错误全量
- 丢弃健康检查、静态资源
- 字段裁剪:不要把整段请求体、Token、身份证打进日志
脱敏:密码、Cookie、证件号、手机号进日志是事故。应在采集器或应用侧打码,而不是指望查询的人「注意别看」。
保留:越久越贵。你们cht_log_aws当前设置为 保留约 20 天,单次查询窗口最大约 72 小时。所以:
- 查日志先收窄时间,不要一上来查 30 天
SELECT * - 审计日志(
cht_log_aws_audit)往往要更长留存、更少字段,和业务调试日志分开
这就是「日志原理」里和存储绑定的部分:不是记越多越好,而是记对的、能查的、能留得起的。
7. 索引与查询原理(操作前要懂)
日志存储一般会:
- 按时间分片
- 对少量列建索引(你们:
service、appcode、trace、traceid) - 全文检索(
match_all)扫的是文本,通常比「等值过滤索引列」更重
所以查询要:
- 先定时间范围
- 优先过滤索引字段(
service、trace) - 再用
match_all('timeout')或str_match(error, 'timeout')收窄
不要:无时间范围 + 全文搜一个很宽的词。
第二部分 操作
1. 结构化日志最低字段集
新日志至少带:
| 字段 | 说明 |
|---|---|
时间 | ISO8601 或平台 |
| info / warn / error |
| 服务名,稳定、短 |
| 与 Trace 同一值 |
| 失败时必填,一种名字用到底 |
可选 |
|
不要再发明errmessage第五个同义词。已有流字段乱,新服务按这一套即可。
2. 在 OpenObserve 里检索
组织默认default,业务日志流cht_log_aws。时间用微秒时间戳;查询窗口尽量 ≤ 72h。
按服务看最近错误:
SELECT _timestamp, service, appcode, trace, error, error_msg, http_code, content FROM cht_log_aws WHERE service = '某个服务名' AND (level = 'error' OR error IS NOT NULL OR error_msg IS NOT NULL) ORDER BY _timestamp DESC全文:
SELECT _timestamp, service, trace, content FROM cht_log_aws WHERE match_all('timeout')聚合:哪个服务错误最多:
SELECT service, COUNT(*) AS cnt FROM cht_log_aws WHERE error IS NOT NULL OR errcode IS NOT NULL GROUP BY service ORDER BY cnt DESC字段不统一时,聚合会不准——这正是结构化没做好的代价。
3. 用同一 ID 验证日志 ↔ 链路
在test_otel(traces)里取一条trace_id。
在日志里查:
SELECT _timestamp, service, appcode, level, error, content, trace, traceid FROM cht_log_aws WHERE trace = '<这条 trace_id>' OR trace_id = '<这条 trace_id>' OR traceid = '<这条 trace_id>'审计流也可查:
SELECT * FROM cht_log_aws_audit WHERE trace = '<trace_id>'三种结果都要记进实验:
- 两边都有:关联成功,记下服务名、时间是否接近。
- 只有 Trace 没有日志:日志没打关联字段,或时间范围/流不对,或日志采样丢掉了。
- 只有日志没有 Trace:没插桩、Trace 采样丢了,或 ID 不是同一套。
4. 错误日志告警(最小可用)
最小规则例如:
- 条件:某
service在 5 分钟内level=error条数 > N - 通知:值班人
- 消噪:排除健康检查;N 按基线调,避免一条 ERROR 就叫
第三部分 AI 要点(只建框架)
计划里有两条线,目前只要分得清,不要混:
| 主线 | 含义 | 例子 |
|---|---|---|
AI for Observability | 用 AI 分析已有日志/链路/指标 | 自然语言转 SQL、错误摘要、日志聚类 |
Observability for AI | 观测 LLM/Agent 自己 | token、工具调用 span |
日志上的 AI 能力:
- 聚类 / Pattern:把上万条相似 ERROR 收成几种模板
- NL → 查询:你说「查订单超时」,模型写出 SQL
- 摘要:把堆栈收成几句人话
风险:幻觉(编造不存在的字段或结论)。规范是:先检索,再生成;结论必须能指回具体查询和原文。
延伸
| 组件 | 定位 |
|---|---|
ELK(Elasticsearch / Logstash / Kibana) | 经典日志栈,倒排检索强,成本高 |
Grafana Loki | 标签索引 + 日志内容,成本往往更低,标签基数要控制 |
Fluent Bit / Vector | 采集与加工(解析、脱敏、路由),不是存储 |
OpenObserve | 你们的统一平台:日志 + 链路(+ 指标)一起查 |
术语表
| 术语 | 含义 |
|---|---|
Observability | 由外部遥测推断系统内部状态的能力 |
Logs / Traces / Metrics | 三大支柱:事件、请求链、数字趋势 |
Signal | OTEL 对三类遥测的统称 |
OpenTelemetry (OTEL) | 厂商无关的采集与传输标准 |
OTLP | OTEL 的导出协议 |
Collector | 接收、处理、转发遥测的独立进程 |
Instrumentation | 插桩,在运行路径上采集遥测 |
SLI / SLO / SLA | 测量指标 / 内部目标 / 对外合同 |
RED | Rate、Errors、Duration |
Structured log | 字段稳定的日志(如 JSON) |
Severity / level | 日志级别 |
Resource | 谁在发数据(服务名、环境) |
Attribute | 单条记录上的键值 |
| 跨日志与链路的请求 ID |
Cardinality | 字段取值组合数;过高会爆成本和 schema |
Retention | 数据保留时长 |
Sampling | 只保留一部分数据以控制成本 |
Agent | 采集侧进程(读文件、stdout 或自动插桩) |
AI for Observability | 用 AI 分析遥测 |
Observability for AI | 对 AI 应用做遥测 |
日志实验记录怎么写
- 采集说明:数据从哪来、流名(
cht_log_aws)、组织default、关联字段trace、保留约 20 天、查询窗口约 72h。 - 典型查询 2~3 条:例如按
service过滤、match_all、按服务COUNT聚合;贴 SQL 和结果摘要(条数、时间范围)。 - 一次
trace_id关联:ID、Trace 是否存在、日志是否命中、时间/服务是否一致、若不命中则分析缺字段还是时间/采样问题。 - 观察:例如字段名不统一(
errorvserror_msg)、192 列 schema、索引字段有哪些。
总结:
- 日志看「发生了什么」,链路看「请求怎么走」,指标看「整体健不健康」。
- 可观测性靠统一字段把三类数据连起来,关键是
trace_id(你们日志里是trace)。- 结构化日志的价值是可过滤、可聚合、可告警;字段名混乱会变成上百列。
- 采集是「应用 → Agent/Collector → 存储 → 查询」;查询先锁时间,再用索引字段。
- 日志最贵,要靠级别、采样、脱敏、保留控制;AI 只能辅助,结论必须能回溯到查询。