统计数据

是否接口上线后查询日志麻烦,统计繁琐,使用本插件可以直接一次性查询

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         统计数据
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  是否接口上线后查询日志麻烦,统计繁琐,使用本插件可以直接一次性查询
// @author       DaiXukai([email protected])
// @match        https://next.api.aliyun.com/**
// @icon         https://next.api.aliyun.com/favicon.ico
// @license      GNU GPLv3
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// ==/UserScript==

// 使用方式:
//     1. 打开网站 https://next.api.aliyun.com/api/Sls/2020-12-30/GetHistograms
//     2. 登录 RAM 账号
//     3. 点击脚本“执行”,输入参数等待结果

// 插件配置
const config = {
    // 日志库配置
    logstore: {
        // 访问日志
        access: "hd_manbo_portal_access",
        // 错误日志
        error: "hd_manbo_portal_error",
    }
}
// 运行状态
const state = {
    running: false,
    inputQuery: "",
    inputDate: "1,2",
    eleBox: undefined
}

/**
 * prompt 用户输入,忽略空字符串
 * @param {string} promptMsg 输入提示信息
 * @param {string} def 默认输入内容(占位)
 * @param {RegExp} regex 校验内容正则表达式
 * @throws 1 点击取消抛出
 * @returns string 用户输入内容
 */
function input(promptMsg, def, regex) {
    let tmpInput = "";
    while (!regex.test(tmpInput)) {
        tmpInput = prompt(promptMsg, def);
        if (tmpInput === null || tmpInput === undefined) {
            // 点击取消
            throw 1;
        }
        tmpInput = tmpInput.trim();
    }
    return tmpInput;
}

/**
 * 请求接口查询日志统计数
 * @param {string} logstore 日志库名
 * @param {number} from 开始时间戳(秒)
 * @param {number} to 结束时间戳(秒)
 * @param {string} query 查询语句
 * @returns Promise 结果为统计到的日志数量
 */
async function request(logstore, from, to, query) {
    let resp = await fetch("https://next.api.aliyun.com/api/auth/product/openAPIRequest", {
        "headers": {
            "accept": "application/json",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "bx-v": "2.5.6",
            "cache-control": "no-cache",
            "content-type": "application/json",
            "pragma": "no-cache",
            "sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin"
        },
        "referrer": "https://next.api.aliyun.com/api/Sls/2020-12-30/GetHistograms?sdkStyle=dara&params=%7B%22to%22:1703073392,%22from%22:1703072486,%22logstore%22:%22hd_service_live_info%22,%22project%22:%22hd-live%22,%22query%22:%22444%22%7D&tab=DEBUG&lang=PYTHON",
        "referrerPolicy": "strict-origin-when-cross-origin",
        "body": "{\"action\":\"GetHistograms\",\"product\":\"Sls\",\"bodyStyle\":null,\"apiStyle\":\"ROA\",\"apiVersion\":\"2020-12-30\",\"accessKeyId\":null,\"proxyEndpoint\":\"http://popunify-inner-pre.aliyuncs.com\",\"endpoint\":\"cn-beijing.log.aliyuncs.com\",\"regionId\":\"cn-beijing\",\"paramObject\":{\"method\":\"GET\",\"path\":\"/logstores/{logstore}/index?type=histogram\",\"params\":\"[{\\\"name\\\":\\\"project\\\",\\\"position\\\":\\\"Host\\\",\\\"required\\\":true,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"String\\\",\\\"title\\\":\\\"project 名称。\\\",\\\"example\\\":\\\"ali-test-project\\\",\\\"description\\\":\\\"project 名称。\\\"},{\\\"name\\\":\\\"logstore\\\",\\\"position\\\":\\\"Path\\\",\\\"required\\\":true,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"String\\\",\\\"title\\\":\\\"logstore 名称。\\\",\\\"example\\\":\\\"test-logstore\\\",\\\"description\\\":\\\"Logstore名称。\\\"},{\\\"name\\\":\\\"from\\\",\\\"position\\\":\\\"Query\\\",\\\"required\\\":true,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"Long\\\",\\\"title\\\":\\\"查询开始时间点。UNIX时间戳格式,表示从1970-1-1 00:00:00 UTC计算起的秒数。\\\\n\\\\n时间区间遵循“左闭右开”原则,即该时间区间包括区间开始时间点,但不包括区间结束时间点。如果from和to的值相同,则为无效区间,函数直接返回错误。\\\",\\\"example\\\":\\\"1409529600\\\",\\\"description\\\":\\\"子时间区间的开始时间点。UNIX时间戳格式,表示从1970-1-1 00:00:00 UTC计算起的秒数。\\\"},{\\\"name\\\":\\\"to\\\",\\\"position\\\":\\\"Query\\\",\\\"required\\\":true,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"Long\\\",\\\"title\\\":\\\"查询结束时间点。UNIX时间戳格式,表示从1970-1-1 00:00:00 UTC计算起的秒数。\\\\n\\\\n时间区间遵循“左闭右开”原则,即该时间区间包括区间开始时间点,但不包括区间结束时间点。如果from和to的值相同,则为无效区间,函数直接返回错误。\\\",\\\"example\\\":\\\"1409569200\\\",\\\"description\\\":\\\"子时间区间的结束时间点。UNIX时间戳格式,表示从1970-1-1 00:00:00 UTC计算起的秒数。\\\"},{\\\"name\\\":\\\"topic\\\",\\\"position\\\":\\\"Query\\\",\\\"required\\\":false,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"String\\\",\\\"title\\\":\\\"日志主题。\\\",\\\"example\\\":\\\"topic\\\",\\\"description\\\":\\\"日志主题。\\\"},{\\\"name\\\":\\\"query\\\",\\\"position\\\":\\\"Query\\\",\\\"required\\\":false,\\\"checkBlank\\\":false,\\\"visibility\\\":\\\"Public\\\",\\\"deprecated\\\":false,\\\"type\\\":\\\"String\\\",\\\"title\\\":\\\"查询语句。仅支持查询语句,不支持分析语句。关于查询语句的详细语法,请参见查询语法。\\\",\\\"example\\\":\\\"with_pack_meta\\\",\\\"description\\\":\\\"查询语句。仅支持查询语句,不支持分析语句。关于查询语句的详细语法,请参见[查询语法](~~43772~~)。\\\"}]\",\"protocol\":\"HTTP|HTTPS\"},\"params\":{\"to\":" + to + ",\"from\":" + from + ",\"logstore\":\"" + logstore + "\",\"project\":\"hd-live\",\"query\":\"" + query + "\"},\"credential\":{\"type\":\"ak\"}}",
        "method": "POST",
        "mode": "cors",
        "credentials": "include"
    });
    if (resp.status !== 200) {
        alert("请求失败,请重试");
        throw Error(`请求失败 logstore=${logstore}, from=${from}, to=${to}, query=${query}\nresponse=>${JSON.stringify(resp)}`);
    }
    let data = await resp.json();
    if (data.code === "E_NEED_LOGIN") {
        // 未登录
        alert("请先登录后重试\nhttps://next.api.aliyun.com/api/Sls/2020-12-30/GetHistograms");
        throw Error(`未登录`);
    }
    if (data.code !== 0) {
        alert("请求失败,未知异常,请重试");
        throw Error(`请求失败 logstore=${logstore}, from=${from}, to=${to}, query=${query}\nresponse=>${JSON.stringify(resp)}`);
    }
    data = data.data
    // 计数
    let count = 0;
    if (data.result === undefined){
        return 0;
    }
    for (let item of data.result) {
        count += item.count;
    }
    return count;
}

/**
 * html 字符串转换为 Node 对象
 * @param {string} html html 代码段
 * @returns Node 转换出的 Node 元素
 */
function parseDom(html) {
    let template = document.createElement('template');
    template.innerHTML = html;
    return template.content.childNodes[0];
}

function closeResult() {
    if (state.eleBox) {
        state.eleBox.remove();
        state.eleBox = undefined;
    }
}

unsafeWindow.closeResult = closeResult;

function showResult(data) {
    console.log("result", data);
    let eleBox = parseDom(`<div id="_sls_result_box_" style="padding: 12px; background: white; border-radius: 16px; position: fixed; z-index: 999; top: 0; bottom:0; left: 0; right: 0; margin: auto auto; width: fit-content; height: fit-content; box-shadow: rgba(0, 0, 0, 0.3) 0 19px 38px, rgba(0, 0, 0, 0.22) 0 15px 12px; display: flex; flex-direction: column; align-items: center;"></div>`)
    let eleTable = parseDom(`<table style="background: white; border: 1px solid #818181; border-collapse: collapse;"></table>`);
    let eleThead = document.createElement('thead');
    let eleTbody = document.createElement('tbody');
    eleTable.appendChild(eleThead);
    eleTable.appendChild(eleTbody);
    eleBox.appendChild(eleTable);
    let eleButton = parseDom(`<button onclick="window.closeResult()" style="background: #00adff; margin-top: 16px; border: 0; border-radius: 8px; padding: 4px 24px; color: white;">关闭</button>`)
    eleBox.appendChild(eleButton);

    // 表头
    let eleTr = document.createElement('tr');
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>日期</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>查询接口(已排除登录校验和用户禁用)</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>接口名称</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>全天请求次数</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>失败总数</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>成功率</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>晚高峰请求次数</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>晚高峰失败次数</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>晚高峰成功率</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>晚高峰QPS</th>"));
    eleThead.appendChild(eleTr);

    // 表内容
    for (let item of data) {
        eleTr = document.createElement('tr');
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.date}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.api}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'></td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.dayAccessCount}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.dayErrorCount}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${parseFloat(Math.floor(item.daySuccessRatio * 100) / 100)}%</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.peakAccessCount}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.peakErrorCount}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${parseFloat(Math.floor(item.peakSuccessRatio * 100) / 100)}%</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${parseFloat(Math.floor(item.peakQps * 10000) / 10000)}</td`));
        eleTbody.appendChild(eleTr);
    }

    document.body.appendChild(eleBox);
    state.eleBox = eleBox;

    GM_notification({
        title: "有咖互动接口数据统计",
        text: "统计完成",
        timeout: 3000,
    });
}

// 脚本主体
GM_registerMenuCommand("执行", async () => {
    if (state.running) {
        return;
    }
    try {
        state.running = true;
        closeResult();
        // 输入查询参数
        state.inputQuery = input(`请输入查询接口(英文逗号分隔,如 "/dynamic/lottery/basic/info, /dynamic/lottery/detail")`, state.inputQuery, /^.+(,.+)*$/);
        state.inputDate = input(`请输入查询日期(前一天为 1,前第二天为 2,以此类推,英文逗号分隔,如 "1,2" 查询前两天)`, state.inputDate, /^[1-9]\d{0,6}(,[1-9]\d{0,6})*$/);

        // 解析参数
        let apiList = state.inputQuery.split(",");
        for (let i in apiList) {
            apiList[i] = apiList[i].trim();
        }
        let dateList = state.inputDate.split(",");
        for (let i in dateList) {
            dateList[i] = parseInt(dateList[i].trim());
        }

        GM_notification({
            title: "有咖互动接口数据统计",
            text: "执行统计中,请勿关闭窗口,请稍等...",
            timeout: 3000,
        });

        // 获取今天的开始时间戳(秒)
        const now = new Date();
        const todayStartTimestamp = new Date(`${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()} 00:00:00`).getTime() / 1000;

        let data = [];
        for (let api of apiList) {
            for (let date of dateList) {
                let startTimestamp = todayStartTimestamp - date * 24 * 3600;
                let startTime = new Date(startTimestamp * 1000);
                let endTimestamp = todayStartTimestamp - (date - 1) * 24 * 3600;
                // 全天请求次数
                let dayAccessCount = await request(config.logstore.access, startTimestamp, endTimestamp, `#\\"${api} remote\\"`);
                // 失败总数
                let dayErrorCount = await request(config.logstore.error, startTimestamp, endTimestamp, `#\\"| requestURI:${api} |\\" not 用户鉴权失败 not 用户已被禁用`);
                // 成功率
                let daySuccessRatio = dayAccessCount === 0 ? 100 : ((1 - dayErrorCount / dayAccessCount) * 100);
                // 晚高峰请求次数 20:00-24:00
                let peakAccessCount = await request(config.logstore.access, endTimestamp - 4 * 3600, endTimestamp, `#\\"${api} remote\\"`);
                // 晚高峰失败次数 20:00-24:00
                let peakErrorCount = await request(config.logstore.error, endTimestamp - 4 * 3600, endTimestamp, `#\\"| requestURI:${api} |\\" not 用户鉴权失败 not 用户已被禁用`);
                // 晚高峰成功率
                let peakSuccessRatio = peakAccessCount === 0 ? 100 : ((1 - (peakErrorCount / peakAccessCount)) * 100);
                // 晚高峰QPS
                let peakQps = peakAccessCount / (4 * 3600);
                data.push({
                    date: `${startTime.getFullYear()}-${startTime.getMonth() + 1}-${startTime.getDate()}`,
                    api,
                    dayAccessCount,
                    dayErrorCount,
                    daySuccessRatio,
                    peakAccessCount,
                    peakErrorCount,
                    peakSuccessRatio,
                    peakQps
                });
            }
        }
        showResult(data);
    } finally {
        state.running = false;
    }
}, "Random");

// 脚本主体
GM_registerMenuCommand("kafka 消息发送统计", async () => {
    if (state.running) {
        return;
    }
    try {
        state.running = true;
        closeResult();

        GM_notification({
            title: "有咖互动接口数据统计",
            text: "执行统计中,请勿关闭窗口,请稍等...",
            timeout: 3000,
        });

        // 获取今天的开始时间戳(秒)
        const todayStartTimestamp = new Date(`2024-03-24 00:00:00`).getTime() / 1000;

        let topicList = [
            "add_room_tags",
            "adv_balance_deduction_check",
            "async_forbid_2_im_server",
            "async_forbid_4_living_room_2_im_server",
            "async_group_change",
            "async_h5_cancellation_account",
            "async_push_activity_message",
            "async_send_audit_message",
            "async_send_fans_group_member_change",
            "async_send_talker_audit_message",
            "backpack_operation",
            "calculate_novel_rank_list_score",
            "calculate_ranking_information",
            "calculate_user_ranking_information",
            "chapter_publish",
            "collect_update_sync_to_all",
            "combination_goods_reward",
            "comment_incr_sort_value",
            "comment_info_sync",
            "communicate_accept",
            "communicate_accept_by_id",
            "communicate_add_rmb",
            "communicate_invite",
            "communicate_refund",
            "dispatch_apns_message",
            "dispatch_jiguang_message",
            "doudian_message_handle_push",
            "drama_count_sync_to_es",
            "drama_pay_to_vip_return_gold",
            "drama_resource_zip_retry",
            "drama_role_add_popularity",
            "fans_group_member_consume",
            "fans_group_order",
            "fans_ranking_user_action",
            "full_site_float_screen_first_level",
            "full_site_float_screen_second_level",
            "gacha_broadcast_fist_push_consume",
            "gacha_broadcast_second_push_consume",
            "get_attention_weibouid_from_openapi",
            "gift_bag_gift_queue",
            "gift_push_msg_first_level",
            "gift_push_msg_second_level",
            "global_index_create",
            "global_index_update",
            "global_index_update_video_info",
            "global_index_video_info",
            "good_msg_database_sync_h5",
            "good_msg_sync_h5",
            "good_msg_sync_h5_live_show",
            "google_renewal_retry",
            "google_renewal_status_retry",
            "group_discovery_content_base_score_sync",
            "group_discovery_group_base_score_sync",
            "gt_push",
            "h5_activity_topic",
            "h5_bi_data_monitor",
            "h5_event_good_reward",
            "h5_gacha_info_topic",
            "h5_live_room_heart_beat",
            "h5_specific_repost_vip",
            "h5_sync_server_api_niudan_log",
            "h5_sync_server_api_room_log",
            "h5_user_exp_result",
            "hidden_lottie_gift_send",
            "hongdou_grab_red_packet_reward",
            "huajiao_big_gift_notify_queue",
            "ice_chaved_topic",
            "init_novice_mission",
            "init_user_send_gold",
            "ios_renewal_retry",
            "ios_renewal_status_retry",
            "ios_retry_verify_receipt",
            "kila-user-change-log",
            "kila_client_upload_log",
            "klive_h5_activity",
            "klive_h5_room_heartbeat",
            "klive_inviter_reward",
            "klive_sync_h5_user_exp_add",
            "klive_user_exp_activity",
            "link_small_video",
            "lottery_condition_status_info",
            "meet_game_record_queue",
            "noble_push_msg",
            "novel_es_info",
            "novel_monitor",
            "novel_msg_sync_h5",
            "novel_pay_sync_h5",
            "novel_statistic_count_sync_to_es",
            "novice_follow_user",
            "operate_room_to_category",
            "order_send_category_reward",
            "order_send_communicate_reward",
            "order_send_gift_reward",
            "order_send_house_reward",
            "order_send_question_reward",
            "order_send_recharge_reward",
            "order_send_reward",
            "order_send_share_question_reward",
            "other_platform_buy_member",
            "pendant_gift_push_msg",
            "pika_month_live_reward",
            "pika_ranklist_topic",
            "process_when_drama_zip_complete",
            "purify_word_sync_new_es",
            "push_jiguang_retry",
            "push_message",
            "question_add_rmb",
            "question_refund",
            "question_video_process",
            "radio_drama_collect_push",
            "random_video_match_push_queue",
            "random_video_match_queue",
            "record_month_active_user_for_push",
            "red_packet_grab_im",
            "red_packet_grab_over",
            "red_packet_refund",
            "red_packet_refund_new",
            "red_packet_send_im",
            "renewal_unsign",
            "room_record_profit",
            "room_resource_zip",
            "room_resource_zip_retry",
            "room_server_test_topic",
            "room_timeline_auto_recommend",
            "room_weight_update",
            "search_server_test_topic",
            "send_chat_room_message",
            "send_hongdou_diamond_queue",
            "send_sms_after_user_withdraw",
            "send_sms_when_create_room",
            "send_weibo_when_create_room",
            "slive_gift_queue",
            "sync_account_trans_detail",
            "sync_account_trans_detail_for_h5",
            "sync_api_log_for_h5",
            "sync_audit_text",
            "sync_biz_data_2_h5",
            "sync_change_user_image",
            "sync_elastic_search_consumer_center",
            "sync_h5_command_kafka_log",
            "sync_h5_heat_beat_user_exp_add",
            "sync_h5_ios_order",
            "sync_h5_user_exp_add",
            "sync_h5_user_exp_add_h5",
            "sync_interaction_data_2_h5",
            "sync_oms_auditing_info",
            "sync_oms_auditing_info_live_show",
            "sync_pika_room_heart_beat",
            "sync_room_counters_change",
            "sync_room_end_for_h5",
            "sync_room_exposure_gacha_log_to_h5",
            "sync_room_number_to_weibo",
            "sync_room_statistic_to_es",
            "sync_send_audit_result",
            "sync_specific_log_for_h5",
            "sync_star_pamper_value",
            "sync_trans_detail_for_statistics",
            "sync_trans_detail_to_statistics",
            "sync_user_like_record",
            "sync_user_relation_to_es",
            "sync_user_stats_to_es",
            "sync_user_work_role",
            "sync_waku_h5_user_exp_add",
            "sync_weibo_comment",
            "sync_year_end_new_user_task_activity",
            "synd_h5_msg",
            "talker_match",
            "talker_user_close",
            "test1",
            "third_user_add_portrait",
            "ugc_keyword_audit",
            "ugc_keyword_remove",
            "user_add_balance_act",
            "user_device_emulator_info",
            "user_letter_image",
            "user_relation_update",
            "video_resource_pack",
            "video_statistic_count_sync_to_es",
            "weibo_livecard_callback_update_trigger",
            "weibo_login_recommend_attention_anchor",
        ];
        let logList = [
            "chat_service_info",
            "hd_callback",
            "hd_manbo_portal_info",
            "hd_oms_live_info",
            "hd_portal_live_info",
            "hd_push_dispatcher_live_info",
            "hd_push_live_info",
            "hd_service_live_info",
            "room_kila_info",
            "room_manbo_info",
            "room_server_info",
            "base_manbo_info",
            "base_portal_info",
        ];
        let data = [];
        let error = [];
        for (let topic of topicList) {
            let startTimestamp = todayStartTimestamp;
            let startTime = new Date(startTimestamp * 1000);
            let endTimestamp = todayStartTimestamp + 24 * 3600;
            let dayAccessCount = 0;
            let peakAccessCount = 0;
            try{
                for (let logName of logList) {
                    // 全天请求次数
                    let tmp = await request(logName, startTimestamp, endTimestamp, `#\\"MQ send topic:${topic},\\"`);
                    dayAccessCount += tmp;
                    if(tmp > 0){
                        // 晚高峰请求次数 20:00-24:00
                        peakAccessCount += await request(logName, endTimestamp - 4 * 3600, endTimestamp, `#\\"MQ send topic:${topic},\\"`);
                    }
                }
            }catch(err){
                error.push(topic);
                console.error(err);
                continue;
            }
            data.push({
                topic,
                dayAccessCount,
                peakAccessCount
            });
        }
        showResultKafka(data);
        console.log(error);
        let csvData = [["查询 topic", "全天请求次数", "晚高峰请求次数"]];
        data.forEach(record => csvData.push([record.topic, record.dayAccessCount, record.peakAccessCount]));
        exportCSV(csvData, "topic.csv");
    } finally {
        state.running = false;
    }
}, "Random");


function exportCSV(data, filename) {
    let csv = data.map(row => row.join(',')).join('\n');
    let blob = new Blob([csv], {
        type: 'text/csv;charset=utf-8;'
    });
    let link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = filename;
    link.click();
}

function showResultKafka(data) {
    console.log("result", data);
    let eleBox = parseDom(`<div id="_sls_result_box_" style="padding: 12px; background: white; border-radius: 16px; position: fixed; z-index: 999; top: 0; bottom:0; left: 0; right: 0; margin: auto auto; width: fit-content; height: fit-content; box-shadow: rgba(0, 0, 0, 0.3) 0 19px 38px, rgba(0, 0, 0, 0.22) 0 15px 12px; display: flex; flex-direction: column; align-items: center;"></div>`)
    let eleTable = parseDom(`<table style="background: white; border: 1px solid #818181; border-collapse: collapse;"></table>`);
    let eleThead = document.createElement('thead');
    let eleTbody = document.createElement('tbody');
    eleTable.appendChild(eleThead);
    eleTable.appendChild(eleTbody);
    eleBox.appendChild(eleTable);
    let eleButton = parseDom(`<button onclick="window.closeResult()" style="background: #00adff; margin-top: 16px; border: 0; border-radius: 8px; padding: 4px 24px; color: white;">关闭</button>`)
    eleBox.appendChild(eleButton);

    // 表头
    let eleTr = document.createElement('tr');
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>查询 topic</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>全天请求次数</th>"));
    eleTr.append(parseDom("<th style='padding: 8px 16px; border: 1px solid #818181'>晚高峰请求次数</th>"));
    eleThead.appendChild(eleTr);

    // 表内容
    for (let item of data) {
        eleTr = document.createElement('tr');
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.topic}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.dayAccessCount}</td>`));
        eleTr.append(parseDom(`<td style='padding: 8px 16px; border: 1px solid #818181'>${item.peakAccessCount}</td>`));
        eleTbody.appendChild(eleTr);
    }

    document.body.appendChild(eleBox);
    state.eleBox = eleBox;

    GM_notification({
        title: "有咖互动接口数据统计",
        text: "统计完成",
        timeout: 3000,
    });
}