|
@@ -15,6 +15,10 @@ const TIMESTAMP_FORMAT = `YYYY-MM-DD[T]HH:mm:ss.SSSZZ`;
|
|
|
const REDUCE_INTERVAL_MINUTES = 5;
|
|
const REDUCE_INTERVAL_MINUTES = 5;
|
|
|
const REDUCE_GROUP_MINUTES = 1;
|
|
const REDUCE_GROUP_MINUTES = 1;
|
|
|
|
|
|
|
|
|
|
+const MONITOR_MOUNTS = !!process.env.MONITOR_MOUNTS ? process.env.MONITOR_MOUNTS.split(':') : [];
|
|
|
|
|
+
|
|
|
|
|
+Logger.info('[INFO] Monitoring Drives:', MONITOR_MOUNTS);
|
|
|
|
|
+
|
|
|
const CSV_COLS = {
|
|
const CSV_COLS = {
|
|
|
buffer: {
|
|
buffer: {
|
|
|
time: 0,
|
|
time: 0,
|
|
@@ -65,7 +69,18 @@ export class Collector {
|
|
|
const time = now.format(TIMESTAMP_FORMAT);
|
|
const time = now.format(TIMESTAMP_FORMAT);
|
|
|
const cpu = (await exec(`./cpu.sh`)).trim();
|
|
const cpu = (await exec(`./cpu.sh`)).trim();
|
|
|
const ram = (await exec(`./ram.sh`)).trim();
|
|
const ram = (await exec(`./ram.sh`)).trim();
|
|
|
- const data = `${time};${cpu};${ram}\n`;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const hdd: string[] = [];
|
|
|
|
|
+ for (const mount of MONITOR_MOUNTS) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const stats = (await exec(`./hdd.sh "${mount}"`)).trim();
|
|
|
|
|
+ if (stats?.length) hdd.push(`${mount} ${stats}`);
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ Logger.warn('[WARN] Error while getting space usage of mount', mount, ':', err);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const data = `${time};${cpu};${ram}${hdd.length ? `;${hdd.join(';')}` : ''}\n`;
|
|
|
|
|
|
|
|
// Time to reduce buffer?
|
|
// Time to reduce buffer?
|
|
|
const firstBufferTime = await this.getFirstBufferTime();
|
|
const firstBufferTime = await this.getFirstBufferTime();
|
|
@@ -156,7 +171,10 @@ export class Collector {
|
|
|
const firstTime = moment(valueBuffer[0].time);
|
|
const firstTime = moment(valueBuffer[0].time);
|
|
|
const currentTime = moment(data.time);
|
|
const currentTime = moment(data.time);
|
|
|
if (moment.duration(currentTime.diff(firstTime)).abs().asMinutes() >= REDUCE_GROUP_MINUTES) {
|
|
if (moment.duration(currentTime.diff(firstTime)).abs().asMinutes() >= REDUCE_GROUP_MINUTES) {
|
|
|
- const { cpu, ram, count } = valueBuffer.reduce(
|
|
|
|
|
|
|
+ type IntermediateValues = { sum: number; peak: number; max: number };
|
|
|
|
|
+ type IntermediateDriveData = { [mount: string]: IntermediateValues };
|
|
|
|
|
+ type IntermediateSums = { ram: IntermediateValues; cpu: IntermediateValues; hdd?: IntermediateDriveData; count: number };
|
|
|
|
|
+ const { cpu, ram, count, hdd } = valueBuffer.reduce(
|
|
|
(res, cur) => {
|
|
(res, cur) => {
|
|
|
res.count++;
|
|
res.count++;
|
|
|
res.cpu.sum += cur.cpu;
|
|
res.cpu.sum += cur.cpu;
|
|
@@ -164,9 +182,20 @@ export class Collector {
|
|
|
res.ram.sum += cur.ram.used;
|
|
res.ram.sum += cur.ram.used;
|
|
|
res.ram.peak = Math.max(res.ram.peak, cur.ram.used);
|
|
res.ram.peak = Math.max(res.ram.peak, cur.ram.used);
|
|
|
res.ram.max = cur.ram.max;
|
|
res.ram.max = cur.ram.max;
|
|
|
|
|
+
|
|
|
|
|
+ if (cur.hdd && Object.keys(cur.hdd).length) {
|
|
|
|
|
+ res.hdd = Object.keys(cur.hdd).reduce((res_hdd, mount) => {
|
|
|
|
|
+ if (!res_hdd[mount]) res_hdd[mount] = { sum: 0, peak: 0, max: 0 };
|
|
|
|
|
+ res_hdd[mount].sum += cur.hdd[mount].used;
|
|
|
|
|
+ res_hdd[mount].peak = Math.max(res_hdd[mount].peak, cur.hdd[mount].used);
|
|
|
|
|
+ res_hdd[mount].max += cur.hdd[mount].max;
|
|
|
|
|
+ return res_hdd;
|
|
|
|
|
+ }, {} as IntermediateDriveData);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return res;
|
|
return res;
|
|
|
},
|
|
},
|
|
|
- { ram: { sum: 0, peak: 0, max: 0 }, cpu: { sum: 0, peak: 0 }, count: 0 }
|
|
|
|
|
|
|
+ { ram: { sum: 0, peak: 0, max: 0 }, cpu: { sum: 0, peak: 0 }, count: 0 } as IntermediateSums
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
reduced.push({
|
|
reduced.push({
|
|
@@ -179,7 +208,17 @@ export class Collector {
|
|
|
avg: ram.sum / count,
|
|
avg: ram.sum / count,
|
|
|
peak: ram.peak,
|
|
peak: ram.peak,
|
|
|
max: ram.max
|
|
max: ram.max
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ hdd: hdd
|
|
|
|
|
+ ? Object.keys(hdd).reduce((res, mount) => {
|
|
|
|
|
+ res[mount] = {
|
|
|
|
|
+ avg: hdd[mount].sum / count,
|
|
|
|
|
+ peak: hdd[mount].peak,
|
|
|
|
|
+ max: hdd[mount].max
|
|
|
|
|
+ };
|
|
|
|
|
+ return res;
|
|
|
|
|
+ }, {} as ReducedDriveData)
|
|
|
|
|
+ : undefined
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
Logger.debug('[DEBUG] ReducedData:', JSON.stringify(reduced[reduced.length - 1]));
|
|
Logger.debug('[DEBUG] ReducedData:', JSON.stringify(reduced[reduced.length - 1]));
|
|
@@ -209,25 +248,64 @@ export class Collector {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private parseBufferedData(line: string[]): BufferedData {
|
|
private parseBufferedData(line: string[]): BufferedData {
|
|
|
- const cpu = Number(line[CSV_COLS.buffer.cpu]);
|
|
|
|
|
|
|
+ // TIMESTAMP
|
|
|
const time = moment(line[CSV_COLS.buffer.time], TIMESTAMP_FORMAT).toDate();
|
|
const time = moment(line[CSV_COLS.buffer.time], TIMESTAMP_FORMAT).toDate();
|
|
|
- let ramSplit = line[CSV_COLS.buffer.ram].split(' ');
|
|
|
|
|
- const unit = ramSplit[1];
|
|
|
|
|
- ramSplit = ramSplit[0].split('/');
|
|
|
|
|
- const [used, max] = ramSplit;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // CPU
|
|
|
|
|
+ const cpu = Number(line[CSV_COLS.buffer.cpu]);
|
|
|
|
|
+
|
|
|
|
|
+ // RAM
|
|
|
|
|
+ let [stats, unit] = line[CSV_COLS.buffer.ram].split(' ');
|
|
|
|
|
+ const [used, max] = stats.split('/');
|
|
|
const factor = this.parseByteUnit(unit);
|
|
const factor = this.parseByteUnit(unit);
|
|
|
|
|
|
|
|
|
|
+ const lastCol = CSV_COLS.buffer.ram;
|
|
|
|
|
+
|
|
|
|
|
+ // HDD (?)
|
|
|
|
|
+ let hdd: BufferedDriveData;
|
|
|
|
|
+ if (MONITOR_MOUNTS.length && line.length > lastCol + 1) {
|
|
|
|
|
+ for (let i = 1; i <= MONITOR_MOUNTS.length; i++) {
|
|
|
|
|
+ if (lastCol + i > line.length - 1) break;
|
|
|
|
|
+
|
|
|
|
|
+ const data = line[lastCol + i];
|
|
|
|
|
+ const [mount, stats] = data.split(' ');
|
|
|
|
|
+ const [used, max] = stats.split('/');
|
|
|
|
|
+
|
|
|
|
|
+ if (!hdd) hdd = {};
|
|
|
|
|
+ hdd[mount] = {
|
|
|
|
|
+ used: Number(used),
|
|
|
|
|
+ max: Number(max)
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
time,
|
|
time,
|
|
|
cpu,
|
|
cpu,
|
|
|
ram: {
|
|
ram: {
|
|
|
used: Number(used) * factor,
|
|
used: Number(used) * factor,
|
|
|
max: Number(max) * factor
|
|
max: Number(max) * factor
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ hdd
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private parseReducedData(line: string[]): ReducedData {
|
|
private parseReducedData(line: string[]): ReducedData {
|
|
|
|
|
+ const lastCol = CSV_COLS.reduced.ram.max;
|
|
|
|
|
+
|
|
|
|
|
+ // HDD (?)
|
|
|
|
|
+ let hdd: ReducedDriveData;
|
|
|
|
|
+ if (MONITOR_MOUNTS.length && line.length > lastCol + 1) {
|
|
|
|
|
+ hdd = {};
|
|
|
|
|
+ for (let i = 1; lastCol + i + 3 < line.length; i += 4) {
|
|
|
|
|
+ hdd[line[lastCol + i]] = {
|
|
|
|
|
+ avg: Number(line[lastCol + i + 1]),
|
|
|
|
|
+ peak: Number(line[lastCol + i + 2]),
|
|
|
|
|
+ max: Number(line[lastCol + i + 3])
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
time: moment(line[CSV_COLS.reduced.time], TIMESTAMP_FORMAT).toDate(),
|
|
time: moment(line[CSV_COLS.reduced.time], TIMESTAMP_FORMAT).toDate(),
|
|
|
cpu: {
|
|
cpu: {
|
|
@@ -238,7 +316,8 @@ export class Collector {
|
|
|
avg: Number(line[CSV_COLS.reduced.ram.avg]),
|
|
avg: Number(line[CSV_COLS.reduced.ram.avg]),
|
|
|
peak: Number(line[CSV_COLS.reduced.ram.peak]),
|
|
peak: Number(line[CSV_COLS.reduced.ram.peak]),
|
|
|
max: Number(line[CSV_COLS.reduced.ram.max])
|
|
max: Number(line[CSV_COLS.reduced.ram.max])
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ hdd
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -262,7 +341,8 @@ export class Collector {
|
|
|
return [
|
|
return [
|
|
|
moment(data.time).format(TIMESTAMP_FORMAT),
|
|
moment(data.time).format(TIMESTAMP_FORMAT),
|
|
|
data.cpu,
|
|
data.cpu,
|
|
|
- `${(data.ram.used / this.byteFactors['M']).toFixed(2)}/${(data.ram.max / this.byteFactors['M']).toFixed(2)} MiB`
|
|
|
|
|
|
|
+ `${(data.ram.used / this.byteFactors['M']).toFixed(2)}/${(data.ram.max / this.byteFactors['M']).toFixed(2)} MiB`,
|
|
|
|
|
+ ...(data.hdd ? Object.keys(data.hdd).map(mount => `${mount} ${data.hdd[mount].used}/${data.hdd[mount].max}`) : [])
|
|
|
].join(';');
|
|
].join(';');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -273,7 +353,13 @@ export class Collector {
|
|
|
data.cpu.peak.toFixed(2),
|
|
data.cpu.peak.toFixed(2),
|
|
|
data.ram.avg.toFixed(2),
|
|
data.ram.avg.toFixed(2),
|
|
|
data.ram.peak.toFixed(2),
|
|
data.ram.peak.toFixed(2),
|
|
|
- data.ram.max.toFixed(2)
|
|
|
|
|
|
|
+ data.ram.max.toFixed(2),
|
|
|
|
|
+ ...(data.hdd
|
|
|
|
|
+ ? Object.keys(data.hdd).reduce((res, mount) => {
|
|
|
|
|
+ res.push(mount, data.hdd[mount].avg.toFixed(2), data.hdd[mount].peak.toFixed(2), data.hdd[mount].max.toFixed(2));
|
|
|
|
|
+ return res;
|
|
|
|
|
+ }, [])
|
|
|
|
|
+ : [])
|
|
|
].join(';');
|
|
].join(';');
|
|
|
}
|
|
}
|
|
|
|
|
|