日志采集与传输:本地日志采集、远程日志传输(TCP/UDP/HTTP)、日志压缩与加密

日志这东西,平时没人看,一出问题就成救命稻草了。我在密钥烧录项目里吃过不少亏,日志不全、传丢了、被篡改,哪个都够你喝一壶的。今天咱们就把日志采集和传输这块掰开揉碎了讲清楚。

本地日志采集:第一道防线

日志采集,说白了就是把设备运行时的关键信息记下来。我习惯把日志分成三个等级:

等级 说明 我常用的场景
INFO 正常流程记录 烧录开始、密钥写入成功、校验通过
WARN 异常但可恢复 通信超时重试、校验码不一致但重试后通过
ERROR 不可恢复故障 硬件错误、密钥写入失败、安全违规

嗯,这里要注意:ERROR 级别的日志必须包含时间戳、设备ID、错误码和上下文数据。我曾经遇到过一个案子,设备报错但日志里只有"烧录失败"四个字,排查了三天才发现是电源波动导致的——要是当时多记个电压值,半小时就能定位。

本地日志采集的核心原则:

  • 环形缓冲区:固定大小,新日志覆盖最旧的,防止撑爆存储
  • 原子写入:每条日志必须完整写入,不能出现半条
  • 分级过滤:生产环境只记录 WARN 及以上,调试时开 INFO

代码实现上,我推荐用这种结构:

typedef struct {
    uint32_t timestamp;
    uint8_t  device_id[16];
    uint8_t  level;      // 0=INFO, 1=WARN, 2=ERROR
    uint16_t error_code;
    uint8_t  data_len;
    uint8_t  data[256];  // 上下文数据
    uint32_t crc32;      // 完整性校验
} log_entry_t;

为什么加 CRC32?你想想看,日志在存储过程中可能因为掉电、存储介质老化而损坏。没有校验,你拿到的就是一堆垃圾数据。

远程日志传输:把数据送出去

本地日志再全,人不去现场也白搭。远程传输才是真正的价值所在。我做过三种传输方式,各有各的脾气。

TCP 传输:可靠但重

TCP 适合关键日志,比如 ERROR 级别的。它保证数据不丢、不乱序。但代价是连接开销大,握手三次,断线重连也麻烦。

我的经验:TCP 传输一定要加心跳机制。我遇到过设备 TCP 连接处于半开状态,服务器以为还连着,设备其实早挂了。心跳间隔设 30 秒,连续三次无响应就重建连接。

UDP 传输:轻量但丢

UDP 适合大量 INFO 日志,丢了也不心疼。但要注意:UDP 包最大 1472 字节(以太网 MTU 1500 减去 IP 头 20 和 UDP 头 8)。超了就得分片,分片丢一个整个包就废了。

我一般这么处理:

// 每条日志单独一个 UDP 包
// 包结构:包序号(4字节) + 日志数据(最多1460字节)
// 接收端按序号排序,检测丢包

HTTP 传输:简单但慢

HTTP 适合设备有完整网络栈的场景。用 POST 请求把日志以 JSON 或二进制形式发到服务器。优点是防火墙友好,缺点嘛——每次请求都要建连接,效率低。

方式 可靠性 实时性 资源消耗 我推荐场景
TCP 高 中 高 ERROR 日志、密钥操作记录
UDP 低 高 低 INFO 日志、性能数据
HTTP 中 低 中 批量上报、非实时场景

日志压缩:省带宽就是省钱

设备多了,日志量是惊人的。一台烧录设备一天可能产生几十 MB 日志,一万台就是几百 GB。不压缩,传输成本和存储成本都扛不住。

我常用的压缩方案:

  • LZ4:压缩速度极快,适合实时传输。压缩比一般 2:1 到 3:1
  • zlib:压缩比高(4:1 到 6:1),但慢。适合非实时批量上传
  • 自定义差分编码:连续日志有很多重复字段(设备ID、时间戳),只传变化的部分

注意:压缩和解压都要消耗 CPU。低端 MCU 上跑 zlib 可能把 CPU 占满,影响烧录主流程。我建议在 MCU 上用 LZ4,在网关上用 zlib 二次压缩。

日志加密:别让密钥裸奔

日志里可能包含密钥的哈希值、设备序列号、固件版本等敏感信息。明文传输等于把家底亮给黑客看。

我个人的加密策略:

  1. 传输层加密:TLS/DTLS 加密整个通道。这是基础,必须做
  2. 应用层加密:对日志中的敏感字段单独加密。即使通道被破解,数据还是安全的
  3. 完整性校验:HMAC 或数字签名,防止日志被篡改
// 应用层加密示例(AES-128-GCM)
// 加密日志中的密钥相关字段
uint8_t key_hash[32];
uint8_t encrypted_hash[48]; // 密文 + IV + tag
aes_gcm_encrypt(key_hash, sizeof(key_hash), 
                device_key, encrypted_hash);

为什么会这样设计?因为传输层加密只保护传输过程,日志落地后还是明文的。应用层加密让日志在服务器上也是密文,只有授权人员才能解密查看。

整体架构:一张图说清楚

下面这张图展示了日志从采集到传输的完整链路:

日志采集与传输架构图 日志采集模块 环形缓冲区 分级过滤 压缩引擎 LZ4 / zlib 差分编码 加密模块 AES-128-GCM HMAC 完整性 TCP 传输 可靠连接 / 心跳保活 UDP 传输 轻量快速 / 包序号 HTTP 传输 JSON / 二进制 POST 日志服务器 解密 → 解压 → 存储 → 分析 💡 说明: 日志先压缩后加密,可以节省带宽。但注意压缩和加密的顺序不能颠倒——先加密后压缩,压缩率会大幅下降。

避坑指南

做日志传输这些年,我踩过的坑能写本书。挑几个典型的说说:

  • 我曾经把日志缓冲区设成全局变量,结果多任务环境下日志乱序了。后来改成每个任务独立的日志缓冲区,再统一合并。
  • 我曾经用 UDP 传 ERROR 日志,结果网络抖动丢了几条关键日志,排查事故时死活找不到原因。现在 ERROR 日志必须走 TCP。
  • 我曾经忘了给日志加时间戳,结果日志到了服务器上,根本不知道哪条在前哪条在后。嗯,这个教训挺深刻的。

我的建议:日志系统上线前,一定要做压力测试。模拟设备满负荷运行,看日志会不会丢、会不会撑爆存储。我一般会连续跑 72 小时,检查日志的完整性和连续性。

日志采集与传输,看似简单,实则处处是坑。但只要把采集、压缩、加密、传输这四个环节都做到位,你的日志系统就能在关键时刻救你一命。