Skip to content
This repository has been archived by the owner on Apr 24, 2022. It is now read-only.

implemented prometheus metrics endpoint #2398

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 145 additions & 1 deletion libapicore/ApiServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ void ApiConnection::onRecvSocketDataCompleted(
}

// Do we support path ?
if (http_path != "/" && http_path != "/getstat1")
if (http_path != "/" && http_path != "/getstat1" && http_path != "/metrics")
{
std::string what =
"The requested resource " + http_path + " not found on this server";
Expand Down Expand Up @@ -858,6 +858,34 @@ void ApiConnection::onRecvSocketDataCompleted(
}
}

if (http_method == "GET" && (http_path == "/metrics"))
{
try
{
std::string body = getHttpMinerMetrics();
ss.clear();
ss << http_ver << " "
<< "200 Ok Error\r\n"
<< "Server: " << ethminer_get_buildinfo()->project_name_with_version
<< "\r\n"
<< "Content-Type: text/plain; charset=utf-8\r\n"
<< "Content-Length: " << body.size() << "\r\n\r\n"
<< body << "\r\n";
}
catch (const std::exception& _ex)
{
std::string what = "Internal error : " + std::string(_ex.what());
ss.clear();
ss << http_ver << " "
<< "500 Internal Server Error\r\n"
<< "Server: " << ethminer_get_buildinfo()->project_name_with_version
<< "\r\n"
<< "Content-Type: text/plain\r\n"
<< "Content-Length: " << what.size() << "\r\n\r\n"
<< what << "\r\n";
}
}

sendSocketData(ss.str(), true);
m_message.clear();
}
Expand Down Expand Up @@ -1194,6 +1222,122 @@ std::string ApiConnection::getHttpMinerStatDetail()
return _ret.str();
}

std::string ApiConnection::getHttpMinerMetrics()
{
Json::Value jStat = getMinerStatDetail();
uint64_t durationSeconds = jStat["host"]["runtime"].asUInt64();
std::string hostname = jStat["host"]["name"].asString();
std::string version = jStat["host"]["version"].asString();
std::string pool = jStat["connection"]["uri"].asString();

std::string help = "# HELP ";
std::string type = "# TYPE ";
std::string start = "ethminer_";

std::stringstream _ret;

// Info
_ret << help << start << "info Info of Miner Host." << std::endl;
_ret << type << start << "info gauge" << std::endl;
_ret << start << "info{hostname=\"" << hostname << "\",version=\""<< version << "\",pool=\""<< pool <<"\"} 1" << std::endl;

//Uptime
_ret << help << start << "uptime_sec Uptime of Miner in seconds." << std::endl;
_ret << type << start << "uptime_sec counter" << std::endl;
_ret << start << "uptime_sec " << to_string(durationSeconds) << std::endl;


//Device info
_ret << help << start << "device_info Info of Miner Devices." << std::endl;
_ret << type << start << "device_info gauge" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
_ret << start << "device_info{id=\""<< to_string(i) <<"\",pci=\"" << device["hardware"]["pci"].asString() << "\",name=\""<< device["hardware"]["name"].asString() << "\",mode=\""<< device["_mode"].asString() <<"\",paused=\""<< device["mining"]["paused"].asBool() <<"\"} 1" << std::endl;
}

//Difficulty
_ret << help << start << "difficulty Mining Difficulty." << std::endl;
_ret << type << start << "difficulty gauge" << std::endl;
_ret << start << "difficulty " << to_string(jStat["mining"]["difficulty"].asDouble()) << std::endl;


//epoch
_ret << help << start << "epoch Mining Difficulty." << std::endl;
_ret << type << start << "epoch gauge" << std::endl;
_ret << start << "epoch " << to_string(jStat["mining"]["epoch"].asDouble()) << std::endl;

//Device hashrate
double total_hashrate = 0;
_ret << help << start << "hashrate Hashrate of Miner Devices." << std::endl;
_ret << type << start << "hashrate gauge" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
double hashrate = std::stoul(device["mining"]["hashrate"].asString(), nullptr, 16);
total_hashrate += hashrate;
_ret << start << "hashrate{id=\""<< to_string(i) <<"\"} " << to_string(hashrate) << std::endl;
}

//Device Power
double total_power = 0;
_ret << help << start << "power Power of Miner Devices." << std::endl;
_ret << type << start << "power gauge" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
double power = device["hardware"]["sensors"][2].asDouble();
total_power += power;
_ret << start << "power{id=\""<< to_string(i) <<"\"} " << to_string(power) << std::endl;
}

//Device Shares
unsigned int total_shares[] = {0, 0, 0};
_ret << help << start << "shares Shares of Miner Devices." << std::endl;
_ret << type << start << "shares counter" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
uint shares[] = {device["mining"]["shares"][0].asUInt(), device["mining"]["shares"][1].asUInt(), device["mining"]["shares"][2].asUInt()};
total_shares[0] += shares[0];
total_shares[1] += shares[1];
total_shares[2] += shares[2];
_ret << start << "shares{state=\"accepted\",id=\""<< to_string(i) <<"\"} " << total_shares[0] << std::endl;
_ret << start << "shares{state=\"rejected\",id=\""<< to_string(i) <<"\"} " << total_shares[1] << std::endl;
_ret << start << "shares{state=\"failed\",id=\""<< to_string(i) <<"\"} " << total_shares[2] << std::endl;
}

//Device Temp
_ret << help << start << "temp of Miner Devices in °C." << std::endl;
_ret << type << start << "temp gauge" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
_ret << start << "temp{id=\""<< to_string(i) <<"\"} " << device["hardware"]["sensors"][0].asString() << std::endl;
}
//Device Fan
_ret << help << start << "fan of Miner Devices in %." << std::endl;
_ret << type << start << "fan gauge" << std::endl;
for (Json::Value::ArrayIndex i = 0; i != jStat["devices"].size(); i++){
Json::Value device = jStat["devices"][i];
_ret << start << "fan{id=\""<< to_string(i) <<"\"} " << device["hardware"]["sensors"][1].asString() << std::endl;
}

//Total hashrate
_ret << help << start << "hashrate_total Total Hashrate of Miner." << std::endl;
_ret << type << start << "hashrate_total gauge" << std::endl;
_ret << start << "hashrate_total " << total_hashrate << std::endl;

//Total power
_ret << help << start << "power_total Total Power of Miner." << std::endl;
_ret << type << start << "power_total gauge" << std::endl;
_ret << start << "power_total " << total_power << std::endl;

//Total shares
_ret << help << start << "shares_total Total Shares of Miner." << std::endl;
_ret << type << start << "shares_total counter" << std::endl;
_ret << start << "shares_total{state=\"accepted\"} " << total_shares[0] << std::endl;
_ret << start << "shares_total{state=\"rejected\"} " << total_shares[1] << std::endl;
_ret << start << "shares_total{state=\"failed\"} " << total_shares[2] << std::endl;

return _ret.str();
}

/**
* @brief Return a total and per GPU detailed list of current status
* As we return here difficulty and share counts (which are not getting resetted if we
Expand Down
2 changes: 2 additions & 0 deletions libapicore/ApiServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class ApiConnection

std::string getHttpMinerStatDetail();

std::string getHttpMinerMetrics();

Disconnected m_onDisconnected;

int m_sessionId;
Expand Down