Prometheus stats split over viewers, incoming and outgoing.

Load balancer updated to use new split stats and provide info per host and/or stream if requested over its port.
This commit is contained in:
Thulinma 2016-05-15 00:20:53 +02:00
parent 39a61b6380
commit dfc41cc596
9 changed files with 199 additions and 62 deletions

View file

@ -7,6 +7,7 @@
#include "controller_statistics.h"
#include "controller_limits.h"
#include "controller_push.h"
#include "controller_storage.h"
#ifndef KILL_ON_EXIT
#define KILL_ON_EXIT false
@ -43,13 +44,17 @@ std::map<std::string, unsigned int> Controller::activeStreams;
struct streamTotals {
unsigned long long upBytes;
unsigned long long downBytes;
unsigned long long clients;
unsigned long long inputs;
unsigned long long outputs;
unsigned long long viewers;
unsigned int timeout;
};
static std::map<std::string, struct streamTotals> streamStats;
static unsigned long long servUpBytes = 0;
static unsigned long long servDownBytes = 0;
static unsigned long long servClients = 0;
static unsigned long long servInputs = 0;
static unsigned long long servOutputs = 0;
static unsigned long long servViewers = 0;
Controller::sessIndex::sessIndex(std::string dhost, unsigned int dcrc, std::string dstreamName, std::string dconnector){
host = dhost;
@ -211,8 +216,19 @@ void Controller::statSession::update(unsigned long index, IPC::statExchange & da
if (currDown + currUp > COUNTABLE_BYTES){
std::string streamName = data.streamName();
if (prevUp + prevDown < COUNTABLE_BYTES){
++servClients;
streamStats[streamName].clients++;
if (data.connector() == "INPUT"){
++servInputs;
streamStats[streamName].inputs++;
sessionType = SESS_INPUT;
}else if (data.connector() == "OUTPUT"){
++servOutputs;
streamStats[streamName].outputs++;
sessionType = SESS_OUTPUT;
}else{
++servViewers;
streamStats[streamName].viewers++;
sessionType = SESS_VIEWER;
}
streamStats[streamName].upBytes += currUp;
streamStats[streamName].downBytes += currDown;
}else{
@ -222,6 +238,10 @@ void Controller::statSession::update(unsigned long index, IPC::statExchange & da
}
}
Controller::sessType Controller::statSession::getSessType(){
return sessionType;
}
/// Archives the given connection.
void Controller::statSession::wipeOld(unsigned long long cutOff){
if (firstSec > cutOff){
@ -262,6 +282,7 @@ Controller::statSession::statSession(){
sync = 1;
wipedUp = 0;
wipedDown = 0;
sessionType = SESS_UNSET;
}
/// Moves the given connection to the given session
@ -1083,6 +1104,9 @@ void Controller::handlePrometheus(HTTP::Parser & H, Socket::Connection & conn, i
if (mode == PROMETHEUS_TEXT){
std::stringstream response;
response << "# HELP mist_logs Count of log messages since server start.\n";
response << "# TYPE mist_logs counter\n";
response << "mist_logs " << Controller::logCounter << "\n\n";
response << "# HELP mist_cpu Total CPU usage in tenths of percent.\n";
response << "# TYPE mist_cpu gauge\n";
response << "mist_cpu " << cpu_use << "\n\n";
@ -1095,57 +1119,68 @@ void Controller::handlePrometheus(HTTP::Parser & H, Socket::Connection & conn, i
{//Scope for shortest possible blocking of statsMutex
tthread::lock_guard<tthread::mutex> guard(statsMutex);
response << "# HELP mist_sessions_cached Number of sessions active in the last ~10 minutes.\n";
response << "# TYPE mist_sessions_cached gauge\n";
response << "mist_sessions_cached " << sessions.size() << "\n\n";
//collect the data first
std::map<std::string, unsigned long> clients;
unsigned long totClients = 0;
std::map<std::string, struct streamTotals> streams;
unsigned long totViewers = 0, totInputs = 0, totOutputs = 0;
unsigned int t = Util::epoch() - STATS_DELAY;
//check all sessions
if (sessions.size()){
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
if (it->second.hasDataFor(t) && it->second.isViewerOn(t)){
clients[it->first.streamName]++;
totClients++;
switch (it->second.getSessType()){
case SESS_UNSET:
case SESS_VIEWER:
streams[it->first.streamName].viewers++;
totViewers++;
break;
case SESS_INPUT:
streams[it->first.streamName].inputs++;
totInputs++;
break;
case SESS_OUTPUT:
streams[it->first.streamName].outputs++;
totOutputs++;
break;
}
}
}
}
response << "# HELP mist_sessions_current Number of sessions active right now, server-wide.\n";
response << "# TYPE mist_sessions_current gauge\n";
response << "mist_sessions_current " << totClients << "\n\n";
response << "# HELP mist_sessions_total Number of sessions active right now, server-wide, by type.\n";
response << "# TYPE mist_sessions_total gauge\n";
response << "mist_sessions_total{sessType=\"viewers\"} " << totViewers << "\n";
response << "mist_sessions_total{sessType=\"incoming\"} " << totInputs << "\n";
response << "mist_sessions_total{sessType=\"outgoing\"} " << totOutputs << "\n";
response << "mist_sessions_total{sessType=\"cached\"} " << sessions.size() << "\n\n";
response << "# HELP mist_sessions_total Count of unique sessions since server start.\n";
response << "# TYPE mist_sessions_total counter\n";
response << "mist_sessions_total " << servClients << "\n\n";
response << "# HELP mist_sessions_count Counts of unique sessions by type since server start.\n";
response << "# TYPE mist_sessions_count counter\n";
response << "mist_sessions_count{sessType=\"viewers\"} " << servViewers << "\n";
response << "mist_sessions_count{sessType=\"incoming\"} " << servInputs << "\n";
response << "mist_sessions_count{sessType=\"outgoing\"} " << servOutputs << "\n\n";
response << "# HELP mist_upload_total Count of bytes uploaded since server start.\n";
response << "# TYPE mist_upload_total counter\n";
response << "mist_upload_total " << servUpBytes << "\n\n";
response << "# HELP mist_bw_total Count of bytes handled since server start, by direction.\n";
response << "# TYPE mist_bw_total counter\n";
response << "mist_bw_total{direction=\"up\"} " << servUpBytes << "\n";
response << "mist_bw_total{direction=\"down\"} " << servDownBytes << "\n\n";
response << "# HELP mist_download_total Count of bytes downloaded since server start.\n";
response << "# TYPE mist_download_total counter\n";
response << "mist_download_total " << servDownBytes << "\n\n";
response << "# HELP mist_current Number of sessions for a given stream active right now.\n";
response << "# TYPE mist_current gauge\n";
for (std::map<std::string, unsigned long>::iterator it = clients.begin(); it != clients.end(); ++it){
response << "mist_current{stream=\"" << it->first << "\"} " << it->second << "\n";
response << "# HELP mist_viewers Number of sessions by type and stream active right now.\n";
response << "# TYPE mist_viewers gauge\n";
for (std::map<std::string, struct streamTotals>::iterator it = streams.begin(); it != streams.end(); ++it){
response << "mist_sessions{stream=\"" << it->first << "\",sessType=\"viewers\"} " << it->second.viewers << "\n";
response << "mist_sessions{stream=\"" << it->first << "\",sessType=\"incoming\"} " << it->second.inputs << "\n";
response << "mist_sessions{stream=\"" << it->first << "\",sessType=\"outgoing\"} " << it->second.outputs << "\n";
}
response << "\n# HELP mist_sessions Count of unique sessions since stream start.\n";
response << "# TYPE mist_sessions counter\n";
response << "# HELP mist_upload Count of bytes uploaded since stream start.\n";
response << "# TYPE mist_upload counter\n";
response << "# HELP mist_download Count of bytes downloaded since stream start.\n";
response << "# TYPE mist_download counter\n";
std::set<std::string> mustWipe;
response << "\n# HELP mist_viewcount Count of unique viewer sessions since stream start, per stream.\n";
response << "# TYPE mist_viewcount counter\n";
response << "# HELP mist_bw Count of bytes handled since stream start, by direction.\n";
response << "# TYPE mist_bw counter\n";
for (std::map<std::string, struct streamTotals>::iterator it = streamStats.begin(); it != streamStats.end(); ++it){
response << "mist_sessions{stream=\"" << it->first << "\"} " << it->second.clients << "\n";
response << "mist_upload{stream=\"" << it->first << "\"} " << it->second.upBytes << "\n";
response << "mist_download{stream=\"" << it->first << "\"} " << it->second.downBytes << "\n";
response << "mist_viewcount{stream=\"" << it->first << "\"} " << it->second.viewers << "\n";
response << "mist_bw{stream=\"" << it->first << "\",direction=\"up\"} " << it->second.upBytes << "\n";
response << "mist_bw{stream=\"" << it->first << "\",direction=\"down\"} " << it->second.downBytes << "\n";
}
}
H.Chunkify(response.str(), conn);
@ -1155,37 +1190,59 @@ void Controller::handlePrometheus(HTTP::Parser & H, Socket::Connection & conn, i
resp["cpu"] = cpu_use;
resp["mem_total"] = mem_total;
resp["mem_used"] = (mem_total - mem_free - mem_bufcache);
resp["logs"] = (long long)Controller::logCounter;
{//Scope for shortest possible blocking of statsMutex
tthread::lock_guard<tthread::mutex> guard(statsMutex);
//collect the data first
std::map<std::string, unsigned long> clients;
unsigned long totClients = 0;
std::map<std::string, struct streamTotals> streams;
unsigned long totViewers = 0, totInputs = 0, totOutputs = 0;
unsigned int t = Util::epoch() - STATS_DELAY;
//check all sessions
if (sessions.size()){
for (std::map<sessIndex, statSession>::iterator it = sessions.begin(); it != sessions.end(); it++){
if (it->second.hasDataFor(t) && it->second.isViewerOn(t)){
clients[it->first.streamName]++;
totClients++;
switch (it->second.getSessType()){
case SESS_UNSET:
case SESS_VIEWER:
streams[it->first.streamName].viewers++;
totViewers++;
break;
case SESS_INPUT:
streams[it->first.streamName].inputs++;
totInputs++;
break;
case SESS_OUTPUT:
streams[it->first.streamName].outputs++;
totOutputs++;
break;
}
}
}
}
resp["sess_cached"] = (long long)sessions.size();
resp["sess_current"] = (long long)totClients;
resp["sess_total"] = (long long)servClients;
resp["upload"] = (long long)servUpBytes;
resp["download"] = (long long)servDownBytes;
resp["curr"].append((long long)totViewers);
resp["curr"].append((long long)totInputs);
resp["curr"].append((long long)totOutputs);
resp["curr"].append((long long)sessions.size());
resp["tot"].append((long long)servViewers);
resp["tot"].append((long long)servInputs);
resp["tot"].append((long long)servOutputs);
resp["bw"].append((long long)servUpBytes);
resp["bw"].append((long long)servDownBytes);
for (std::map<std::string, unsigned long>::iterator it = clients.begin(); it != clients.end(); ++it){
resp["streams"][it->first]["sess_current"] = (long long)it->second;
for (std::map<std::string, struct streamTotals>::iterator it = streams.begin(); it != streams.end(); ++it){
resp["streams"][it->first]["curr"].append((long long)it->second.viewers);
resp["streams"][it->first]["curr"].append((long long)it->second.inputs);
resp["streams"][it->first]["curr"].append((long long)it->second.outputs);
}
std::set<std::string> mustWipe;
for (std::map<std::string, struct streamTotals>::iterator it = streamStats.begin(); it != streamStats.end(); ++it){
resp["streams"][it->first]["sess_total"] = (long long)it->second.clients;
resp["streams"][it->first]["upload"] = (long long)it->second.upBytes;
resp["streams"][it->first]["download"] = (long long)it->second.downBytes;
resp["streams"][it->first]["tot"].append((long long)it->second.viewers);
resp["streams"][it->first]["tot"].append((long long)it->second.inputs);
resp["streams"][it->first]["tot"].append((long long)it->second.outputs);
resp["streams"][it->first]["bw"].append((long long)it->second.upBytes);
resp["streams"][it->first]["bw"].append((long long)it->second.downBytes);
}
}
H.Chunkify(resp.toString(), conn);

View file

@ -31,6 +31,13 @@ namespace Controller {
long long up;
};
enum sessType {
SESS_UNSET = 0,
SESS_INPUT,
SESS_OUTPUT,
SESS_VIEWER
};
/// This is a comparison and storage class that keeps sessions apart from each other.
/// Whenever two of these objects are not equal, it will create a new session.
class sessIndex {
@ -72,7 +79,9 @@ namespace Controller {
std::deque<statStorage> oldConns;
std::map<unsigned long, statStorage> curConns;
char sync;
sessType sessionType;
public:
sessType getSessType();
statSession();
void wipeOld(unsigned long long);
void finish(unsigned long index);

View file

@ -16,6 +16,7 @@ namespace Controller {
JSON::Value Storage; ///< Global storage of data.
tthread::mutex configMutex;
tthread::mutex logMutex;
unsigned long long logCounter = 0;
bool configChanged = false;
///\brief Store and print a log message.
@ -36,6 +37,7 @@ namespace Controller {
timeinfo = localtime (&rawtime);
strftime(buffer,100,"%F %H:%M:%S",timeinfo);
std::cout << "[" << buffer << "] " << kind << ": " << message << std::endl;
logCounter++;
}
///\brief Write contents to Filename

View file

@ -9,6 +9,7 @@ namespace Controller {
extern tthread::mutex logMutex;///< Mutex for log thread.
extern tthread::mutex configMutex;///< Mutex for server config access.
extern bool configChanged; ///< Bool that indicates config must be written to SHM.
extern unsigned long long logCounter; ///<Count of logged messages since boot
/// Store and print a log message.
void Log(std::string kind, std::string message);