Add LOG_LEVEL logger helper with createLogger() utility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 19:25:44 -05:00
co-authored by Claude Opus 4.6
parent 8d4628bb94
commit eb836eb2c7
10 changed files with 84 additions and 18 deletions
+12
View File
@@ -9,6 +9,7 @@ import {
isPasswordLoginDisabled,
} from "@/lib/config";
import { db } from "@/lib/db/client";
import { createLogger } from "@/lib/logger";
import {
getUserCount,
isRegistrationOpen,
@@ -36,7 +37,18 @@ const oidcPlugin = isOidcConfigured()
]
: [];
const authLog = createLogger("auth");
export const auth = betterAuth({
logger: {
// Suppress unset secret/low entropy warnings during build
disabled: process.env.NEXT_PHASE === "phase-production-build",
level: "debug",
log: (level, message, ...args) => {
const fn = authLog[level as keyof typeof authLog];
if (fn) fn(message, ...args);
},
},
database: drizzleAdapter(db, {
provider: "sqlite",
}),
+7 -4
View File
@@ -7,6 +7,7 @@ import {
titles,
userTitleStatus,
} from "@/lib/db/schema";
import { createLogger } from "@/lib/logger";
import { refreshAvailability } from "@/lib/services/availability";
import {
cacheEpisodeStills,
@@ -21,6 +22,8 @@ import {
} from "@/lib/services/metadata";
import { getTvDetails } from "@/lib/tmdb/client";
const log = createLogger("cron");
const DAY = 24 * 60 * 60 * 1000;
const RATE_LIMIT_MS = 300;
@@ -42,13 +45,13 @@ function schedule(name: string, cron: string, handler: () => Promise<void>) {
name,
protect: true,
catch: (err: unknown) => {
console.error(`[scheduler] Job ${name} failed:`, err);
log.error(`Job ${name} failed:`, err);
},
},
async () => {
console.log(`[scheduler] Running job: ${name}`);
log.info(`Running job: ${name}`);
await handler();
console.log(`[scheduler] Completed job: ${name}`);
log.info(`Completed job: ${name}`);
},
),
);
@@ -213,7 +216,7 @@ export function startJobs() {
schedule("refreshTvChildren", "30 */12 * * *", refreshTvChildrenJob);
schedule("cacheImages", "0 1,13 * * *", cacheImagesJob);
console.log(`[scheduler] Started ${jobs.size} jobs`);
log.info(`Started ${jobs.size} jobs`);
}
export function stopJobs() {
+5 -2
View File
@@ -1,8 +1,11 @@
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
import { createLogger } from "@/lib/logger";
import { db } from "./client";
const log = createLogger("db");
export function runMigrations() {
console.log("[migrate] Running database migrations...");
log.info("Running database migrations...");
migrate(db, { migrationsFolder: "./drizzle" });
console.log("[migrate] Database migrations complete");
log.info("Database migrations complete");
}
+27
View File
@@ -0,0 +1,27 @@
const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 } as const;
type Level = keyof typeof LEVELS;
const currentLevel: Level =
(process.env.LOG_LEVEL as Level) in LEVELS
? (process.env.LOG_LEVEL as Level)
: "info";
export function createLogger(prefix: string) {
const fmt = (msg: string) => `[${prefix}] ${msg}`;
return {
error(msg: string, ...args: unknown[]) {
if (LEVELS[currentLevel] >= LEVELS.error)
console.error(fmt(msg), ...args);
},
warn(msg: string, ...args: unknown[]) {
if (LEVELS[currentLevel] >= LEVELS.warn) console.warn(fmt(msg), ...args);
},
info(msg: string, ...args: unknown[]) {
if (LEVELS[currentLevel] >= LEVELS.info) console.log(fmt(msg), ...args);
},
debug(msg: string, ...args: unknown[]) {
if (LEVELS[currentLevel] >= LEVELS.debug)
console.debug(fmt(msg), ...args);
},
};
}
+4 -1
View File
@@ -3,6 +3,7 @@ import { eq } from "drizzle-orm";
import { Vibrant } from "node-vibrant/node";
import { db } from "@/lib/db/client";
import { titles } from "@/lib/db/schema";
import { createLogger } from "@/lib/logger";
import {
downloadAndCacheImage,
getLocalImagePath,
@@ -10,6 +11,8 @@ import {
isImageCached,
} from "@/lib/services/image-cache";
const log = createLogger("colors");
export interface ColorPalette {
vibrant: string | null;
darkVibrant: string | null;
@@ -58,7 +61,7 @@ export async function extractAndStoreColors(
return colors;
} catch (err) {
console.error(`Failed to extract colors for title ${titleId}:`, err);
log.error(`Failed to extract colors for title ${titleId}:`, err);
return null;
}
}
+9 -6
View File
@@ -7,6 +7,7 @@ import {
titleRecommendations,
titles,
} from "@/lib/db/schema";
import { createLogger } from "@/lib/logger";
import {
getMovieDetails,
getRecommendations,
@@ -29,6 +30,8 @@ import {
imageCacheEnabled,
} from "./image-cache";
const log = createLogger("metadata");
export async function importTitle(
tmdbId: number,
type: "movie" | "tv",
@@ -277,10 +280,10 @@ export async function refreshTvChildren(
})
.run();
}
} catch {
} catch (err) {
// Skip this season and continue with the rest — partial data is
// better than aborting entirely. The next refresh cycle will retry.
console.error(`Failed to fetch season ${sn} for TMDB ${tmdbId}`);
log.error(`Failed to fetch season ${sn} for TMDB ${tmdbId}:`, err);
}
}
}
@@ -457,8 +460,8 @@ export async function getTitleWithChildren(id: string): Promise<{
.run();
await refreshTvChildren(id, title.tmdbId, show.number_of_seasons);
title = db.select().from(titles).where(eq(titles.id, id)).get() ?? title;
} catch {
// Continue with whatever data we have
} catch (err) {
log.debug(`Failed to hydrate shell TV title ${id}:`, err);
}
}
@@ -483,8 +486,8 @@ export async function getTitleWithChildren(id: string): Promise<{
.where(eq(titles.id, id))
.run();
title = db.select().from(titles).where(eq(titles.id, id)).get() ?? title;
} catch {
// Continue with whatever data we have
} catch (err) {
log.debug(`Failed to hydrate shell movie title ${id}:`, err);
}
}