chore: reduce polling log noise across backend and nginx (#336)

This commit is contained in:
Daniel Volz
2026-02-27 00:54:21 +01:00
committed by GitHub
parent 19ba4bb7d2
commit 6b27d234d9
13 changed files with 226 additions and 28 deletions
-4
View File
@@ -78,10 +78,6 @@ async function runMigrations() {
const migrateResult = await runDrizzleMigrations(db);
if (!migrateResult.success) {
log.error(`[DB] Migration error: ${migrateResult.error}`);
} else if (migrateResult.warning) {
log.warn(`[DB] Migration warning: ${migrateResult.warning}`);
} else {
log.debug(`[DB] Drizzle migrations completed`);
}
// Run ALTER TABLE migrations for backward compatibility
+5 -6
View File
@@ -88,13 +88,12 @@ export async function runDrizzleMigrations(
await migrate(database, { migrationsFolder });
return { success: true };
} catch (err: unknown) {
// If the error is about existing schema objects, the DB is already up-to-date
// This happens when ALTER migrations in client.ts have already added the columns,
// or when tables were created before drizzle migrations were introduced
if ((err as Error).message?.includes("duplicate column") || (err as Error).message?.includes("already exists")) {
return { success: true, warning: `Schema already up-to-date: ${(err as Error).message}` };
const msg = (err as Error).message ?? "";
// Duplicate column / already exists = DB is already up-to-date (expected for existing DBs)
if (msg.includes("duplicate column") || msg.includes("already exists")) {
return { success: true };
}
return { success: false, error: (err as Error).message };
return { success: false, error: msg };
}
}
+9 -1
View File
@@ -58,10 +58,18 @@ function sanitizeCorrelationId(headers: IncomingHttpHeaders): string | null {
}
function buildLoggerOptions(level: string) {
return {
const base = {
level,
timestamp: () => `,"time":"${new Date().toISOString()}"`,
};
// Human-readable logs in development; structured JSON in production/test
if (process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "test") {
return {
...base,
transport: { target: "pino-pretty", options: { translateTime: "SYS:yyyy-mm-dd HH:MM:ss.l" } },
};
}
return base;
}
/** Create and configure Fastify app (without starting) */
+4 -2
View File
@@ -137,8 +137,9 @@ async function validateShareDoseId(share: typeof shareTokens.$inferSelect, doseI
export async function doseRoutes(app: FastifyInstance) {
// ---------------------------------------------------------------------------
// GET /doses/taken - PROTECTED: Get all taken doses for the user
// Suppress request logs — polled every 5s by frontend
// ---------------------------------------------------------------------------
app.get("/doses/taken", { preHandler: requireAuth }, async (request, reply) => {
app.get("/doses/taken", { preHandler: requireAuth, logLevel: "warn" }, async (request, reply) => {
const userId = await getUserId(request, reply);
// Get all taken doses for this user (no time limit)
@@ -304,8 +305,9 @@ export async function doseRoutes(app: FastifyInstance) {
// ---------------------------------------------------------------------------
// GET /share/:token/doses - PUBLIC: Get taken doses for a share link
// Suppress request logs — polled every 5s by SharedSchedule
// ---------------------------------------------------------------------------
app.get<{ Params: { token: string } }>("/share/:token/doses", async (request, reply) => {
app.get<{ Params: { token: string } }>("/share/:token/doses", { logLevel: "warn" }, async (request, reply) => {
const { token } = request.params;
const { share, reason } = await getActiveShareToken(token);
+2 -3
View File
@@ -10,11 +10,10 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
const backendVersion = packageJson.version || "unknown";
export async function healthRoutes(app: FastifyInstance) {
// Exempt from rate limit - lightweight health check
app.get("/health", { config: { rateLimit: false } }, async () => ({
// Exempt from rate limit + suppress request logs (called every 30s by Docker healthcheck)
app.get("/health", { config: { rateLimit: false }, logLevel: "warn" }, async () => ({
status: "ok",
version: backendVersion,
smtpConfigured: Boolean(process.env.SMTP_HOST),
shoutrrrConfigured: Boolean(process.env.SHOUTRRR_URL),
}));
}
+2 -1
View File
@@ -284,7 +284,8 @@ export async function settingsRoutes(app: FastifyInstance) {
}
// Get settings for current user
app.get("/settings", async (request, reply) => {
// Suppress request logs — polled every 30s for reminder status refresh
app.get("/settings", { logLevel: "warn" }, async (request, reply) => {
const userId = await getUserId(request, reply);
const settings = await getOrCreateUserSettings(userId);
-2
View File
@@ -868,7 +868,6 @@ describe("E2E Tests with Real Routes", () => {
const json = response.json();
expect(json.status).toBe("ok");
expect(typeof json.smtpConfigured).toBe("boolean");
expect(typeof json.shoutrrrConfigured).toBe("boolean");
});
});
@@ -1289,7 +1288,6 @@ describe("E2E Tests with Real Routes", () => {
const json = response.json();
expect(json.status).toBe("ok");
expect(typeof json.smtpConfigured).toBe("boolean");
expect(typeof json.shoutrrrConfigured).toBe("boolean");
});
});
+8 -4
View File
@@ -23,18 +23,22 @@ function shouldLog(level: string): boolean {
return LOG_LEVELS[level] >= getLevel();
}
function ts(): string {
return new Date().toISOString();
}
export const log = {
debug(msg: string): void {
if (shouldLog("debug")) console.log(msg);
if (shouldLog("debug")) console.log(`[${ts()}] [DEBUG] ${msg}`);
},
info(msg: string): void {
if (shouldLog("info")) console.log(msg);
if (shouldLog("info")) console.log(`[${ts()}] [INFO] ${msg}`);
},
warn(msg: string): void {
if (shouldLog("warn")) console.warn(msg);
if (shouldLog("warn")) console.warn(`[${ts()}] [WARN] ${msg}`);
},
error(msg: string): void {
if (shouldLog("error")) console.error(msg);
if (shouldLog("error")) console.error(`[${ts()}] [ERROR] ${msg}`);
},
};