依托于腾讯的vconsole实现日志上传功能

100 阅读5分钟

一、复现bug的痛点-- 日志收集方式

在软件测试过程中,当遇到一个bug时,复现这个问题是解决问题的第一步。然而,直接通过界面操作进行复现(例如截图或录屏)往往不能提供足够的信息来理解问题的根本原因,特别是对于那些偶发性的问题。这些问题可能不会在每次尝试中都出现,这就增加了它们的复现难度。因此,获取和分析实时日志成为了解决这类问题的关键。

日志记录了程序运行期间的各种状态变化、执行路径以及异常情况等详细信息,是定位问题根源的重要依据。为了有效地利用日志来帮助复现和解决bug,我们需要能够方便地获取到这些日志数据。这不仅意味着要确保系统在开发时就设计有完善的日志记录机制,还需要有一种便捷的方式让测试人员能够在问题发生的同时获取到相应的日志信息,而不是事后通过回忆或者间接方式去推测问题的发生过程。

理想情况下,应该有一个工具或平台支持实时查看和检索日志的功能,使得测试人员可以在发现bug的第一时间快速访问相关的日志内容。这样不仅能加快问题的定位速度,还能提高问题复现的概率,从而加速整个调试过程。此外,结合自动化测试脚本与日志分析工具,可以进一步提升对偶发性问题的捕捉能力,为开发团队提供更精确的问题描述和修复建议。总之,高效利用日志是解决难以复现bug的有效途径之一。

二、 vconsole介绍

VConsole 是一个由 WeChat 开源的轻量级、可嵌入的 JavaScript 日志打印面板,专为前端开发者设计,方便在移动设备上调试网页或微信小程序。它能够帮助开发者轻松查看控制台日志、网络请求、DOM 结构以及本地存储等信息,极大地提高了在移动环境下进行调试的效率。

以下是 VConsole 的一些主要特点:

  • 便捷的日志查看:VConsole 提供了一个可视化的界面来展示 console 方法(如 log, info, warn, error)输出的信息,让开发者可以在移动设备上直接查看这些日志,无需依赖电脑上的浏览器开发工具。
  • 网络请求监控:不仅可以查看页面发起的所有网络请求详情,包括请求头、响应头、状态码等信息,还可以通过它来分析网络性能问题。
  • 元素检查功能:允许开发者检查页面中的 DOM 元素,并查看其样式属性等详细信息,类似于桌面浏览器的元素审查工具。
  • 系统信息展示:提供了一些关于当前运行环境的基本信息,例如操作系统、浏览器版本等,有助于快速了解用户的访问环境。
  • 易于集成与使用:只需引入 VConsole 的脚本文件到项目中,即可在页面底部看到一个入口按钮,点击后便可展开详细的调试面板。

总的来说,VConsole 是一个非常实用的工具,尤其是在移动设备调试不便的情况下,它可以作为开发者的好帮手,简化了前端调试流程,提高了工作效率。无论是对于日常开发还是紧急问题排查,VConsole 都能提供有力的支持。

三、 在vconsole基础上开发的vconsolelogs,支持上传日志

vconsole的git仓库代码如图所示

image.png

3.1 修改log.model.ts,增加上传日志请求

` // 收集数据 包括log日志、网络请求日志、cookie、localstorage等 public async uploadLogs( logOptions: VConsoleLogOptions, onProgress?: (progress: number) => void ): Promise { let logData; try { const logStores = Store.getAll(); const storeData = Store.getRaw("system"); const uploadData = get(uploadRequestList); // 获取 uploadRequestList 的数据

  // 获取存储相关数据
  const defaultStorages = get(storageStore.defaultStorages);
  const activedStorage = get(storageStore.activedName);
  const storageUpdateTime = get(storageStore.updateTime);

  // 收集所有数据
  logData = {
    // this.exportLog收集的需要导出的日志,
    console: { default: this.exportLogs, system: storeData.logList }, // 使用 uploadLogQueue 中的日志
    network: uploadData, // 上传请求数据
    storage: {
      defaultStorages,
      activedStorage,
      updateTime: storageUpdateTime,
      data: {
        localStorage: this._getLocalStorageData(),
        sessionStorage: this._getSessionStorageData(),
        cookies: this._getCookieData(),
      },
    },
    system: {
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href,
    },
  };

  // 压缩数据并转换为 Base64
  const compressedData = this.compressData(logData);

  // 使用配置中的上传端点
  const UPLOAD_ENDPOINT = logOptions?.uploadUrl;

  // 使用 XMLHttpRequest 发送到服务器
  const result = await new Promise<{
    code: number;
    message?: string;
    data: { id: string };
  }>((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("POST", UPLOAD_ENDPOINT);
    xhr.setRequestHeader("Content-Type", "application/json");

    xhr.onload = () => {
      if (xhr.status === 200 || xhr.status === 201) {
        try {
          const response = JSON.parse(xhr.responseText);
          resolve(response);
        } catch (e) {
          reject(new Error("解析响应失败"));
        }
      } else {
        reject(new Error(`上传失败: ${xhr.status}`));
      }
    };

    xhr.onerror = (event) => {
      const error = new Error("网络错误");
      error.message = xhr.statusText;
      reject(error);
    };
    xhr.send(JSON.stringify({ extra: compressedData }));
  });

  if (result.code !== 200 && result.code !== 201) {
    // 修改这里以处理 201 状态码
    throw new Error(`上传失败: ${result.message || "未知错误"}`);
  }

  return `${logOptions?.showUrl}&id=${result.data.id}`; // 返回上传后的 ID
} catch (error) {
  throw error;
}
// 防止太大导致请求出问题
private compressData(data: any): string {
const jsonString = JSON.stringify(data, (key, value) => {
  // 去除不必要的字段
  if (key === "largeField" || key === "unnecessaryField") {
    return undefined;
  }
  return value;
});

// 使用 pako 进行 gzip 压缩
const compressed = pako.gzip(jsonString, { level: 9 });

// 使用 js-base64 将压缩后的数据转换为 Base64
return Base64.fromUint8Array(compressed);

} } `

3.2 修改log.ts,增加点击上传按钮

image.png

3.3 添加上传弹窗,用于去平台查看上传后的日志

image.png

image.png

四、新建一个前后端项目去展示日志

使用mongoose去存储数据,具体见mongoose使用

image.png 拿到数据,写一个前端界面去展示即可

image.png

vconsole的部分可见:github.com/MXY-LF/vcon… 开箱即用,不过需要自己写一个存储、展示日志的前后端项目(next.js、next.js都可)

npm:www.npmjs.com/package/vco…