Implement centralized logging with RMABLogger

Replaces scattered console statements with a unified RMABLogger across backend API routes and services. Adds LOG_LEVEL-based filtering, job-aware database persistence, and context-based logging. Updates documentation to describe the new logging system and usage patterns. Also documents qBittorrent CSRF header fix
This commit is contained in:
kikootwo
2026-01-12 12:45:48 -05:00
parent ba5f5cf7d6
commit 682836237b
118 changed files with 1623 additions and 1079 deletions
+10 -7
View File
@@ -14,6 +14,9 @@ import {
isAlreadyRequested,
isAlreadySwiped,
} from '@/lib/bookdate/helpers';
import { RMABLogger } from '@/lib/utils/logger';
const logger = RMABLogger.create('API.BookDate.Generate');
async function handler(req: AuthenticatedRequest) {
try {
@@ -54,7 +57,7 @@ async function handler(req: AuthenticatedRequest) {
};
// Build prompt and call AI (same as recommendations endpoint, but doesn't check cache)
console.log('[BookDate] Force generating new recommendations for user:', userId);
logger.info('Force generating new recommendations for user', { userId });
const prompt = await buildAIPrompt(userId, userPreferences);
const aiResponse = await callAI(config.provider, config.model, config.apiKey, prompt);
@@ -62,7 +65,7 @@ async function handler(req: AuthenticatedRequest) {
throw new Error('Invalid AI response format: missing recommendations array');
}
console.log(`[BookDate] AI returned ${aiResponse.recommendations.length} recommendations`);
logger.debug('AI returned recommendations', { count: aiResponse.recommendations.length });
// Match to Audnexus and filter
const batchId = `batch_${Date.now()}`;
@@ -88,14 +91,14 @@ async function handler(req: AuthenticatedRequest) {
const audnexusMatch = await matchToAudnexus(rec.title, rec.author);
if (!audnexusMatch) {
console.warn(`[BookDate] No Audnexus match: "${rec.title}" by ${rec.author}`);
logger.warn('No Audnexus match', { title: rec.title, author: rec.author });
continue;
}
// Check again if in library with ASIN for exact matching
// This catches books that might have different titles (e.g., "The Tenant" vs "The Tenant (Unabridged)")
if (await isInLibrary(userId, audnexusMatch.title, audnexusMatch.author, audnexusMatch.asin)) {
console.log(`[BookDate] Book "${audnexusMatch.title}" (ASIN: ${audnexusMatch.asin}) is in library, skipping`);
logger.debug('Book is in library, skipping', { title: audnexusMatch.title, asin: audnexusMatch.asin });
continue;
}
@@ -122,12 +125,12 @@ async function handler(req: AuthenticatedRequest) {
}
} catch (error) {
console.warn(`[BookDate] Match error for "${rec.title}":`, error);
logger.warn('Match error', { title: rec.title, error: error instanceof Error ? error.message : String(error) });
continue;
}
}
console.log(`[BookDate] Matched ${matched.length} new recommendations`);
logger.info('Matched new recommendations', { count: matched.length });
if (matched.length === 0) {
return NextResponse.json(
@@ -163,7 +166,7 @@ async function handler(req: AuthenticatedRequest) {
});
} catch (error: any) {
console.error('[BookDate] Generate error:', error);
logger.error('Generate error', { error: error instanceof Error ? error.message : String(error) });
return NextResponse.json(
{
error: error.message || 'Failed to generate new recommendations',