#include "SystemUsage.h" #include "BoostLog.h" #include "DateTime.h" #include "NetworkUtility.h" #include "ServiceManager.h" #include #include #include #include #include #include #include #include #include SystemUsage::SystemUsage(boost::asio::io_context &ioContext, const std::string &accessToken) : m_ioContext(ioContext), m_accessToken(accessToken) { m_timer = std::make_shared(m_ioContext); // LOG(info) << "access token: " << m_accessToken; } void SystemUsage::start() { using namespace std::chrono; m_timer->expires_after(seconds(10)); m_timer->async_wait([this](const boost::system::error_code &error) { if (error) { LOG(error) << error.message(); return; } auto currentCpuStats = readCpuData(); int usage = 100.0f * cpuUsage(m_lastCpuStats, currentCpuStats); publish("yuyun_cpu_usage", usage, "%", "CPU占用率"); publish("yuyun_disk_usage", static_cast(100.0f * diskUsage("/")), "%", "磁盘占用率"); m_lastCpuStats = currentCpuStats; m_networkStats.push_back(networkStats("ens18")); while (m_networkStats.size() >= 2) { constexpr auto SpeedInterval = seconds(60 * 15); auto front = m_networkStats.front(); auto back = m_networkStats.back(); auto duration = duration_cast(back.time - front.time); if (duration > SpeedInterval) { m_networkStats.pop_front(); } else { constexpr auto gigabyte = 1024 * 1024 * 1024; auto receiveGigabyte = static_cast(back.receiveBytes - front.receiveBytes) / gigabyte; auto transmitGigabyte = static_cast(back.transmitBytes - front.transmitBytes) / gigabyte; auto speed = (receiveGigabyte + transmitGigabyte) / duration.count() * SpeedInterval.count(); LOG(info) << "network speed: " << std::fixed << std::setprecision(2) << speed << "GB/h"; static system_clock::time_point lastNotify; auto now = system_clock::now(); if ((speed >= 1.f) && (duration_cast(now - lastNotify) > minutes(10))) { // 一个小时1GB的流量 std::ostringstream oss; oss << "当前服务器流量存在异常, " << DateTime::toString(front.time) << " - " << DateTime::toString(back.time) << ": " << std::endl; oss << "进出总速率: " << std::fixed << std::setprecision(2) << speed << "GB/h" << std::endl; oss << "接收流量: " << std::fixed << std::setprecision(2) << receiveGigabyte << "GB" << std::endl; oss << "发送流量: " << std::fixed << std::setprecision(2) << transmitGigabyte << "GB" << std::endl; auto manager = Amass::Singleton::instance(); if (manager) manager->sendMessage(NotifyServerChan, oss.str()); LOG(warning) << oss.str(); lastNotify = now; } break; } } start(); }); } void SystemUsage::publish(const std::string_view &deviceName, float value, const std::string_view &unit, const std::string_view &friendlyName) { // LOG(info) << "cpu usage: " << usage << "%"; Http::Client http(m_ioContext, Http::Transparent); std::ostringstream oss; oss << "Bearer " << m_accessToken; http.addRequestField(boost::beast::http::field::authorization, oss.str()); http.addRequestField(boost::beast::http::field::content_type, "application/json"); boost::json::object request; request["state"] = value; boost::json::object attributes; attributes["unit_of_measurement"] = unit; attributes["friendly_name"] = friendlyName; request["attributes"] = std::move(attributes); oss.str(""); oss << "/api/states/sensor." << deviceName; boost::system::error_code error; auto reply = http.post("iot.amass.fun", "80", oss.str(), boost::json::serialize(request), error); if (error) { LOG(error) << error.message(); } } SystemUsage::CpuStats SystemUsage::readCpuData() { CpuStats result; std::ifstream proc_stat("/proc/stat"); if (proc_stat.good()) { std::string line; getline(proc_stat, line); unsigned int *stats_p = (unsigned int *)&result; std::stringstream iss(line); std::string cpu; iss >> cpu; while (iss >> *stats_p) { stats_p++; }; } proc_stat.close(); return result; } float SystemUsage::cpuUsage(const CpuStats &first, const CpuStats &second) { const float active_time = static_cast(second.totalActive() - first.totalActive()); const float idle_time = static_cast(second.totalIdle() - first.totalIdle()); const float total_time = active_time + idle_time; return active_time / total_time; } float SystemUsage::diskUsage(const std::string &disk) { struct statvfs diskData; statvfs(disk.c_str(), &diskData); auto total = diskData.f_blocks; auto free = diskData.f_bfree; auto diff = total - free; float result = static_cast(diff) / total; return result; } SystemUsage::NetworkStats SystemUsage::networkStats(const std::string &interface) { NetworkStats ret; try { std::ifstream ifs("/proc/net/dev"); if (!ifs.is_open()) { LOG(error) << "failed to open /proc/net/dev"; return ret; } std::string line; while (std::getline(ifs, line)) { if (line.find(interface) == std::string::npos) continue; boost::trim(line); std::vector results; boost::split(results, line, boost::algorithm::is_any_of(" "), boost::token_compress_on); if (results.size() < 9) continue; ret.interface = results.at(0); ret.interface = ret.interface.substr(0, ret.interface.size() - 1); // Remove the trailing ':' ret.receiveBytes = std::stoll(results.at(1)); ret.transmitBytes = std::stoll(results.at(9)); // LOG(info) << "interface: " << ret.interface << ", receive bytes: " << ret.receiveBytes // << ", transmit bytes: " << ret.transmitBytes; break; } } catch (const std::exception &e) { LOG(error) << e.what(); } return ret; }