日志格式:标准日志格式规范、时间戳精度与同步、关键字段定义与扩展
聊到高频交易系统的日志,我第一个想到的就是「标准化」。你想想看,一个每秒处理几十万笔订单的系统,日志要是乱七八糟的,出了问题你根本没法查。我个人习惯是,在项目启动的第一天就把日志格式定死,后面谁也别想改。
标准日志格式规范
高频交易的日志,说白了就是给机器看的,不是给人看的。所以格式必须严格、一致、可解析。我见过太多团队,日志格式三天两头变,最后写解析脚本的人想骂娘。
我推荐使用 JSON Lines 格式,每行一个完整的 JSON 对象。为什么?因为 JSON 是自描述的,字段增减不影响解析,而且几乎所有语言都有现成的库。
{
"timestamp": "2025-03-15T14:30:22.123456789Z",
"level": "INFO",
"source": "order_gateway",
"session_id": "SESS_20250315_001234",
"message": "Order received",
"data": {
"order_id": "ORD_987654321",
"symbol": "BTC/USDT",
"side": "BUY",
"price": 65000.50,
"quantity": 0.1
}
}
嗯,这里要注意:不要用空格缩进,每行必须是一个紧凑的 JSON。高频日志一天能写几个 GB,多一个空格都是浪费磁盘 IO。
时间戳精度与同步
时间戳是高频交易日志的灵魂。我曾在项目中遇到过一个问题:两台服务器的时间差了 2 毫秒,结果订单的「先来后到」完全乱套了。你想想看,在高频交易里,2 毫秒够一个套利策略跑好几轮了。
时间戳精度方面,我建议:
- 纳秒级精度:使用
clock_gettime(CLOCK_REALTIME, ...)或 C++ 的std::chrono::high_resolution_clock - UTC 时间:永远不要用本地时间,跨时区交易时你会疯掉
- 格式固定:ISO 8601 格式,带时区标识
Z或+08:00
时间同步怎么做?我个人的经验是:
- PTP(精确时间协议):硬件级同步,精度可达亚微秒级。交易所内部网络必备。
- NTP 备选:如果硬件不支持 PTP,至少用 NTP 做软件同步,精度在 1-10 毫秒。
- GPS 时钟源:核心节点直接接 GPS 授时模块,避免网络延迟干扰。
关键字段定义与扩展
日志字段不能随便加,得有规矩。我习惯把字段分成三类:必选字段、业务字段、扩展字段。
| 字段类别 | 字段名 | 说明 | 示例 |
|---|---|---|---|
| 必选 | timestamp | 事件发生时间(纳秒级 UTC) | 2025-03-15T14:30:22.123456789Z |
| 必选 | level | 日志级别:DEBUG/INFO/WARN/ERROR/FATAL | INFO |
| 必选 | source | 产生日志的模块名 | order_gateway |
| 必选 | session_id | 会话追踪 ID | SESS_20250315_001234 |
| 业务 | order_id | 订单唯一标识 | ORD_987654321 |
| 业务 | symbol | 交易对 | BTC/USDT |
| 扩展 | latency_us | 处理耗时(微秒) | 125 |
| 扩展 | extra | 预留扩展字段(JSON 对象) | {"retry_count": 2} |
为什么要有 extra 字段?说白了就是给未来留个后门。你永远不知道明天业务方会要求加什么字段。与其改格式,不如直接塞进 extra 里。我见过一个团队,半年改了 7 次日志格式,每次都要改解析脚本、改监控面板、改报警规则……简直是灾难。
extra 字段里加一个 version 子字段,比如 "extra": {"version": 1, "data": {...}}。这样即使以后扩展字段结构变了,解析程序也能根据版本号做兼容处理。
知识体系结构图
下面这张图,是我自己梳理的日志格式核心逻辑。你看一眼就能明白各个部分的关系。
这张图把日志格式的三个核心维度串起来了。左边是格式规范,中间是时间同步,右边是字段定义。我个人觉得,中间的时间同步是最容易被忽视的,但恰恰是最要命的。你日志格式再漂亮,字段定义再全,时间不对全是废纸。
好了,关于日志格式的核心内容就这些。记住,在高频交易的世界里,日志不是给人看的,是给机器和算法看的。每一行日志,都可能是未来排查故障的唯一线索。所以,从一开始就把规矩立好,后面能省下无数麻烦。