Skip to content

Commit

Permalink
feat(analytics): Send memory metrics into amplitude
Browse files Browse the repository at this point in the history
We can make a dashboard and track the memory consumptions for the app. FOr this we need to send the metrics to the amplitude
  • Loading branch information
n0th1ng-else committed Sep 13, 2024
1 parent c90cda3 commit 0147941
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/analytics/amplitude/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isDevelopment } from "../../common/environment.js";
const logger = new Logger("analytics:amplitude");

export const collectEvents = async (
chatId: number,
chatId: number | "system_executor",
events: AnalyticsEventExt[],
) => {
if (!amplitudeToken) {
Expand Down
116 changes: 71 additions & 45 deletions src/memory/index.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,12 @@
import { Logger } from "../logger/index.js";
import type { AnalyticsEventExt } from "../analytics/ga/types.js";
import { collectEvents } from "../analytics/amplitude/index.js";

const logger = new Logger("memory");

export const SIZE_UNIT = 1024;

export const getMB = (mb: number): number => {
return SIZE_UNIT * SIZE_UNIT * mb;
};

export const printCurrentMemoryStat = (
limit?: number,
offset = 15,
): Promise<void> => {
const stat = getMemoryUsageMb();
const line = `Current usage [rss=${stat.rss}Mb] [heapTotal=${stat.heapTotal}Mb] [heapUsed=${stat.heapUsed}Mb]`;
if (!limit) {
logger.info(line);
return Promise.resolve();
}

const fullStat = 100;
const dangerStat = fullStat - offset;
const warningStat = dangerStat - offset;
const statDiff = (stat.rss * fullStat) / limit;

if (statDiff > fullStat) {
logger.error(
`The process exceeds memory limit ${limit}Mb! ${line}`,
new Error("The process exceeds memory limit"),
);
return Promise.resolve();
}

if (statDiff > dangerStat) {
logger.error(line, new Error(line));
return Promise.resolve();
}

if (statDiff > warningStat) {
logger.warn(line);
return Promise.resolve();
}

logger.info(line);
return Promise.resolve();
const getMb = (stat: number): number => {
const mb = getMB(1);
return Math.ceil(stat / mb);
};

const getMemoryUsageMb = (): NodeJS.MemoryUsage => {
Expand All @@ -57,7 +20,70 @@ const getMemoryUsageMb = (): NodeJS.MemoryUsage => {
};
};

const getMb = (stat: number): number => {
const mb = getMB(1);
return Math.ceil(stat / mb);
export const SIZE_UNIT = 1024;

export const getMB = (mb: number): number => {
return SIZE_UNIT * SIZE_UNIT * mb;
};

export const printCurrentMemoryStat = async (
limit?: number,
offset = 15,
): Promise<number> => {
return Promise.resolve().then(() => {
const stat = getMemoryUsageMb();
const line = `Current usage [rss=${stat.rss}Mb] [heapTotal=${stat.heapTotal}Mb] [heapUsed=${stat.heapUsed}Mb]`;
if (!limit) {
logger.info(line);
return stat.rss;
}

const fullStat = 100;
const dangerStat = fullStat - offset;
const warningStat = dangerStat - offset;
const statDiff = (stat.rss * fullStat) / limit;

if (statDiff > fullStat) {
logger.error(
`The process exceeds memory limit ${limit}Mb! ${line}`,
new Error("The process exceeds memory limit"),
);
return stat.rss;
}

if (statDiff > dangerStat) {
logger.error(line, new Error(line));
return stat.rss;
}

if (statDiff > warningStat) {
logger.warn(line);
return stat.rss;
}

logger.info(line);
return stat.rss;
});
};

export const sendMemoryStatAnalytics = async (
sizeMb: number,
appVersion: string,
): Promise<void> => {
const event: AnalyticsEventExt = {
name: "flow",
params: {
size: sizeMb,
app_version: appVersion,
page_location: "system/internal",
page_title: `Internal system metrics`,
thread_id: 0,
engagement_time_msec: 0,
language: "en",
screen_resolution: "1920x1080",
page_referrer: "https://dev.null",
page_meta: "",
},
};
await collectEvents("system_executor", [event]);
};
24 changes: 16 additions & 8 deletions src/server/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ import { getDb } from "../db/index.js";
import { StripePayment } from "../donate/stripe.js";
import { TelegramBotModel } from "../telegram/bot.js";
import { ScheduleDaemon } from "../scheduler/index.js";
import { printCurrentMemoryStat } from "../memory/index.js";
import { printCurrentStorageUsage } from "../storage/index.js";
import {
printCurrentMemoryStat,
sendMemoryStatAnalytics,
} from "../memory/index.js";
import {
printCurrentStorageUsage,
sendStorageStatAnalytics,
} from "../storage/index.js";
import { StopListener } from "../process/index.js";
import { getHostName } from "./tunnel.js";
import { Logger } from "../logger/index.js";
Expand Down Expand Up @@ -95,12 +101,14 @@ export const prepareInstance = async (
};

export const prepareStopListener = (): StopListener => {
const memoryDaemon = new ScheduleDaemon("memory", () =>
printCurrentMemoryStat(envy.memoryLimit),
).start();
const storageDaemon = new ScheduleDaemon("storage", () =>
printCurrentStorageUsage("file-temp"),
).start();
const memoryDaemon = new ScheduleDaemon("memory", async () => {
const value = await printCurrentMemoryStat(envy.memoryLimit);
await sendMemoryStatAnalytics(value, envy.appVersion);
}).start();
const storageDaemon = new ScheduleDaemon("storage", async () => {
const value = await printCurrentStorageUsage("file-temp");
await sendStorageStatAnalytics(value, envy.appVersion);
}).start();

const stopListener = new StopListener().addTrigger(() => {
memoryDaemon.stop();
Expand Down
34 changes: 31 additions & 3 deletions src/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import { resolve as resolvePath } from "node:path";
import { readdirSync, statSync } from "node:fs";
import { Logger } from "../logger/index.js";
import { getMB } from "../memory/index.js";
import type { AnalyticsEventExt } from "../analytics/ga/types.js";
import { collectEvents } from "../analytics/amplitude/index.js";

const logger = new Logger("storage");

export const printCurrentStorageUsage = async (dir: string): Promise<void> => {
export const printCurrentStorageUsage = async (
dir: string,
): Promise<number> => {
return Promise.resolve().then(() => {
try {
const folder = resolvePath(process.cwd(), dir);
Expand All @@ -23,19 +27,43 @@ export const printCurrentStorageUsage = async (dir: string): Promise<void> => {
const message = `Current storage ${size} ${list}`;
if (cacheSizeMBytes < 50) {
logger.info(message);
return;
return cacheSizeMBytes;
}
if (cacheSizeMBytes < 100) {
logger.warn(message);
return;
return cacheSizeMBytes;
}

logger.error(
message,
new Error("The process exceeds cache storage limit"),
);
return cacheSizeMBytes;
} catch (err) {
logger.error("Unable to read the cache folder size", err);
return 0;
}
});
};

export const sendStorageStatAnalytics = async (
sizeMb: number,
appVersion: string,
): Promise<void> => {
const event: AnalyticsEventExt = {
name: "flow",
params: {
size: sizeMb,
app_version: appVersion,
page_location: "system/internal",
page_title: `Internal system metrics`,
thread_id: 0,
engagement_time_msec: 0,
language: "en",
screen_resolution: "1920x1080",
page_referrer: "https://dev.null",
page_meta: "",
},
};
await collectEvents("system_executor", [event]);
};

0 comments on commit 0147941

Please sign in to comment.