check-disk.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /// 磁盘空间监控
  2. const { exec } = require('child_process');
  3. const { promisify } = require('util');
  4. const execAsync = promisify(exec);
  5. const { sendEmail } = require('./email');
  6. const { sendSms } = require('./sms');
  7. const SmsTemplate = require('./sms-templates');
  8. // 阈值配置
  9. const USAGE_THRESHOLD = 90; // 使用率阈值 90%
  10. const FREE_SPACE_THRESHOLD = 10; // 剩余空间阈值 10G
  11. async function getDiskInfo() {
  12. try {
  13. const { stdout } = await execAsync('df -h /');
  14. const lines = stdout.trim().split('\n');
  15. if (lines.length < 2) {
  16. throw new Error('无法解析df命令输出');
  17. }
  18. // 解析df输出
  19. const dataLine = lines[1].trim().split(/\s+/);
  20. const [filesystem, size, used, avail, usePercent, mountpoint] = dataLine;
  21. return {
  22. filesystem,
  23. size,
  24. used,
  25. avail,
  26. usePercent: parseInt(usePercent),
  27. mountpoint,
  28. raw: stdout
  29. };
  30. } catch (error) {
  31. throw new Error(`获取磁盘信息失败: ${error.message}`);
  32. }
  33. }
  34. function parseSize(sizeStr) {
  35. // 将 "106G", "1.5T" 等格式转换为GB数值
  36. const match = sizeStr.match(/^([\d.]+)([KMGT]?)$/i);
  37. if (!match) return 0;
  38. const value = parseFloat(match[1]);
  39. const unit = match[2].toUpperCase();
  40. const multipliers = { '': 1/1024, 'K': 1/1024/1024, 'M': 1/1024, 'G': 1, 'T': 1024 };
  41. return value * (multipliers[unit] || 1);
  42. }
  43. async function checkDiskSpace() {
  44. let diskInfo;
  45. try {
  46. diskInfo = await getDiskInfo();
  47. } catch (e) {
  48. console.error('磁盘信息获取失败:', e.message);
  49. return {
  50. result: false,
  51. errcode: 200,
  52. title: '磁盘监控:无法获取磁盘信息',
  53. sms: SmsTemplate.SMS_DISK_ERROR,
  54. data: [e.message],
  55. diskInfo: null
  56. };
  57. }
  58. const usagePercent = diskInfo.usePercent;
  59. const availGB = parseSize(diskInfo.avail);
  60. console.log(`磁盘使用率: ${usagePercent}%, 剩余空间: ${availGB.toFixed(2)}GB`);
  61. // 检查是否超过阈值
  62. if (usagePercent >= USAGE_THRESHOLD || availGB < FREE_SPACE_THRESHOLD) {
  63. const reasons = [];
  64. if (usagePercent >= USAGE_THRESHOLD) {
  65. reasons.push(`使用率达到 ${usagePercent}% (阈值: ${USAGE_THRESHOLD}%)`);
  66. }
  67. if (availGB < FREE_SPACE_THRESHOLD) {
  68. reasons.push(`剩余空间仅 ${availGB.toFixed(2)}GB (阈值: ${FREE_SPACE_THRESHOLD}GB)`);
  69. }
  70. return {
  71. result: false,
  72. errcode: 201,
  73. title: '磁盘空间告警',
  74. sms: SmsTemplate.SMS_DISK_WARNING,
  75. data: [
  76. `文件系统: ${diskInfo.filesystem}`,
  77. `挂载点: ${diskInfo.mountpoint}`,
  78. `总容量: ${diskInfo.size}`,
  79. `已使用: ${diskInfo.used}`,
  80. `可用空间: ${diskInfo.avail}`,
  81. `使用率: ${usagePercent}%`,
  82. '',
  83. '告警原因:',
  84. ...reasons,
  85. '',
  86. '详细信息:',
  87. diskInfo.raw
  88. ],
  89. diskInfo
  90. };
  91. }
  92. return {
  93. result: true,
  94. errcode: 0,
  95. title: '磁盘空间正常',
  96. sms: SmsTemplate.SMS_RESUME,
  97. data: [
  98. `文件系统: ${diskInfo.filesystem}`,
  99. `使用率: ${usagePercent}%`,
  100. `剩余空间: ${diskInfo.avail}`
  101. ],
  102. diskInfo
  103. };
  104. }
  105. async function run(lastErrCode = 0) {
  106. console.log("检查磁盘空间...", new Date());
  107. try {
  108. let result = null;
  109. // 连续检测3次才发送通知,减少误报
  110. for (let i = 0; i < 3; i++) {
  111. result = await checkDiskSpace();
  112. console.log(result);
  113. if (!result.result) {
  114. console.warn("磁盘空间检查出现异常,一分钟后重试");
  115. await delay(60 * 1000);
  116. } else {
  117. console.log("磁盘空间正常");
  118. break;
  119. }
  120. }
  121. if (!result.result) {
  122. console.error("连续3次检查磁盘空间异常,发送通知");
  123. sendEmail(result.title, result.data);
  124. sendSms(result.sms);
  125. console.log("5分钟后再试");
  126. setTimeout(run, 5 * 60 * 1000, result.errcode);
  127. return;
  128. } else {
  129. // 如果上次是出错的情况,那么属于服务恢复,发送恢复通知
  130. if (lastErrCode === 200 || lastErrCode === 201) {
  131. sendEmail(result.title, result.data);
  132. sendSms(result.sms);
  133. }
  134. }
  135. } catch (err) {
  136. console.error(err.stack);
  137. }
  138. }
  139. function delay(ms) {
  140. return new Promise(done => setTimeout(done, ms));
  141. }
  142. module.exports = { run, checkDiskSpace };
  143. if (require.main == module) {
  144. run();
  145. }