npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

console-capturer

v1.1.9

Published

It can be used to catch all errors in the system and provide performance monitoring

Readme

errorCaptured 工具函数使用文档

请优先下载最新版本库 errorCaptured 是一个用于捕获和管理前端错误、日志、网络请求、性能数据等信息的工具函数。它可以帮助开发者监控应用的运行状态,并在需要时将数据持久化存储 QQ 讨论群:324710217

安装

npm i console-capturer

CDN地址

<script src="https://cdn.jsdelivr.net/npm/console-capturer/dist/index.min.js"></script>

使用示例

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>errorCaptured Demo</title>
    <style>
      body {
        font-family: "Arial", sans-serif;
        max-width: 800px;
        margin: 0 auto;
        padding: 20px;
      }
      button {
        margin: 5px;
        padding: 8px 12px;
        cursor: pointer;
      }
      pre {
        background-color: #f5f5f5;
        padding: 10px;
        border-radius: 5px;
        overflow-x: auto;
      }
      .log-container {
        margin-top: 20px;
        border: 1px solid #ddd;
        padding: 10px;
        height: 300px;
        overflow-y: auto;
      }
      .log-entry {
        margin-bottom: 5px;
        padding: 5px;
        border-bottom: 1px solid #eee;
      }
      .log-type {
        font-weight: bold;
        margin-right: 10px;
      }
      .log-time {
        color: #666;
        font-size: 0.9em;
      }
      .log-message {
        margin-top: 5px;
      }
      .ERROR,
      .WINDOW_ERROR,
      .PROMISE_REJECTION,
      .NETWORK_ERROR {
        background-color: #ffebee;
      }
      .WARN {
        background-color: #fff8e1;
      }
      .INFO {
        background-color: #e1f5fe;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/console-capturer/dist/index.min.js"></script>
  </head>
  <body>
    <h1>errorCaptured 演示</h1>

    <div>
      <h3>测试操作</h3>
      <button id="logBtn">打印日志</button>
      <button id="warnBtn">打印警告</button>
      <button id="errorBtn">打印错误</button>
      <button id="throwErrorBtn">抛出错误</button>
      <button id="promiseErrorBtn">Promise错误</button>
      <button id="fetchBtn">网络请求</button>
      <button id="reportErrorLogBtn">测试 reportError 内打印</button>
      <button id="lookAllLog">查看所有的日志信息</button>
    </div>

    <div>
      <h3>捕获的日志</h3>
      <div id="logOutput" class="log-container"></div>
    </div>

    <script>
      const errorCapture = errorCaptured({
        captureConsole: true,
        captureErrors: true,
        captureNetwork: true,
        maxLogs: 100,
        reportError: (newItems, beforePush, afterPush, originalConsole) => {
          // 这个回调中的日志和错误不会被记录
          originalConsole.log("⚡ 报告新日志:", newItems);

          // 如果是错误类型的日志,可以在这里进行上报
          if (
            newItems.some((item) =>
              [
                "ERROR",
                "WINDOW_ERROR",
                "PROMISE_REJECTION",
                "NETWORK_ERROR",
              ].includes(item.type)
            )
          ) {
            originalConsole.warn("⚠️ 检测到错误,可以在这里发送到服务器");
          }
        },
      });

      // 显示日志到页面
      function renderLogs() {
        const logOutput = document.getElementById("logOutput");
        logOutput.innerHTML = "";

        errorCapture.logs.forEach((log) => {
          const logEntry = document.createElement("div");
          logEntry.className = `log-entry ${log.type}`;

          // 显示日志类型和时间
          const logHeader = document.createElement("div");

          const logType = document.createElement("span");
          logType.className = "log-type";
          logType.textContent = log.type;
          logHeader.appendChild(logType);

          const logTime = document.createElement("span");
          logTime.className = "log-time";
          logTime.textContent = new Date(log.timestamp).toLocaleTimeString();
          logHeader.appendChild(logTime);

          logEntry.appendChild(logHeader);

          // 显示日志消息
          const logMessage = document.createElement("div");
          logMessage.className = "log-message";

          if (log.type === "NETWORK" || log.type === "NETWORK_ERROR") {
            logMessage.textContent = `${log.subtype} ${log.url} - Status: ${log.status}`;
            if (log.duration) {
              logMessage.textContent += ` (${log.duration}ms)`;
            }
          } else if (log.messages) {
            logMessage.textContent = log.messages
              .map((msg) =>
                typeof msg === "object" ? JSON.stringify(msg) : msg
              )
              .join(" ");
          }

          logEntry.appendChild(logMessage);

          // 如果有堆栈信息,添加一个折叠的详情部分
          if (log.stack) {
            const details = document.createElement("details");
            const summary = document.createElement("summary");
            summary.textContent = "Stack trace";
            details.appendChild(summary);

            const pre = document.createElement("pre");
            pre.textContent = log.stack;
            details.appendChild(pre);

            logEntry.appendChild(details);
          }

          logOutput.appendChild(logEntry);
        });

        // 滚动到底部
        logOutput.scrollTop = logOutput.scrollHeight;
      }

      // 添加按钮事件
      document.getElementById("logBtn").addEventListener("click", () => {
        console.log("这是一条普通日志", { data: "some data" });
        renderLogs();
      });

      document.getElementById("warnBtn").addEventListener("click", () => {
        console.warn("这是一条警告信息");
        renderLogs();
      });

      document.getElementById("errorBtn").addEventListener("click", () => {
        console.error("这是一条错误信息");
        renderLogs();
      });

      document.getElementById("throwErrorBtn").addEventListener("click", () => {
        try {
          throw new Error("手动抛出的错误");
        } catch (error) {
          console.error("捕获到错误:", error);
        }
        renderLogs();
      });

      document
        .getElementById("promiseErrorBtn")
        .addEventListener("click", () => {
          // 创建一个会被拒绝的 Promise
          new Promise((resolve, reject) => {
            setTimeout(() => {
              reject("Promise 被拒绝了");
            }, 100);
          }).catch((err) => {
            console.log("Promise错误被捕获:", err);
          });

          // 一个未捕获的 Promise 错误
          setTimeout(() => {
            new Promise((resolve, reject) => {
              reject("未捕获的 Promise 错误");
            });
            setTimeout(renderLogs, 200);
          }, 200);
        });

      document
        .getElementById("fetchBtn")
        .addEventListener("click", async () => {
          try {
            // 成功的请求
            const response = await fetch(
              "https://jsonplaceholder.typicode.com/todos/1"
            );
            const data = await response.json();
            console.log("请求成功:", data);

            // 失败的请求
            setTimeout(async () => {
              try {
                await fetch("https://non-existent-domain-123456.com");
              } catch (error) {
                console.error("请求失败:", error);
              }
              renderLogs();
            }, 500);
          } catch (error) {
            console.error("请求出错:", error);
          }
          setTimeout(renderLogs, 1000);
        });

      document
        .getElementById("reportErrorLogBtn")
        .addEventListener("click", () => {
          console.log("测试 reportError 内的打印不被捕获");
          console.error(
            "这个错误会被 reportError 处理,里面的打印不应该被捕获"
          );
          renderLogs();
        });

      document.getElementById("lookAllLog").addEventListener("click", () => {
        alert("日志已打印在控制台,请打开控制台查看");
        const { originalConsole, logs } = errorCapture;
        originalConsole.log("捕获到的所有日志:",logs);
      });
      // 初始渲染
      renderLogs();
    </script>
  </body>
</html>

捕获日志结构展示

[
  {
    type: "LOG",
    timestamp: "2023-10-01T12:00:00.000Z",
    messages: ["Hello, World!"],
  },
  {
    type: "NETWORK",
    subtype: "FETCH",
    timestamp: "2023-10-01T12:00:01.000Z",
    url: "https://jsonplaceholder.typicode.com/todos/1",
    status: 200,
    duration: 150,
  },
  {
    type: "ERROR",
    timestamp: "2023-10-01T12:00:02.000Z",
    messages: ["ReferenceError: foo is not defined"],
    stack: "Error stack trace...",
  },
];

入参介绍

import errorCaptured from "./errorCaptured";

const { logs, currentlogs, disableAllCaptures } = errorCaptured({
  captureConsole: true, // 是否捕获 console 日志
  captureErrors: true, // 是否捕获全局错误
  captureNetwork: true, // 是否捕获网络请求
  capturePerformance: false, // 是否捕获性能数据
  enableStorage: true, // 是否启用持久化存储
  storageType: "local", // 存储类型(可选 'local' 或 'session')
  storageName: "console_logs", // 存储名称
  storageMaxItems: 100, // 存储的最大条目数
  storageInterval: 5000, // 存储间隔时间(毫秒)
  maxLogs: 1000, // 日志的最大条数
  reportError: (newItems, beforePush, afterPush, originalConsole) => {
    /**
     * newItems: 本次新增的日志数据
     * beforePush: 添加本次新增日志之前的数据,即上一次的日志数据
     * afterPush: 添加本次日志之后调用的数据,即最新的日志数据
     * originalConsole: 原始的 console 对象,使用该方法打印,不会被日志监控所记录
     */
    // 自定义错误报告逻辑
    // 这里建议使用 originalConsole.log 打印输出,可以避免打印被监听
    originalConsole.log("New logs:", newItems);
  },
});

返回值介绍

errorCaptured 返回一个对象,包含以下属性和方法:

/**
 * 日志捕获器的配置和状态信息。
 * @property {Array} logs - 这里面存储了所有捕获到的日志信息。
 * @property {Array} currentlogs - 本次新增的日志。
 * @property {Object} originalConsole - 原始的console对象,可以用于控制台打印日志,但是不会被监听。
 * @property {Object} cleanupFunctions - 包含各个监控功能的撤销方法的,可以用于撤销单独的监控功能。
 * @property {function} disableAllCaptures - 撤销所有日志监控功能。
 *
 * @example
  const { logs, 
          currentlogs, 
          originalConsole, 
          cleanupFunctions, 
          disableAllCaptures 
        } = errorCaptured({
          // 你需要传入的配置项
        });
 */

日志数据结构属性解释

每条日志是一个对象,包含以下字段(具体数据结构可参见上方 捕获日志结构展示):

/**
 * 日志数据库字段说明。
 * @typedef {Object} LogDatabaseFields
 * @property {string} type - 日志类型,如"LOG","NETWORK","ERROR"等
 * @property {string} timestamp - 日志时间戳,格式为ISO8601。记录日志打印的时间
 * @property {Array} messages - 日志内容,为本次日志的输出内容和报错信息
 * @property {string} [subtype] - 网络日志类型(仅适用于网络日志),如"FETCH","XHR"等,表示网络请求类型
 * @property {string} [url] - 网络请求的URL(仅适用于网络日志),表示网络请求的URL
 * @property {number} [status] - 网络请求的状态码(仅适用于网络日志),表示网络请求的状态码
 * @property {number} [duration] - 请求持续时间,表示调用此次接口或者加载此次资源所耗费的时间
 * @property {string} [stack] - 错误堆栈信息(存在于错误日志)。如果该字段是网络日志,则表示堆栈跟踪信息。
 * 
 * @example
 * const logEntry = {
 *   type: 'NETWORK',
 *   subtype: 'FETCH',
 *   timestamp: '2023-10-05T14:48:00.000Z',
 *   messages: ['Fetch request sent', 'Response received'],
 *   url: 'https://example.com/api/data',
 *   status: 200,
 *   duration: 1234, // in milliseconds
 *   stack: null // No error stack for network logs
 * };
 * 
 * // For a log entry that is an error
 * const errorEntry = {
 *   type: 'ERROR',
 *   subtype: null,
 *   timestamp: '2023-10-05T14:49:00.000Z',
 *   messages: ['Uncaught TypeError: Cannot read property 'x' of undefined'],
 *   url: null,
 *   status: null,
 *   duration: null,
 *   stack: 'TypeError: Cannot read property 'x' of undefined\n    at ...'
 * };
 */

注意事项

撤销监控:如果需要恢复原始状态,撤销所有日志监控,可以调用 disableAllCaptures 方法。 持久化存储:启用持久化存储后,日志会被定时保存到 localStorage 或 sessionStorage 中。 性能开销:捕获大量日志可能会影响性能,建议根据实际需求配置 maxLogs 和 storageMaxItems 参数。 originalConsole:如果你不想此次输出被监听,可以设置 originalConsole.log()进行打印,此次打印结构不会被监听。 reportError:在 reportError 回调函数中,建议使用 originalConsole.log 打印输出,可以避免打印被监听

相关 API

建议

如果对此工具包有更好的建议或需要支持新的功能,欢迎提 issue 或者加本人的 QQ:1844119859。