k8s接口统计

迅速查看最耗时的接口、最大的接口、调用次数最多的接口

// ==UserScript==
// @name         k8s接口统计
// @namespace    http://tampermonkey.net/
// @version      2024-05-23
// @description  迅速查看最耗时的接口、最大的接口、调用次数最多的接口
// @author       Peter Chiang
// @match        http://*/*
// @match        https://*/*
// @icon         https://www.microsoft.com/favicon.ico
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

})();

// 出口接口延迟
function getOutgoingApis(hitsArray) {
    let outPutDetails = [];
    hitsArray.forEach((hit) => {
      // 获取_source.message的值
      const messageString = hit._source.message;
      const appname = hit._source.appname;
      // 解析message字符串为JSON对象
      try {
        const messageJSON = JSON.parse(messageString);
        const utcDateString = hit._source['@timestamp'];
        const date = new Date(utcDateString);
        const localDateString = date.toLocaleString();

        if (messageJSON.path && messageJSON.path.length > 0) {
          // 将message的大小和对应的uri存储到数组中
          outPutDetails.push({
            size: messageString.length / 1000,
            appname,
            path: messageJSON.path,
            latency: parseInt(messageJSON.latency),
            timestamp: localDateString,
            trace_id: messageJSON.trace_id,
          });
        }
      } catch (parseError) {
        // console.info('Error parsing message to JSON:', parseError);
      }
    });
    return outPutDetails;
}

// 微服务接口大小排序
function getMicroServiceApis(hitsArray) {
    let messageDetails = [];
    hitsArray.forEach((hit) => {
      // 获取_source.message的值
      const messageString = hit._source.message;
      const appname = hit._source.appname;
      // 解析message字符串为JSON对象
      try {
        const messageJSON = JSON.parse(messageString);
        if (messageJSON.uri && messageJSON.uri.length > 0) {
          // 将message的大小和对应的uri存储到数组中
          messageDetails.push({
            size: messageString.length / 1000,
            appname,
            path: messageJSON.uri,
            latency: parseFloat(messageJSON.latency),
            trace_id: messageJSON.trace_id,
          });
        }
      } catch (parseError) {
        // console.info('Error parsing message to JSON:', parseError);
      }
    });
    return messageDetails;
}

function countAndSortPaths(arr) {
    // 创建一个 Map 来存储每个路径及其出现次数
    const pathCount = new Map();

    // 遍历数组,统计每个路径的出现次数
    for (const item of arr) {
        if (pathCount.has(item.path)) {
            pathCount.set(item.path, pathCount.get(item.path) + 1);
        } else {
            pathCount.set(item.path, 1);
        }
    }
    // 将 Map 转换为对象数组,并按照出现次数排序
    const sortedPaths = Array.from(pathCount.entries()).sort((a, b) => b[1] - a[1]);

    // 创建一个对象来存储排序后的路径及其出现次数
    const result = [];
    for (const [path, count] of sortedPaths) {
        result.push({
            path,
            count
        });
    }
    return result;
}


function arrayToCSV(dataArray) {
    // 将对象数组转换为 CSV 字符串
    const csvRows = [];
    const headers = ['appname', 'path', 'size', 'latency', 'count', 'trace_id'];
    csvRows.push(headers.join(','));
    for (const row of dataArray) {
        const values = headers.map(header => {
            let value = row[header] || '';
            if (typeof value === "string") {
                value = value.replace(/"/g, '""');
            }
            return value;
        }).join(',');
        csvRows.push(values);
    }

    return csvRows.join('\n');
}

function downloadCSV(csvData, filename) {
    // 创建 Blob 对象
    const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
    // 创建下载链接
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.href = url;
    link.download = filename;
    // 添加链接至 DOM,模拟点击下载
    document.body.appendChild(link);
    link.click();
    // 移除链接并释放 URL
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
}

// 保存原始的 XMLHttpRequest 方法
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;

// 重写 open 方法
XMLHttpRequest.prototype.open = function(method, url) {
    this._method = method;
    this._url = url;
    return originalOpen.apply(this, arguments);
};

// 重写 send 方法
XMLHttpRequest.prototype.send = function(body) {
    this.addEventListener('load', function() {
        const keyword1 = '/elasticsearch';
        const keyword2 = '/_search';
        const url = this._url;
        const needResponds = (url && url.includes(keyword1) && url.includes(keyword2) && this.status === 200)
        if (needResponds) {

            const jsonData = JSON.parse(this.responseText);
            // 获取原始数据
            const hitsArray = jsonData.hits.hits;
            // APP接口
            const outGoing = getOutgoingApis(hitsArray);
            let outGoingSort = outGoing.sort((item1, item2)=> item2.latency - item1.latency);
            const latenceTop20 = outGoingSort.slice(0, 20);
            console.log('latenceTop20', latenceTop20);
            // 微服务接口
            const microApis = getMicroServiceApis(hitsArray);
            const microApisSort = microApis.sort((item1, item2)=> item2.size - item1.size);
            const sizeTop20 = microApisSort.slice(0, 20);
            console.log('sizeTop20', sizeTop20);
            // 频率
            const pathsArray = outGoing;
            pathsArray.concat(microApis);
            const frequnceyTop20 = countAndSortPaths(pathsArray);

            const latencyHeader = {appname: '延迟TOP20', size: 0, path: '', latency: 0, timestamp: 0};
            const sizeHeader = {appname: '数据量TOP20'};
            const frequenceHeader = {appname: '调用频次TOP20'};
            let cvsData = [];
            cvsData.push(latencyHeader);
            cvsData = cvsData.concat(latenceTop20);
            cvsData.push(sizeHeader);
            cvsData = cvsData.concat(sizeTop20);
            cvsData.push(frequenceHeader);
            cvsData = cvsData.concat(frequnceyTop20);
            const cvsResult = arrayToCSV(cvsData);
            //  转化和下载
            const inputElement = document.querySelector('.euiFieldText.euiFieldText--fullWidth.euiFieldText--inGroup');
            const timeString = new Date().toLocaleString();
            // const downloadFuc = () => downloadCSV(cvsResult, `k8s-${timeString}-${inputElement.value || ''}`);

            const oldButton = document.getElementById('pertercvs');
            if (oldButton) {
                oldButton.remove();
            }
            // 通过类名选择器找到 <span> 元素
            const spanElement = document.querySelector('span.euiButton__text');
            // 创建一个新的 <button> 元素
            const downloadBtn = document.createElement('button');
            downloadBtn.textContent = '统计数据';
            downloadBtn.id = 'pertercvs';
            if (spanElement) {
                spanElement.parentNode.insertBefore(downloadBtn, spanElement.nextSibling);
                downloadBtn.addEventListener('click', function(){
                downloadCSV(cvsResult, `k8s-${timeString}-${inputElement.value || ''}`);
            });
            }


        }

    });
    return originalSend.apply(this, arguments);
};

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址