Skip to content

Commit

Permalink
impr(server): store some logs forever (@Miodec) (monkeytypegame#5697)
Browse files Browse the repository at this point in the history
!nuf
  • Loading branch information
Miodec authored Jul 31, 2024
1 parent 1cbb9fa commit 2510c3f
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 60 deletions.
3 changes: 2 additions & 1 deletion backend/src/api/controllers/quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { verify } from "../../utils/captcha";
import Logger from "../../utils/logger";
import { MonkeyResponse } from "../../utils/monkey-response";
import { ObjectId } from "mongodb";
import { addLog } from "../../dal/logs";

async function verifyCaptcha(captcha: string): Promise<void> {
if (!(await verify(captcha))) {
Expand Down Expand Up @@ -70,7 +71,7 @@ export async function approveQuote(
}

const data = await NewQuotesDAL.approve(quoteId, editText, editSource, name);
void Logger.logToDb("system_quote_approved", data, uid);
void addLog("system_quote_approved", data, uid);

return new MonkeyResponse(data.message, data.quote);
}
Expand Down
13 changes: 7 additions & 6 deletions backend/src/api/controllers/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
Configuration,
PostResultResponse,
} from "@monkeytype/shared-types";
import { addLog } from "../../dal/logs";

try {
if (!anticheatImplemented()) throw new Error("undefined");
Expand Down Expand Up @@ -103,7 +104,7 @@ export async function getResults(
limit,
offset,
});
void Logger.logToDb(
void addLog(
"user_results_requested",
{
limit,
Expand All @@ -130,7 +131,7 @@ export async function deleteAll(
const { uid } = req.ctx.decodedToken;

await ResultDAL.deleteAll(uid);
void Logger.logToDb("user_results_deleted", "", uid);
void addLog("user_results_deleted", "", uid);
return new MonkeyResponse("All results deleted");
}

Expand Down Expand Up @@ -205,7 +206,7 @@ export async function addResult(
if (req.ctx.configuration.results.objectHashCheckEnabled) {
const serverhash = objectHash(completedEvent);
if (serverhash !== resulthash) {
void Logger.logToDb(
void addLog(
"incorrect_result_hash",
{
serverhash,
Expand Down Expand Up @@ -306,7 +307,7 @@ export async function addResult(
const earliestPossible = (lastResultTimestamp ?? 0) + testDurationMilis;
const nowNoMilis = Math.floor(Date.now() / 1000) * 1000;
if (lastResultTimestamp && nowNoMilis < earliestPossible - 1000) {
void Logger.logToDb(
void addLog(
"invalid_result_spacing",
{
lastTimestamp: lastResultTimestamp,
Expand Down Expand Up @@ -376,7 +377,7 @@ export async function addResult(
if (req.ctx.configuration.users.lastHashesCheck.enabled) {
let lastHashes = user.lastReultHashes ?? [];
if (lastHashes.includes(resulthash)) {
void Logger.logToDb(
void addLog(
"duplicate_result",
{
lastHashes,
Expand Down Expand Up @@ -591,7 +592,7 @@ export async function addResult(
await UserDAL.incrementTestActivity(user, completedEvent.timestamp);

if (isPb) {
void Logger.logToDb(
void addLog(
"user_new_pb",
`${completedEvent.mode + " " + completedEvent.mode2} ${
completedEvent.wpm
Expand Down
27 changes: 17 additions & 10 deletions backend/src/api/controllers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
UserProfile,
UserProfileDetails,
} from "@monkeytype/shared-types";
import { addImportantLog, addLog, deleteUserLogs } from "../../dal/logs";

async function verifyCaptcha(captcha: string): Promise<void> {
if (!(await verify(captcha))) {
Expand Down Expand Up @@ -68,7 +69,7 @@ export async function createNewUser(
}

await UserDAL.addUser(name, email, uid);
void Logger.logToDb("user_created", `${name} ${email}`, uid);
void addImportantLog("user_created", `${name} ${email}`, uid);

return new MonkeyResponse("User created");
} catch (e) {
Expand Down Expand Up @@ -206,6 +207,7 @@ export async function deleteUser(
//cleanup database
await Promise.all([
UserDAL.deleteUser(uid),
deleteUserLogs(uid),
deleteAllApeKeys(uid),
deleteAllPresets(uid),
deleteConfig(uid),
Expand All @@ -219,7 +221,7 @@ export async function deleteUser(
//delete user from
await AuthUtil.deleteUser(uid);

void Logger.logToDb(
void addImportantLog(
"user_deleted",
`${userInfo.email} ${userInfo.name}`,
uid
Expand Down Expand Up @@ -259,7 +261,7 @@ export async function resetUser(
promises.push(GeorgeQueue.unlinkDiscord(userInfo.discordId, uid));
}
await Promise.all(promises);
void Logger.logToDb("user_reset", `${userInfo.email} ${userInfo.name}`, uid);
void addImportantLog("user_reset", `${userInfo.email} ${userInfo.name}`, uid);

return new MonkeyResponse("User reset");
}
Expand Down Expand Up @@ -289,7 +291,7 @@ export async function updateName(
}

await UserDAL.updateName(uid, name, user.name);
void Logger.logToDb(
void addImportantLog(
"user_name_updated",
`changed name from ${user.name} to ${name}`,
uid
Expand All @@ -308,7 +310,7 @@ export async function clearPb(
uid,
req.ctx.configuration.dailyLeaderboards
);
void Logger.logToDb("user_cleared_pbs", "", uid);
void addImportantLog("user_cleared_pbs", "", uid);

return new MonkeyResponse("User's PB cleared");
}
Expand All @@ -323,7 +325,7 @@ export async function optOutOfLeaderboards(
uid,
req.ctx.configuration.dailyLeaderboards
);
void Logger.logToDb("user_opted_out_of_leaderboards", "", uid);
void addImportantLog("user_opted_out_of_leaderboards", "", uid);

return new MonkeyResponse("User opted out of leaderboards");
}
Expand Down Expand Up @@ -377,7 +379,7 @@ export async function updateEmail(
}
}

void Logger.logToDb(
void addImportantLog(
"user_email_updated",
`changed email to ${newEmail}`,
uid
Expand Down Expand Up @@ -461,7 +463,7 @@ export async function getUser(
};

const agentLog = buildAgentLog(req);
void Logger.logToDb("user_data_requested", agentLog, uid);
void addLog("user_data_requested", agentLog, uid);
void UserDAL.logIpAddress(uid, agentLog.ip, userInfo);

let inboxUnreadSize = 0;
Expand Down Expand Up @@ -556,7 +558,7 @@ export async function linkDiscord(
await UserDAL.linkDiscord(uid, discordId, discordAvatar);

await GeorgeQueue.linkDiscord(discordId, uid);
void Logger.logToDb("user_discord_link", `linked to ${discordId}`, uid);
void addImportantLog("user_discord_link", `linked to ${discordId}`, uid);

return new MonkeyResponse("Discord account linked", {
discordId,
Expand Down Expand Up @@ -585,7 +587,7 @@ export async function unlinkDiscord(

await GeorgeQueue.unlinkDiscord(discordId, uid);
await UserDAL.unlinkDiscord(uid);
void Logger.logToDb("user_discord_unlinked", discordId, uid);
void addImportantLog("user_discord_unlinked", discordId, uid);

return new MonkeyResponse("Discord account unlinked");
}
Expand Down Expand Up @@ -957,6 +959,8 @@ export async function setStreakHourOffset(

await UserDAL.setStreakHourOffset(uid, hourOffset);

void addImportantLog("user_streak_hour_offset_set", { hourOffset }, uid);

return new MonkeyResponse("Streak hour offset set");
}

Expand All @@ -980,6 +984,8 @@ export async function toggleBan(
if (discordIdIsValid) await GeorgeQueue.userBanned(discordId, true);
}

void addImportantLog("user_ban_toggled", { banned: !user.banned }, uid);

return new MonkeyResponse(`Ban toggled`, {
banned: !user.banned,
});
Expand All @@ -990,6 +996,7 @@ export async function revokeAllTokens(
): Promise<MonkeyResponse> {
const { uid } = req.ctx.decodedToken;
await AuthUtil.revokeTokensByUid(uid);
void addImportantLog("user_tokens_revoked", "", uid);
return new MonkeyResponse("All tokens revoked");
}

Expand Down
3 changes: 2 additions & 1 deletion backend/src/dal/leaderboards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { setLeaderboard } from "../utils/prometheus";
import { isDevEnvironment } from "../utils/misc";
import { getCachedConfiguration } from "../init/configuration";
import { LeaderboardEntry } from "@monkeytype/shared-types";
import { addLog } from "./logs";

export async function get(
mode: string,
Expand Down Expand Up @@ -235,7 +236,7 @@ export async function update(
const timeToRunIndex = (end2 - start2) / 1000;
const timeToSaveHistogram = (end3 - start3) / 1000; // not sent to prometheus yet

void Logger.logToDb(
void addLog(
`system_lb_update_${language}_${mode}_${mode2}`,
`Aggregate ${timeToRunAggregate}s, loop 0s, insert 0s, index ${timeToRunIndex}s, histogram ${timeToSaveHistogram}`
);
Expand Down
58 changes: 58 additions & 0 deletions backend/src/dal/logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Collection, ObjectId } from "mongodb";
import * as db from "../init/db";
import Logger from "../utils/logger";

type DbLog = {
_id: ObjectId;
type?: string;
timestamp: number;
uid: string;
important?: boolean;
event: string;
message: string | Record<string, unknown>;
};

export const getLogsCollection = (): Collection<DbLog> =>
db.collection<DbLog>("logs");

async function insertIntoDb(
event: string,
message: string | Record<string, unknown>,
uid = "",
important = false
): Promise<void> {
const dbLog: DbLog = {
_id: new ObjectId(),
timestamp: Date.now(),
uid: uid ?? "",
event: event,
message: message,
important: important,
};

if (!important) delete dbLog.important;

Logger.info(`${event}\t${uid}\t${JSON.stringify(message)}`);

await getLogsCollection().insertOne(dbLog);
}

export async function addLog(
event: string,
message: string | Record<string, unknown>,
uid = ""
): Promise<void> {
await insertIntoDb(event, message, uid);
}

export async function addImportantLog(
event: string,
message: string | Record<string, unknown>,
uid = ""
): Promise<void> {
await insertIntoDb(event, message, uid, true);
}

export async function deleteUserLogs(uid: string): Promise<void> {
await getLogsCollection().deleteMany({ uid });
}
9 changes: 7 additions & 2 deletions backend/src/dal/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
Mode2,
PersonalBest,
} from "@monkeytype/contracts/schemas/shared";
import { addImportantLog } from "./logs";

const SECONDS_PER_HOUR = 3600;

Expand Down Expand Up @@ -859,7 +860,7 @@ export async function recordAutoBanEvent(
}

await getUsersCollection().updateOne({ uid }, { $set: updateObj });
void Logger.logToDb(
void addImportantLog(
"user_auto_banned",
{ autoBanTimestamps, banningUser },
uid
Expand Down Expand Up @@ -1077,7 +1078,11 @@ export async function updateStreak(
if (isYesterday(streak.lastResultTimestamp, streak.hourOffset ?? 0)) {
streak.length += 1;
} else if (!isToday(streak.lastResultTimestamp, streak.hourOffset ?? 0)) {
void Logger.logToDb("streak_lost", JSON.parse(JSON.stringify(streak)), uid);
void addImportantLog(
"streak_lost",
JSON.parse(JSON.stringify(streak)),
uid
);
streak.length = 1;
}

Expand Down
7 changes: 4 additions & 3 deletions backend/src/init/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Logger from "../utils/logger";
import { identity } from "../utils/misc";
import { BASE_CONFIGURATION } from "../constants/base-configuration";
import { Configuration } from "@monkeytype/shared-types";
import { addLog } from "../dal/logs";

const CONFIG_UPDATE_INTERVAL = 10 * 60 * 1000; // 10 Minutes

Expand Down Expand Up @@ -84,7 +85,7 @@ export async function getLiveConfiguration(): Promise<Configuration> {
}); // Seed the base configuration.
}
} catch (error) {
void Logger.logToDb(
void addLog(
"fetch_configuration_failure",
`Could not fetch configuration: ${error.message}`
);
Expand All @@ -102,7 +103,7 @@ async function pushConfiguration(configuration: Configuration): Promise<void> {
await db.collection("configuration").replaceOne({}, configuration);
serverConfigurationUpdated = true;
} catch (error) {
void Logger.logToDb(
void addLog(
"push_configuration_failure",
`Could not push configuration: ${error.message}`
);
Expand All @@ -122,7 +123,7 @@ export async function patchConfiguration(

await getLiveConfiguration();
} catch (error) {
void Logger.logToDb(
void addLog(
"patch_configuration_failure",
`Could not patch configuration: ${error.message}`
);
Expand Down
11 changes: 6 additions & 5 deletions backend/src/jobs/delete-old-logs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CronJob } from "cron";
import * as db from "../init/db";
import Logger from "../utils/logger";
import { getCachedConfiguration } from "../init/configuration";
import { addLog } from "../dal/logs";

const CRON_SCHEDULE = "0 0 0 * * *";
const LOG_MAX_AGE_DAYS = 30;
Expand All @@ -13,11 +13,12 @@ async function deleteOldLogs(): Promise<void> {
return;
}

const data = await db
.collection("logs")
.deleteMany({ timestamp: { $lt: Date.now() - LOG_MAX_AGE_MILLISECONDS } });
const data = await db.collection("logs").deleteMany({
timestamp: { $lt: Date.now() - LOG_MAX_AGE_MILLISECONDS },
$or: [{ important: false }, { important: { $exists: false } }],
});

void Logger.logToDb(
void addLog(
"system_logs_deleted",
`${data.deletedCount} logs deleted older than ${LOG_MAX_AGE_DAYS} day(s)`,
undefined
Expand Down
3 changes: 2 additions & 1 deletion backend/src/middlewares/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { isDevEnvironment } from "../utils/misc";
import { ObjectId } from "mongodb";
import { version } from "../version";
import { addLog } from "../dal/logs";

type DBError = {
_id: ObjectId;
Expand Down Expand Up @@ -70,7 +71,7 @@ async function errorHandlingMiddleware(
const { uid, errorId } = monkeyResponse.data;

try {
await Logger.logToDb(
await addLog(
"system_error",
`${monkeyResponse.status} ${errorId} ${error.message} ${error.stack}`,
uid
Expand Down
Loading

0 comments on commit 2510c3f

Please sign in to comment.