2024-05-03 22:30:46 +08:00
|
|
|
#include "SystemUsage.h"
|
|
|
|
#include "BoostLog.h"
|
2024-11-09 19:21:15 +08:00
|
|
|
#include "DateTime.h"
|
2024-05-03 22:30:46 +08:00
|
|
|
#include "NetworkUtility.h"
|
2024-11-09 19:21:15 +08:00
|
|
|
#include "ServiceManager.h"
|
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
2024-05-03 22:30:46 +08:00
|
|
|
#include <boost/json/object.hpp>
|
|
|
|
#include <boost/json/serialize.hpp>
|
|
|
|
#include <fstream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
#include <sys/sysinfo.h>
|
|
|
|
|
|
|
|
SystemUsage::SystemUsage(boost::asio::io_context &ioContext, const std::string &accessToken)
|
|
|
|
: m_ioContext(ioContext), m_accessToken(accessToken) {
|
|
|
|
m_timer = std::make_shared<boost::asio::system_timer>(m_ioContext);
|
|
|
|
// LOG(info) << "access token: " << m_accessToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SystemUsage::start() {
|
2024-11-09 19:21:15 +08:00
|
|
|
using namespace std::chrono;
|
|
|
|
m_timer->expires_after(seconds(10));
|
2024-05-03 22:30:46 +08:00
|
|
|
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<int>(100.0f * diskUsage("/")), "%", "磁盘占用率");
|
|
|
|
m_lastCpuStats = currentCpuStats;
|
2024-11-09 19:21:15 +08:00
|
|
|
|
|
|
|
m_networkStats.push_back(networkStats("eth0"));
|
|
|
|
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<seconds>(back.time - front.time);
|
|
|
|
if (duration > SpeedInterval) {
|
|
|
|
m_networkStats.pop_front();
|
|
|
|
} else {
|
|
|
|
constexpr auto gigabyte = 1024 * 1024 * 1024;
|
|
|
|
auto receiveGigabyte = static_cast<float>(back.receiveBytes - front.receiveBytes) / gigabyte;
|
|
|
|
auto transmitGigabyte = static_cast<float>(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";
|
|
|
|
if (speed >= 1.f) { // 一个小时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<ServiceManager>::instance();
|
|
|
|
if (manager) manager->sendMessage(NotifyServerChan, oss.str());
|
|
|
|
LOG(warning) << oss.str();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-05-03 22:30:46 +08:00
|
|
|
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<float>(second.totalActive() - first.totalActive());
|
|
|
|
const float idle_time = static_cast<float>(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<float>(diff) / total;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2024-11-09 19:21:15 +08:00
|
|
|
|
|
|
|
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<std::string> 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;
|
|
|
|
}
|