Add power consumption metric to power supply monitoring module (#17329)
Eldar Yusupov committed
Apr 22, 2024 at 16:35 UTC
632f7a367ecb05eaa232c4b571d65f7f9d6ed94b
6 files changed
+137
-75
src/collectors/all.h
+5
-4
@@ -377,10 +377,11 @@
377
378
// Linux Power Supply
379
380
-#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 4 charts per power supply
381
-#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9501
382
-#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9502
383
-#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9503
380
+#define NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY 9500 // 5 charts per power supply
381
+#define NETDATA_CHART_PRIO_POWER_SUPPLY_POWER 9501
382
+#define NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE 9502
383
+#define NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY 9503
384
+#define NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE 9504
385
386
// Wireless
387
src/collectors/proc.plugin/README.md
+1
@@ -518,6 +518,7 @@ and metrics:
518
# battery capacity = yes
519
# battery charge = no
520
# battery energy = no
521
+ # battery power = yes
522
# power supply voltage = no
523
# keep files open = auto
524
# directory to monitor = /sys/class/power_supply
src/collectors/proc.plugin/integrations/power_supply.md
+1
@@ -68,6 +68,7 @@ Metrics:
68
| Metric | Dimensions | Unit |
69
|:------|:----------|:----|
70
| powersupply.capacity | capacity | percentage |
71
+| powersupply.power | now | percentage |
72
| powersupply.charge | empty_design, empty, now, full, full_design | Ah |
73
| powersupply.energy | empty_design, empty, now, full, full_design | Wh |
74
| powersupply.voltage | min_design, min, now, max, max_design | V |
src/collectors/proc.plugin/metadata.yaml
+6
@@ -5123,6 +5123,12 @@ modules:
5123
chart_type: line
5124
dimensions:
5125
- name: capacity
5126
+ - name: powersupply.power
5127
+ description: Battery discharge rate
5128
+ unit: "W"
5129
+ chart_type: line
5130
+ dimensions:
5131
+ - name: power
5132
- name: powersupply.charge
5133
description: Battery charge
5134
unit: "Ah"
src/collectors/proc.plugin/sys_class_power_supply.c
+121
-70
@@ -3,11 +3,19 @@
3
#include "plugin_proc.h"
4
5
#define PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME "/sys/class/power_supply"
6
+#define PROP_VALUE_LENGTH_MAX 30
7
8
const char *ps_property_names[] = { "charge", "energy", "voltage"};
9
const char *ps_property_titles[] = {"Battery charge", "Battery energy", "Power supply voltage"};
10
const char *ps_property_units[] = { "Ah", "Wh", "V"};
11
12
+const long ps_property_priorities[] = {
13
+ NETDATA_CHART_PRIO_POWER_SUPPLY_CHARGE,
14
+ NETDATA_CHART_PRIO_POWER_SUPPLY_ENERGY,
15
+ NETDATA_CHART_PRIO_POWER_SUPPLY_VOLTAGE
16
+};
17
+
18
+
19
const char *ps_property_dim_names[] = {"empty_design", "empty", "now", "full", "full_design",
20
"empty_design", "empty", "now", "full", "full_design",
21
"min_design", "min", "now", "max", "max_design"};
@@ -29,6 +37,8 @@ struct ps_property {
37
char *title;
38
char *units;
39
40
+ long priority;
41
+
42
RRDSET *st;
43
44
struct ps_property_dim *property_dim_root;
@@ -36,12 +46,13 @@ struct ps_property {
46
struct ps_property *next;
47
};
48
39
-struct capacity {
49
+struct simple_property {
50
char *filename;
51
int fd;
52
53
RRDSET *st;
54
RRDDIM *rd;
55
+ bool ok;
56
unsigned long long value;
57
};
58
@@ -50,7 +61,7 @@ struct power_supply {
61
uint32_t hash;
62
int found;
63
53
- struct capacity *capacity;
64
+ struct simple_property *capacity, *power;
65
66
struct ps_property *property_root;
67
@@ -60,17 +71,22 @@ struct power_supply {
71
static struct power_supply *power_supply_root = NULL;
72
static int files_num = 0;
73
74
+static void free_simple_prop(struct simple_property *prop) {
75
+ if(likely(prop)) {
76
+ if(likely(prop->st)) rrdset_is_obsolete___safe_from_collector_thread(prop->st);
77
+ freez(prop->filename);
78
+ if(likely(prop->fd != -1)) close(prop->fd);
79
+ files_num--;
80
+ freez(prop);
81
+ }
82
+}
83
+
84
void power_supply_free(struct power_supply *ps) {
85
if(likely(ps)) {
86
87
// free capacity structure
67
- if(likely(ps->capacity)) {
68
- if(likely(ps->capacity->st)) rrdset_is_obsolete___safe_from_collector_thread(ps->capacity->st);
69
- freez(ps->capacity->filename);
70
- if(likely(ps->capacity->fd != -1)) close(ps->capacity->fd);
71
- files_num--;
72
- freez(ps->capacity);
73
- }
88
+ free_simple_prop(ps->capacity);
89
+ free_simple_prop(ps->power);
90
freez(ps->name);
91
92
struct ps_property *pr = ps->property_root;
@@ -116,14 +132,78 @@ static void add_labels_to_power_supply(struct power_supply *ps, RRDSET *st) {
132
rrdlabels_add(st->rrdlabels, "device", ps->name, RRDLABEL_SRC_AUTO);
133
}
134
135
+static void read_simple_property(struct simple_property *prop, bool keep_fds_open) {
136
+ char buffer[PROP_VALUE_LENGTH_MAX + 1];
137
+
138
+ prop->ok = false;
139
+ if(unlikely(prop->fd == -1)) {
140
+ prop->fd = open(prop->filename, O_RDONLY | O_CLOEXEC, 0666);
141
+ if(unlikely(prop->fd == -1)) {
142
+ collector_error("Cannot open file '%s'", prop->filename);
143
+ return;
144
+ }
145
+ }
146
+ ssize_t r = read(prop->fd, buffer, PROP_VALUE_LENGTH_MAX);
147
+ if(unlikely(r < 1)) {
148
+ collector_error("Cannot read file '%s'", prop->filename);
149
+ }
150
+ else {
151
+ buffer[r] = '\0';
152
+ prop->value = str2ull(buffer, NULL);
153
+ prop->ok = true;
154
+ }
155
+
156
+ if(unlikely(!keep_fds_open)) {
157
+ close(prop->fd);
158
+ prop->fd = -1;
159
+ }
160
+ else if(unlikely(prop->ok && lseek(prop->fd, 0, SEEK_SET) == -1)) {
161
+ collector_error("Cannot seek in file '%s'", prop->filename);
162
+ close(prop->fd);
163
+ prop->fd = -1;
164
+ }
165
+ return;
166
+}
167
+
168
+static void rrdset_create_simple_prop(struct power_supply *ps, struct simple_property *prop, char *title, char *dim, collected_number divisor, char *units, long priority, int update_every) {
169
+ if(unlikely(!prop->st)) {
170
+ char id[RRD_ID_LENGTH_MAX + 1], context[RRD_ID_LENGTH_MAX + 1];
171
+ snprintfz(id, RRD_ID_LENGTH_MAX, "powersupply_%s", dim);
172
+ snprintfz(context, RRD_ID_LENGTH_MAX, "powersupply.%s", dim);
173
+
174
+ prop->st = rrdset_create_localhost(
175
+ id
176
+ , ps->name
177
+ , NULL
178
+ , dim
179
+ , context
180
+ , title
181
+ , units
182
+ , PLUGIN_PROC_NAME
183
+ , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
184
+ , priority
185
+ , update_every
186
+ , RRDSET_TYPE_LINE
187
+ );
188
+
189
+ add_labels_to_power_supply(ps, prop->st);
190
+ }
191
+
192
+ if(unlikely(!prop->rd)) prop->rd = rrddim_add(prop->st, dim, NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE);
193
+ rrddim_set_by_pointer(prop->st, prop->rd, prop->value);
194
+
195
+ rrdset_done(prop->st);
196
+}
197
+
198
int do_sys_class_power_supply(int update_every, usec_t dt) {
199
(void)dt;
121
- static int do_capacity = -1, do_property[3] = {-1};
200
+ static int do_capacity = -1, do_power = -1, do_property[3] = {-1};
201
static int keep_fds_open = CONFIG_BOOLEAN_NO, keep_fds_open_config = -1;
202
static char *dirname = NULL;
203
204
if(unlikely(do_capacity == -1)) {
205
do_capacity = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery capacity", CONFIG_BOOLEAN_YES);
206
+ do_power = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery power", CONFIG_BOOLEAN_YES);
207
do_property[0] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery charge", CONFIG_BOOLEAN_NO);
208
do_property[1] = config_get_boolean("plugin:proc:/sys/class/power_supply", "battery energy", CONFIG_BOOLEAN_NO);
209
do_property[2] = config_get_boolean("plugin:proc:/sys/class/power_supply", "power supply voltage", CONFIG_BOOLEAN_NO);
@@ -175,13 +255,24 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
255
char filename[FILENAME_MAX + 1];
256
snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "capacity");
257
if (stat(filename, &stbuf) == 0) {
178
- ps->capacity = callocz(sizeof(struct capacity), 1);
258
+ ps->capacity = callocz(sizeof(struct simple_property), 1);
259
ps->capacity->filename = strdupz(filename);
260
ps->capacity->fd = -1;
261
files_num++;
262
}
263
}
264
265
+ if(likely(do_power != CONFIG_BOOLEAN_NO)) {
266
+ char filename[FILENAME_MAX + 1];
267
+ snprintfz(filename, FILENAME_MAX, "%s/%s/%s", dirname, de->d_name, "power_now");
268
+ if (stat(filename, &stbuf) == 0) {
269
+ ps->power = callocz(sizeof(struct simple_property), 1);
270
+ ps->power->filename = strdupz(filename);
271
+ ps->power->fd = -1;
272
+ files_num++;
273
+ }
274
+ }
275
+
276
// allocate memory and initialize structures for every property and file found
277
size_t pr_idx, pd_idx;
278
size_t prev_idx = 3; // there is no property with this index
@@ -210,6 +301,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
301
pr->name = strdupz(ps_property_names[pr_idx]);
302
pr->title = strdupz(ps_property_titles[pr_idx]);
303
pr->units = strdupz(ps_property_units[pr_idx]);
304
+ pr->priority = ps_property_priorities[pr_idx];
305
prev_idx = pr_idx;
306
pr->next = ps->property_root;
307
ps->property_root = pr;
@@ -242,52 +334,29 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
334
335
// read capacity file
336
if(likely(ps->capacity)) {
245
- char buffer[30 + 1];
246
-
247
- if(unlikely(ps->capacity->fd == -1)) {
248
- ps->capacity->fd = open(ps->capacity->filename, O_RDONLY | O_CLOEXEC, 0666);
249
- if(unlikely(ps->capacity->fd == -1)) {
250
- collector_error("Cannot open file '%s'", ps->capacity->filename);
251
- power_supply_free(ps);
252
- ps = NULL;
253
- }
254
- }
337
+ read_simple_property(ps->capacity, keep_fds_open);
338
+ }
339
256
- if (ps)
257
- {
258
- ssize_t r = read(ps->capacity->fd, buffer, 30);
259
- if(unlikely(r < 1)) {
260
- collector_error("Cannot read file '%s'", ps->capacity->filename);
261
- power_supply_free(ps);
262
- ps = NULL;
263
- }
264
- else {
265
- buffer[r] = '\0';
266
- ps->capacity->value = str2ull(buffer, NULL);
340
+ // read power file
341
+ if(likely(ps->power)) {
342
+ read_simple_property(ps->power, keep_fds_open);
343
+ }
344
268
- if(unlikely(!keep_fds_open)) {
269
- close(ps->capacity->fd);
270
- ps->capacity->fd = -1;
271
- }
272
- else if(unlikely(lseek(ps->capacity->fd, 0, SEEK_SET) == -1)) {
273
- collector_error("Cannot seek in file '%s'", ps->capacity->filename);
274
- close(ps->capacity->fd);
275
- ps->capacity->fd = -1;
276
- }
277
- }
278
- }
345
+ if(unlikely((!ps->power || !ps->power->ok) && (!ps->capacity || !ps->capacity->ok))) {
346
+ power_supply_free(ps);
347
+ ps = NULL;
348
}
349
350
// read property files
351
int read_error = 0;
352
struct ps_property *pr;
284
- if (ps)
353
+ if (likely(ps))
354
{
355
for(pr = ps->property_root; pr && !read_error; pr = pr->next) {
356
struct ps_property_dim *pd;
357
for(pd = pr->property_dim_root; pd; pd = pd->next) {
358
if(likely(!pd->always_zero)) {
290
- char buffer[30 + 1];
359
+ char buffer[PROP_VALUE_LENGTH_MAX + 1];
360
361
if(unlikely(pd->fd == -1)) {
362
pd->fd = open(pd->filename, O_RDONLY | O_CLOEXEC, 0666);
@@ -299,7 +368,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
368
}
369
}
370
302
- ssize_t r = read(pd->fd, buffer, 30);
371
+ ssize_t r = read(pd->fd, buffer, PROP_VALUE_LENGTH_MAX);
372
if(unlikely(r < 1)) {
373
collector_error("Cannot read file '%s'", pd->filename);
374
read_error = 1;
@@ -346,30 +415,12 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
415
continue;
416
}
417
349
- if(likely(ps->capacity)) {
350
- if(unlikely(!ps->capacity->st)) {
351
- ps->capacity->st = rrdset_create_localhost(
352
- "powersupply_capacity"
353
- , ps->name
354
- , NULL
355
- , "capacity"
356
- , "powersupply.capacity"
357
- , "Battery capacity"
358
- , "percentage"
359
- , PLUGIN_PROC_NAME
360
- , PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
361
- , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY
362
- , update_every
363
- , RRDSET_TYPE_LINE
364
- );
365
-
366
- add_labels_to_power_supply(ps, ps->capacity->st);
367
- }
368
-
369
- if(unlikely(!ps->capacity->rd)) ps->capacity->rd = rrddim_add(ps->capacity->st, "capacity", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
370
- rrddim_set_by_pointer(ps->capacity->st, ps->capacity->rd, ps->capacity->value);
418
+ if(likely(ps->capacity && ps->capacity->ok)) {
419
+ rrdset_create_simple_prop(ps, ps->capacity, "Battery capacity", "capacity", 1, "percentage", NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY, update_every);
420
+ }
421
372
- rrdset_done(ps->capacity->st);
422
+ if(likely(ps->power && ps->power->ok)) {
423
+ rrdset_create_simple_prop(ps, ps->power, "Battery power", "power", 1000000, "W", NETDATA_CHART_PRIO_POWER_SUPPLY_POWER, update_every);
424
}
425
426
struct ps_property *pr;
@@ -389,7 +440,7 @@ int do_sys_class_power_supply(int update_every, usec_t dt) {
440
, pr->units
441
, PLUGIN_PROC_NAME
442
, PLUGIN_PROC_MODULE_POWER_SUPPLY_NAME
392
- , NETDATA_CHART_PRIO_POWER_SUPPLY_CAPACITY
443
+ , pr->priority
444
, update_every
445
, RRDSET_TYPE_LINE
446
);
src/web/gui/dashboard_info.js
+3
-1
@@ -6813,6 +6813,9 @@ netdataDashboard.context = {
6813
info: 'The current battery charge.'
6814
},
6815
6816
+ 'powersupply.power': {
6817
+ info: 'The current battery discharge rate in Watts.'
6818
+ },
6819
'powersupply.charge': {
6820
info: '<p>The battery charge in Amp-hours.</p>'+
6821
'<p><b>now</b> - actual charge value. '+
@@ -6839,7 +6842,6 @@ netdataDashboard.context = {
6842
'<b>max_design</b>, <b>min_design</b> - design values for maximal and minimal power supply voltages. '+
6843
'Maximal/minimal means values of voltages when battery considered "full"/"empty" at normal conditions.</p>'
6844
},
6842
-
6845
// ------------------------------------------------------------------------
6846
// VMware vSphere
6847