// ==UserScript==
// @name 小程序日志辅助工具
// @namespace http://tampermonkey.net/
// @version 0.1.7
// @description 小程序事件日志查询的辅助工具
// @author Cme
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.19/lodash.js
// @require https://cdn.bootcdn.net/ajax/libs/dayjs/1.8.32/dayjs.min.js
// @require https://code.jquery.com/jquery-3.6.4.min.js
// @match https://wedata.weixin.qq.com/*
// @match https://mp.weixin.qq.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=qq.com
// @grant GM_addElement
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @connect *
// @run-at context-menu
// @run-at document-start
// @license MIT
// ==/UserScript==
'use strict';
let style = `
.diy-tool {
background: rgb(0,206,139);
background: linear-gradient(90deg, rgba(0,206,139,1) 0%, rgba(0,212,255,1) 100%);
}
.flex-style {
display: flex;
align-items: center;
justify-content: center;
}
.functions-list {
justify-content: space-evenly;
flex-flow: wrap;
}
.function-item {
flex: 1;
height: 50px;
white-space: nowrap;
}
.result-content {
max-height: 40vh;
}
.result-list {
display: flex;
flex-direction: column;
}
.json-document {
padding: 1em 2em;
}
ul.json-dict, ol.json-array {
list-style-type: none;
margin: 0 0 0 1px;
border-left: 1px dotted #ccc;
padding-left: 2em;
}
.json-string {
color: #0B7500;
}
.json-literal {
color: #1A01CC;
font-weight: bold;
}
/* Toggle button */
a.json-toggle {
position: relative;
color: inherit;
text-decoration: none;
}
a.json-toggle:focus {
outline: none;
}
a.json-toggle:before {
font-size: 1.1em;
color: #c0c0c0;
content: "\\25BC";
position: absolute;
display: inline-block;
width: 1em;
text-align: center;
line-height: 1em;
left: -1.2em;
}
a.json-toggle:hover:before {
color: #aaa;
}
a.json-toggle.collapsed:before {
transform: rotate(-90deg);
}
a.json-placeholder {
color: #aaa;
padding: 0 1em;
text-decoration: none;
}
a.json-placeholder:hover {
text-decoration: underline;
}
.hidden {
height: 0;
padding: 0;
overflow: hidden;
}
.config-content {
flex-flow: wrap;
flex-direction: column;
align-items: flex-start;
}
.config-module::before {
content: attr(data-title);
position: absolute;
top: -23px;
font-weight: bold;
}
.config-module {
flex: 1;
flex-wrap: wrap;
width: 100%;
display: flex;
background: rgb(0 206 139 / 10%);
margin-top: 35px;
padding: 10px;
box-sizing: border-box;
border-radius: 10px;
position: relative;
}
.config-items {
min-height: 50px;
width: 100%;
justify-content: flex-start;
}
.config-title {
font-weight: 600;
margin-right: 20px;
flex-shrink: 0;
width: 98px;
text-align: left;
}
.old-log {
padding: 0!important;
text-decoration: none;
}
.diy-page:hover .diy-page-img {
display: flex
}
.diy-page-img {
display:none;
background: #10AEFF;
position: absolute;
width: 150px;
height: 324px;
left: calc(100% + 10px);
padding: 4px;
top: 50%;
transform: translateY(-50%);
border-radius: 4px;
z-index:1;
}
.diy-page-img::after {
content: '';
width: 0;
height: 0;
border-right: 50px solid skyblue;
border-top: 50px solid transparent;
border-bottom: 50px solid transparent;
position: absolute;
left: -50px;
top:50%;
transform: translateY(-50%);
}
`
GM_addElement('script', {
src: 'https://cdn.jsdelivr.net/npm/sweetalert2@11',
type: 'text/javascript'
});
GM_addElement('link', {
src: 'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
});
// GM_addElement(document.getElementsByTagName('HTML')[0], 'style', {
// textContent: style
// });
GM_addStyle(style);
let initSetting = {
filters: {
requestUrl: '', //统计接口请求次数的目标接口
timeout: 5, // 统计接口请求耗时的预设值
collapsed: false, // 格式化默认展开
rootCollapsable: false, // 根层级默认展开
},
results: {
jsonView: false, //是否展示json
}
}
let filters = {},
results = {};
let lock = null;
let HTML = "";
let traceInfo = {
status: false, //是否生效
startTraceId: "" // 初级traceId
}
function freshValue() {
document.getElementsByClassName('wrap_3_H1pthMEy')[0] && (document.getElementsByClassName('wrap_3_H1pthMEy')[0].style.display = 'block');
let storage = GM_getValue('setting');
let res = (storage && JSON.parse(storage)) || initSetting; // 获取配置数据
filters = res.filters;
results = res.results;
checkEnv();
}
// 添加入口按钮
function addButton() {
checkEnv();
GM_addElement(document.getElementById("realtime-query-panel").firstChild, 'button', {
class: 'weui-desktop-btn weui-desktop-btn_primary diy-tool',
textContent: '辅助工具',
style: 'margin-left:20px'
});
let ele = document.getElementsByClassName('diy-tool')[0];
ele.addEventListener("click", function() {
// showSettingDialog()
FunctionsPanel();
})
}
function initListener() {
let openid = document.getElementById('openid');
let requestTime = document.getElementById('requestTime');
let requestCount = document.getElementById('requestCount');
let reg = document.getElementById('reg');
let reset = document.getElementById('reset');
let viewHistory = document.getElementById('viewHistory');
openid?.addEventListener("click", function() {
Swal.close();
openIdResult();
})
requestTime?.addEventListener("click", function() {
Swal.close();
apiTime();
})
requestCount?.addEventListener("click", function() {
Swal.close();
apiCount();
})
reg?.addEventListener("click", function() {
Swal.close();
evalData();
})
reset?.addEventListener("click", function() {
Swal.close();
resets();
})
viewHistory?.addEventListener("click", function() {
Swal.close();
viewHistoryFun();
})
}
function randomNum(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function randomColor(opacity = 1) {
let min = 0;
let max = 255;
return `rgba(${randomNum(min,max)},${randomNum(min,max)},${randomNum(min,max)}, ${opacity})`
}
// 将数据json格式化
function dealJson(options) {
let res = ''
try {
res = eval('(' + decodeURIComponent(options) + ')');
} catch (err) {
res = options
}
return res;
}
function iconType(typeTxt) {
let Elements = {
DF_enterEvent: '<svg class="icon" style="width: 2em;height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" p-id="1495"><path d="M835.584 63.488q26.624 0 49.664 10.24t40.448 27.648 27.648 40.448 10.24 49.664l0 641.024q0 26.624-10.24 49.664t-27.648 40.448-40.448 27.648-49.664 10.24l-448.512 0q-26.624 0-49.664-10.24t-40.448-27.648-27.648-40.448-10.24-49.664l0-192.512 128 0 0 192.512 448.512 0 0-641.024-448.512 0 0 192.512-128 0 0-192.512q0-26.624 10.24-49.664t27.648-40.448 40.448-27.648 49.664-10.24l448.512 0zM513.024 614.4q0-19.456-9.728-28.672t-24.064-9.216l-378.88 0q-14.336 0-25.088-7.68t-10.752-26.112l0-52.224q0-29.696 9.216-37.376t35.84-7.68l31.744 0q24.576 0 58.368 0.512t73.728 1.024 77.312 1.024 69.12 0.512l51.2 0q22.528 0 32.256-16.384t9.728-35.84l0-49.152q0-20.48 8.704-25.6t26.112 9.216 47.104 32.768 61.952 37.888 62.976 38.4 49.152 34.304q14.336 11.264 14.336 30.72t-11.264 28.672q-16.384 14.336-44.544 32.256t-59.392 36.864-60.928 37.376-48.128 33.792q-23.552 19.456-34.816 19.968t-11.264-30.208l0-49.152z" p-id="1496"></path></svg>',
DF_elementClick: '<svg class="icon" style="width: 2em;height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" p-id="6497"><path d="M775.779 418.172l-2.051 0c-15.159 0-27.6 4.355-39.583 11.771-10.486-30.584-37.159-52.615-71.513-52.615-15.16 0-29.638 4.354-41.62 11.77-10.487-30.583-37.172-52.615-71.527-52.615-13.399 0-25.85 3.357-36.873 9.255l0-78.691c0-42.861-32.418-77.606-75.59-77.606-43.173 0-78.17 34.745-78.17 77.606l0 301.282-47.49-47.295c-30.528-30.306-84.558-25.992-110.55 0s-43.038 78.308-5.818 115.528l218.306 216.875c4.504 4.471 9.455 8.2 14.663 11.353 39.803 32.47 85.412 51.692 181.857 51.692 220.324 0 240.728-118.865 240.728-265.492L850.548 495.777C850.547 452.917 818.952 418.172 775.779 418.172zM809.403 650.988c0 124.069-0.593 224.647-199.585 224.647-84.298 0-134.907-18.796-173.246-56.858L229.904 613.455c-18.285-18.285-13.687-41.664 1.282-56.633 14.968-14.968 42.441-15.486 56.902-1.131 0 0 36.259 36.045 67.498 67.1 23.641 23.502 44.408 44.145 44.408 44.145l0-391.72c0-20.302 16.578-36.76 37.028-36.76 20.449 0 34.448 16.459 34.448 36.76l0 249.154 0.415 0c-0.27 1.32-0.415 2.685-0.415 4.085 0 11.278 9.21 20.423 20.571 20.423 11.36 0 20.57-9.144 20.57-20.423 0-1.4-0.144-2.765-0.415-4.085l0.415 0L512.611 422.257c0-20.302 14.795-36.761 35.245-36.761 0 0 36.232-0.49 36.232 36.761l0 134.787 0.415 0c-0.27 1.321-0.415 2.686-0.415 4.085 0 11.279 9.21 20.423 20.57 20.423 11.361 0 20.571-9.143 20.571-20.423 0-1.399-0.144-2.764-0.415-4.085l0.415 0 0-93.942c0-20.303 14.559-36.762 35.01-36.762 0 0 36.983 2.303 36.983 36.762l0 118.449 0.415 0c-0.269 1.32-0.415 2.686-0.415 4.085 0 11.279 9.21 20.423 20.571 20.423s20.055-9.143 20.055-20.423c0-1.399-0.136-2.765-0.392-4.085l0.392 0 0-80.872c0-20.302 15.255-36.761 35.704-36.761 0 0 35.851-1.45 35.851 36.761C809.403 500.679 809.403 617.954 809.403 650.988zM328.307 382.755l0-68.631c-6.531-14.641-10.24-30.817-10.24-47.884 0-65.037 52.723-117.76 117.76-117.76s117.76 52.723 117.76 117.76c0 8.884-1.05 17.509-2.935 25.82 14.812 0.578 28.176 6.726 37.904 16.597 3.771-13.518 5.99-27.685 5.99-42.417 0-87.658-71.062-158.72-158.72-158.72s-158.72 71.062-158.72 158.72C277.107 312.36 296.898 353.755 328.307 382.755z" p-id="6498"></path></svg>',
DF_exposureEvent: '<svg class="icon" style="width: 2em;height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" p-id="11915"><path d="M819.2 341.333333a34.133333 34.133333 0 0 1 34.133333 34.133334v375.466666a34.133333 34.133333 0 0 1-34.133333 34.133334H204.8a34.133333 34.133333 0 0 1-34.133333-34.133334V375.466667a34.133333 34.133333 0 0 1 34.133333-34.133334h34.133333v-34.133333a34.133333 34.133333 0 0 1 34.133334-34.133333h85.333333a34.133333 34.133333 0 0 1 34.133333 34.133333v34.133333h153.6l13.448534-58.811733A34.133333 34.133333 0 0 1 592.861867 256h179.626666a34.133333 34.133333 0 0 1 33.28 26.5216L819.2 341.333333z m0 358.4H597.333333a17.066667 17.066667 0 0 1 0-34.133333h221.866667v-51.2H648.533333a17.066667 17.066667 0 0 1 0-34.133333h170.666667V392.533333a17.066667 17.066667 0 0 0-17.066667-17.066666H221.866667a17.066667 17.066667 0 0 0-17.066667 17.066666v341.333334a17.066667 17.066667 0 0 0 17.066667 17.066666h580.266666a17.066667 17.066667 0 0 0 17.066667-17.066666v-34.133334zM290.133333 307.2a17.066667 17.066667 0 0 0-17.066666 17.066667v17.066666h85.333333v-17.066666a17.066667 17.066667 0 0 0-17.066667-17.066667h-51.2z m319.488-17.066667a17.066667 17.066667 0 0 0-16.1792 11.6736L580.266667 341.333333h204.8l-13.175467-39.5264a17.066667 17.066667 0 0 0-16.196267-11.6736h-146.056533zM426.666667 699.733333a136.533333 136.533333 0 1 1 0-273.066666 136.533333 136.533333 0 0 1 0 273.066666z m0-68.266666a68.266667 68.266667 0 1 0 0-136.533334 68.266667 68.266667 0 0 0 0 136.533334z m256-221.866667h85.333333a17.066667 17.066667 0 0 1 17.066667 17.066667v17.066666a17.066667 17.066667 0 0 1-17.066667 17.066667h-85.333333a17.066667 17.066667 0 0 1-17.066667-17.066667v-17.066666a17.066667 17.066667 0 0 1 17.066667-17.066667z" fill="#D8D8D8" p-id="11916"></path></svg>',
DF_pageView: '<svg class="icon" style="width: 2em;height: 2em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1042 1024" version="1.1" p-id="24825"><path d="M581.632 697.344l126.976 0 0 194.56q0 33.792-10.24 58.88t-27.136 40.96-39.424 23.552-48.128 7.68l-452.608 0q-24.576 0-48.128-9.728t-41.472-27.136-29.184-40.96-11.264-52.224l0-706.56q0-24.576 11.776-47.104t30.208-39.936 40.96-28.16 45.056-10.752l449.536 0q26.624 0 50.176 11.776t41.472 29.696 28.16 40.448 10.24 44.032l0 188.416-126.976 0 1.024-195.584-452.608 0-1.024 713.728 452.608 0 0-195.584zM1021.952 505.856q37.888 30.72 2.048 60.416-20.48 15.36-44.544 37.888t-50.176 46.592-51.712 47.616-47.104 40.96q-23.552 18.432-40.448 18.432t-16.896-24.576q2.048-14.336 0.512-35.84t-1.536-36.864q0-17.408-12.288-21.504t-29.696-4.096l-40.96 0-62.464 0q-34.816 0-73.216-0.512t-73.216-0.512l-62.464 0-41.984 0q-8.192 0-17.92-1.536t-17.408-6.656-12.288-14.336-4.608-23.552q0-19.456-0.512-46.08t0.512-47.104q0-27.648 13.312-37.888t43.008-9.216l33.792 0 59.392 0q32.768 0 70.144 0.512t71.168 0.512l61.44 0 38.912 0q25.6 1.024 43.52-4.096t17.92-22.528q0-14.336 1.024-32.256t1.024-32.256q0-23.552 12.8-29.696t32.256 9.216q19.456 16.384 45.568 38.4t52.736 45.056 52.736 45.568 47.616 39.936z" p-id="24826"></path></svg>',
DF_behavior: '<svg class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7789"><path d="M992.544 595.296a31.968 31.968 0 0 0-45.248 0l-201.376 201.376-95.04-95.04a31.968 31.968 0 1 0-45.248 45.248l117.664 117.664a31.968 31.968 0 0 0 45.248 0l224-224a31.968 31.968 0 0 0 0-45.248zM800 352a32 32 0 0 0-32-32H256a32 32 0 0 0 0 64h512a32 32 0 0 0 32-32zM256 544a32 32 0 0 0 0 64h288a32 32 0 0 0 0-64H256zM771.104 928H195.04C175.904 928 160 911.68 160 891.072V132.928C160 112.32 175.904 96 195.04 96h633.92C848.096 96 864 112.32 864 132.928v350.528a32 32 0 0 0 64 0V132.928C928 77.376 883.84 32 828.96 32H195.04C140.16 32 96 77.376 96 132.928v758.144C96 946.624 140.16 992 195.04 992h576.064a32 32 0 0 0 0-64z" p-id="7790"></path></svg>'
}
return Elements[typeTxt];
}
var richPageName = ({path, name}) => {
if(!__PG_RMK__?.[path]?.remark_url) return name || '';
return`<div class="weui-desktop-popover__wrp" extclass="img-preview" style="display: inline-block;">
<span class="diy-page" style="position: relative">
<div style="color: rgb(87, 107, 149);" class="diy-page-name">${name}</div>
<img src="${__PG_RMK__[path].remark_url}" class="diy-page-img">
</span>
</div>`
}
// 摘捡需要的字段数据
function pickData({track_type, track_datas, track_timestamp}) {
let str = ''
let {df_pagename, df_pageUrl, df_prepagename, df_prepageUrl, df_modulename, df_elementname} = track_datas;
let types = {
"DF_enterEvent": "进入了",
"DF_elementClick": "点击了",
"DF_pageView": "离开了",
"DF_exposureEvent": "曝光了",
"DF_behavior": "执行了"
}
if(track_type == "DF_enterEvent" ){
if(df_prepageUrl) str += richPageName({name:df_prepagename,path:df_prepageUrl});
else str += `<span title="${divTitle(track_datas)}">启动小程序</span>`
str += `<span title="${divTitle(track_datas)}">${types[track_type]}</span>` + richPageName({name:df_pagename,path:df_pageUrl})
}else if( track_type == "DF_pageView" ){
str += `<span title="${divTitle(track_datas)}">${types[track_type]}</span>` + richPageName({name:df_pagename,path:df_pageUrl})
}else {
str += richPageName({name:df_pagename,path:df_pageUrl}) + `<span title="${divTitle(track_datas)}">${types[track_type] + (df_modulename ? df_modulename + "模块" : "") + (df_modulename && df_elementname ? "下的" : "") + (df_elementname ? "元素:" + df_elementname : "")}</span>`
}
return{
"icon": iconType(track_type),
"text": str,
"事件类型:": track_type,
"timestamp": track_timestamp,
"当前页面路径:": df_pageUrl,
"当前页面名称:": df_pagename,
"上页面路径:": df_prepageUrl,
"上页面名称:": df_prepagename,
"模块名称:": df_modulename,
"元素名称:": df_elementname,
"datas": track_datas
}
}
function divTitle(datas){
let result = []
for(let item in datas){
result.push(`${item}--->>>${datas[item]}`)
}
return result.join("\n")
}
// 有traceId的时候的处理方案
// function formatToTree(ary, pid, pidName = 'parentId') {
// return ary.filter((item) => {
// return pid === undefined ? item.track_parentid === "" : item.track_parentid === pid
// }).map((item) => {
// item.children = formatToTree(ary, item.track_trace);
// return item;
// })
// }
function formatToTree(ary, pid, pidName = 'track_parentid') {
return ary.filter((item) => item[pidName] === pid).map((item) => {
item.children = formatToTree(ary, item.track_trace);
return item;
});
}
function test(e){
console.log(e)
}
// 有traceId的时候html处理
function traceWhitHtml(list, str = ''){
if(list.length == 0){
return str;
}else if(list.length ==1){
let {icon, text, datas} = pickData(list[0]);
str += `<div class="trace-items">${icon}${text}</div>`
return traceWhitHtml(list[0].children, str);
}else {
list = list.sort((a,b) => new Date(a.track_timestamp || a.track_datas.df_activitytime).getTime() - new Date(b.track_timestamp || b.track_datas.df_activitytime).getTime())
for(let ii of list){
let {icon, text, datas} = pickData(ii);
str += `<div class="trace-item" data-time="${datas.df_activitytime}">${icon}${text}
${traceWhitHtml(ii.children)}
</div>`
}
return str;
}
}
// 没有traceId的时候的处理方案
function dealDataWithoutTraceid(keys, viewObj){
let HTML_Str = "";
keys = keys.sort((a, b) => new Date(a).getTime() - new Date(b).getTime());
for(let key of keys){
HTML_Str += `<div class="history-time" style="background:${randomColor()}">${key}</div>`
let list = viewObj[key]?.sort((a,b)=> new Date(a.timestamp ? parseInt(a.timestamp) : a.datas.df_activitytime).getTime() - new Date(b.timestamp ? parseInt(b.timestamp) : b.datas.df_activitytime).getTime());
for(let item of list){
let {icon, text, datas, timestamp} = item;
HTML_Str += `<div class="history-items">${icon}${text}</div>`
}
}
return `<div class="trace-items">${HTML_Str}</div>`;
}
// 梳理用户浏览足迹,仅仅针对埋点类型数据
function viewHistoryFun() {
let datas = document.getElementsByClassName("logitem_3jpgWXCKDU") || [];
let viewObj = {};
let keys = [];
let tempList = [];
let hasTraceId = traceInfo.status;
let HTML_Str = ""
for (let items of datas) {
// 过滤异常值已经非埋点类型数据
if (items.innerHTML == " event_value ") continue;
let {
wxdata_perf_extra_info1, // 请求入参等信息
wxdata_perf_extra_info2, // 返回体
wxdata_perf_extra_info3, // 设备、场景、用户基础信息等等
track_datas,// 埋点数据
track_platform,//埋点平台
track_env,//埋点环境
track_type, //埋点事件类型
track_trace,// traceId
track_parentid,//父级traceId
track_timestamp//毫秒级时间戳
} = dealJson(items.innerHTML);
if (!track_type) continue;
!track_trace && (hasTraceId = false);
try{
if(track_datas){
// 格式化完整数据
track_datas = dealJson(track_datas);
// traceId情况下的数据收集;一旦发现没有traceId,仅使用老版本方式进行足迹排序
hasTraceId && tempList.push({
track_datas,// 埋点数据
track_platform,//埋点平台
track_env,//埋点环境
track_type, //埋点事件类型
track_trace,
track_parentid,
track_timestamp
});
// 没有traceId情况下收集数据
!keys.includes(track_datas.df_activitytime) && keys.push(track_datas.df_activitytime);
if(!viewObj[track_datas.df_activitytime]) viewObj[track_datas.df_activitytime] = [pickData({track_type, track_datas,track_timestamp})]
else viewObj[track_datas.df_activitytime].push(pickData({track_type, track_datas,track_timestamp}));
}
}catch(err){}
}
if(hasTraceId){
let resultList = formatToTree(tempList, traceInfo.startTraceId);
console.log(resultList)
HTML_Str = resultList.length<= 1 ? `<div class="trace-item">${traceWhitHtml(resultList)}</div>` : traceWhitHtml(resultList);
}else {
if(traceInfo.status) window.alert("因部分记录缺失traceId,本次改用事件时间排序");
HTML_Str = dealDataWithoutTraceid(keys, viewObj);
}
let style = `
.history-popop {
width:80%
}
.history-content {
height: 60vh;
width:100%;
text-align: left;
display: flex;
justify-content: space-between;
overflow-x:auto
}
.history-time {
color: white;
font-size: 20px;
border-radius: 10px;
padding: 0 10px;
width: fit-content;
}
.history-items {
margin-left: 20px;
line-height: 25px;
}
.trace-item::before{
content: attr(data-time);
}
.trace-item {
display: flex;
min-width: 45%;
flex-direction: column;
}
.trace-items {
}
`
GM_addStyle(style)
Swal.fire({
title: `浏览足迹(部分顺序会有异常)`,
showClass: {
popup: 'animate__animated animate__fadeInDown history-popop'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
},
html: `
<div class="history-content">
${HTML_Str}
</div>
`,
showDenyButton: true,
denyButtonText: `返回`,
confirmButtonText: '关闭'
}).then((result) => {
if (result.isDenied) {
FunctionsPanel()
}
});
}
// 将数据折叠
function evalData() {
let datas = document.getElementsByClassName("logitem_3jpgWXCKDU") || [];
let index = 0;
let viewObj = {}
for (let items of datas) {
index ++;
if (items.innerHTML == " event_value ") continue;
let {
wxdata_perf_extra_info1, // 请求入参等信息
wxdata_perf_extra_info2, // 返回体
wxdata_perf_extra_info3, // 设备、场景、用户基础信息等等
track_datas,// 埋点数据
track_platform,//埋点平台
track_env,//埋点环境
track_type, //埋点事件类型
track_parentid,//父traceid
track_trace, //traceid
track_timestamp, //事件时间戳
} = dealJson(items.innerHTML);
try {
if(track_datas){
track_datas = dealJson(track_datas);
// 根据时间排列
let className = "json-" + new Date(track_datas.df_activitytime).getTime() + '-' + index ;
let element = document.getElementsByClassName(className);
if (element && element.length) {
element[0].setAttribute("class", className + ' json-document');
} else {
let ele = document.createElement("div");
ele.className = className;
items.parentNode.appendChild(ele);
$("." + className).jsonViewer({
track_parentid,
track_trace,
track_platform,
track_env,
track_type,
track_datas,
track_timestamp
},{
collapsed: false,
rootCollapsable: false,
});
}
}else if(wxdata_perf_extra_info1){
let requestInfo = {},responseInfo = {},baseInfo = {};
wxdata_perf_extra_info1.split("#").map(item => {
let index = item.indexOf("=");
let key = item.slice(0, index);
let val = dealJson(item.slice(index + 1, item.length));
item && (requestInfo[key] = val);
});
responseInfo = dealJson(wxdata_perf_extra_info2);
wxdata_perf_extra_info3.split("#").map(item => {
let index = item.indexOf("=");
let key = item.slice(0, index);
let val = dealJson(item.slice(index + 1, item.length));
item && (baseInfo[key] = val);
});
let className = "json-" + baseInfo.timestamp;
let element = document.getElementsByClassName(className);
if (element && element.length) {
element[0].setAttribute("class", className + ' json-document');
} else {
let ele = document.createElement("div");
ele.className = className;
items.parentNode.appendChild(ele);
$("." + className).jsonViewer({
requestInfo,
responseInfo,
baseInfo
});
}
}
items.setAttribute("class", "logitem_3jpgWXCKDU hidden");
} catch (err) {}
}
}
// 重置数据展示方式
function resets() {
let datas = document.getElementsByClassName("logitem_3jpgWXCKDU") || [];
for (let items of datas) {
items.setAttribute("class", "logitem_3jpgWXCKDU");
let jsonClass = items.parentElement.lastElementChild.getAttribute("class");
if (jsonClass.indexOf("json-document") >= 0 && jsonClass.indexOf("hidden") < 0) {
items.parentElement.lastElementChild.setAttribute("class", jsonClass + " hidden")
}
// let nodes = items.parentNode.childNodes;
// if(nodes.length > 1){
// items.parentNode.removeChild(nodes[1]);
// }
}
}
// 统计接口耗时
function apiTime() {
var datas = document.getElementsByClassName("logitem_3jpgWXCKDU") || [];
let time = filters.timeout || 5;
let HTML_Str = '';
let count = 0;
for (let item of datas) {
let list = [];
let info = '';
if (item.innerHTML !== ' event_value ') {
item.innerHTML.replace(/(#timestamp=|requestTimeStamp":)[0-9]+\d/g, (res) => {
list.push(res.split(list.length ? ":" : "=")[1])
})
if (list[1] && list[0]) {
let num = Math.abs((list[0] - list[1])) / 1000
if (num >= time) {
item.innerHTML.replace(/(#requestUrl=)[0-9a-zA-Z:\/\/.]*/g, (r1) => {
info = r1.split("=")[1]
})
let openid = item.parentNode.parentElement.parentNode.firstChild.getElementsByClassName(
"openid_1UqzQDgj70")[0].firstChild.innerHTML
let T = new Date(parseInt(list[0]));
HTML_Str += `
<div class="api-result" style="background:${randomColor(0.4)}">
<div class="api-name flex-style">${info.replace("https://m.dongfangfuli.com", "")}</div>
<div class="api-times flex-style">
<div class="api-times-items"><span class="time-span">请求时间</span>${list[0]}</div>
<div class="api-times-items"><span class="time-span">响应时间</span>${list[1]}</div>
<div class="api-times-items"><span class="time-span">记录时间</span>${T.getHours()}:${T.getMinutes()}</div>
<div class="api-times-items"><span class="time-span">请求耗时</span>${num}</div>
</div>
</div>
`
count++;
}
}
}
};
let style = `
.api-result {
user-select: text;
border-radius: 10px;
margin-bottom: 10px;
}
.api-openid {
display: flex;
padding: 0 20px;
margin: 10px 0;
}
.api-name {
height:50px
}
.api-times {
}
.api-times-items {
}
.time-span {
font-weight: 600
}
.local {
flex:1
}
`
GM_addStyle(style)
Swal.fire({
title: `不小于${time}秒有${count}条请求记录`,
showClass: {
popup: 'animate__animated animate__fadeInDown'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
},
html: `
<div class="result-content">
<div class="result-list">
${HTML_Str}
</div>
</div>
`,
showDenyButton: true,
denyButtonText: `返回`,
confirmButtonText: '关闭'
}).then((result) => {
if (result.isDenied) {
FunctionsPanel()
}
});
}
// 统计openid
function openIdResult() {
var objs = {};
var lists = document.getElementsByClassName("openid_1UqzQDgj70");
for (let i of lists) {
let key = i.firstChild.innerHTML
if (key && key != 'openid') {
if (objs[key]) objs[key]++;
else objs[key] = 1
}
}
let num = 0;
let consoleStr = '';
let HTML_str = '';
for (let it in objs) {
consoleStr += it + '<br/>'
num++
HTML_str += `
<div class="openid-items flex-style" style="color:${randomColor()};order:-${objs[it]}">
<div class="openid-key">${it}</div>
<div class="openid-num">${objs[it]}</div>
</div>`
}
let style = `
.openid-items {
height: 30px;
justify-content: space-between;
}
.openid-key {
font-size: 20px;
font-weight: 600;
user-select: text
}
.openid-num {
font-size: 20px;
font-weight: 600;
}
`
GM_addStyle(style)
Swal.fire({
title: `共计${num}人、次`,
showClass: {
popup: 'animate__animated animate__fadeInDown'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
},
html: `
<div class="result-content">
<div class="result-list">
${HTML_str}
</div>
</div>
`,
showDenyButton: true,
denyButtonText: `返回`,
confirmButtonText: '关闭'
}).then((result) => {
if (result.isDenied) {
FunctionsPanel()
}
})
}
// 统计接口调用次数
function apiCount() {
var datas = document.getElementsByClassName("logitem_3jpgWXCKDU") || [];
let apis = {};
let count = 0;
let HTML_Str = '';
for (let item of datas) {
let url = '';
if (item.innerHTML !== ' event_value ') {
item.innerHTML.replace(/(#requestUrl=)[0-9a-zA-Z:\/\/.]*/g, (r1) => {
url = r1.split("=")[1]
})
if (!filters.requestUrl || (filters.requestUrl && filters.requestUrl == url)) {
let openid = item.parentNode.parentElement.parentNode.firstChild.getElementsByClassName(
"openid_1UqzQDgj70")[0].firstChild.innerHTML;
if (apis[url]) {
apis[url].push(openid)
} else {
apis[url] = [openid]
}
}
}
};
for (let item in apis) {
count++;
HTML_Str += `
<div class="api-count-result" style="background:${randomColor(0.4)}">
<div class="api-count-name flex-style">${item}</div>
<div class="api-count-times flex-style">
<div class="api-count-times-items"><span class="time-count-span">请求次数:</span>${apis[item].length}</div>
<div class="api-count-times-items"><span class="time-count-span">请求人数:</span>${Array.from(new Set(apis[item])).length}</div>
</div>
</div>
`
}
let style = `
.api-count-result {
user-select: text;
border-radius: 10px;
margin-bottom: 10px;
padding: 10px;
}
.api-count-openid {
display: flex;
padding: 0 20px;
margin: 10px 0;
}
.api-count-name {
height:50px
}
.api-count-times {
}
.api-count-times-items {
margin-right: 20px;
}
.time-count-span {
font-weight: 600
}
`
GM_addStyle(style)
Swal.fire({
title: filters.requestUrl ? filters.requestUrl + ' 接口请求记录' : count + '个接口请求记录',
showClass: {
popup: 'animate__animated animate__fadeInDown'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
},
html: `
<div class="result-content">
<div class="result-list">
${HTML_Str}
</div>
</div>
`,
showDenyButton: true,
denyButtonText: `返回`,
confirmButtonText: '关闭'
}).then((result) => {
if (result.isDenied) {
FunctionsPanel()
}
});
}
// 功能面板
function FunctionsPanel() {
let token = GM_getValue('token');
let element = "";
if (token) {
// element = `<a href="https://mp.weixin.qq.com/wxamp/userlog/list?token=${token}&lang=zh_CN" target="_blank" class="function-item swal2-confirm swal2-styled old-log flex-style">老版本日志</a>`
}
Swal.fire({
// title: "功能类目",
html: `
<div class="flex-style functions-list">
<button type="button" class="function-item swal2-confirm swal2-styled" id="openid">统计openid</button>
${HTML}
<button type="button" class="function-item swal2-confirm swal2-styled" id="reg">格式化数据</button>
<button type="button" class="function-item swal2-confirm swal2-styled" id="reset">重置</button>
${element}
</div>
`,
showCancelButton: true,
cancelButtonText: '关闭',
confirmButtonText: '配置'
}).then(({
isConfirmed,
isDismissed
}) => {
isConfirmed && showSettingDialog()
});
initListener();
}
// 设置面板
function showSettingDialog() {
Swal.fire({
title: "配置信息",
html: `
<div class="config-content flex-style">
<div class="config-module" data-title="接口操作">
<div class="flex-style config-items">
<span class="config-title">目标接口:</span>
<textarea id="requestUrl" rows="5" cols="60" style="display: flex">${filters.requestUrl}</textarea>
</div>
<div class="flex-style config-items">
<span class="config-title">超时阈值:</span>
<input id="timeout" value="${filters.timeout}" type="number" label="timeout" name="timeout" style="width:50px;display: flex" />秒
</div>
</div>
<div class="config-module" data-title="数据格式化">
<div class="flex-style config-items">
<span class="config-title">根节点:</span>
<select id="rootCollapsable">
<option value="0">展开</option>
<option value="1">折叠</option>
</select>
</div>
<div class="flex-style config-items"> <span class="config-title">子节点:</span>
<select id="collapsed">
<option value="0">展开</option>
<option value="1">折叠</option>
</select>
</div>
</div>
<div class="config-module" data-title="浏览足迹">
<div class="flex-style config-items">
<span class="config-title">TraceId追踪:</span>
<select id="useTraceId">
<option value="0">不启用</option>
<option value="1">启用</option>
</select>
</div>
<div class="flex-style config-items">
<span class="config-title">初始TraceId:</span>
<input id="parentTraceId" value="${traceInfo.startTraceId}" type="text" placeholder="仅traceId启用追踪时可用" label="parentTraceId" name="parentTraceId" style="width:200px;display: flex" />
</div>
</div>
</div>
`,
confirmButtonText: "保存"
}).then(({
isConfirmed,
isDismissed
}) => {
if (isConfirmed) {
let requestUrl = document.getElementById("requestUrl").value;
let timeout = document.getElementById("timeout").value || filters.timeout || 5;
let collapsed = Boolean(Number(document.getElementById("collapsed").value));
let rootCollapsable = Boolean(Number(document.getElementById("rootCollapsable").value));
let useTraceId = Boolean(Number(document.getElementById("useTraceId").value));
let parentTraceId = document.getElementById("parentTraceId").value || "";
let res = {
filters: {
requestUrl,
timeout,
collapsed,
rootCollapsable
},
results: filters.results
}
GM_setValue('setting', JSON.stringify(res));
traceInfo. status = useTraceId;
traceInfo. startTraceId = parentTraceId;
freshValue();
FunctionsPanel();
}
});
let collapsed = filters.collapsed ? "1" : "0";
let rootCollapsable = filters.rootCollapsable ? "1" : "0";
let useTraceId = traceInfo. status ? "1" : "0";
$("#collapsed").find(`option[value='${collapsed}']`).attr("selected", true);
$("#rootCollapsable").find(`option[value='${rootCollapsable}']`).attr("selected", true);
$("#useTraceId").find(`option[value='${useTraceId}']`).attr("selected", true);
}
function checkEnv(){
let queryBtn = document.getElementById("realtime-query-panel").firstChild.firstChild;
queryBtn?.addEventListener("click", function() {
HTML = '';
let openid = document.getElementsByClassName('weui-desktop-form__input')[7].value;
let type = document.getElementsByClassName('weui-desktop-form__dropdown__search')[2].value.replace(/[\n|\t| ]/g, '')
if(type == '基础监控'){
HTML += `<button type="button" class="function-item swal2-confirm swal2-styled" id="requestTime">统计接口耗时(>=${filters.timeout})秒</button>
<button type="button" class="function-item swal2-confirm swal2-styled" id="requestCount">统计接口调用次数</button>`
}else if(openid && type == '埋点'){
HTML += `<button type="button" class="function-item swal2-confirm swal2-styled" id="viewHistory">浏览足迹</button>`
}
})
}
// 初始化
function start() {
if (lock) return;
lock = setTimeout(() => {
clearTimeout(lock);
lock = null;
freshValue();
addButton();
JsonFun();
}, 1000);
}
let old = history.pushState
history.pushState = function(...arg) {
if (arg && arg.length && arg.length > 2 && (arg[2] == '/mp2/realtime-log/data' || arg[2] == '/mp2/report-manage/event/event_monitor')) {
start();
}
return old.call(this, ...arg)
}
let url = location.href;
let token = new URL(url).searchParams.get("token");
if (token) GM_setValue('token', token);
if (url == 'https://wedata.weixin.qq.com/mp2/realtime-log/data' || url.indexOf('https://wedata.weixin.qq.com/mp2/report-manage/event/event_monitor') >= 0) {
start();
}
function JsonFun() {
/**
* Check if arg is either an array with at least 1 element, or a dict with at least 1 key
* @return boolean
*/
function isCollapsable(arg) {
return arg instanceof Object && Object.keys(arg).length > 0;
}
/**
* Check if a string looks like a URL, based on protocol
* This doesn't attempt to validate URLs, there's no use and syntax can be too complex
* @return boolean
*/
function isUrl(string) {
var protocols = ['http', 'https', 'ftp', 'ftps'];
for (var i = 0; i < protocols.length; ++i) {
if (string.startsWith(protocols[i] + '://')) {
return true;
}
}
return false;
}
/**
* Return the input string html escaped
* @return string
*/
function htmlEscape(s) {
return s;
return s.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/'/g, ''')
.replace(/"/g, '"');
}
/**
* Transform a json object into html representation
* @return string
*/
function json2html(json, options) {
var html = '';
if (typeof json === 'string') {
// Escape tags and quotes
json = htmlEscape(json);
if (options.withLinks && isUrl(json)) {
html += '<a href="' + json + '" class="json-string" target="_blank">' + json + '</a>';
} else {
// Escape double quotes in the rendered non-URL string.
json = json.replace(/"/g, '\\"');
html += '<span class="json-string">"' + json + '"</span>';
}
} else if (typeof json === 'number' || typeof json === 'bigint') {
html += '<span class="json-literal">' + json + '</span>';
} else if (typeof json === 'boolean') {
html += '<span class="json-literal">' + json + '</span>';
} else if (json === null) {
html += '<span class="json-literal">null</span>';
} else if (json instanceof Array) {
if (json.length > 0) {
html += '[<ol class="json-array">';
for (var i = 0; i < json.length; ++i) {
html += '<li>';
// Add toggle button if item is collapsable
if (isCollapsable(json[i])) {
html += '<a href class="json-toggle"></a>';
}
html += json2html(json[i], options);
// Add comma if item is not last
if (i < json.length - 1) {
html += ',';
}
html += '</li>';
}
html += '</ol>]';
} else {
html += '[]';
}
} else if (typeof json === 'object') {
// Optional support different libraries for big numbers
// json.isLosslessNumber: package lossless-json
// json.toExponential(): packages bignumber.js, big.js, decimal.js, decimal.js-light, others?
if (options.bigNumbers && (typeof json.toExponential === 'function' || json.isLosslessNumber)) {
html += '<span class="json-literal">' + json.toString() + '</span>';
} else {
var keyCount = Object.keys(json).length;
if (keyCount > 0) {
html += '{<ul class="json-dict">';
for (var key in json) {
if (Object.prototype.hasOwnProperty.call(json, key)) {
// define a parameter of the json value first to prevent get null from key when the key changed by the function `htmlEscape(key)`
let jsonElement = json[key];
key = htmlEscape(key);
var keyRepr = options.withQuotes ?
'<span class="json-string">"' + key + '"</span>' : key;
html += '<li>';
// Add toggle button if item is collapsable
if (isCollapsable(jsonElement)) {
html += '<a href class="json-toggle">' + keyRepr + '</a>';
} else {
html += keyRepr;
}
html += ': ' + json2html(jsonElement, options);
// Add comma if item is not last
if (--keyCount > 0) {
html += ',';
}
html += '</li>';
}
}
html += '</ul>}';
} else {
html += '{}';
}
}
}
return html;
}
/**
* jQuery plugin method
* @param json: a javascript object
* @param options: an optional options hash
*/
$.fn.jsonViewer = function(json, options) {
// Merge user options with default options
options = Object.assign({}, {
collapsed: filters.collapsed,
rootCollapsable: filters.rootCollapsable,
withQuotes: false,
withLinks: true,
bigNumbers: false
}, options);
// jQuery chaining
return this.each(function() {
// Transform to HTML
var html = json2html(json, options);
if (options.rootCollapsable && isCollapsable(json)) {
html = '<a href class="json-toggle"></a>' + html;
}
// Insert HTML in target DOM element
$(this).html(html);
$(this).addClass('json-document');
// Bind click on toggle buttons
$(this).off('click');
$(this).on('click', 'a.json-toggle', function() {
var target = $(this).toggleClass('collapsed').siblings(
'ul.json-dict, ol.json-array');
target.toggle();
if (target.is(':visible')) {
target.siblings('.json-placeholder').remove();
} else {
var count = target.children('li').length;
var placeholder = count + (count > 1 ? ' items' : ' item');
target.after('<a href class="json-placeholder">' + placeholder + '</a>');
}
return false;
});
// Simulate click on toggle button when placeholder is clicked
$(this).on('click', 'a.json-placeholder', function() {
$(this).siblings('a.json-toggle').click();
return false;
});
if (options.collapsed == true) {
// Trigger click to collapse all nodes
$(this).find('a.json-toggle').click();
}
});
};
};