找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 14|回复: 0

网站监测工具

[复制链接]

2

主题

0

回帖

6

积分

新手上路

积分
6
发表于 2025-3-29 10:23:30 | 显示全部楼层 |阅读模式
这个是我自己做的一个用来监测网站是否可以正常访问,单文件双击 网站监测工具.html 运行。
可以快速知道自己网站运行状态。
1. 网址管理功能
添加网址:用户可以在输入框中输入要监测的网页地址,点击 “添加网址” 按钮,工具会对输入的网址进行格式验证。验证通过后,网址会被添加到左侧面板的列表中,并且列表项后有 “删除” 按钮。
删除网址:在左侧面板的网址列表中,每个网址后面都有一个 “删除” 按钮,点击该按钮可以将对应的网址从监测列表中移除,同时也会从筛选下拉框中移除该网址选项。
筛选网址:右侧面板有一个 “筛选网址” 的下拉框,用户可以选择具体的网址进行筛选,只显示该网址的监测日志,也可以选择 “全部” 来显示所有网址的监测日志。
2. 监测功能
设置监测间隔:用户可以在输入框中设置监测间隔时间(单位为秒),默认值为 60 秒。
开始 / 停止监测:点击 “开始监测” 按钮,工具会立即对所有已添加的网址进行一次检测,之后按照设置的监测间隔时间循环检测。点击 “停止监测” 按钮可停止监测。
重试机制:在进行网址检测时,如果请求失败,会进行最多 3 次重试,若重试后仍失败,则记录错误日志。
3. 日志记录与显示功能
日志记录:每次对网址进行检测后,会记录网址的状态(正常或异常)、响应时间、时间戳以及错误信息(若有)。日志记录会存储在 logs 数组中,当日志数量超过 1000 条时,会移除最早的日志记录。
日志显示:右侧面板的日志容器会显示所有或筛选后的监测日志,正常状态的日志为黑色,异常状态的日志为红色。日志会自动滚动到最底部以显示最新信息。



  1. <!DOCTYPE html>
  2. <html lang="en">

  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>网站监测工具</title>
  7.     <style>
  8.         body {
  9.             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  10.             background-color: #f9f9f9;
  11.             display: flex;
  12.             margin: 0;
  13.             padding: 0;
  14.             height: 100vh;
  15.         }

  16.         /* 调整左右面板的高度,减去一行日志的大致高度(假设为 30px) */
  17.         .left-panel,
  18.         .right-panel {
  19.             flex: 1;
  20.             padding: 20px;
  21.             background-color: #fff;
  22.             border-radius: 8px;
  23.             box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  24.             margin: 20px;
  25.             height: calc(100vh - 40px - 30px);
  26.             overflow-y: auto;
  27.             display: flex;
  28.             flex-direction: column;
  29.         }

  30.         .right-panel {
  31.             flex: 2;
  32.         }

  33.         h1 {
  34.             color: #333;
  35.             margin-bottom: 20px;
  36.             text-align: center;
  37.         }

  38.         .input-container {
  39.             margin-bottom: 15px;
  40.             display: flex;
  41.             align-items: center;
  42.         }

  43.         input {
  44.             padding: 10px;
  45.             margin-right: 10px;
  46.             border: 1px solid #ccc;
  47.             border-radius: 4px;
  48.             flex: 1;
  49.             transition: all 0.3s ease;
  50.         }

  51.         input.invalid {
  52.             color: red;
  53.             animation: shake 0.5s ease-in-out 3;
  54.         }

  55.         @keyframes shake {
  56.             0% {
  57.                 transform: translateX(0);
  58.             }
  59.             25% {
  60.                 transform: translateX(-5px);
  61.             }
  62.             50% {
  63.                 transform: translateX(5px);
  64.             }
  65.             75% {
  66.                 transform: translateX(-5px);
  67.             }
  68.             100% {
  69.                 transform: translateX(0);
  70.             }
  71.         }

  72.         button {
  73.             padding: 10px 15px;
  74.             background-color: #007BFF;
  75.             color: white;
  76.             border: none;
  77.             border-radius: 4px;
  78.             cursor: pointer;
  79.             transition: background-color 0.3s ease;
  80.         }

  81.         button:hover {
  82.             background-color: #0056b3;
  83.         }

  84.         #monitor-button.started {
  85.             background-color: #dc3545;
  86.         }

  87.         ul {
  88.             list-style-type: none;
  89.             padding: 0;
  90.             margin-top: 10px;
  91.             /* 假设每个网址高度约 30px,显示 5 个网址的高度 */
  92.             height: calc(5 * (30px + 5px));
  93.             overflow-y: auto;
  94.             flex: 1;
  95.         }

  96.         li {
  97.             background-color: #f4f4f9;
  98.             padding: 10px;
  99.             margin-bottom: 5px;
  100.             border-radius: 4px;
  101.             display: flex;
  102.             justify-content: space-between;
  103.             align-items: center;
  104.         }

  105.         #log-container {
  106.             margin-top: 10px;
  107.             height: calc(11 * (30px + 5px));
  108.             overflow-y: auto;
  109.             border: 1px solid #ccc;
  110.             padding: 10px;
  111.             border-radius: 4px;
  112.         }

  113.         #filter-container {
  114.             margin-bottom: 10px;
  115.         }

  116.         #filter-url {
  117.             padding: 8px;
  118.             border: 1px solid #ccc;
  119.             border-radius: 4px;
  120.             width: 100%;
  121.         }
  122.     </style>
  123. </head>

  124. <body>
  125.     <div class="left-panel">
  126.         <h1>网站监测工具</h1>
  127.         <div class="input-container">
  128.             <input type="text" id="url-input" placeholder="输入要监测的网页地址">
  129.             <button id="add-url">添加网址</button>
  130.         </div>
  131.         <div class="input-container">
  132.             <label for="interval">监测间隔 (秒):</label>
  133.             <input type="number" id="interval" value="60">
  134.             <button id="monitor-button">开始监测</button>
  135.         </div>
  136.         <ul id="url-list"></ul>
  137.     </div>
  138.     <div class="right-panel">
  139.         <div id="filter-container">
  140.             <label for="filter-url">筛选网址:</label>
  141.             <select id="filter-url">
  142.                 <option value="">全部</option>
  143.             </select>
  144.         </div>
  145.         <div id="log-container"></div>
  146.     </div>
  147.     <script>
  148.         const urlInput = document.getElementById('url-input');
  149.         const addUrlButton = document.getElementById('add-url');
  150.         const intervalInput = document.getElementById('interval');
  151.         const monitorButton = document.getElementById('monitor-button');
  152.         const urlList = document.getElementById('url-list');
  153.         const logContainer = document.getElementById('log-container');
  154.         const filterUrlSelect = document.getElementById('filter-url');

  155.         let urls = [];
  156.         let logs = [];
  157.         let intervalId;
  158.         let isMonitoring = false;
  159.         const MAX_RETRIES = 3;
  160.         const MAX_LOGS = 1000;
  161.         let isNetworkConnected = true;

  162.         addUrlButton.addEventListener('click', () => {
  163.             let url = urlInput.value.trim();
  164.             if (!url) {
  165.                 urlInput.classList.add('invalid');
  166.                 setTimeout(() => {
  167.                     urlInput.classList.remove('invalid');
  168.                 }, 1500);
  169.                 alert('请输入有效的网址');
  170.                 return;
  171.             }
  172.             if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('www.')) {
  173.                 url = 'https://' + url;
  174.             } else if (url.startsWith('www.')) {
  175.                 url = 'https://' + url;
  176.             }
  177.             if (!/^https?:\/\/.+/i.test(url)) {
  178.                 urlInput.classList.add('invalid');
  179.                 setTimeout(() => {
  180.                     urlInput.classList.remove('invalid');
  181.                 }, 1500);
  182.                 alert('请输入以 http://、https:// 或 www. 开头的有效网址');
  183.                 return;
  184.             }
  185.             urlInput.classList.remove('invalid');
  186.             urls.push(url);
  187.             const li = document.createElement('li');
  188.             li.textContent = url;
  189.             const deleteButton = document.createElement('button');
  190.             deleteButton.textContent = '删除';
  191.             deleteButton.addEventListener('click', () => {
  192.                 const index = urls.indexOf(url);
  193.                 if (index > -1) {
  194.                     urls.splice(index, 1);
  195.                     urlList.removeChild(li);
  196.                     const options = Array.from(filterUrlSelect.options);
  197.                     const optionToRemove = options.find(option => option.value === url);
  198.                     if (optionToRemove) {
  199.                         filterUrlSelect.removeChild(optionToRemove);
  200.                     }
  201.                 }
  202.             });
  203.             li.appendChild(deleteButton);
  204.             urlList.appendChild(li);
  205.             urlInput.value = '';

  206.             const option = document.createElement('option');
  207.             option.value = url;
  208.             option.textContent = url;
  209.             filterUrlSelect.appendChild(option);
  210.         });

  211.         monitorButton.addEventListener('click', () => {
  212.             if (!isMonitoring) {
  213.                 const interval = parseInt(intervalInput.value) * 1000;
  214.                 // 立即检测一次所有添加的网址
  215.                 urls.forEach(url => {
  216.                     checkUrl(url, 0);
  217.                 });
  218.                 // 按间隔时间循环检测
  219.                 intervalId = setInterval(() => {
  220.                     const newInterval = parseInt(intervalInput.value) * 1000;
  221.                     if (newInterval!== interval) {
  222.                         clearInterval(intervalId);
  223.                         intervalId = setInterval(() => {
  224.                             urls.forEach(url => {
  225.                                 checkUrl(url, 0);
  226.                             });
  227.                         }, newInterval);
  228.                     }
  229.                     if (isNetworkConnected) {
  230.                         urls.forEach(url => {
  231.                             checkUrl(url, 0);
  232.                         });
  233.                     }
  234.                 }, interval);
  235.                 monitorButton.classList.add('started');
  236.                 monitorButton.textContent = '停止监测';
  237.                 isMonitoring = true;
  238.             } else {
  239.                 clearInterval(intervalId);
  240.                 monitorButton.classList.remove('started');
  241.                 monitorButton.textContent = '开始监测';
  242.                 isMonitoring = false;
  243.             }
  244.         });

  245.         filterUrlSelect.addEventListener('change', () => {
  246.             const selectedUrl = filterUrlSelect.value;
  247.             displayLogs(selectedUrl);
  248.         });

  249.         function monitorUrlsImmediately(urls, interval) {
  250.             // 先立即执行一次监测
  251.             urls.forEach(url => {
  252.                 checkUrl(url, 0);
  253.             });
  254.             // 再按间隔时间循环监测
  255.             intervalId = setInterval(() => {
  256.                 urls.forEach(url => {
  257.                     checkUrl(url, 0);
  258.                 });
  259.             }, interval);
  260.         }

  261.         function checkUrl(url, retryCount) {
  262.             if (!isNetworkConnected) {
  263.                 handleLog(url, null, '电脑网络连接异常');
  264.                 return;
  265.             }
  266.             const startTime = performance.now();
  267.             const controller = new AbortController();
  268.             const signal = controller.signal;
  269.             const timeoutId = setTimeout(() => {
  270.                 controller.abort();
  271.             }, 5000);

  272.             const requestOptions = {
  273.                 mode: 'cors',
  274.                 signal: signal,
  275.                 headers: {
  276.                     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
  277.                 }
  278.             };

  279.             fetch(url, requestOptions)
  280.               .then(() => {
  281.                     clearTimeout(timeoutId);
  282.                     const endTime = performance.now();
  283.                     const responseTime = endTime - startTime;
  284.                     handleLog(url, responseTime, null);
  285.                     isNetworkConnected = true;
  286.                 })
  287.               .catch(error => {
  288.                     clearTimeout(timeoutId);
  289.                     const endTime = performance.now();
  290.                     const responseTime = endTime - startTime;
  291.                     if (retryCount < MAX_RETRIES) {
  292.                         setTimeout(() => {
  293.                             checkUrl(url, retryCount + 1);
  294.                         }, 1000);
  295.                     } else {
  296.                         let errorMessage = error.message;
  297.                         if (error.name === 'AbortError') {
  298.                             errorMessage = '请求超时';
  299.                         } else if (error.message.includes('NetworkError')) {
  300.                             errorMessage = '网络错误,请检查网络连接或代理设置';
  301.                             isNetworkConnected = false;
  302.                         }
  303.                         handleLog(url, responseTime, errorMessage);
  304.                     }
  305.                 });
  306.         }

  307.         function handleLog(url, responseTime, errorMessage) {
  308.             let status;
  309.             if (errorMessage === '电脑网络连接异常') {
  310.                 status = '异常';
  311.             } else if (responseTime!== null && responseTime < 800) {
  312.                 status = '正常';
  313.                 errorMessage = null;
  314.             } else {
  315.                 status = '异常';
  316.                 if (!errorMessage) {
  317.                     errorMessage = `响应时间过长: ${responseTime} 毫秒`;
  318.                 }
  319.             }
  320.             const log = {
  321.                 url,
  322.                 status,
  323.                 responseTime: responseTime!== null? Math.round(responseTime) : null,
  324.                 timestamp: new Date().toLocaleString(),
  325.                 error: errorMessage
  326.             };
  327.             logs.push(log);
  328.             if (logs.length > MAX_LOGS) {
  329.                 logs.shift();
  330.             }
  331.             displayLogs(filterUrlSelect.value);
  332.             updateUrlColor(url, status === '正常');
  333.         }

  334.         function updateUrlColor(url, isNormal) {
  335.             const listItems = urlList.getElementsByTagName('li');
  336.             for (let i = 0; i < listItems.length; i++) {
  337.                 if (listItems[i].textContent.includes(url)) {
  338.                     if (isNormal) {
  339.                         listItems[i].style.color = 'black';
  340.                     } else {
  341.                         listItems[i].style.color = 'red';
  342.                     }
  343.                     break;
  344.                 }
  345.             }
  346.         }

  347.         function displayLogs(filterUrl) {
  348.             logContainer.innerHTML = '';
  349.             const filteredLogs = filterUrl? logs.filter(log => log.url === filterUrl) : logs;
  350.             filteredLogs.forEach(log => {
  351.                 const logEntry = document.createElement('p');
  352.                 let statusColor;
  353.                 if (log.status === '正常') {
  354.                     statusColor = 'black';
  355.                 } else {
  356.                     statusColor = 'red';
  357.                 }
  358.                 logEntry.style.color = statusColor;
  359.                 logEntry.textContent = `${log.timestamp} - ${log.url} - 状态: ${log.status}`;
  360.                 if (log.responseTime!== null) {
  361.                     logEntry.textContent += ` - 响应时间: ${log.responseTime} 毫秒`;
  362.                 }
  363.                 if (log.error) {
  364.                     logEntry.textContent += ` - 错误信息: ${log.error}`;
  365.                 }
  366.                 logContainer.appendChild(logEntry);
  367.             });
  368.             // 自动滚动到最底部显示最新信息
  369.             logContainer.scrollTop = logContainer.scrollHeight;
  370.         }
  371.     </script>
  372. </body>

  373. </html>
复制代码




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一起港湾 ( 青ICP备2025004122号-1 )

GMT+8, 2025-4-8 04:04 , Processed in 0.085758 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表