console-capturer
v1.1.9
Published
It can be used to catch all errors in the system and provide performance monitoring
Maintainers
Readme
errorCaptured 工具函数使用文档
请优先下载最新版本库 errorCaptured 是一个用于捕获和管理前端错误、日志、网络请求、性能数据等信息的工具函数。它可以帮助开发者监控应用的运行状态,并在需要时将数据持久化存储 QQ 讨论群:324710217
安装
npm i console-capturerCDN地址
<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。
