客户端日志上报怎么设计?让线上问题不再只靠用户截图

线上问题最难排查的情况,不是系统直接崩了,而是用户说:

复制代码
刚才不能用了
点了没反应
字幕突然不刷新了
我也不知道发生了什么

这类反馈很常见,但如果客户端没有日志,上来只能反复追问:

复制代码
你用的什么系统?
当时点了哪个按钮?
有没有报错?
网络正常吗?
能不能录屏?

排查效率会非常低。

所以,客户端日志上报不是可有可无的功能,而是产品稳定性的重要基础。


一、客户端日志要解决什么问题?

客户端日志的核心目标不是"记录越多越好",而是帮助开发快速回答几个问题:

复制代码
用户当时做了什么?
系统进入了哪个页面?
接口有没有失败?
设备和版本是什么?
错误发生在哪个阶段?
是否可以复现同类问题?

例如一个实时翻译产品出现"字幕不刷新",仅靠用户描述很难判断原因。

可能是:

复制代码
麦克风权限异常
网络连接中断
接口请求失败
实时连接断开
服务端任务状态异常
前端渲染报错

如果客户端能上报关键日志,排查方向会清楚很多。


二、日志要分级

不要所有日志都当成错误。

常见日志等级可以分为:

复制代码
DEBUG:调试信息
INFO:正常业务流程
WARN:异常但可恢复
ERROR:明确错误
FATAL:严重崩溃或不可继续使用

例如:

复制代码
logger.info('translation_task_start', {
  taskId,
  languagePair: 'en-zh'
});

logger.warn('websocket_reconnect', {
  retryCount: 2
});

logger.error('translation_task_failed', {
  taskId,
  errorCode: 'NETWORK_TIMEOUT'
});

这样后端分析日志时,可以优先关注 ERRORFATAL,而不是在大量普通信息中查找问题。


三、日志内容应该包含哪些字段?

一条可用的客户端日志,最好包含这些基础字段:

复制代码
logId:日志唯一 ID
level:日志等级
eventName:事件名称
timestamp:发生时间
userId:用户 ID
deviceId:设备 ID
appVersion:客户端版本
platform:平台,如 Web、Windows、macOS、iOS、Android
page:当前页面
message:日志描述
extra:额外上下文

示例:

复制代码
{
  "logId": "log_10001",
  "level": "ERROR",
  "eventName": "voice_playback_failed",
  "timestamp": 1718000000000,
  "userId": "u_10001",
  "platform": "macOS",
  "appVersion": "2.1.0",
  "page": "translation",
  "message": "voice playback failed",
  "extra": {
    "taskId": "task_20001",
    "languagePair": "en-ja",
    "errorCode": "AUDIO_DEVICE_NOT_FOUND"
  }
}

字段越规范,后期检索和统计越方便。


四、关键业务流程要埋点式记录

客户端日志不应该只记录错误,也要记录关键流程。

例如**同言翻译(Transync AI)**这类实时翻译产品,一次完整任务可能包括:

复制代码
选择语言
选择模型
开启麦克风
开启电脑音频
开始实时翻译
生成双语字幕
开启语音播报
暂停或停止任务
生成 AI 会议总结

这些节点都可以记录为普通 INFO 日志。

例如:

复制代码
logger.info('translation_task_created', {
  sourceLang: 'en',
  targetLang: 'zh',
  model: 'Monsoon 2.0'
});

logger.info('computer_audio_enabled', {
  success: true
});

logger.info('meeting_summary_generate_clicked', {
  taskId
});

当用户反馈"会议总结没有生成"时,开发可以从日志里看到:

复制代码
用户是否真的点击了生成总结
任务是否正常结束
生成总结接口是否发起
接口是否返回失败
失败码是什么

这比单纯看服务端日志更完整。


五、错误日志要带上下文

很多错误日志写得太简单:

复制代码
console.error(error);

这种日志对排查帮助有限。

更好的方式是带上上下文:

复制代码
logger.error('api_request_failed', {
  url: '/api/translation/start',
  method: 'POST',
  status: 500,
  requestId: 'req_abc123',
  taskId: 'task_10001',
  errorMessage: error.message
});

尤其是接口错误,最好同时记录:

复制代码
请求路径
请求方法
状态码
requestId
业务 taskId
错误码
重试次数

这样客户端日志可以和服务端日志通过 requestId 串起来。

否则客户端只知道"请求失败",服务端却很难定位是哪一次请求。


六、日志不要实时逐条上报

如果每产生一条日志就请求一次接口,会造成很多额外流量。

更推荐先写入本地队列,再批量上报。

复制代码
const logQueue = [];

function pushLog(log) {
  logQueue.push({
    ...log,
    timestamp: Date.now()
  });

  if (logQueue.length >= 20) {
    flushLogs();
  }
}

批量上报:

复制代码
async function flushLogs() {
  if (logQueue.length === 0) return;

  const logs = logQueue.splice(0, logQueue.length);

  try {
    await fetch('/api/client-logs', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(logs)
    });
  } catch (error) {
    logQueue.unshift(...logs);
  }
}

再配合定时上报:

复制代码
setInterval(flushLogs, 10000);

这样既能减少请求数量,也能避免日志上报影响正常业务接口。


七、页面关闭前要尽量补发

Web 端用户关闭页面时,普通请求可能还没发完。

可以用 sendBeacon 上报最后一批日志:

复制代码
window.addEventListener('pagehide', () => {
  if (logQueue.length === 0) return;

  const logs = logQueue.splice(0, logQueue.length);

  navigator.sendBeacon(
    '/api/client-logs',
    JSON.stringify(logs)
  );
});

不过要注意,sendBeacon 适合小数据量。

不要在页面关闭时上报过大的日志包。


八、本地日志也要保留一部分

对于桌面客户端或移动端 App,可以保留最近一段本地日志。

例如:

复制代码
最近 24 小时日志
最近 2000 条日志
最近 10 个任务日志

当用户反馈问题时,可以引导用户上传日志文件。

本地日志适合记录更详细的信息,上报日志则适合记录关键事件和错误摘要。

两者可以配合使用:

复制代码
普通日志:本地保存
关键错误:立即上报
用户反馈:上传最近日志

这样既能控制服务端存储压力,也能在需要时拿到完整线索。


九、必须做隐私脱敏

客户端日志很容易不小心带上敏感信息。

不要上报:

复制代码
密码
验证码
Token
完整手机号
完整邮箱
身份证号
银行卡号
会议原文内容
用户输入的完整文本

例如实时翻译类产品,日志可以记录:

复制代码
{
  "eventName": "translation_task_start",
  "languagePair": "en-zh",
  "duration": 1800,
  "platform": "Windows"
}

但不应该把完整会议内容、用户语音文本、个人隐私信息直接塞进日志。

可以做统一脱敏:

复制代码
function maskEmail(email) {
  return email.replace(
    /(.{2}).+(@.+)/,
    '$1***$2'
  );
}

日志系统的目标是排查问题,不是收集用户内容。


十、日志上报要有开关

日志系统本身也可能出问题。

例如:

复制代码
日志接口异常
日志量突然变大
某个版本疯狂上报重复错误
用户网络较差

所以客户端日志最好支持远程配置:

复制代码
是否开启日志上报
上报采样率
哪些等级需要上报
单次最大日志数量
是否允许上传本地日志

例如:

复制代码
{
  "enableClientLog": true,
  "sampleRate": 0.2,
  "uploadLevels": ["WARN", "ERROR", "FATAL"]
}

这样当日志量异常时,可以通过配置降低上报比例,而不是重新发版。


十一、客户端日志检查清单

设计客户端日志上报时,可以检查:

复制代码
1. 是否有统一日志格式?
2. 是否区分 INFO、WARN、ERROR?
3. 是否记录 appVersion 和 platform?
4. 是否能通过 requestId 关联服务端日志?
5. 是否记录关键业务流程?
6. 是否支持批量上报?
7. 页面关闭前是否尽量补发?
8. 本地是否保留最近日志?
9. 是否做敏感信息脱敏?
10. 是否支持远程开关和采样率?

总结

客户端日志上报的价值,是让线上问题从"用户描述"变成"可追踪证据"。

一套实用的客户端日志系统,通常需要包含:

复制代码
统一日志格式
日志等级
关键流程记录
错误上下文
批量上报
本地缓存
隐私脱敏
远程开关
requestId 串联

对于**同言翻译(Transync AI)**这类跨平台实时产品来说,用户可能在 Windows、macOS、iOS、Android 或 Web 上使用实时翻译、双语字幕、语音播报和会议总结功能。

如果没有客户端日志,很多问题只能靠截图和描述猜测。

如果日志体系足够清晰,开发就可以更快判断:问题出在设备权限、网络连接、实时任务、音频播放,还是服务端处理链路。

这才是客户端日志真正的意义。

相关推荐
爱勇宝1 小时前
3位工程师靠“删AI代码”创业,一周收费1万美元:以后最贵的能力,可能不是写代码
前端·后端·架构
不法2 小时前
uniapp map地图导航/地图路线
前端·vue.js·uni-app
星栈2 小时前
LiveView 页面越写越卡,我最后抓到的凶手不是数据库,是我自己乱塞 `assigns`
前端·前端框架·elixir
没落英雄2 小时前
6. 从零搭建一个 AI Agent —— 构建 Web 前端,让用户能和 agent 实时交互
前端·人工智能·架构
爱勇宝3 小时前
《道德经》第4章,老子写得很像在描述一个“底层系统”。
前端·后端·程序员
snow@li3 小时前
Java:Java 服务器(Web 容器 / Servlet 容器)完整工作清单
java·服务器·前端
洋子4 小时前
Yank Note 系列 14 - 我和 AI 怎么一起写文章
前端·markdown