obmon  1.4.0
 All Classes Functions Variables Typedefs Enumerations Groups Pages
ObSensorSystem.cpp
1 #include <ObSensorSystem.h>
2 #include <cctype>
3 #include <fstream>
4 #include <sstream>
5 
6 ObSensorSystem::ObSensorSystem(std::string name) : ObSensor(name) {
10 
11  _sysinfo = glibtop_init();
12  char **devices = glibtop_get_netlist(&_netlist);
13  glibtop_netload *net;
14  if (_netloads.empty()) {
15  for (unsigned int i = 0; i < _netlist.number; i++) {
16  net = new glibtop_netload;
17  _netloads.push_back(net);
18  _netnames.push_back(devices[i]);
19  free(devices[i]);
20  }
21  free(devices);
22  }
23 
24  if (getenv("OBMON_RES_CPU"))
25  _resCpu = atoi(getenv("OBMON_RES_CPU"));
26 
27  // =========================================================================
28  // Disk devices filtering initialization (R.I.P. English)
29  //
30  // std::map ensures, that if user enters same device twice, then only one
31  // entry will remain.
32  std::map<std::string, bool> allowedDisks_;
33  if (getenv("OBMON_ALLOWED_DISKS")) {
34  std::istringstream envAllowDisks(getenv("OBMON_ALLOWED_DISKS"));
35 
36  // Iterate over env var
37  for (std::string diskStr; std::getline(envAllowDisks, diskStr, ','); ) {
38  bool permitAny = false;
39 
40  if (diskStr.empty()) // Either disallow...
41  {
42  continue;
43  }
44  if (diskStr.back() == '+') // ... or allow all devices beginning w/ string
45  {
46  diskStr.pop_back();
47  permitAny = true;
48  }
49  // Add entry to map
50  allowedDisks_[diskStr] = permitAny;
51  }
52  } else // Import default permits
53  {
54  allowedDisks_["sd"] = false; // Only primary sd* device
55  allowedDisks_["nvme"] = false; // All NVMe devices
56  allowedDisks_["md"] = false; // All MegaRaid devices
57  }
58 
59  // Convert map to std::vector (yes, I know it's terrible...)
60  for (auto entry : allowedDisks_) {
61  allowedDisks.push_back(entry);
62  }
63 
64  // Sort vector by string length, so we get longest matches first
65  std::sort(allowedDisks.begin(), allowedDisks.end(),
66  [](std::pair<std::string, bool> const &a,
67  std::pair<std::string, bool> const &b) {
68  return a.first.length() > b.first.length();
69  });
70  // Some rudimentary logging
71  for (auto &a : allowedDisks) {
72  _logger->info("Allowed disk device [{}]:[{}]", a.first, a.second);
73  }
74 
75  // =========================================================================
76  // Network devices filtering initialization
77  //
78  if (getenv("OBMON_ALLOWED_NETWORK")) {
79  std::istringstream envAllowNICs(getenv("OBMON_ALLOWED_NETWORK"));
80  // Since NICs will be only explicit, we're gonna just create a vector.
81  // Also, this is not user-proof, but that shouldn't be a problem.
82  for (std::string netStr; std::getline(envAllowNICs, netStr, ','); ) {
83  allowedNICs.push_back(netStr);
84  }
85  }
86 
87  std::string const diskSpeedEnvStr = "OBMON_MAX_DISK_SPEED";
88  if (getenv(diskSpeedEnvStr.c_str())) {
89  // Get env and parse it to int
90  // Cast int to uint
91  uint32_t usrDiskSpeed =
92  static_cast<uint32_t>(std::stoi(getenv(diskSpeedEnvStr.c_str())));
93  if (usrDiskSpeed > 1) {
94  maxDiskSpeed = static_cast<double>(sz_MiB_kiB_(usrDiskSpeed));
95  }
96  }
97 }
98 
103 
104  for (auto net : _netloads)
105  delete net;
106 }
107 
112 
113  _first = new ObSensorSystem("value");
114  _second = new ObSensorSystem("value");
115  _change = new ObSensorSystem("change");
116 
117  return true;
118 }
119 
124 
125  _logger->trace("ObSensorSystem::process cpu ...");
126  glibtop_get_cpu(&_cpu);
127  _logger->trace("ObSensorSystem::process load average ...");
128  glibtop_get_loadavg(&_loadavg);
129  _logger->trace("ObSensorSystem::process mem ...");
130  glibtop_get_mem(&_mem);
131 
132  std::string net_name;
133  unsigned int i = 0;
134  for (auto net : _netloads) {
135  net_name = _netnames.at(i++);
136  _logger->trace("ObSensorSystem::process net {}", net_name.data());
137  glibtop_get_netload(net, net_name.data());
138  }
139 
140  // Drive stats
141  // Intermediate variables... Ugly, but working
142  uint64_t majorID = 0;
143  uint64_t minorID = 0;
144  std::string devName;
145 
146  uint64_t rCount = 0;
147  uint64_t rMerged = 0;
148  uint64_t rSectors = 0;
149  uint64_t rTime = 0;
150 
151  uint64_t wCount = 0;
152  uint64_t wMerged = 0;
153  uint64_t wSectors = 0;
154  uint64_t wTime = 0;
155 
156  uint64_t currentIOCount = 0;
157  uint64_t timeIO = 0;
158  uint64_t weightedTimeIO = 0;
159 
160  std::ifstream procDisksFile("/proc/diskstats");
161 
162  // I know what you wanna say, but since it works (and it actually works pretty
163  // well) then I don't recommend touching it unless you know what you're doing.
164  while ( //
165  procDisksFile // Read from /proc/diskstats file
166  >> majorID >> minorID >> devName // Get dev/part info
167  >> rCount >> rMerged >> rSectors >> rTime // Get read stats
168  >> wCount >> wMerged >> wSectors >> wTime // Get write stats
169  >> currentIOCount >> timeIO >> weightedTimeIO // Get total IO stats
170  ) {
171  bool allowInfo = false;
172 
173  for (auto permit : allowedDisks) {
174  if (devName.find(permit.first) == 0) {
175  if (permit.second == true) {
176  allowInfo = true;
177  } else {
178  if (permit.first == devName) {
179  allowInfo = true;
180  }
181 
182  if (permit.first == "sd") {
183  if (devName.back() > '9') {
184  allowInfo = true;
185  }
186  } else if (permit.first == "nvme" || permit.first == "md") {
187  if (devName.find('p') == std::string::npos) {
188  allowInfo = true;
189  }
190  }
191  }
192 
193  break;
194  }
195  }
196 
197  if (!/*no*/ allowInfo) {
198  _logger->trace("Disk: Denied [{}:{}:{}]", majorID, minorID, devName);
199  continue;
200  }
201 
202  _logger->debug("Disk: Allowed [{}:{}:{}]", majorID, minorID, devName);
203 
204  // Set shortcut for current device (References FTW!)
205  auto &disk = diskInfos[devName];
206 
207  // Talk about redundancy...
208  disk.majorID = majorID;
209  disk.minorID = minorID;
210  disk.devName = devName;
211  disk.rCount = rCount;
212  disk.rMerged = rMerged;
213  disk.rSectors = rSectors;
214  disk.rTime = rTime;
215  disk.wCount = wCount;
216  disk.wMerged = wMerged;
217  disk.wSectors = wSectors;
218  disk.wTime = wTime;
219  disk.currentIOCount = currentIOCount;
220  disk.timeIO = timeIO;
221  disk.weightedTimeIO = weightedTimeIO;
222  }
223  procDisksFile.close();
224 }
225 
226 void ObSensorSystem::speed(ObSensor *s1, ObSensor *s2, unsigned int timeout) {
230 
231  ObSensorSystem *ss1 = static_cast<ObSensorSystem *>(s1);
232  ObSensorSystem *ss2 = static_cast<ObSensorSystem *>(s2);
233  glibtop_cpu cpu1 = ss1->cpu();
234  glibtop_cpu cpu2 = ss2->cpu();
235 
236  _cpu.total = (cpu2.total - cpu1.total);
237  _cpu.sys = (cpu2.sys - cpu1.sys);
238  _cpu.user = (cpu2.user - cpu1.user);
239  _cpu.nice = (cpu2.nice - cpu1.nice);
240  _cpu.idle = (cpu2.idle - cpu1.idle);
241  _cpu.iowait = (cpu2.iowait - cpu1.iowait);
242  _cpu.irq = (cpu2.irq - cpu1.irq);
243  _cpu.softirq = (cpu2.softirq - cpu1.softirq);
244  _cpu.frequency = (cpu2.frequency - cpu1.frequency);
245 
246  glibtop_mem mem1 = ss1->mem();
247  glibtop_mem mem2 = ss2->mem();
248 
249  _mem.total = (mem2.total - mem1.total) * 1000 / timeout;
250  _mem.used = (mem2.used - mem1.used) * 1000 / timeout;
251  _mem.free = (mem2.free - mem1.free) * 1000 / timeout;
252  _mem.shared = (mem2.shared - mem1.shared) * 1000 / timeout;
253  _mem.buffer = (mem2.buffer - mem1.buffer) * 1000 / timeout;
254  _mem.cached = (mem2.cached - mem1.cached) * 1000 / timeout;
255  _mem.locked = (mem2.locked - mem1.locked) * 1000 / timeout;
256 
257  std::string net_name;
258  glibtop_netload *net1, *net2;
259  unsigned int i = 0;
260  for (auto net : _netloads) {
261  net_name = _netnames.at(i);
262  net1 = ss1->netloads().at(i);
263  net2 = ss2->netloads().at(i);
264  if (net) {
265  net->address = (net2->address - net1->address) * 1000 / timeout;
266  net->bytes_in = (net2->bytes_in - net1->bytes_in) * 1000 / timeout;
267  net->bytes_out = (net2->bytes_out - net1->bytes_out) * 1000 / timeout;
268  net->errors_in = (net2->errors_in - net1->errors_in) * 1000 / timeout;
269  net->errors_out = (net2->errors_out - net1->errors_out) * 1000 / timeout;
270  net->packets_in = (net2->packets_in - net1->packets_in) * 1000 / timeout;
271  net->packets_out =
272  (net2->packets_out - net1->packets_out) * 1000 / timeout;
273  }
274  i++;
275  }
276 
277  auto newDisks = ss2->disks();
278  auto oldDisks = ss1->disks();
279  for (auto &newDisk_ : newDisks) {
280  auto &newDisk = newDisk_.second;
281  auto &oldDisk = oldDisks[newDisk.devName];
282  auto &disk = diskInfos[newDisk.devName];
283 
284  // For now process only r/w sector count...
285  disk.majorID = newDisk.majorID;
286  disk.minorID = newDisk.minorID;
287  disk.devName = newDisk.devName;
288  disk.rSectors = newDisk.rSectors - oldDisk.rSectors;
289  disk.wSectors = newDisk.wSectors - oldDisk.wSectors;
290  }
291 }
292 
293 std::string ObSensorSystem::json(const std::string name) const {
297 
298  using namespace fmt::literals;
299  std::string json;
300  double percent = static_cast<double>(_cpu.total);
301 
302  json += "\"" + name + "\": {";
303 
304  if (type() == SensorType::SPEED) {
305  double busy =
306  ((static_cast<double>(_cpu.total) / percent * 100.0) > 1.0) ? 1.0 : 0.0;
307  // = = = = = = = = = = CPU STAT = = = = = = = = = =
308  json += fmt::format(
309  R"("cpu": {{)"
310  R"("total": {{ "value": {totalV:.3f}, "alpha": {totalA:.2f} }},)"
311  R"("sys": {{ "value": {sysV:.3f}, "alpha": {sysA:.2f} }},)"
312  R"("user": {{ "value": {userV:.3f}, "alpha": {userA:.2f} }},)"
313  R"("nice": {{ "value": {niceV:.3f}, "alpha": {niceA:.2f} }},)"
314  R"("idle": {{ "value": {idleV:.3f}, "alpha": {idleA:.2f} }},)"
315  R"("frequency": {{ "value": {freqV:.3f}, "alpha": 0 }},)"
316 
317  R"("iowait": {{ "value": {iowaitV:.3f}, "alpha": {iowaitA:.2f} }},)"
318  R"("busy": {{ "value": {busyV}, "alpha": {busyA} }})"
319  R"(}}, )",
320  // ----- Variables ----- //
321  "totalV"_a = static_cast<double>(_cpu.total) / percent * 100.0,
322  "totalA"_a = static_cast<double>(_cpu.total) / percent,
323  "sysV"_a = static_cast<double>(_cpu.sys) / percent * 100.0,
324  "sysA"_a = static_cast<double>(_cpu.sys) / percent,
325  "userV"_a = static_cast<double>(_cpu.user) / percent * 100.0,
326  "userA"_a = static_cast<double>(_cpu.user) / percent,
327  "niceV"_a = static_cast<double>(_cpu.nice) / percent * 100.0,
328  "niceA"_a = static_cast<double>(_cpu.nice) / percent,
329  "idleV"_a = static_cast<double>(_cpu.idle) / percent * 100.0,
330  "idleA"_a = 1.0 - static_cast<double>(_cpu.idle) / percent,
331  "iowaitV"_a = static_cast<double>(_cpu.iowait) / percent * 100.0,
332  "iowaitA"_a = static_cast<double>(_cpu.iowait) / percent,
333  "freqV"_a = static_cast<double>(_cpu.frequency), "busyV"_a = busy,
334  "busyA"_a = busy
335  // ----- Clang-format force newline ----- //
336  );
337  }
338 
339  if (type() == SensorType::VALUE) {
340  // = = = = = = = = = = AVG STAT = = = = = = = = = =
341 
342  double ncpu_all = _sysinfo->real_ncpu + 1;
343  double ncpu = ncpu_all - _resCpu;
344  auto &avg = _loadavg.loadavg;
345  json += fmt::format(R"("load1": {{ "value": {:.3f}, "alpha": {:.2f} }},)"
346  R"("load5": {{ "value": {:.3f}, "alpha": {:.2f} }},)"
347  R"("load15": {{ "value": {:.3f}, "alpha": {:.2f} }},)"
348  R"("cpus_all": {},)"
349  R"("cpus": {},)",
350  /* Load 1 */
351  avg[0], avg[0] / ncpu,
352  /* Load 5 */
353  avg[1], avg[1] / ncpu,
354  /* Load 15 */
355  avg[2], avg[2] / ncpu,
356  /* all number of cpus */
357  ncpu_all,
358  /* number of cpus */
359  ncpu
360  // ----- Clang-format force newline ----- //
361  );
362  }
363 
364  if (type() == SensorType::VALUE) {
365  // = = = = = = = = = = MEM STAT = = = = = = = = = =
366  json += fmt::format(
367  R"("mem": {{)"
368  R"("total": {{ "value": {totalV}, "alpha": 1.0 }},)"
369  R"("used": {{ "value": {usedV}, "alpha": {usedA:.2f} }},)"
370  R"("free": {{ "value": {freeV}, "alpha": {freeA:.2f} }},)"
371  R"("shared": {{ "value": {sharedV}, "alpha": {sharedA:.2f} }},)"
372  R"("buffer": {{ "value": {bufferV}, "alpha": {bufferA:.2f} }},)"
373  R"("cached": {{ "value": {cachedV}, "alpha": {cachedA:.2f} }},)"
374  R"("locked": {{ "value": {lockedV}, "alpha": {lockedA:.2f} }})"
375  R"(}},)",
376  // ----- Variables ----- //
377  "totalV"_a = _mem.total, // CF Force newline
378  "usedV"_a = _mem.used,
379  "usedA"_a = static_cast<double>(_mem.used) / _mem.total,
380  "freeV"_a = _mem.free,
381  "freeA"_a = 1.0 - static_cast<double>(_mem.free) / _mem.total,
382  "sharedV"_a = _mem.shared,
383  "sharedA"_a = static_cast<double>(_mem.shared) / _mem.total,
384  "bufferV"_a = _mem.buffer,
385  "bufferA"_a = static_cast<double>(_mem.buffer) / _mem.total,
386  "cachedV"_a = _mem.cached,
387  "cachedA"_a = static_cast<double>(_mem.cached) / _mem.total,
388  "lockedV"_a = _mem.locked,
389  "lockedA"_a = static_cast<double>(_mem.locked) / _mem.total
390  // ----- Clang-format force newline ----- //
391  );
392  }
393 
394  if (type() == SensorType::SPEED) {
395  // = = = = = = = = = = NET STAT = = = = = = = = = =
396  // ---------- BEGIN Network ARRAY ----------
397  json += "\"net\" : [";
398 
399  double const MAX_BYTES_ALPHA = 100 * 1048576; // 100 MB
400 
401  std::string net_name;
402  unsigned int i = 0;
403  for (auto net : _netloads) {
404  net_name = _netnames.at(i);
405  if (allowedNICs.empty() ||
406  std::find(allowedNICs.begin(), allowedNICs.end(), net_name) !=
407  allowedNICs.end()) {
408  if (net) {
409  json += fmt::format(
410  R"({{)"
411  R"("adapter": "{name}", "address": "{address}",)"
412  R"("bytes_in": {{ "value": {inV}, "alpha": {inA:.2f} }},)"
413  R"("bytes_out": {{ "value": {outV}, "alpha": {outA:.2f} }},)"
414  R"("errors_in": {{ "value": {errI}, "alpha": 0 }},)"
415  R"("errors_out": {{ "value": {errO}, "alpha": 0 }},)"
416  R"("packets_in": {{ "value": {pacI}, "alpha": 0 }},)"
417  R"("packets_out": {{ "value": {pacO}, "alpha": 0 }})"
418  R"(}},)",
419  // ----- Variables ----- //
420  "name"_a = net_name, "address"_a = net->address,
421  "inV"_a = net->bytes_in, // CF Force newline
422  "inA"_a = static_cast<double>(net->bytes_in) / MAX_BYTES_ALPHA,
423  "outV"_a = net->bytes_out, // CF Force newline
424  "outA"_a = static_cast<double>(net->bytes_out) / MAX_BYTES_ALPHA,
425  "errI"_a = net->errors_in, "errO"_a = net->errors_out,
426  "pacI"_a = net->packets_in, "pacO"_a = net->packets_out
427  // ----- Clang-format force newline ----- //
428  );
429  }
430  }
431 
432  i++;
433  } // END for (network adapter)
434  // ---------- END Network ARRAY ----------
435 
436  if (json.back() == ',')
437  json.pop_back();
438  json += "],";
439  }
440 
441  if (type() == SensorType::SPEED) {
442  // = = = = = = = = = = DISK STAT = = = = = = = = = =
443  // ---------- BEGIN Disk ARRAY ----------
444  json += R"("disks" : [)";
445 
446  for (auto &disk_ : diskInfos) {
447  auto &disk = disk_.second;
448 
449  double raSectors = disk.rSectors / maxDiskSpeed;
450  double rwSectors = disk.wSectors / maxDiskSpeed;
451 
452  json += fmt::format( //
453  R"({{)"
454  R"("majorID": {majorID}, "minorID": {minorID},)"
455  R"("deviceName": "{devName}",)"
456 
457  R"("bytes_read": {{ "value": {rSectors}, "alpha": {raSectors:.2f} }},)"
458 
459  R"("bytes_write": {{ "value": {wSectors}, "alpha": {rwSectors:.2f} }})"
460  R"(}},)",
461  // ----- Variables ----- //
462  "majorID"_a = disk.majorID, "minorID"_a = disk.minorID,
463  "devName"_a = disk.devName, "rSectors"_a = disk.rSectors * 1024,
464  "raSectors"_a = raSectors > 1.0 ? 1.0 : raSectors,
465  "wSectors"_a = disk.wSectors * 1024,
466  "rwSectors"_a = rwSectors > 1.0 ? 1.0 : rwSectors
467  // -------------------- //
468  );
469  }
470 
471  if (json.back() == ',') {
472  json.pop_back();
473  }
474  json += "]";
475  // ---------- END Disk ARRAY ----------
476  }
477 
478  if (json.back() == ',') {
479  json.pop_back();
480  }
481  // ---------- END ROOT ----------
482  json += "}";
483 
484  return json;
485 }
int _resCpu
Cpus used by system (not for computation)
void process() override
Process function.
std::shared_ptr< spdlog::logger > _logger
Pointer to spd logger.
Definition: ObSensor.h:56
System Obmon sensor class.
ObSensorSystem(std::string name={"sys"})
glibtop_mem mem() const
returns glibtop_mem
std::vector< std::string > _netnames
list of network names
bool init() override
glibtop * _sysinfo
Sys info from glitop.
void speed(ObSensor *s1, ObSensor *s2, unsigned int timeout=1000) override
Calculate time change (speed)
ObSensor * _first
Pointer to first sensor.
Definition: ObSensor.h:60
virtual ~ObSensorSystem() override
std::vector< glibtop_netload * > netloads() const
returns netloads
ObSensor * _change
Pointer to change sensor.
Definition: ObSensor.h:62
glibtop_mem _mem
Mem info from glitop.
ObSensor * _second
Pointer to second sensor.
Definition: ObSensor.h:61
std::string json(const std::string name={"static"}) const override
glibtop_cpu _cpu
CPU info from glitop.
glibtop_cpu cpu() const
returns glibtop_cpu
Base Obmon sensor class.
Definition: ObSensor.h:19
glibtop_loadavg _loadavg
Load average info from glibtop.
SensorType type() const
Returns sensor type.
Definition: ObSensor.h:44
glibtop_netlist _netlist
NetList from glitop.
std::vector< glibtop_netload * > _netloads
List of netload.