JavaScript中可通过高阶函数封装日志逻辑,实现错误捕获、格式化与上报的统一处理:1. createErrorLogger返回增强日志函数;2. withErrorHandling包装异步操作自动报错;3. setupGlobalErrorCapture集成全局监听;4. 多高阶函数组合构建可配置日志管道。

JavaScript 中可以通过高阶函数封装日志收集逻辑,把错误捕获、格式化、上报等行为抽象为可复用的函数,实现统一处理。核心思路是:用一个函数接收原始日志方法(如 console.error 或自定义上报函数),返回一个增强后的日志函数,自动注入时间、环境、堆栈、上下文等字段。
封装带格式化的错误日志函数
定义一个高阶函数 createErrorLogger,它接收配置项(如环境、服务名、上报地址)和底层发送函数,返回一个可直接调用的 logError 函数:
- 自动捕获
error.stack和error.message - 补全时间戳、运行环境(
process.env.NODE_ENV或location.hostname)、用户 ID(若已登录)等元信息 - 支持传入额外上下文(如当前页面 URL、操作步骤、请求参数)
示例:
const createErrorLogger = (config) => {
const { env = 'development', serviceName = 'web-app', sendToServer } = config;
return (error, context = {}) => {
const now = new Date().toISOString();
const formatted = {
timestamp: now,
level: 'error',
env,
serviceName,
message: error?.message || 'Unknown error',
stack: error?.stack || 'No stack trace',
url: typeof window !== 'undefined' ? window.location.href : '',
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',
...context
};
// 同时输出到控制台(开发时方便调试)
if (env === 'development') {
console.error('[Formatted Error]', formatted);
}
// 异步上报(避免阻塞主线程)
if (sendToServer && typeof sendToServer === 'function') {
sendToServer(formatted).catch(() => {});
}
};
};
// 使用
const logError = createErrorLogger({
env: process.env.NODE_ENV,
serviceName: 'checkout-page',
sendToServer: (data) => fetch('/api/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
});
// 在任意地方调用
try {
riskyOperation();
} catch (e) {
logError(e, { action: 'submit-payment', orderId: '12345' });
}
用高阶函数包装异步操作自动捕获错误
对 fetch、Promise 或 API 调用进行统一错误拦截,避免每个地方重复写 try/catch:
立即学习“Java免费学习笔记(深入)”;
- 定义
withErrorHandling,接收一个异步函数和日志函数,返回一个自动捕获并格式化上报错误的新函数 - 保留原始返回值(成功时透传结果),只在失败时触发日志
- 可叠加使用,比如先加重试逻辑,再加错误日志
示例:
const withErrorHandling = (asyncFn, logFn) => async (...args) => {
try {
return await asyncFn(...args);
} catch (error) {
logFn(error, {
operation: asyncFn.name || 'anonymous-async-op',
args: JSON.stringify(args).length < 200 ? args : '[too long]',
timestamp: Date.now()
});
throw error; // 仍抛出,由上层决定是否继续处理
}
};
// 包装 fetch
const safeFetch = withErrorHandling(fetch, logError);
// 使用
safeFetch('/api/user').then(res => res.json()).catch(() => {
// 这里仍可做业务级降级
});
全局错误监听 + 高阶函数增强
结合浏览器原生事件(window.onerror、window.addEventListener('unhandledrejection')),用高阶函数注入统一格式逻辑:
- 避免手动拼接错误对象,统一走格式化函数
- 区分同步错误、异步拒绝、资源加载失败(
error事件) - 过滤掉已知第三方脚本错误(如 CDN 失败)或白名单外域名
示例:
const setupGlobalErrorCapture = (logFn) => {
window.onerror = (message, source, lineno, colno, error) => {
logFn(error || { message, source, lineno, colno }, {
type: 'window-error',
source
});
};
window.addEventListener('unhandledrejection', (event) => {
logFn(event.reason, {
type: 'unhandled-rejection',
promise: event.promise
});
});
// 可选:监听 script/link 加载失败
document.addEventListener('error', (event) => {
if (event.target && ['SCRIPT', 'LINK'].includes(event.target.tagName)) {
logFn(new Error(`Resource load failed: ${event.target.src || event.target.href}`), {
type: 'resource-error',
target: event.target.tagName,
src: event.target.src || event.target.href
});
}
}, true);
};
setupGlobalErrorCapture(logError);
组合多个高阶函数实现日志管道
利用函数式思想,把不同职责拆成独立高阶函数,再组合(类似中间件):
-
withTimestamp:添加时间字段 -
withContext:注入全局上下文(如用户、设备信息) -
withSampling:按比例采样,避免日志爆炸 -
withRetry:上报失败时自动重试
组合方式(从右到左执行):
const enhancedLogger = withRetry(
withSampling(
withContext(
withTimestamp(logError)
)
)
);
这样既保持单个函数职责单一,又灵活可配,适合中大型项目演进。


















