Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(analytics): Downlevel the analytics logs #401

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ coverage-unit
package.json
package-lock.json
pnpm-lock.yaml
*.bin
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tsconfig.json
dist/
assets/
coverage/
*.bin
12 changes: 7 additions & 5 deletions 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 Expand Up @@ -51,8 +51,10 @@ export const collectEvents = async (
),
);
})
.then(() => logger.info("Analytic data Amplitude has been collected"))
.catch((err) =>
logger.warn("Failed to collect analytic data Amplitude", err),
);
.then(() => {
logger.debug("Analytic data Amplitude has been collected");
})
.catch((err) => {
logger.error("Failed to collect analytic data Amplitude", err);
});
};
8 changes: 6 additions & 2 deletions src/analytics/ga/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export const collectEvents = async (
events,
},
})
.then(() => logger.info("Analytic data ga has been collected"))
.catch((err) => logger.warn("Failed to collect analytic data ga", err));
.then(() => {
logger.debug("Analytic data ga has been collected");
})
.catch((err) => {
logger.error("Failed to collect analytic data ga", err);
});
};
13 changes: 8 additions & 5 deletions src/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { Logger } from "../logger/index.js";
import { flattenPromise } from "../common/helpers.js";
import { collectEvents as collectGA } from "./ga/index.js";
import { collectEvents as collectAmplitude } from "./amplitude/index.js";
import { AnalyticsData } from "./ga/types.js";

const logger = new Logger("analytics");

export const collectAnalytics = (analytics: AnalyticsData): Promise<void> => {
export const collectAnalytics = async (
analytics: AnalyticsData,
): Promise<void> => {
logger.debug("Collecting analytic data");
const evts = analytics.getEvents();
const id = analytics.getId();

return Promise.all([collectGA(id, evts), collectAmplitude(id, evts)])
.then(flattenPromise)
.catch((err) => logger.warn("Failed to collect analytic data", err));
try {
await Promise.all([collectGA(id, evts), collectAmplitude(id, evts)]);
} catch (err) {
logger.error("Failed to collect analytic data", err);
}
};
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]);
};