hejianhao
2026-04-13 19beaf98fed259d738467eeb5bf35e4cc74a49cf
feat: 添加 WebSocket 和 HTTP 请求的日志上传功能

引入 uploadMiniLog 工具函数,用于将非心跳 WebSocket 消息以及 HTTP 请求的响应和错误信息上传至日志服务器。这有助于后续的监控和问题排查。
2个文件已修改
61 ■■■■■ 已修改文件
src/utils/request.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/websocket.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.ts
@@ -10,10 +10,11 @@
  getStorage,
  objectSort,
} from '../utils/tools';
import { _XP_SECRET, _UID } from '../constants/api';
import { _ROBOTAXI_HOST, _XP_SECRET, _UID } from '../constants/api';
import { jumpTo } from './jump';
const CODE_SUCCESS = 200
const MINI_LOG_UPLOAD_PATH = '/operation/base/v1/mini-program/miniLog/uploadLog';
const CODE_AUTH_EXPIRED_ARRAY = [12010301, 12010302, 12010303, 12010304, 12010305, 12010306, 12010320, 12010002];
// 12010301, "登录状态已过期,请重新登录", "otp缓存已过期"),
// 12010302, "登录状态已过期,请重新登录", "otp已过期"),
@@ -52,6 +53,17 @@
    header
  })
    .then(async (res) => {
      if (!isMiniLogUploadUrl(url)) {
        uploadMiniLog(
          JSON.stringify({
            type: 'http_response',
            url,
            request: payload,
            response: res?.data,
            timestamp: Date.now()
          })
        );
      }
      // res为包含cookies、header的经过小程序封装的数据
      // 第一层data为接口返回数据,包含接口状态码
      // 第二层data一般为业务数据
@@ -86,6 +98,17 @@
      return data
    })
    .catch((err) => {
      if (!isMiniLogUploadUrl(url)) {
        uploadMiniLog(
          JSON.stringify({
            type: 'http_error',
            url,
            request: payload,
            response: err,
            timestamp: Date.now()
          })
        );
      }
      console.log('request err:', url);
      console.log(err);
@@ -116,6 +139,28 @@
    });
}
const isMiniLogUploadUrl = (url: string = '') => {
  return typeof url === 'string' && url.includes(MINI_LOG_UPLOAD_PATH);
};
export const uploadMiniLog = async (logContent: string) => {
  if (!logContent) return;
  try {
    const header = await buildHeader({ method: 'POST', payload: logContent }, false);
    if (!header) return;
    const uploadUrl = `${_ROBOTAXI_HOST}${MINI_LOG_UPLOAD_PATH}`;
    Taro.request({
      url: uploadUrl,
      method: 'POST',
      data: logContent,
      header: {
        ...header,
        'Content-Type': 'application/json'
      }
    }).catch(() => { });
  } catch (_) { }
};
async function buildHeader(options, needLogin) {
  const { method = 'POST', payload, extraHeader = {} } = options;
  const header = Object.assign({}, PUBLIC_HEADER);
src/utils/websocket.ts
@@ -2,6 +2,7 @@
import { getStorage } from './tools';
import { _UID } from '../constants/api';
import { safeParse } from './Robotaxi-public-method';
import { uploadMiniLog } from './request';
type MessageCallback = (data: any) => void;
@@ -21,6 +22,11 @@
  private heartbeatTimeout: number = 3000;   // 5秒收不到响应即视为掉线
  private listeners: Map<string, MessageCallback[]> = new Map();
  private globalEventsBound: boolean = false;
  private isHeartbeatMessage(data: any) {
    const type = String(data?.type || '').toLowerCase();
    return type === 'ping' || type === 'pong';
  }
  private constructor() { }
@@ -111,6 +117,10 @@
        try {
          data = safeParse(res.data as string, {});
          console.log('收到 WebSocket 消息:', res.data);
          if (!this.isHeartbeatMessage(data)) {
            const messageContent = typeof res.data === 'string' ? res.data : JSON.stringify(res.data);
            uploadMiniLog(messageContent);
          }
        } catch (e) {
          console.warn('解析 WebSocket data 失败:', res.data);
          this.notifyListeners('message', res.data);
@@ -177,6 +187,10 @@
      try {
        data = safeParse(res.data as string, {});
        console.log('收到 WebSocket 消息:', res.data);
        if (!this.isHeartbeatMessage(data)) {
          const messageContent = typeof res.data === 'string' ? res.data : JSON.stringify(res.data);
          uploadMiniLog(messageContent);
        }
      } catch (e) {
        console.warn('解析 WebSocket data 失败:', res.data);
        this.notifyListeners('message', res.data);