This commit is contained in:
Alex Holliday committed 2025-12-30 19:49:13 +00:00
1 parent dea0ff106f
commit f56f91c31d
4 files changed
+156 -3

No files matched your search

@@ -19,6 +19,12 @@ export interface IStatsDaily
disk?: IInfraDiskEntry[];
host?: IInfraHost;
net?: IInfraNetEntry[];
// Docker rollups
dockerRunningPercent?: number;
dockerHealthyPercent?: number;
dockerRunningContainers?: number;
dockerHealthyContainers?: number;
dockerTotalContainers?: number;
}
const InfraCpuSchema = new Schema<IInfraCpu>(
@@ -132,6 +138,13 @@ const StatsDailySchema = new Schema<IStatsDaily>(
disk: { type: [InfraDiskEntrySchema], required: false, default: undefined },
host: { type: InfraHostSchema, required: false },
net: { type: [InfraNetEntrySchema], required: false, default: undefined },
// Docker
dockerRunningPercent: { type: Number },
dockerHealthyPercent: { type: Number },
dockerRunningContainers: { type: Number },
dockerHealthyContainers: { type: Number },
dockerTotalContainers: { type: Number },
},
{ timestamps: true, collection: "stats_daily" }
);
@@ -96,6 +96,11 @@ export interface IStatsHourly
disk?: IInfraDiskEntry[];
host?: IInfraHost;
net?: IInfraNetEntry[];
dockerRunningPercent?: number;
dockerHealthyPercent?: number;
dockerRunningContainers?: number;
dockerHealthyContainers?: number;
dockerTotalContainers?: number;
}
const InfraCpuSchema = new Schema<IInfraCpu>(
@@ -209,6 +214,11 @@ const StatsHourlySchema = new Schema<IStatsHourly>(
disk: { type: [InfraDiskEntrySchema], required: false, default: undefined },
host: { type: InfraHostSchema, required: false },
net: { type: [InfraNetEntrySchema], required: false, default: undefined },
dockerRunningPercent: { type: Number },
dockerHealthyPercent: { type: Number },
dockerRunningContainers: { type: Number },
dockerHealthyContainers: { type: Number },
dockerTotalContainers: { type: Number },
},
{ timestamps: true, collection: "stats_hourly" }
);
+24 -2
View File
@@ -560,14 +560,24 @@ class MonitorService implements IMonitorService {
runningPercent: {
$cond: [
{ $gt: ["$totalContainers", 0] },
{ $multiply: [{ $divide: ["$runningContainers", "$totalContainers"] }, 100] },
{
$multiply: [
{ $divide: ["$runningContainers", "$totalContainers"] },
100,
],
},
null,
],
},
healthyPercent: {
$cond: [
{ $gt: ["$totalContainers", 0] },
{ $multiply: [{ $divide: ["$healthyContainers", "$totalContainers"] }, 100] },
{
$multiply: [
{ $divide: ["$healthyContainers", "$totalContainers"] },
100,
],
},
null,
],
},
@@ -744,6 +754,18 @@ class MonitorService implements IMonitorService {
net: d.net,
};
}
if (monitor.type === "docker") {
return {
...base,
totalContainers: d.dockerTotalContainers,
runningContainers: d.dockerRunningContainers,
healthyContainers: d.dockerHealthyContainers,
totalExposedPorts: d.dockerTotalExposedPorts,
uniqueImages: d.dockerUniqueImages,
runningPercent: d.dockerRunningPercent,
healthyPercent: d.dockerHealthyPercent,
};
}
return base;
});
@@ -50,6 +50,12 @@ class StatsAggregationService implements IStatsAggregationService {
disk: d.disk,
host: d.host,
net: d.net,
// Docker (present when type === "docker")
dockerRunningPercent: d.dockerRunningPercent,
dockerHealthyPercent: d.dockerHealthyPercent,
dockerRunningContainers: d.dockerRunningContainers,
dockerHealthyContainers: d.dockerHealthyContainers,
dockerTotalContainers: d.dockerTotalContainers,
},
},
upsert: true,
@@ -441,6 +447,82 @@ class StatsAggregationService implements IStatsAggregationService {
bulkOps.push(this.makeUpsertOp(el, windowStart, windowEnd))
);
// 4) Docker hourly
const dockerAgg = await Check.aggregate<any>([
{ $match: { ...matchWindow, "metadata.type": "docker" } },
{
$project: {
status: 1,
responseTime: 1,
createdAt: 1,
dockerContainers: 1,
"metadata.monitorId": 1,
"metadata.teamId": 1,
"metadata.type": 1,
},
},
{ $sort: { createdAt: 1 } },
{
$addFields: {
total: { $size: { $ifNull: ["$dockerContainers", []] } },
running: {
$size: {
$filter: {
input: { $ifNull: ["$dockerContainers", []] },
as: "c",
cond: { $eq: ["$$c.running", true] },
},
},
},
healthy: {
$size: {
$filter: {
input: { $ifNull: ["$dockerContainers", []] },
as: "c",
cond: { $eq: ["$$c.health.healthy", true] },
},
},
},
},
},
{
$addFields: {
runningPercent: {
$cond: [
{ $gt: ["$total", 0] },
{ $multiply: [{ $divide: ["$running", "$total"] }, 100] },
null,
],
},
healthyPercent: {
$cond: [
{ $gt: ["$total", 0] },
{ $multiply: [{ $divide: ["$healthy", "$total"] }, 100] },
null,
],
},
},
},
{
$group: {
_id: "$metadata.monitorId",
teamId: { $first: "$metadata.teamId" },
type: { $first: "$metadata.type" },
count: { $sum: 1 },
avgResponseTime: { $avg: "$responseTime" },
dockerRunningPercent: { $avg: "$runningPercent" },
dockerHealthyPercent: { $avg: "$healthyPercent" },
dockerTotalContainers: { $last: "$total" },
dockerRunningContainers: { $last: "$running" },
dockerHealthyContainers: { $last: "$healthy" },
},
},
]);
dockerAgg.forEach((el) =>
bulkOps.push(this.makeUpsertOp(el, windowStart, windowEnd))
);
if (bulkOps.length > 0) {
const res = await StatsHourly.bulkWrite(bulkOps, { ordered: false });
upserts += res.upsertedCount + (res.modifiedCount ?? 0);
@@ -457,7 +539,11 @@ class StatsAggregationService implements IStatsAggregationService {
// Fetch hourly docs for the day
const hourly = await StatsHourly.find({
windowStart: { $gte: dayStart, $lt: dayEnd },
}).lean();
})
.select(
"monitorId teamId type windowStart count avgResponseTime upChecks downChecks avgResponseTimeUp avgResponseTimeDown accessibility bestPractices seo performance cls si fcp lcp tbt cpu memory disk host net dockerRunningPercent dockerHealthyPercent dockerTotalContainers dockerRunningContainers dockerHealthyContainers"
)
.lean();
type Weighted = { sum: number; w: number };
const addW = (acc: Weighted, v?: number, w = 0) => {
@@ -521,6 +607,12 @@ class StatsAggregationService implements IStatsAggregationService {
};
net: Map<string, DeviceAgg>;
lastTs: number; // for choosing "last" fields
// docker
dockerRunningPercent?: Weighted;
dockerHealthyPercent?: Weighted;
dockerTotalContainers?: number;
dockerRunningContainers?: number;
dockerHealthyContainers?: number;
}
const groups = new Map<string, GroupAgg>();
@@ -585,6 +677,12 @@ class StatsAggregationService implements IStatsAggregationService {
addW(g.lcp, (h as any).lcp, w);
addW(g.tbt, (h as any).tbt, w);
// docker
if (!(g as any).dockerRunningPercent) (g as any).dockerRunningPercent = { sum: 0, w: 0 };
if (!(g as any).dockerHealthyPercent) (g as any).dockerHealthyPercent = { sum: 0, w: 0 };
addW((g as any).dockerRunningPercent!, (h as any).dockerRunningPercent, w);
addW((g as any).dockerHealthyPercent!, (h as any).dockerHealthyPercent, w);
const ts = new Date(h.windowStart).getTime();
if (ts >= g.lastTs) {
g.lastTs = ts;
@@ -612,6 +710,11 @@ class StatsAggregationService implements IStatsAggregationService {
kernel_version: host.kernel_version,
pretty_name: host.pretty_name,
};
// docker last snapshot
(g as any).dockerTotalContainers = (h as any).dockerTotalContainers;
(g as any).dockerRunningContainers = (h as any).dockerRunningContainers;
(g as any).dockerHealthyContainers = (h as any).dockerHealthyContainers;
}
// weighted CPU averages
@@ -772,6 +875,11 @@ class StatsAggregationService implements IStatsAggregationService {
disk: diskArr.length > 0 ? diskArr : undefined,
host: g.host,
net: netArr.length > 0 ? netArr : undefined,
dockerRunningPercent: avg((g as any).dockerRunningPercent!),
dockerHealthyPercent: avg((g as any).dockerHealthyPercent!),
dockerTotalContainers: (g as any).dockerTotalContainers,
dockerRunningContainers: (g as any).dockerRunningContainers,
dockerHealthyContainers: (g as any).dockerHealthyContainers,
},
};