6c0fc303e1
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.9 KiB
4.9 KiB
POST /api/v1/applog/batch — 批量上报客户端运行日志
所属:客户端日志组(前缀
/api/v1/applog) | 鉴权:无(不强制登录,user_id可选带上) | ← 返回 API 索引
批量接收客户端 App 运行日志(自动化步骤 / 网络 / 崩溃 / 调试等),逐条封装成单行 JSON 写入独立滚动文件 logs/app-client.log(与服务日志 app-server.log 分开),由阿里云 Logtail 采进独立 SLS logstore,不落库。服务端补 client_ip(X-Forwarded-For)与 time(接收时间,SLS 主时间)。trace_id 提到输出行顶层,便于在 SLS 里跨「客户端 / 服务端」两个 logstore 按 trace 拼出端到端链路。fire-and-forget:写失败也返回 2xx,不 500。
入参
批级公共字段发一次;logs 里每条只带日志本身。每条只有 client_ts/level/trace_id/tag/msg 会提到输出顶层,其余自定义字段一律并入输出的 data(防 SLS 索引列爆炸)。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
device_id |
string | ✅(≤64) | 设备 ID(限流 / 分组键) |
user_id |
int | null | ❌ | 登录用户 ID(未登录可空) |
app_ver |
string | null | ❌(≤32) | App 版本 |
platform |
string | null | ❌(≤16) | 平台(android / ios / harmony) |
sent_at |
int | null | ❌ | 批次发送时间(epoch ms) |
logs |
list[object] | ✅(1-500 条) | 日志数组(每条为对象,内部字段不强类型) |
logs[].client_ts |
int | ❌ | 端侧日志时间(epoch ms) |
logs[].level |
string | ❌ | 级别(服务端归一化大写,截断 ≤16) |
logs[].trace_id |
string | null | ❌ | 关联服务端比价链路的 trace(有服务端交互的日志带上,截断 ≤256) |
logs[].tag |
string | null | ❌ | 模块 / 分类(截断 ≤128) |
logs[].msg |
string | ❌ | 消息主体(超 8KB 字节截断,加 msg_truncated 标记) |
logs[].* |
any | ❌ | 其余任意自定义字段 → 一律并入输出的 data(客户端自带的 data 对象会被合并进来) |
Mock 入参:
{
"device_id": "android_abc123def456",
"user_id": 42,
"app_ver": "0.1.5",
"platform": "android",
"sent_at": 1719993700000,
"logs": [
{
"client_ts": 1719993600000,
"level": "info",
"tag": "automation",
"msg": "compare flow start",
"trace_id": "t_ab12cd34"
},
{
"client_ts": 1719993615000,
"level": "error",
"tag": "network",
"msg": "timeout calling /price/step",
"trace_id": "t_ab12cd34",
"http_status": 504,
"retry": 2
}
]
}
上例第二条的
http_status/retry不在白名单 → 会被并入落盘行的data:{"http_status":504,"retry":2}。
出参
响应 200:AppLogIngestOut
| 字段 | 类型 | 说明 |
|---|---|---|
ok |
bool | 固定 true |
received |
int | 成功写入文件的条数 |
dropped |
int | 跳过的条数(服务端处理 / 序列化失败;正常为 0) |
Mock 出参:
{
"ok": true,
"received": 2,
"dropped": 0
}
错误码
413请求体超过上限(默认 2MB;服务端查Content-Length,在 body 校验前拦截。缺该头时由 nginxclient_max_body_size兜底)422logs为空或超过 500 条 /device_id缺失 / 字段类型不符429触发限流(同 IP 每分钟 > 120 次)
说明
- 落盘 → SLS:逐条写独立文件
logs/app-client.log(单行 JSON,propagate=False不污染app-server.log),由 Logtail JSON 模式采进独立 logstore;不进数据库。落盘行除白名单字段外,服务端另补time(接收时间)、source="client"、service、client_ip及批级device_id/user_id/app_ver/platform/sent_at。 trace_id跨层检索:字段名与服务端日志一致。客户端应给有服务端交互的日志带上当初 API 返回的trace_id(如/api/v1/price/step等透传族返回的 trace),即可在 SLS 里trace_id: "xxx"一查拼出「客户端视角 + 服务端比价链路」;纯客户端日志不带即可。- 白名单 +
data兜底:只有client_ts/level/trace_id/tag/msg上顶层,其余键并入data,把 SLS 索引列钉死在固定集合,防客户端任意 key 撑爆索引。索引字段另有长度上限(level≤16 /trace_id≤256 /tag≤128 /msg≤8KB)。 - fire-and-forget:写文件失败也返回 2xx(避免客户端重试风暴);网络重试可能在 SLS 造成重复条目,可接受。
- 量级建议:客户端做等级过滤 / 采样,攒到一定量再批量上报;单批 ≤500 条、body ≤2MB。
client_ts是端侧时间(客户端时钟不可信),time由服务端补(可靠时间轴)。user_id不靠 JWT:未登录态也要采日志;带上便于按用户排查。