用 alert() 提示符合匹配规则的日志或未捕获异常,帮助开发者在日常使用网页时发现潜藏问题
当前为
// ==UserScript==
// @name [DEBUG] 显式日志
// @version 1.0.1.20210717
// @namespace laster2800
// @author Laster2800
// @description 用 alert() 提示符合匹配规则的日志或未捕获异常,帮助开发者在日常使用网页时发现潜藏问题
// @homepage https://greasyfork.org/zh-CN/scripts/429521
// @supportURL https://greasyfork.org/zh-CN/scripts/429521/feedback
// @license LGPL-3.0
// @include *
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @run-at document-start
// @incompatible firefox 完全不兼容 Greasemonkey,不完全兼容 Violentmonkey
// ==/UserScript==
(function() {
'use strict'
try {
const gmInclude = GM_getValue('include')
const gmExclude = GM_getValue('exclude')
let include = gmInclude ? new RegExp(gmInclude) : null
let exclude = gmExclude ? new RegExp(gmExclude) : null
// 日志
const console = unsafeWindow.console
const logs = ['log', 'warn', 'error']
for (const log of logs) {
const _ = console[log]
console[log] = function() {
const m = [arguments, log.toUpperCase()]
if (match(m, include) && !match(m, exclude)) {
explicit(arguments.length == 1 ? arguments[0] : JSON.stringify(arguments), log.toUpperCase())
}
return _.apply(console, arguments)
}
}
// 未捕获异常
unsafeWindow.addEventListener('error', function(event) { // 正常
const m = [event.error, event.filename, 'Uncaught Exception (Normal)']
if (match(m, include) && !match(m, exclude)) {
explicit(event.error, 'Uncaught Exception (Normal)')
}
})
unsafeWindow.addEventListener('unhandledrejection', function(event) { // from Promise
const m = [event.reason, 'Uncaught Exception (in Promise)']
if (match(m, include) && !match(m, exclude)) {
explicit(event.reason, 'Uncaught Exception (in Promise)')
}
})
// 菜单
GM_registerMenuCommand('设置过滤器', () => {
try {
const sInclude = prompt(`【${GM_info.script.name}】\n\n` + '设置匹配过滤器', include?.source ?? '.*')
if (sInclude) {
include = new RegExp(sInclude)
GM_setValue('include', sInclude)
}
const sExclude = prompt(`【${GM_info.script.name}】\n\n` + '设置排除过滤器', exclude?.source ?? '^LOG$')
if (sExclude) {
exclude = new RegExp(sExclude)
GM_setValue('exclude', sExclude)
}
} catch (e) {
explicit(e)
}
})
GM_registerMenuCommand('说明', () => {
alert(`【${GM_info.script.name}】\n\n` + '规则\n\n* 正则匹配,区分大小写,不必考虑转义。\n\n* 日志:可用 `LOG` / `WARN` / `ERROR` 作为匹配目标。如用 `^LOG$` 作为排除过滤器排除所有 INFO 级别日志。\n* 日志:无法监听到非直接通过 `console` 对象打印出来的日志,如在油猴黑箱中运行的脚本打印出来的日志。\n\n* 未捕获异常(正常):可用 `Uncaught Exception (Normal)` 作为匹配目标。如简单地用 `cau` 来过滤出所有未捕获异常,但可能混杂带 `cau` 信息的日志。\n* 未捕获异常(正常):可用异常抛出处的文件名作为匹配目标。\n\n* 未捕获异常(Promise):可用 `Uncaught Exception (in Promise)` 作为匹配目标。')
})
} catch (e) {
explicit(e)
}
})()
/**
* 显式地显示信息
* @param {*} msg 信息
* @param {string} [label] 标记
*/
function explicit(msg, label) {
alert(`【${GM_info.script.name}】${label ? `【${label}】` : ''}\n\n${msg}`)
}
/**
* @param {*} obj 匹配对象
* @param {RegExp} regex 匹配正则表达式
* @param {number} [depth=5] 匹配查找深度
* @returns {boolean} 是否匹配成功
*/
function match(obj, regex, depth = 5) {
if (obj && regex && depth > 0) {
return core(obj, depth, new Set())
} else {
return false
}
function core(obj, depth, objSet) {
for (var key in obj) {
if (regex.test(key)) {
return true
} else {
try {
var value = obj[key]
if (value) {
if (typeof value == 'object' || typeof value == 'function') {
if (regex.test(value.toString())) {
return true
} else if (depth > 1) {
if (!objSet.has(value)) {
objSet.add(value)
if (core(value, depth - 1)) {
return true
}
}
}
} else {
if (regex.test(String(value))) {
return true
}
}
}
} catch (e) {
// value that cannot be accessed
}
}
}
return false
}
}