diff --git a/.eslintignore b/.eslintignore index 7a454ea0..49a1cea2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -25,3 +25,8 @@ src/services/ src/types/ src/utils/virtualBackgroundUtils.ts src/workers/ +**/*.test.ts +**/*.test.tsx +**/*.spec.ts +**/*.spec.tsx +scripts/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 53c0adf9..eef38854 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -12,6 +12,7 @@ "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-unsafe-function-type": "warn", "@typescript-eslint/no-unused-expressions": "warn", - "prettier/prettier": ["error", { "endOfLine": "auto" }] + "prettier/prettier": ["error", { "endOfLine": "auto" }], + "no-console": "error" } -} +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f17da4a..35abf3c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,15 +124,13 @@ jobs: run: pnpm install --frozen-lockfile - name: Run Tests - shell: bash - run: | - if timeout 30s pnpm vitest run --coverage; then - echo "Tests completed within the 30-second limit." - else - status=$? - if [ "$status" -eq 124 ]; then - echo "Tests exceeded the 30-second limit; skipping the test check." - exit 0 - fi - exit "$status" - fi + timeout-minutes: 5 + run: pnpm run test -- --reporter=default --reporter=json --outputFile=vitest-report.json + + - name: Upload test report on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: vitest-report + path: vitest-report.json + retention-days: 7 diff --git a/src/app/api/auth/discord/callback/route.ts b/src/app/api/auth/discord/callback/route.ts index 512bde2f..9d2ed3fc 100644 --- a/src/app/api/auth/discord/callback/route.ts +++ b/src/app/api/auth/discord/callback/route.ts @@ -3,6 +3,9 @@ import { withRateLimit } from '@/lib/ratelimit'; import { exchangeCodeForToken, getDiscordUser, getDiscordAvatarUrl } from '@/lib/discord/oauth'; import type { AuthResponseDTO, AuthErrorDTO } from '@/types/api/auth.dto'; import { edgeLog } from '@/../infra/edge-config'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-auth-discord-callback'); export const runtime = 'edge'; @@ -103,7 +106,7 @@ export async function GET( return addHeaders(response) as NextResponse; } catch (error) { edgeLog('error', '/api/auth/discord/callback', `Error: ${error}`); - console.error('Discord OAuth callback error:', error); + logger.error('Discord OAuth callback error', { error }); return addHeaders( NextResponse.json({ message: 'Internal server error' }, { status: 500 }), diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index d8c7acc0..d284b0a6 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -5,6 +5,9 @@ import { LoginRequestSchema } from '@/types/api/auth.dto'; import type { AuthResponseDTO, AuthErrorDTO } from '@/types/api/auth.dto'; import { edgeLog } from '@/../infra/edge-config'; import { getVerificationStatus } from '@/lib/auth/email-verification'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-auth-login'); export const runtime = 'nodejs'; @@ -73,7 +76,7 @@ export async function POST( return addHeaders(NextResponse.json({ message: 'Invalid email or password' }, { status: 401 })); } catch (error) { - console.error('Login error:', error); + logger.error('Login error', { error }); return addHeaders(NextResponse.json({ message: 'Internal server error' }, { status: 500 })); } diff --git a/src/app/api/exports/history/route.ts b/src/app/api/exports/history/route.ts index f23c1236..9cb8d39c 100644 --- a/src/app/api/exports/history/route.ts +++ b/src/app/api/exports/history/route.ts @@ -5,6 +5,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { getHistoryByUser } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-exports-history'); // Mock user ID - in production, get from auth session const getCurrentUserId = (): string => 'user-123'; @@ -19,7 +22,7 @@ export async function GET(request: NextRequest) { return NextResponse.json({ history }); } catch (error) { - console.error('Error fetching history:', error); + logger.error('Error fetching history', { error }); return NextResponse.json({ error: 'Failed to fetch history' }, { status: 500 }); } } diff --git a/src/app/api/exports/schedules/[id]/route.ts b/src/app/api/exports/schedules/[id]/route.ts index f1534618..7bb134b9 100644 --- a/src/app/api/exports/schedules/[id]/route.ts +++ b/src/app/api/exports/schedules/[id]/route.ts @@ -12,6 +12,9 @@ import { deleteSchedule, UpdateScheduleInput, } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-exports-schedules-id'); export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { try { @@ -24,7 +27,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ return NextResponse.json({ schedule }); } catch (error) { - console.error('Error fetching schedule:', error); + logger.error('Error fetching schedule', { error }); return NextResponse.json({ error: 'Failed to fetch schedule' }, { status: 500 }); } } @@ -51,7 +54,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< return NextResponse.json({ schedule }); } catch (error) { - console.error('Error updating schedule:', error); + logger.error('Error updating schedule', { error }); return NextResponse.json({ error: 'Failed to update schedule' }, { status: 500 }); } } @@ -66,7 +69,7 @@ export async function DELETE( return NextResponse.json({ success: true }); } catch (error) { - console.error('Error deleting schedule:', error); + logger.error('Error deleting schedule', { error }); return NextResponse.json({ error: 'Failed to delete schedule' }, { status: 500 }); } } diff --git a/src/app/api/exports/schedules/route.ts b/src/app/api/exports/schedules/route.ts index 2c402a2b..04fb563a 100644 --- a/src/app/api/exports/schedules/route.ts +++ b/src/app/api/exports/schedules/route.ts @@ -13,6 +13,9 @@ import { frequencyToCron, validateCronExpression, } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-exports-schedules'); // Mock user ID - in production, get from auth session const getCurrentUserId = (): string => 'user-123'; @@ -24,7 +27,7 @@ export async function GET(request: NextRequest) { return NextResponse.json({ schedules }); } catch (error) { - console.error('Error fetching schedules:', error); + logger.error('Error fetching schedules', { error }); return NextResponse.json({ error: 'Failed to fetch schedules' }, { status: 500 }); } } @@ -54,7 +57,7 @@ export async function POST(request: NextRequest) { return NextResponse.json({ schedule }, { status: 201 }); } catch (error) { - console.error('Error creating schedule:', error); + logger.error('Error creating schedule', { error }); return NextResponse.json({ error: 'Failed to create schedule' }, { status: 500 }); } } diff --git a/src/app/api/exports/templates/[id]/route.ts b/src/app/api/exports/templates/[id]/route.ts index 0db79939..d6ae1bf6 100644 --- a/src/app/api/exports/templates/[id]/route.ts +++ b/src/app/api/exports/templates/[id]/route.ts @@ -7,6 +7,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { getTemplate, updateTemplate, deleteTemplate } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-exports-templates-id'); export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { try { @@ -19,7 +22,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ return NextResponse.json({ template }); } catch (error) { - console.error('Error fetching template:', error); + logger.error('Error fetching template', { error }); return NextResponse.json({ error: 'Failed to fetch template' }, { status: 500 }); } } @@ -37,7 +40,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< return NextResponse.json({ template }); } catch (error) { - console.error('Error updating template:', error); + logger.error('Error updating template', { error }); return NextResponse.json({ error: 'Failed to update template' }, { status: 500 }); } } @@ -52,7 +55,7 @@ export async function DELETE( return NextResponse.json({ success: true }); } catch (error) { - console.error('Error deleting template:', error); + logger.error('Error deleting template', { error }); return NextResponse.json({ error: 'Failed to delete template' }, { status: 500 }); } } diff --git a/src/app/api/exports/templates/route.ts b/src/app/api/exports/templates/route.ts index 2ed68ae9..650a0464 100644 --- a/src/app/api/exports/templates/route.ts +++ b/src/app/api/exports/templates/route.ts @@ -6,6 +6,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { createTemplate, getTemplatesByUser, CreateTemplateInput } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-exports-templates'); // Mock user ID - in production, get from auth session const getCurrentUserId = (): string => 'user-123'; @@ -17,7 +20,7 @@ export async function GET(request: NextRequest) { return NextResponse.json({ templates }); } catch (error) { - console.error('Error fetching templates:', error); + logger.error('Error fetching templates', { error }); return NextResponse.json({ error: 'Failed to fetch templates' }, { status: 500 }); } } @@ -40,7 +43,7 @@ export async function POST(request: NextRequest) { return NextResponse.json({ template }, { status: 201 }); } catch (error) { - console.error('Error creating template:', error); + logger.error('Error creating template', { error }); return NextResponse.json({ error: 'Failed to create template' }, { status: 500 }); } } diff --git a/src/app/api/generate-pdf/route.ts b/src/app/api/generate-pdf/route.ts index bd666b35..099bd6d2 100644 --- a/src/app/api/generate-pdf/route.ts +++ b/src/app/api/generate-pdf/route.ts @@ -1,6 +1,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { generatePDF } from '../../../services/pdf-generation'; import { generateReportHTML, ReportData } from '../../../lib/pdf/templates'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-generate-pdf'); export async function POST(request: NextRequest) { try { @@ -22,7 +25,7 @@ export async function POST(request: NextRequest) { }, }); } catch (error) { - console.error('Error generating PDF:', error); + logger.error('Error generating PDF', { error }); return NextResponse.json({ error: 'Failed to generate PDF' }, { status: 500 }); } } diff --git a/src/app/api/notarization/route.ts b/src/app/api/notarization/route.ts index 4ff7ab70..5f4cc3b7 100644 --- a/src/app/api/notarization/route.ts +++ b/src/app/api/notarization/route.ts @@ -1,5 +1,8 @@ import { NextRequest, NextResponse } from 'next/server'; import { saveTipNotarization } from '@/services/notarizationStore'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-notarization'); interface NotarizationPayload { txHash: string; @@ -36,7 +39,7 @@ export async function POST(request: NextRequest) { return NextResponse.json(record, { status: 201 }); } catch (error) { - console.error('[Notarization API] Error creating notarization:', error); + logger.error('Error creating notarization', { error }); return NextResponse.json({ message: 'Failed to create notarization' }, { status: 500 }); } } diff --git a/src/app/api/performance/db-metrics/route.ts b/src/app/api/performance/db-metrics/route.ts index 793dd5b0..f04f5f1b 100644 --- a/src/app/api/performance/db-metrics/route.ts +++ b/src/app/api/performance/db-metrics/route.ts @@ -1,5 +1,8 @@ import { NextResponse } from 'next/server'; import { dbPool } from '@/lib/db/pool'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-performance-db-metrics'); /** * API endpoint to expose database connection pool metrics @@ -35,7 +38,7 @@ export async function GET() { ], }); } catch (error) { - console.error('Failed to fetch DB metrics:', error); + logger.error('Failed to fetch DB metrics', { error }); return NextResponse.json( { success: false, message: 'Failed to fetch database metrics' }, { status: 500 }, diff --git a/src/app/api/performance/vitals/route.ts b/src/app/api/performance/vitals/route.ts index af7b338e..53694b86 100644 --- a/src/app/api/performance/vitals/route.ts +++ b/src/app/api/performance/vitals/route.ts @@ -54,12 +54,8 @@ async function checkPoorRate(name: string): Promise { const poorPct = (row.poor_count / row.total) * 100; if (poorPct > POOR_ALERT_THRESHOLD_PCT) { - console.warn( - `[PERFORMANCE ALERT] "${name}" poor-rate ${poorPct.toFixed( - 1, - )}% exceeds ${POOR_ALERT_THRESHOLD_PCT}% threshold (${row.poor_count}/${ - row.total - } recent sessions)`, + logger.warn( + `[PERFORMANCE ALERT] "${name}" poor-rate ${poorPct.toFixed(1)}% exceeds ${POOR_ALERT_THRESHOLD_PCT}% threshold (${row.poor_count}/${row.total} recent sessions)`, ); } } @@ -96,14 +92,12 @@ export async function POST(request: NextRequest) { const insertedId = result.rows[0]?.id as string | undefined; if (rating === 'poor') { - console.warn( - `[PERFORMANCE ALERT] Critical degradation detected for ${name} on ${ - url ?? '/' - }. Value: ${value}`, + logger.warn( + `[PERFORMANCE ALERT] Critical degradation detected for ${name} on ${url ?? '/'}. Value: ${value}`, ); await checkPoorRate(name); } else if (rating === 'needs-improvement') { - console.info( + logger.info( `[PERFORMANCE WARNING] ${name} needs improvement on ${url ?? '/'}. Value: ${value}`, ); } @@ -115,7 +109,7 @@ export async function POST(request: NextRequest) { alertTriggered: rating === 'poor', }); } catch (error) { - console.error('[Performance Analytics] Error processing metric:', error); + logger.error('[Performance Analytics] Error processing metric', { error }); return NextResponse.json({ success: false, message: 'Internal Server Error' }, { status: 500 }); } } @@ -152,10 +146,10 @@ export async function GET(request: NextRequest) { range: range ?? '7d', }); } catch (error) { - console.error('[Performance Analytics] Error fetching vitals:', error); + logger.error('[Performance Analytics] Error fetching vitals', { error }); return NextResponse.json( { success: false, message: 'Failed to fetch metrics' }, { status: 500 }, ); } -} +} \ No newline at end of file diff --git a/src/app/api/security/reporting/route.ts b/src/app/api/security/reporting/route.ts index 56cd3029..b76cee10 100644 --- a/src/app/api/security/reporting/route.ts +++ b/src/app/api/security/reporting/route.ts @@ -1,4 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-security-reporting'); interface SecurityReportEnvelope { type?: string; @@ -18,9 +21,11 @@ export async function POST(request: NextRequest) { const reports = normalizeReportBody(payload); if (reports.length > 0) { - console.warn('[security-reporting] received violation reports', { - count: reports.length, - reports, + logger.warn('Received violation reports', { + context: { + count: reports.length, + reports, + }, }); } diff --git a/src/app/api/tipping/route.ts b/src/app/api/tipping/route.ts index 81fa8355..63f619c9 100644 --- a/src/app/api/tipping/route.ts +++ b/src/app/api/tipping/route.ts @@ -1,6 +1,9 @@ import { NextRequest, NextResponse } from 'next/server'; import { randomBytes } from 'crypto'; import { saveTipNotarization } from '@/services/notarizationStore'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('api-tipping'); interface TipRequestBody { recipientId: string; @@ -51,7 +54,7 @@ export async function POST(request: NextRequest) { return NextResponse.json(response, { status: 201 }); } catch (error) { - console.error('[Tipping API] Failed to send tip:', error); + logger.error('Failed to send tip', { error }); return NextResponse.json({ message: 'Failed to process tipping request' }, { status: 500 }); } } diff --git a/src/app/components/dashboard/DashboardGrid.tsx b/src/app/components/dashboard/DashboardGrid.tsx index fe9b6924..f75d9d79 100644 --- a/src/app/components/dashboard/DashboardGrid.tsx +++ b/src/app/components/dashboard/DashboardGrid.tsx @@ -23,6 +23,9 @@ import { Settings, Plus, Grid3X3, Calendar } from 'lucide-react'; import dynamic from 'next/dynamic'; import { useDashboardWidgets } from '../../hooks/useDashboardWidgets'; import { EmptyState } from '@/components'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('DashboardGrid'); const ProgressSummaryWidget = dynamic( () => import('./widgets/ProgressSummaryWidget').then((mod) => mod.ProgressSummaryWidget), @@ -131,7 +134,7 @@ export const DashboardGrid: React.FC = ({ setWidgets(initialWidgets); } } catch (error) { - console.error('Error loading widget layout', error); + logger.error('Error loading widget layout', { error }); if (initialWidgets.length > 0) setWidgets(initialWidgets); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -143,6 +146,14 @@ export const DashboardGrid: React.FC = ({ onWidgetChange?.(widgets); }, [widgets, onWidgetChange]); + try { + saveWidgetLayout(widgets); + onWidgetChange?.(widgets); + } catch (error) { + logger.error('Error saving widget layout', { error }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [widgets]); // Only depend on widgets, not the functions // Persist layout to localStorage at most once per 500ms via debounce useEffect(() => { if (widgets.length === 0) return; diff --git a/src/app/components/notifications/MultiChannelDelivery.tsx b/src/app/components/notifications/MultiChannelDelivery.tsx index d3d27a8e..3b9445d0 100644 --- a/src/app/components/notifications/MultiChannelDelivery.tsx +++ b/src/app/components/notifications/MultiChannelDelivery.tsx @@ -19,6 +19,9 @@ import { NotificationCategory, } from '@/utils/notificationUtils'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('MultiChannelDelivery'); + interface MultiChannelDeliveryProps { userId?: string; onDeliveryComplete?: (results: Record) => void; @@ -133,7 +136,7 @@ export default function MultiChannelDelivery({ }, 2000); } } catch (error) { - console.error('Failed to send notification:', error); + logger.error('Failed to send notification', { error }); setDeliveryStatuses( channels.map((channel) => ({ channel, diff --git a/src/app/components/offline/StorageManager.tsx b/src/app/components/offline/StorageManager.tsx index 38b6de0f..b3393208 100644 --- a/src/app/components/offline/StorageManager.tsx +++ b/src/app/components/offline/StorageManager.tsx @@ -14,6 +14,9 @@ import { } from 'lucide-react'; import { useOfflineModeContext } from '../../context/OfflineModeContext'; import { offlineStorage } from '../../mobile/services/offlineStorage'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('storage-manager'); interface StorageItem { id: string; @@ -69,7 +72,7 @@ export const StorageManager: React.FC = ({ className = '' } setStorageItems(items); } catch (error) { - console.error('Failed to load storage items:', error); + logger.error('Failed to load storage items', { error }); } finally { setIsLoading(false); } @@ -171,7 +174,7 @@ export const StorageManager: React.FC = ({ className = '' } setStorageItems((prev) => prev.filter((item) => !selectedItems.includes(item.id))); setSelectedItems([]); } catch (error) { - console.error('Failed to delete items:', error); + logger.error('Failed to delete items', { error }); } }; @@ -188,7 +191,7 @@ export const StorageManager: React.FC = ({ className = '' } setStorageItems([]); setSelectedItems([]); } catch (error) { - console.error('Failed to clear all data:', error); + logger.error('Failed to clear all data', { error }); } }; diff --git a/src/app/components/quizzes/question-types/CodeChallengeQuestion.tsx b/src/app/components/quizzes/question-types/CodeChallengeQuestion.tsx index 8bd134cf..a8a9ed12 100644 --- a/src/app/components/quizzes/question-types/CodeChallengeQuestion.tsx +++ b/src/app/components/quizzes/question-types/CodeChallengeQuestion.tsx @@ -4,6 +4,8 @@ import { useState } from 'react'; import { Question, useQuizStore } from '@/app/store/quizStore'; import Editor from '@monaco-editor/react'; import { FaCheck, FaTimes, FaPlay } from 'react-icons/fa'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('CodeChallengeQuestion'); interface CodeChallengeQuestionProps { question: Question; @@ -36,7 +38,7 @@ export default function CodeChallengeQuestion({ question }: CodeChallengeQuestio const output = userFunction(testCase.input); return output === testCase.expectedOutput; } catch (error) { - console.error('Error running test:', error); + logger.error('Error running test', { error }); return false; } }, diff --git a/src/app/components/video/BookmarkManager.tsx b/src/app/components/video/BookmarkManager.tsx index 1d020cbc..68a3c779 100644 --- a/src/app/components/video/BookmarkManager.tsx +++ b/src/app/components/video/BookmarkManager.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Bookmark, Clock, Edit2, Trash2, Plus } from 'lucide-react'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('BookmarkManager'); interface Bookmark { id: string; @@ -43,7 +45,7 @@ export const BookmarkManager: React.FC = React.memo( })), ); } catch (error) { - console.error('Failed to load bookmarks:', error); + logger.error('Failed to load bookmarks', { error }); } } }, []); diff --git a/src/app/components/video/NotesTaker.tsx b/src/app/components/video/NotesTaker.tsx index 9ae69545..5792612b 100644 --- a/src/app/components/video/NotesTaker.tsx +++ b/src/app/components/video/NotesTaker.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { MessageSquare, Clock, Edit2, Trash2, Save } from 'lucide-react'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('NotesTaker'); interface Note { id: string; @@ -41,7 +43,7 @@ export const NotesTaker: React.FC = React.memo(({ currentTime, })), ); } catch (error) { - console.error('Failed to load notes:', error); + logger.error('Failed to load notes', { error }); } } }, []); diff --git a/src/app/components/video/VideoPlayer.tsx b/src/app/components/video/VideoPlayer.tsx index b7e2f256..7f85396c 100644 --- a/src/app/components/video/VideoPlayer.tsx +++ b/src/app/components/video/VideoPlayer.tsx @@ -23,6 +23,8 @@ import { useVideoPlayer } from '../../hooks/useVideoPlayer'; import { useVideoLazyLoad } from '../../hooks/useVideoLazyLoad'; import { useAudioEnhancement } from '../../hooks/useAudioEnhancement'; import { AudioInvoiceManager, AudioInvoiceButton } from '@/components/audio'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('VideoPlayer'); interface VideoPlayerProps { src: string; @@ -183,7 +185,7 @@ export const VideoPlayer: React.FC = ({ await videoRef.current.requestPictureInPicture(); } } catch (error) { - console.error('PiP error:', error); + logger.error('PiP error', { error }); } }, []); diff --git a/src/app/context/OfflineModeContext.tsx b/src/app/context/OfflineModeContext.tsx index 0244205c..c529fa3b 100644 --- a/src/app/context/OfflineModeContext.tsx +++ b/src/app/context/OfflineModeContext.tsx @@ -9,6 +9,9 @@ import React, { ReactNode, } from 'react'; import { useOfflineMode } from '../hooks/useOfflineMode'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('offline-mode-context'); interface OfflineModeContextType { isOnline: boolean; @@ -104,7 +107,7 @@ export const OfflineModeProvider: React.FC = ({ childr setSyncStatus('synced'); setLastSyncTime(new Date()); } catch (error) { - console.error('Failed to enable offline mode:', error); + logger.error('Failed to enable offline mode', { error }); setSyncStatus('error'); } }, [initializeOfflineMode]); @@ -117,7 +120,7 @@ export const OfflineModeProvider: React.FC = ({ childr setLastSyncTime(null); setPendingSyncCount(0); } catch (error) { - console.error('Failed to disable offline mode:', error); + logger.error('Failed to disable offline mode', { error }); } }, [cleanupOfflineMode]); @@ -134,7 +137,7 @@ export const OfflineModeProvider: React.FC = ({ childr setLastSyncTime(new Date()); setPendingSyncCount(0); } catch (error) { - console.error('Failed to sync offline data:', error); + logger.error('Failed to sync offline data', { error }); setSyncStatus('error'); } }, [isOnline, syncData]); @@ -145,7 +148,7 @@ export const OfflineModeProvider: React.FC = ({ childr setPendingSyncCount(0); setStorageUsage({ used: 0, total: 0, percentage: 0 }); } catch (error) { - console.error('Failed to clear offline data:', error); + logger.error('Failed to clear offline data', { error }); } }, [clearData]); diff --git a/src/app/error.tsx b/src/app/error.tsx index 42bae866..c0816cfb 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -2,6 +2,8 @@ import React, { useEffect } from 'react'; import { UserFriendlyErrorDisplay } from '@/components/errors/UserFriendlyErrorDisplay'; import { errorReportingService } from '@/services/errorReporting'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('ErrorPage'); export default function ErrorBoundary({ error, @@ -16,7 +18,7 @@ export default function ErrorBoundary({ errorMessage: error.message, digest: error.digest, }); - console.error(error); + logger.error('Application error', { error }); }, [error]); return ( diff --git a/src/app/hooks/useDashboardWidgets.tsx b/src/app/hooks/useDashboardWidgets.tsx index c65563b4..e6dd1ae9 100644 --- a/src/app/hooks/useDashboardWidgets.tsx +++ b/src/app/hooks/useDashboardWidgets.tsx @@ -1,3 +1,7 @@ +import { useState, useEffect, useCallback } from 'react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-dashboard-widgets'); import { useState, useEffect, useCallback, useRef } from 'react'; interface Widget { @@ -22,7 +26,7 @@ export const useDashboardWidgets = () => { return JSON.parse(saved); } } catch (error) { - console.error('Failed to load widget layout:', error); + logger.error('Failed to load widget layout', { error }); } return []; }, []); @@ -57,6 +61,7 @@ export const useDashboardWidgets = () => { } }, 500); } catch (error) { + logger.error('Failed to save widget layout', { error }); console.error('Failed to schedule dashboard layout save:', error); } }, []); @@ -328,7 +333,7 @@ export const useDashboardWidgets = () => { } return false; } catch (error) { - console.error('Failed to import widget config:', error); + logger.error('Failed to import widget config', { error }); return false; } }, diff --git a/src/app/hooks/useNotifications.tsx b/src/app/hooks/useNotifications.tsx index 73f8c64b..62aa0af1 100644 --- a/src/app/hooks/useNotifications.tsx +++ b/src/app/hooks/useNotifications.tsx @@ -21,6 +21,9 @@ import { NotificationService, generateRecommendations, } from '@/lib/notifications'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-notifications'); interface UseNotificationsOptions { userId?: string; @@ -224,7 +227,7 @@ export function useNotifications(options: UseNotificationsOptions = {}): UseNoti localStorage.setItem(PREFERENCES_STORAGE_KEY, JSON.stringify(defaultPrefs)); } } catch (error) { - console.error('Failed to load notification preferences:', error); + logger.error('Failed to load notification preferences', { error }); const defaultPrefs = NotificationService.createDefaultPreferences(userId); setPreferences(defaultPrefs); } finally { @@ -400,7 +403,7 @@ export function useNotifications(options: UseNotificationsOptions = {}): UseNoti setPreferences(parsed); } } catch (error) { - console.error('Failed to load preferences:', error); + logger.error('Failed to load preferences', { error }); } }, []); @@ -420,7 +423,7 @@ export function useNotifications(options: UseNotificationsOptions = {}): UseNoti try { localStorage.setItem(PREFERENCES_STORAGE_KEY, JSON.stringify(updated)); } catch (error) { - console.error('Failed to save preferences:', error); + logger.error('Failed to save preferences', { error }); throw error; } }, @@ -467,7 +470,7 @@ export function useNotifications(options: UseNotificationsOptions = {}): UseNoti const results = await NotificationService.deliverToChannels(notification, [channel]); return results[0]?.success ?? false; } catch (error) { - console.error(`Failed to deliver notification ${notification.id} via ${channel}:`, error); + logger.error(`Failed to deliver notification ${notification.id} via ${channel}`, { error }); return false; } }, @@ -493,7 +496,7 @@ export function useNotifications(options: UseNotificationsOptions = {}): UseNoti return successMap; } catch (error) { - console.error('Failed to deliver notification to channels:', error); + logger.error('Failed to deliver notification to channels', { error }); const errorMap: Record = {} as Record< NotificationChannel, boolean diff --git a/src/app/hooks/useOfflineMode.tsx b/src/app/hooks/useOfflineMode.tsx index 1e128a16..8bcf7359 100644 --- a/src/app/hooks/useOfflineMode.tsx +++ b/src/app/hooks/useOfflineMode.tsx @@ -2,6 +2,9 @@ import { useCallback, useState } from 'react'; import { openDB, IDBPDatabase } from 'idb'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-offline-mode'); // Database schema interface CourseData { @@ -212,7 +215,7 @@ export const useOfflineMode = () => { setDb(database); setIsInitialized(true); } catch (error) { - console.error('Failed to initialize offline mode:', error); + logger.error('Failed to initialize offline mode', { error }); throw error; } }, []); @@ -250,7 +253,7 @@ export const useOfflineMode = () => { await db.saveCourse(course); } catch (error) { - console.error('Failed to download course:', error); + logger.error('Failed to download course', { error }); throw error; } }, @@ -335,7 +338,7 @@ export const useOfflineMode = () => { await db.removeFromSyncQueue(item.id); } } catch (error) { - console.error('Failed to sync data:', error); + logger.error('Failed to sync data', { error }); throw error; } }, [db]); diff --git a/src/app/hooks/useProfileUpdate.ts b/src/app/hooks/useProfileUpdate.ts index 6d85f606..625d7793 100644 --- a/src/app/hooks/useProfileUpdate.ts +++ b/src/app/hooks/useProfileUpdate.ts @@ -1,4 +1,6 @@ import { useCallback, useMemo, useState } from 'react'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('useProfileUpdate'); interface ProfileData { firstName: string; @@ -36,7 +38,7 @@ export function useProfileUpdate() { return response.data; } catch (error) { - console.error('Error updating profile:', error); + logger.error('Error updating profile', { error }); throw error; } finally { setIsLoading(false); diff --git a/src/app/hooks/useSearch.tsx b/src/app/hooks/useSearch.tsx index b8fb602f..98046d58 100644 --- a/src/app/hooks/useSearch.tsx +++ b/src/app/hooks/useSearch.tsx @@ -2,6 +2,8 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { useRouter } from 'next/navigation'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('useSearch'); export interface SearchResult { id: string; @@ -162,7 +164,7 @@ export const useSearch = () => { try { setSearchHistory(JSON.parse(saved)); } catch (e) { - console.error('Error loading search history:', e); + logger.error('Error loading search history', { error: e }); } } }, []); diff --git a/src/app/mobile/components/MobileLearningInterface.tsx b/src/app/mobile/components/MobileLearningInterface.tsx index 35bb467e..70e8ae91 100644 --- a/src/app/mobile/components/MobileLearningInterface.tsx +++ b/src/app/mobile/components/MobileLearningInterface.tsx @@ -9,6 +9,9 @@ import { useMobileOptimization } from '../hooks/useMobileOptimization'; import { apiService } from '../services/api'; import { offlineStorage } from '../services/offlineStorage'; import { Course, Lesson } from '../types/mobile'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('MobileLearningInterface'); type Tab = 'home' | 'courses' | 'downloads' | 'progress' | 'profile'; @@ -57,7 +60,7 @@ export default function MobileLearningInterface() { } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load data'); - console.error('Error loading data:', err); + logger.error('Error loading data', { error: err }); } finally { setLoading(false); } @@ -72,7 +75,7 @@ export default function MobileLearningInterface() { try { await apiService.startLearningSession(lesson.id); } catch (err) { - console.error('Failed to start session:', err); + logger.error('Failed to start session', { error: err }); } } }; @@ -83,7 +86,7 @@ export default function MobileLearningInterface() { // End learning session await apiService.updateLessonProgress(currentLesson.id, true); } catch (err) { - console.error('Failed to update progress:', err); + logger.error('Failed to update progress', { error: err }); } } setShowVideo(false); diff --git a/src/app/mobile/components/MobileProgressTracker.tsx b/src/app/mobile/components/MobileProgressTracker.tsx index 0b204089..9316a54e 100644 --- a/src/app/mobile/components/MobileProgressTracker.tsx +++ b/src/app/mobile/components/MobileProgressTracker.tsx @@ -11,6 +11,9 @@ import { import { apiService } from '../services/api'; import { offlineStorage } from '../services/offlineStorage'; import { Course, Lesson, UserProgress } from '../types/mobile'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('MobileProgressTracker'); interface ProgressCourse extends Course { lessons: Lesson[]; @@ -99,7 +102,7 @@ export default function MobileProgressTracker() { } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load progress data'); - console.error('Error loading progress:', err); + logger.error('Error loading progress', { error: err }); } finally { setLoading(false); } @@ -171,7 +174,7 @@ export default function MobileProgressTracker() { try { await apiService.updateLessonProgress(lessonId, newCompleted); } catch (err) { - console.error('Failed to sync progress:', err); + logger.error('Failed to sync progress', { error: err }); } } }; diff --git a/src/app/mobile/components/OfflineContentManager.tsx b/src/app/mobile/components/OfflineContentManager.tsx index cc3a84f1..66abce00 100644 --- a/src/app/mobile/components/OfflineContentManager.tsx +++ b/src/app/mobile/components/OfflineContentManager.tsx @@ -3,6 +3,9 @@ import { Download, Check, Wifi, WifiOff, Trash2, AlertCircle, RefreshCw } from ' import { apiService } from '../services/api'; import { offlineStorage } from '../services/offlineStorage'; import { Course, OfflineContent } from '../types/mobile'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('OfflineContentManager'); interface DownloadProgress { courseId: string; @@ -60,7 +63,7 @@ export default function OfflineContentManager() { setStorageTotal(storage.total); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load data'); - console.error('Error loading offline content:', err); + logger.error('Error loading offline content', { error: err }); } finally { setLoading(false); } @@ -134,7 +137,7 @@ export default function OfflineContentManager() { setDownloads((prev) => prev.filter((d) => d.courseId !== courseId)); }, 3000); } catch (err) { - console.error('Download error:', err); + logger.error('Download error', { error: err }); setDownloads((prev) => prev.map((download) => download.courseId === courseId @@ -168,7 +171,7 @@ export default function OfflineContentManager() { await apiService.deleteDownloadedCourse(courseId); } } catch (err) { - console.error('Delete error:', err); + logger.error('Delete error', { error: err }); setError('Failed to delete course'); } }; diff --git a/src/app/mobile/components/TouchOptimizedControls.tsx b/src/app/mobile/components/TouchOptimizedControls.tsx index 1f7bf121..f6598622 100644 --- a/src/app/mobile/components/TouchOptimizedControls.tsx +++ b/src/app/mobile/components/TouchOptimizedControls.tsx @@ -1,5 +1,8 @@ import { useState, useRef, useEffect } from 'react'; import { Play, Pause, SkipBack, SkipForward, Volume2, VolumeX, Maximize, X } from 'lucide-react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('TouchOptimizedControls'); interface TouchOptimizedControlsProps { videoTitle: string; @@ -56,7 +59,7 @@ export default function TouchOptimizedControls({ } setIsPlaying(!isPlaying); } catch (err) { - console.error('Error playing video:', err); + logger.error('Error playing video', { error: err }); } } resetControlsTimer(); @@ -128,7 +131,7 @@ export default function TouchOptimizedControls({ if (!document.fullscreenElement) { element.requestFullscreen().catch((err) => { - console.error('Error entering fullscreen:', err); + logger.error('Error entering fullscreen', { error: err }); }); } else { document.exitFullscreen(); @@ -172,7 +175,7 @@ export default function TouchOptimizedControls({ const handlePause = () => setIsPlaying(false); const handleEnded = () => setIsPlaying(false); const handleError = () => { - console.error('Video error:', video.error); + logger.error('Video error', { error: video.error }); }; video.addEventListener('loadedmetadata', handleLoadedMetadata); diff --git a/src/app/mobile/services/offlineStorage.ts b/src/app/mobile/services/offlineStorage.ts index 1918301d..24d876a9 100644 --- a/src/app/mobile/services/offlineStorage.ts +++ b/src/app/mobile/services/offlineStorage.ts @@ -1,5 +1,7 @@ // src/services/offlineStorage.ts import { Course, OfflineContent, Lesson } from '../types/mobile'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('offlineStorage'); const DB_NAME = 'LearningAppDB'; const DB_VERSION = 3; @@ -372,7 +374,7 @@ class OfflineStorageService { percentage: (used / total) * 100, }; } catch (error) { - console.error('Failed to get storage estimate:', error); + logger.error('Failed to get storage estimate', { error }); return { used: 0, total: 5000 * 1024 * 1024, percentage: 0 }; } } diff --git a/src/app/onboarding/page.tsx b/src/app/onboarding/page.tsx index 7a3de75c..ee191177 100644 --- a/src/app/onboarding/page.tsx +++ b/src/app/onboarding/page.tsx @@ -25,6 +25,8 @@ import { useNotification } from '@/hooks/use-notification'; import { useAnalytics } from '@/hooks/useAnalytics'; import type { EventProperties } from '@/utils/analytics'; import type { WizardStep, FieldDescriptor, FormState } from '@/form-management/types/core'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('OnboardingPage'); // Define field configuration for onboarding const onboardingFields: FieldDescriptor[] = [ @@ -154,7 +156,7 @@ export default function OnboardingPage() { try { track(name as any, properties); } catch (err) { - console.warn('[Onboarding Analytics] Failed to track event', err); + logger.warn('[Onboarding Analytics] Failed to track event', { error: err }); } }, [track], diff --git a/src/app/qr-code-demo/page.tsx b/src/app/qr-code-demo/page.tsx index 5ce8e5cc..c3560b52 100644 --- a/src/app/qr-code-demo/page.tsx +++ b/src/app/qr-code-demo/page.tsx @@ -5,6 +5,9 @@ import { QRCodeComponent, ShareModal } from '@/components'; import { Download, Printer, Copy, Share2 } from 'lucide-react'; import { downloadQRCode, printQRCode, copyQRCodeToClipboard } from '@/utils/generate-qr'; import toast from 'react-hot-toast'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('QRCodeDemoPage'); /** * QR Code Feature Demo Page @@ -27,7 +30,7 @@ export default function QRCodeDemoPage() { toast.success('QR code downloaded!'); } catch (error) { toast.error('Failed to download QR code'); - console.error(error); + logger.error('Failed to download QR code', { error }); } finally { setIsLoading(false); } @@ -41,7 +44,7 @@ export default function QRCodeDemoPage() { toast.success('Print dialog opened'); } catch (error) { toast.error('Failed to open print dialog'); - console.error(error); + logger.error('Failed to open print dialog', { error }); } finally { setIsLoading(false); } @@ -55,7 +58,7 @@ export default function QRCodeDemoPage() { toast.success('QR code copied to clipboard'); } catch (error) { toast.error('Failed to copy QR code'); - console.error(error); + logger.error('Failed to copy QR code', { error }); } finally { setIsLoading(false); } diff --git a/src/app/tooltip-demo/page.tsx b/src/app/tooltip-demo/page.tsx index ac623e4c..f263ecc3 100644 --- a/src/app/tooltip-demo/page.tsx +++ b/src/app/tooltip-demo/page.tsx @@ -11,6 +11,8 @@ import React, { useState } from 'react'; import { Tooltip, TooltipPlacement } from '@/components/ui/Tooltip'; import { useTooltipAnomalyDetection } from '@/hooks/useTooltipAnomalyDetection'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('TooltipDemoPage'); const PLACEMENTS: TooltipPlacement[] = ['top', 'bottom', 'left', 'right']; @@ -21,7 +23,7 @@ export default function TooltipDemoPage() { rapidToggleWindowMs: 3000, longHoverThresholdMs: 10000, multiOpenThreshold: 3, - onAnomaly: (e) => console.warn('[TooltipAnomaly]', e), + onAnomaly: (e) => logger.warn('[TooltipAnomaly]', { error: e }), }); return ( diff --git a/src/app/web3-demo/page.tsx b/src/app/web3-demo/page.tsx index 4978d993..a5783549 100644 --- a/src/app/web3-demo/page.tsx +++ b/src/app/web3-demo/page.tsx @@ -4,6 +4,9 @@ import React, { useState } from 'react'; import { Wallet, Send, Image as ImageIcon, TrendingUp, ChevronDown, Terminal } from 'lucide-react'; import { WalletConnector, TransactionManager, NFTGallery, DeFiInterface } from '@/components/web3'; import { useWeb3Wallet } from '@/hooks/useWeb3Wallet'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('web3-demo-page'); type DemoTab = 'wallet' | 'transactions' | 'nfts' | 'defi' | 'code'; @@ -107,7 +110,7 @@ export default function Web3DemoPage() { { - console.log('Transaction sent:', txHash); + logger.info('Transaction sent', { context: { txHash } }); }} /> @@ -121,7 +124,7 @@ export default function Web3DemoPage() { { - console.log('Selected NFT:', nft); + logger.info('Selected NFT', { context: { nft } }); }} /> @@ -134,7 +137,7 @@ export default function Web3DemoPage() { { - console.log(`Staked ${amount} in ${protocol} for ${duration} days`); + logger.info(`Staked ${amount} in ${protocol} for ${duration} days`, { context: { protocol, amount, duration } }); }} /> @@ -221,10 +224,10 @@ const result = performSecurityChecks( ); if (!result.isSecure) { - console.error('Warnings:', result.warnings); - console.error('Errors:', result.errors); + logger.error('Security warnings', { context: { warnings: result.warnings } }); + logger.error('Security errors', { context: { errors: result.errors } }); } else { - console.log('Transaction is safe to proceed'); + logger.info('Transaction is safe to proceed'); }`} )} diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx index 7b7d3b74..e8a597cd 100644 --- a/src/components/CommandPalette.tsx +++ b/src/components/CommandPalette.tsx @@ -11,6 +11,8 @@ import { type ShortcutCommand, useKeyboardShortcuts, } from '@/hooks/useKeyboardShortcuts'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('CommandPalette'); function navigateTo(path: string): void { if (typeof window === 'undefined') return; @@ -320,6 +322,10 @@ export function CommandPalette() { setPollModalOpen(false)} + onCreate={(draft: PollDraft) => { + // TODO: integrate with poll creation backend/GraphQL. + // For now, keep placeholder to satisfy typing and modal behavior. + logger.info('Create poll draft', { context: { draft } }); onCreate={async (draft: PollDraft) => { if (isSubmittingPoll) return; setIsSubmittingPoll(true); diff --git a/src/components/ShareModal.tsx b/src/components/ShareModal.tsx index 6c2b8d83..bf8722cf 100644 --- a/src/components/ShareModal.tsx +++ b/src/components/ShareModal.tsx @@ -6,6 +6,9 @@ import { Modal } from '@/components/ui/Modal'; import QRCodeComponent from '@/components/QRCode'; import { downloadQRCode, printQRCode, copyQRCodeToClipboard } from '@/utils/generate-qr'; import toast from 'react-hot-toast'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('share-modal'); export interface ShareModalProps { isOpen: boolean; @@ -68,7 +71,7 @@ export function ShareModal({ toast.success('QR code downloaded successfully'); } catch (error) { toast.error('Failed to download QR code'); - console.error(error); + logger.error('Failed to download QR code', { error }); } finally { setIsLoading(false); } @@ -83,7 +86,7 @@ export function ShareModal({ toast.success('Print dialog opened'); } catch (error) { toast.error('Failed to open print dialog'); - console.error(error); + logger.error('Failed to open print dialog', { error }); } finally { setIsLoading(false); } @@ -98,7 +101,7 @@ export function ShareModal({ toast.success('QR code copied to clipboard'); } catch (error) { toast.error('Failed to copy QR code'); - console.error(error); + logger.error('Failed to copy QR code', { error }); } finally { setIsLoading(false); } @@ -110,7 +113,7 @@ export function ShareModal({ toast.success('URL copied to clipboard'); } catch (error) { toast.error('Failed to copy URL'); - console.error(error); + logger.error('Failed to copy URL', { error }); } }, [shareUrl]); diff --git a/src/components/collaboration/VideoConference.tsx b/src/components/collaboration/VideoConference.tsx index a66abf6d..4ef2bc9f 100644 --- a/src/components/collaboration/VideoConference.tsx +++ b/src/components/collaboration/VideoConference.tsx @@ -17,6 +17,9 @@ import { useFraudDetection } from '../../hooks/useFraudDetection'; import { useVirtualBackground } from '../../hooks/useVirtualBackground'; import { fraudDetectionService, FraudDetectionService } from '../../services/fraud-detection'; import type { FraudDetectionResult } from '../../services/fraud-detection'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('VideoConference'); interface VideoConferenceProps { roomId: string; @@ -131,7 +134,7 @@ export function VideoConference({ try { await pcRef.current.addIceCandidate(new RTCIceCandidate(candidate)); } catch (error) { - console.error('Failed to add ICE candidate', error); + logger.error('Failed to add ICE candidate', { error }); } }); @@ -208,7 +211,7 @@ export function VideoConference({ return processedStream; } catch (error) { setStatus('Unable to access camera or microphone'); - console.error(error); + logger.error('Error starting local stream', { error }); return null; } }; @@ -325,7 +328,7 @@ export function VideoConference({ }; } catch (error) { setStatus('Screen sharing canceled or unavailable'); - console.error(error); + logger.error('Screen sharing error', { error }); } }; diff --git a/src/components/forms/AdvancedValidation.tsx b/src/components/forms/AdvancedValidation.tsx index 71e9185d..aa7db470 100644 --- a/src/components/forms/AdvancedValidation.tsx +++ b/src/components/forms/AdvancedValidation.tsx @@ -9,6 +9,8 @@ import React, { useState, useEffect } from 'react'; import { Loader2, XCircle, AlertTriangle, CheckCircle2 } from 'lucide-react'; import { ValidationEngineImpl } from '@/form-management/validation/validation-engine'; import { ValidationResult, ValidationRule, FormState } from '@/form-management/types/core'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('AdvancedValidation'); interface AdvancedValidationProps { fieldId: string; @@ -58,7 +60,7 @@ export const AdvancedValidation: React.FC = ({ onValidationComplete(result); } } catch (error) { - console.error('Validation error:', error); + logger.error('Validation error', { error }); } finally { setIsValidating(false); } diff --git a/src/components/forms/AutoSaveManager.tsx b/src/components/forms/AutoSaveManager.tsx index 664e635e..d7856d12 100644 --- a/src/components/forms/AutoSaveManager.tsx +++ b/src/components/forms/AutoSaveManager.tsx @@ -9,6 +9,9 @@ import React, { useState, useEffect, useRef } from 'react'; import { AutoSaveManagerImpl } from '@/form-management/auto-save/auto-save-manager'; import { FormState, SaveStatus } from '@/form-management/types/core'; import { useNotification } from '@/hooks/use-notification'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('AutoSaveManager'); interface AutoSaveManagerProps { formId: string; @@ -91,7 +94,7 @@ export const AutoSaveManager: React.FC = ({ try { await autoSaveManager.saveNow(formId, formState); } catch (error) { - console.error('Auto-save failed:', error); + logger.error('Auto-save failed', { error }); } finally { isSavingRef.current = false; } @@ -105,7 +108,7 @@ export const AutoSaveManager: React.FC = ({ try { await autoSaveManager.saveNow(formId, formState); } catch (error) { - console.error('Manual save failed:', error); + logger.error('Manual save failed', { error }); } }; @@ -114,7 +117,7 @@ export const AutoSaveManager: React.FC = ({ await autoSaveManager.clearDraft(formId); setLastSavedTime(''); } catch (error) { - console.error('Clear draft failed:', error); + logger.error('Clear draft failed', { error }); } }; diff --git a/src/components/forms/DynamicFormBuilder.tsx b/src/components/forms/DynamicFormBuilder.tsx index 3782298c..9f5cb32c 100644 --- a/src/components/forms/DynamicFormBuilder.tsx +++ b/src/components/forms/DynamicFormBuilder.tsx @@ -14,6 +14,9 @@ import { AutoSaveManagerImpl } from '@/form-management/auto-save/auto-save-manag import { useNotification } from '@/hooks/use-notification'; import { useMutation } from '@/hooks/useMutation'; import { SubmitButton } from '@/components/forms/SubmitButton'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('DynamicFormBuilder'); interface DynamicFormBuilderProps { config: FormConfiguration | string; @@ -65,7 +68,7 @@ export const DynamicFormBuilder: React.FC = ({ const validation = parser.validate(parsedConfig); if (!validation.isValid) { - console.error('Invalid form configuration:', validation.errors); + logger.error('Invalid form configuration', { context: { errors: validation.errors } }); return; } @@ -74,7 +77,7 @@ export const DynamicFormBuilder: React.FC = ({ // Initialize dependencies stateManager.initializeDependencies(parsedConfig.fields, parsedConfig.conditionalLogic || []); } catch (error) { - console.error('Error parsing form configuration:', error); + logger.error('Error parsing form configuration', { error }); } }, [config, stateManager]); diff --git a/src/components/forms/FormWizardController.tsx b/src/components/forms/FormWizardController.tsx index e0d236b5..7cb02969 100644 --- a/src/components/forms/FormWizardController.tsx +++ b/src/components/forms/FormWizardController.tsx @@ -17,6 +17,9 @@ import { ValidationEngineImpl } from '@/form-management/validation/validation-en import { useNotification } from '@/hooks/use-notification'; import { useMutation } from '@/hooks/useMutation'; import { SubmitButton } from '@/components/forms/SubmitButton'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('FormWizardController'); interface FormWizardControllerProps { steps: WizardStep[]; @@ -127,7 +130,7 @@ export const FormWizardController: React.FC = ({ const isValid = await validateCurrentStep(); if (!isValid) { - console.warn('Current step validation failed'); + logger.warn('Current step validation failed'); return; } @@ -153,13 +156,13 @@ export const FormWizardController: React.FC = ({ const handleGoToStep = async (stepIndex: number) => { if (!allowNonLinearNavigation) { if (!completedSteps.includes(stepIndex) && stepIndex !== currentStepIndex) { - console.warn('Cannot navigate to incomplete step'); + logger.warn('Cannot navigate to incomplete step'); return; } } if (stepIndex < 0 || stepIndex >= steps.length) { - console.warn('Invalid step index'); + logger.warn('Invalid step index'); return; } diff --git a/src/components/i18n/InternationalizationEngine.tsx b/src/components/i18n/InternationalizationEngine.tsx index bb6ae40d..eb162ab1 100644 --- a/src/components/i18n/InternationalizationEngine.tsx +++ b/src/components/i18n/InternationalizationEngine.tsx @@ -9,6 +9,8 @@ import { useInternationalization } from '@/hooks/useInternationalization'; import { preloadTranslations } from '@/locales/translationManager'; import { getAvailableLanguages } from '@/locales/config'; import type { LanguageCode } from '@/locales/types'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('InternationalizationEngine'); interface InternationalizationEngineProps { /** @@ -61,7 +63,7 @@ export function InternationalizationEngine({ setPreloadStatus(status); } } catch (err) { - console.warn('Failed to preload some translations:', err); + logger.warn('Failed to preload some translations', { error: err }); } } diff --git a/src/components/i18n/LocalizationTester.tsx b/src/components/i18n/LocalizationTester.tsx index d287a1cc..a26f8309 100644 --- a/src/components/i18n/LocalizationTester.tsx +++ b/src/components/i18n/LocalizationTester.tsx @@ -10,6 +10,9 @@ import { getAvailableLanguages } from '@/locales/config'; import { hasTranslation } from '@/locales/translationManager'; import { AlertCircle, CheckCircle2, XCircle, Search, Download } from 'lucide-react'; import type { LanguageCode, Translations } from '@/locales/types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('LocalizationTester'); interface LocalizationTesterProps { /** @@ -108,13 +111,13 @@ export function LocalizationTester({ }); } } catch (error) { - console.error(`Failed to validate ${lang}:`, error); + logger.error(`Failed to validate ${lang}`, { error }); } } setIssues(newIssues); } catch (error) { - console.error('Validation failed:', error); + logger.error('Validation failed', { error }); } finally { setIsValidating(false); } diff --git a/src/components/layout/HeaderComponent.tsx b/src/components/layout/HeaderComponent.tsx index 7a7d855b..b9bdea10 100644 --- a/src/components/layout/HeaderComponent.tsx +++ b/src/components/layout/HeaderComponent.tsx @@ -5,6 +5,9 @@ import Link from 'next/link'; import { toast } from 'sonner'; import { useWallet } from '@/hooks/useWallet'; import { IGrantContext } from '@/types/grants'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('HeaderComponent'); export default function HeaderComponent() { const { connected, publicKey, connect } = useWallet(); @@ -51,7 +54,7 @@ export default function HeaderComponent() { }); } } catch (err) { - console.error('Failed to synchronize active authorization grants:', err); + logger.error('Failed to synchronize active authorization grants', { error: err }); if (isMounted) { setGrantsState((prev) => ({ ...prev, loading: false })); } diff --git a/src/components/shared/ImageUploader.tsx b/src/components/shared/ImageUploader.tsx index 48629ada..83ef78f9 100644 --- a/src/components/shared/ImageUploader.tsx +++ b/src/components/shared/ImageUploader.tsx @@ -4,6 +4,9 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react'; import type { ChangeEvent } from 'react'; import Image from 'next/image'; import { dataWarehouse } from '@/lib/dataWarehouse'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('image-uploader'); interface ImageUploaderProps { onImageSelect: (file: File) => void; @@ -140,7 +143,7 @@ function ImageUploader({ onImageSelect, initialImageUrl, className = '' }: Image fileSize: optimizedFile.size, fileType: optimizedFile.type, }) - .catch(console.error); + .catch((error) => logger.error('Failed to track image upload event', { error })); onImageSelect(optimizedFile); } @@ -151,7 +154,7 @@ function ImageUploader({ onImageSelect, initialImageUrl, className = '' }: Image } URL.revokeObjectURL(videoObjectUrl); } catch (error) { - console.error('Video optimization failed:', error); + logger.error('Video optimization failed', { error }); } } else if (file.type.startsWith('image/')) { const objectUrl = URL.createObjectURL(file); @@ -164,7 +167,7 @@ function ImageUploader({ onImageSelect, initialImageUrl, className = '' }: Image fileSize: file.size, fileType: file.type, }) - .catch(console.error); + .catch((error) => logger.error('Failed to track image upload event', { error })); onImageSelect(file); } diff --git a/src/components/shared/ReleaseNotes.tsx b/src/components/shared/ReleaseNotes.tsx index a0f47c48..706472a4 100644 --- a/src/components/shared/ReleaseNotes.tsx +++ b/src/components/shared/ReleaseNotes.tsx @@ -1,6 +1,8 @@ 'use client'; import React, { useState, useEffect } from 'react'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('ReleaseNotes'); export interface ReleaseNote { version: string; @@ -34,7 +36,7 @@ export const ReleaseNotes: React.FC = () => { await new Promise((resolve) => setTimeout(resolve, 500)); setNotes(data); } catch (error) { - console.error('Failed to fetch release notes:', error); + logger.error('Failed to fetch release notes', { error }); } finally { setLoading(false); } diff --git a/src/components/ui/theme-toggle.tsx b/src/components/ui/theme-toggle.tsx index 262abca3..92dc9105 100644 --- a/src/components/ui/theme-toggle.tsx +++ b/src/components/ui/theme-toggle.tsx @@ -5,6 +5,8 @@ import { useTheme } from '@/lib/theme-provider'; import { useSettingsStore } from '@/lib/settings/store'; import { Moon, Sun } from 'lucide-react'; import { useEffect, useState } from 'react'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('ThemeToggle'); function ThemeToggleFallback() { return ( @@ -50,7 +52,7 @@ function ThemeToggleControl() { setTheme(next); patchSettings({ theme: next }); } catch (err) { - console.error('ThemeToggle error:', err); + logger.error('ThemeToggle error', { error: err }); } }; diff --git a/src/components/virtualizedcourselist.tsx b/src/components/virtualizedcourselist.tsx index b22c3929..c858ac51 100644 --- a/src/components/virtualizedcourselist.tsx +++ b/src/components/virtualizedcourselist.tsx @@ -9,6 +9,9 @@ import React, { useCallback, ReactNode, } from 'react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('NotificationSocket'); export type NotificationType = | 'info' @@ -65,7 +68,7 @@ class NotificationSocketService { this.ws = new WebSocket(this.url); this.ws.onopen = () => { - console.info('[NotificationSocket] Connected'); + logger.info('[NotificationSocket] Connected'); this.reconnectDelay = 1000; // reset back-off }; @@ -78,7 +81,7 @@ class NotificationSocketService { this.listeners.forEach((cb) => cb(notification)); } } catch { - console.warn('[NotificationSocket] Failed to parse message', event.data); + logger.warn('[NotificationSocket] Failed to parse message', event.data); } }; @@ -90,18 +93,18 @@ class NotificationSocketService { }; this.ws.onerror = (err) => { - console.error('[NotificationSocket] Error', err); + logger.error('[NotificationSocket] Error', { error: err }); this.ws?.close(); }; } catch (err) { - console.error('[NotificationSocket] Failed to open', err); + logger.error('[NotificationSocket] Failed to open', { error: err }); this.scheduleReconnect(); } } private scheduleReconnect() { this.reconnectTimer = setTimeout(() => { - console.info(`[NotificationSocket] Reconnecting…`); + logger.info(`[NotificationSocket] Reconnecting…`); this.open(); this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay); }, this.reconnectDelay); diff --git a/src/components/web3/DeFiInterface.tsx b/src/components/web3/DeFiInterface.tsx index 196d60c0..a5e2fc3c 100644 --- a/src/components/web3/DeFiInterface.tsx +++ b/src/components/web3/DeFiInterface.tsx @@ -5,6 +5,9 @@ import { TrendingUp, Lock, Loader2, ArrowUpRight, Zap, Info, Check } from 'lucid import { useWeb3Wallet } from '@/hooks/useWeb3Wallet'; import { InvestmentSearchBar, InvestmentItem } from './InvestmentSearchBar'; import { walletCache, walletCacheKeys, CACHE_TTL } from '@/utils/web3/walletCache'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('DeFiInterface'); interface StakingPosition { id: string; @@ -117,7 +120,7 @@ export const DeFiInterface: React.FC = ({ walletCache.set(cacheKey, positions, CACHE_TTL.DEFI_POSITIONS); setStakingPositions(positions); } catch (error) { - console.error('[DeFiInterface] Error fetching positions:', error); + logger.error('Error fetching positions', { error }); } finally { setIsLoading(false); } @@ -160,7 +163,7 @@ export const DeFiInterface: React.FC = ({ setSelectedProtocol(null); setActiveTab('positions'); } catch (error) { - console.error('[DeFiInterface] Staking failed:', error); + logger.error('Staking failed', { error }); } finally { setIsStaking(false); } @@ -183,7 +186,7 @@ export const DeFiInterface: React.FC = ({ }); onUnstake?.(positionId); } catch (error) { - console.error('[DeFiInterface] Unstaking failed:', error); + logger.error('Unstaking failed', { error }); } }, [onUnstake, wallet.address], diff --git a/src/components/web3/NFTGallery.tsx b/src/components/web3/NFTGallery.tsx index dd219807..8e61c885 100644 --- a/src/components/web3/NFTGallery.tsx +++ b/src/components/web3/NFTGallery.tsx @@ -14,6 +14,8 @@ import { } from 'lucide-react'; import { useWeb3Wallet } from '@/hooks/useWeb3Wallet'; import { walletCache, walletCacheKeys, CACHE_TTL } from '@/utils/web3/walletCache'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('NFTGallery'); interface NFT { id: string; @@ -147,7 +149,7 @@ export const NFTGallery: React.FC = ({ } catch (err) { const message = err instanceof Error ? err.message : 'Failed to fetch NFTs'; setError(message); - console.error('[NFTGallery] Error fetching NFTs:', err); + logger.error('[NFTGallery] Error fetching NFTs', { error: err }); } finally { setIsLoading(false); } diff --git a/src/components/web3/TransactionManager.tsx b/src/components/web3/TransactionManager.tsx index e5ea3fb2..3b628c44 100644 --- a/src/components/web3/TransactionManager.tsx +++ b/src/components/web3/TransactionManager.tsx @@ -12,6 +12,8 @@ import { EyeOff, } from 'lucide-react'; import { useWeb3Wallet, type TransactionDetails } from '@/hooks/useWeb3Wallet'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('TransactionManager'); interface TransactionManagerProps { className?: string; @@ -64,7 +66,7 @@ export const TransactionManager: React.FC = ({ try { setTxHistory(JSON.parse(saved)); } catch (error) { - console.error('[TransactionManager] Failed to load history:', error); + logger.error('[TransactionManager] Failed to load history', { error }); setTxHistory([]); } } else { diff --git a/src/context/OfflineModeContext.tsx b/src/context/OfflineModeContext.tsx index f5674d36..589e66ac 100644 --- a/src/context/OfflineModeContext.tsx +++ b/src/context/OfflineModeContext.tsx @@ -16,6 +16,9 @@ import { SyncConflict, SyncResult, } from '../services/offlineSync'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('offline-mode-context'); interface OfflineModeContextType { isOnline: boolean; @@ -137,7 +140,7 @@ export const OfflineModeProvider: React.FC = ({ childr await refreshStorageUsage(); await refreshSyncStatus(); } catch (error) { - console.error('Failed to enable offline mode:', error); + logger.error('Failed to enable offline mode', { error }); setSyncStatus('error'); } }, [initializeOfflineMode, refreshStorageUsage, refreshSyncStatus]); @@ -152,7 +155,7 @@ export const OfflineModeProvider: React.FC = ({ childr setPendingConflicts([]); setStorageUsage({ used: 0, total: 0, percentage: 0 }); } catch (error) { - console.error('Failed to disable offline mode:', error); + logger.error('Failed to disable offline mode', { error }); } }, [cleanupOfflineMode]); @@ -176,7 +179,7 @@ export const OfflineModeProvider: React.FC = ({ childr await refreshSyncStatus(); return result; } catch (error) { - console.error('Failed to sync offline data:', error); + logger.error('Failed to sync offline data', { error }); setSyncStatus('error'); return { success: false, @@ -204,7 +207,7 @@ export const OfflineModeProvider: React.FC = ({ childr setPendingConflicts([]); setStorageUsage({ used: 0, total: 0, percentage: 0 }); } catch (error) { - console.error('Failed to clear offline data:', error); + logger.error('Failed to clear offline data', { error }); } }, [cleanupOfflineMode, initializeOfflineMode]); diff --git a/src/context/ToastContext.tsx b/src/context/ToastContext.tsx index b6e79dde..639552d1 100644 --- a/src/context/ToastContext.tsx +++ b/src/context/ToastContext.tsx @@ -16,6 +16,9 @@ import { type CircuitBreakerConfig, type CircuitBreakerMetrics, } from '@/utils/circuitBreaker'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('ToastContext'); export interface ToastMessage { id: string; @@ -77,9 +80,8 @@ export function ToastProvider({ }, () => { // Fallback: Log to console when circuit is open - console.warn('[Toast Circuit Breaker] Toast notification suppressed:', { - type, - message, + logger.warn('[Toast Circuit Breaker] Toast notification suppressed', { + context: { type, message }, }); // Optionally show a simplified fallback toast const id = Math.random().toString(36).substring(2, 11); @@ -100,7 +102,7 @@ export function ToastProvider({ }, ) .catch((error: unknown) => { - console.error('[Toast Circuit Breaker] Error:', error); + logger.error('[Toast Circuit Breaker] Error', { error }); }); }, [removeToast, circuitBreaker], diff --git a/src/form-management/auto-save/auto-save-manager.ts b/src/form-management/auto-save/auto-save-manager.ts index eb6c36af..b4985441 100644 --- a/src/form-management/auto-save/auto-save-manager.ts +++ b/src/form-management/auto-save/auto-save-manager.ts @@ -11,6 +11,9 @@ import { Subscription, DraftData, } from '../types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('auto-save-manager'); interface SaveQueueItem { formId: string; @@ -174,14 +177,14 @@ export class AutoSaveManagerImpl implements AutoSaveManager { // Validate data integrity if (!this.validateDraftIntegrity(draftData)) { - console.warn(`Draft data integrity check failed for form ${formId}`); + logger.warn(`Draft data integrity check failed for form ${formId}`); return null; } this.lastKnownUpdatedAt.set(formId, new Date(draftData.updatedAt)); return draftData.data; } catch (error) { - console.error('Error loading draft:', error); + logger.error('Error loading draft', { error }); return null; } } @@ -196,7 +199,7 @@ export class AutoSaveManagerImpl implements AutoSaveManager { this.disableAutoSave(formId); this.saveStatus.delete(formId); } catch (error) { - console.error('Error clearing draft:', error); + logger.error('Error clearing draft', { error }); throw error; } } @@ -261,7 +264,7 @@ export class AutoSaveManagerImpl implements AutoSaveManager { for (const item of queue) { if (item.retryCount >= this.maxRetries) { - console.warn(`Max retries reached for form ${item.formId}`); + logger.warn(`Max retries reached for form ${item.formId}`); continue; } diff --git a/src/form-management/examples/auto-save-example.ts b/src/form-management/examples/auto-save-example.ts index eee23aac..840e891e 100644 --- a/src/form-management/examples/auto-save-example.ts +++ b/src/form-management/examples/auto-save-example.ts @@ -7,6 +7,8 @@ import { AutoSaveManagerImpl } from '../auto-save/auto-save-manager.js'; import { FormState } from '../types/core.js'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('AutoSaveExample'); // Example: Basic Auto-Save Setup export function basicAutoSaveExample() { @@ -23,7 +25,7 @@ export function basicAutoSaveExample() { if (status.lastSaved) { } if (status.error) { - console.error('Save error:', status.error.message); + logger.error('Save error', { error: status.error }); } }); @@ -63,7 +65,7 @@ export async function manualSaveExample() { try { await autoSaveManager.saveNow(formId, formState); } catch (error) { - console.error('Failed to save form data:', error); + logger.error('Failed to save form data', { error }); } return autoSaveManager; diff --git a/src/form-management/state/dependency-manager.ts b/src/form-management/state/dependency-manager.ts index 0ee7cc56..52b96a72 100644 --- a/src/form-management/state/dependency-manager.ts +++ b/src/form-management/state/dependency-manager.ts @@ -10,6 +10,9 @@ import { FieldDescriptor, ValidationResult, } from '../types/core'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('dependency-manager'); export interface DependencyGraph { [fieldId: string]: { @@ -208,7 +211,7 @@ export class DependencyManager { try { return condition(state); } catch (error) { - console.error('Error evaluating conditional rule:', error); + logger.error('Error evaluating conditional rule', { error }); return false; } } diff --git a/src/form-management/state/form-state-manager.ts b/src/form-management/state/form-state-manager.ts index fc360294..97a68cc6 100644 --- a/src/form-management/state/form-state-manager.ts +++ b/src/form-management/state/form-state-manager.ts @@ -15,6 +15,8 @@ import { } from '../types/core'; import type { FormStateManager as IFormStateManager } from '../types/interfaces'; import { DependencyManager, CascadeUpdateResult } from './dependency-manager'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('FormStateManager'); export class FormStateManager implements IFormStateManager { private state: FormState; @@ -163,7 +165,7 @@ export class FormStateManager implements IFormStateManager { try { callback(event); } catch (error) { - console.error('Error in state change callback:', error); + logger.error('Error in state change callback', { error }); } }); } @@ -525,7 +527,7 @@ export class FormStateManager implements IFormStateManager { // Validate dependencies const validation = this.dependencyManager.validateDependencies(fields); if (!validation.isValid) { - console.warn('Dependency validation errors:', validation.errors); + logger.warn('Dependency validation errors', { context: { errors: validation.errors } }); } } diff --git a/src/form-management/validation/async-validation-manager.ts b/src/form-management/validation/async-validation-manager.ts index 19b278d9..79b6d798 100644 --- a/src/form-management/validation/async-validation-manager.ts +++ b/src/form-management/validation/async-validation-manager.ts @@ -3,6 +3,9 @@ */ import { ValidationResult, FormState, ValidationFunction } from '../types/core.js'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('async-validation-manager'); export interface AsyncValidationState { isLoading: boolean; @@ -361,7 +364,7 @@ export class AsyncValidationManager { try { callback(response); } catch (error) { - console.error('Error in async validation callback:', error); + logger.error('Error in async validation callback', { error }); } }); } diff --git a/src/form-management/validation/image-optimization-task-manager.ts b/src/form-management/validation/image-optimization-task-manager.ts index d3a6646d..14e1ffe0 100644 --- a/src/form-management/validation/image-optimization-task-manager.ts +++ b/src/form-management/validation/image-optimization-task-manager.ts @@ -1,4 +1,7 @@ import { optimizeImage, ImageOptimizationOptions } from './image-optimizer'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('image-optimization-task-manager'); export interface ImageOptimizationTask { id: string; @@ -301,7 +304,7 @@ export class ImageOptimizationTaskManager { try { cb(task, state); } catch (err) { - console.error('Error in task manager change callback:', err); + logger.error('Error in task manager change callback', { error: err }); } }); } diff --git a/src/form-management/validation/validation-feedback-display.ts b/src/form-management/validation/validation-feedback-display.ts index 9263d652..e5c3cf25 100644 --- a/src/form-management/validation/validation-feedback-display.ts +++ b/src/form-management/validation/validation-feedback-display.ts @@ -3,6 +3,9 @@ */ import { ValidationResult, ValidationError, ValidationWarning } from '../types/core.js'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('validation-feedback-display'); export interface FeedbackDisplayOptions { position: 'top' | 'bottom' | 'left' | 'right' | 'inline' | 'tooltip'; @@ -484,7 +487,7 @@ export class ValidationFeedbackDisplay { try { callback(state); } catch (error) { - console.error('Error in feedback display callback:', error); + logger.error('Error in feedback display callback', { error }); } }); } diff --git a/src/hooks/useAdvancedForms.tsx b/src/hooks/useAdvancedForms.tsx index e5dc9842..a0e4803f 100644 --- a/src/hooks/useAdvancedForms.tsx +++ b/src/hooks/useAdvancedForms.tsx @@ -13,6 +13,9 @@ import { ValidationResult, SaveStatus, } from '@/form-management/types/core'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-advanced-forms'); interface UseAdvancedFormsOptions { formId: string; @@ -273,7 +276,7 @@ export const useAdvancedForms = (options: UseAdvancedFormsOptions): UseAdvancedF const isValid = await validateForm(); if (!isValid) { - console.warn('Form validation failed'); + logger.warn('Form validation failed'); return; } @@ -288,7 +291,7 @@ export const useAdvancedForms = (options: UseAdvancedFormsOptions): UseAdvancedF stateManager.completeSubmission(true); } catch (error) { - console.error('Form submission error:', error); + logger.error('Form submission error', { error }); stateManager.completeSubmission(false); throw error; } finally { diff --git a/src/hooks/useAdvancedSearch.tsx b/src/hooks/useAdvancedSearch.tsx index b57efc63..e63da3e2 100644 --- a/src/hooks/useAdvancedSearch.tsx +++ b/src/hooks/useAdvancedSearch.tsx @@ -10,6 +10,9 @@ import { getSearchSuggestions, parseAdvancedQuery, } from '../utils/searchUtils'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-advanced-search'); const DEFAULT_FILTERS: SearchFilters = { types: ['all'], @@ -49,7 +52,7 @@ export const useAdvancedSearch = () => { ) as string[]; setHistory(storedHistory); } catch (e) { - console.error('Failed to parse search history', e); + logger.error('Failed to parse search history', { error: e }); } finally { hasLoadedHistory.current = true; } @@ -62,7 +65,7 @@ export const useAdvancedSearch = () => { try { localStorage.setItem('search_history_terms', JSON.stringify(history)); } catch (e) { - console.error('Failed to save search history', e); + logger.error('Failed to save search history', { error: e }); } } }, [history]); @@ -158,7 +161,7 @@ export const useAdvancedSearch = () => { setResults(mockResults); addToHistory(query.text); } catch (error) { - console.error('Search error:', error); + logger.error('Search error', { error }); } finally { if (currentRequestId === activeRequestRef.current) { setIsSearching(false); diff --git a/src/hooks/useAnalyticsErrorTracking.tsx b/src/hooks/useAnalyticsErrorTracking.tsx index 86c8074b..5bfbec41 100644 --- a/src/hooks/useAnalyticsErrorTracking.tsx +++ b/src/hooks/useAnalyticsErrorTracking.tsx @@ -6,6 +6,9 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { v4 as uuidv4 } from 'uuid'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-analytics-error-tracking'); // Error types for analytics-specific errors export type AnalyticsErrorType = @@ -129,7 +132,7 @@ export const useAnalyticsErrorTracking = ( }); } catch (reportingErr) { // Fail silently - don't create an infinite loop of error reporting - console.warn('Failed to report error to server:', reportingErr); + logger.warn('Failed to report error to server', { error: reportingErr }); } }, []); @@ -162,7 +165,7 @@ export const useAnalyticsErrorTracking = ( // Log to console in development for debugging if (process.env.NODE_ENV === 'development') { - console.error(`[Analytics Error] ${type}: ${message}`, { + logger.error(`[Analytics Error] ${type}: ${message}`, { error, context: newError.context, }); diff --git a/src/hooks/useErrorHandling.tsx b/src/hooks/useErrorHandling.tsx index 3b8a8bda..08946a83 100644 --- a/src/hooks/useErrorHandling.tsx +++ b/src/hooks/useErrorHandling.tsx @@ -14,6 +14,9 @@ import { retryWithBackoff, } from '@/utils/errorUtils'; import { useToast } from '@/context/ToastContext'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-error-handling'); export interface UseErrorHandlingState { error: any | null; @@ -98,7 +101,9 @@ export function useErrorHandling(options: UseErrorHandlingOptions = {}) { })); if (reportErrors) { - errorReportingService.reportError(error).catch(console.error); + errorReportingService.reportError(error).catch((err) => { + logger.error('Failed to report error', { error: err }); + }); } // Show toast notification for errors diff --git a/src/hooks/useInternationalization.tsx b/src/hooks/useInternationalization.tsx index 79605fe0..a7449297 100644 --- a/src/hooks/useInternationalization.tsx +++ b/src/hooks/useInternationalization.tsx @@ -26,6 +26,9 @@ import { formatDuration, } from '@/utils/i18nUtils'; import i18n, { loadLocale } from '@/lib/i18n/config'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-internationalization'); interface I18nContextValue { language: LanguageCode; @@ -95,9 +98,9 @@ export function I18nProvider({ if (missing.length > 0) { // Limit output to first 50 keys to avoid flooding logs const sample = missing.slice(0, 50); - console.warn( + logger.warn( `Translations for '${language}' missing ${missing.length} keys. Sample:`, - sample, + { sample }, ); } } diff --git a/src/hooks/useOfflineSync.ts b/src/hooks/useOfflineSync.ts index a8693e99..0618c98e 100644 --- a/src/hooks/useOfflineSync.ts +++ b/src/hooks/useOfflineSync.ts @@ -1,6 +1,9 @@ 'use client'; import { useState, useEffect, useCallback } from 'react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-offline-sync'); /** * Hook to manage offline state and handle background/foreground syncing when connectivity returns @@ -34,7 +37,7 @@ export function useOfflineSync(syncCallback?: () => Promise) { setLastSynced(new Date()); } catch (error) { - console.error('Offline synchronization failed:', error); + logger.error('Offline synchronization failed', { error }); } finally { setIsSyncing(false); } diff --git a/src/hooks/useOptimisticUpdates.tsx b/src/hooks/useOptimisticUpdates.tsx index 83dd2596..a74fce15 100644 --- a/src/hooks/useOptimisticUpdates.tsx +++ b/src/hooks/useOptimisticUpdates.tsx @@ -1,4 +1,7 @@ import { useState, useCallback } from 'react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-optimistic-updates'); /** * Hook for handling optimistic UI updates with rollback support. @@ -22,7 +25,7 @@ export const useOptimisticUpdates = () => { try { updateFn(); } catch (e) { - console.error('[OptimisticUpdate] Local update failed:', e); + logger.error('[OptimisticUpdate] Local update failed', { error: e }); setIsUpdating(false); return; } @@ -34,7 +37,7 @@ export const useOptimisticUpdates = () => { return result; } catch (e) { // 3. Rollback on failure - console.error('[OptimisticUpdate] API call failed, rolling back:', e); + logger.error('[OptimisticUpdate] API call failed, rolling back', { error: e }); setError(e instanceof Error ? e : new Error(String(e))); rollbackFn(); setIsUpdating(false); diff --git a/src/hooks/usePerformanceMonitoring.tsx b/src/hooks/usePerformanceMonitoring.tsx index 446f4dad..f2b936bc 100644 --- a/src/hooks/usePerformanceMonitoring.tsx +++ b/src/hooks/usePerformanceMonitoring.tsx @@ -21,6 +21,9 @@ import { type PerformanceTrendPoint, subscribeCoreWebVitals, } from '@/utils/performanceUtils'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-performance-monitoring'); export interface UsePerformanceMonitoringOptions { enableTrendRecording?: boolean; @@ -92,7 +95,7 @@ function usePerformanceMonitoringState( if (!d.has(toastKey)) { d.add(toastKey); setAlerts((prev) => [...prev.slice(-49), alert]); - console.warn(`[Performance] ${alert.message}`); + logger.warn(`[Performance] ${alert.message}`); if (enableToasts) { const toastOpts = { id: toastKey, duration: 6000 }; if (metric.rating === 'poor') { diff --git a/src/hooks/usePerformanceOptimization.tsx b/src/hooks/usePerformanceOptimization.tsx index 089c2afc..4dc81185 100644 --- a/src/hooks/usePerformanceOptimization.tsx +++ b/src/hooks/usePerformanceOptimization.tsx @@ -1,6 +1,9 @@ 'use client'; import { useEffect, useRef } from 'react'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-performance-optimization'); interface UsePerformanceOptions { componentName: string; @@ -21,7 +24,7 @@ export const usePerformanceOptimization = ({ const duration = endTime - startTime.current; if (duration > threshold) { - console.warn( + logger.warn( `[Performance Warning] Component "${componentName}" took ${duration.toFixed( 2, )}ms to mount. Threshold is ${threshold}ms.`, @@ -39,7 +42,7 @@ export const usePerformanceOptimization = ({ const end = performance.now(); const duration = end - start; if (duration > threshold) { - console.warn( + logger.warn( `[Performance Warning] Interaction "${actionName}" in "${componentName}" took ${duration.toFixed( 2, )}ms.`, diff --git a/src/hooks/useSubscription.ts b/src/hooks/useSubscription.ts index 7b97ab58..b7a2682a 100644 --- a/src/hooks/useSubscription.ts +++ b/src/hooks/useSubscription.ts @@ -8,6 +8,9 @@ import { isConnectionError, formatSubscriptionError, } from '@/lib/graphql/subscriptions'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-subscription'); export { ConnectionState, getConnectionManager, isConnectionError, formatSubscriptionError }; @@ -334,7 +337,7 @@ export function usePollableSubscription s.settings); @@ -48,7 +51,7 @@ export function useVirtualBackground() { const errorMessage = err instanceof Error ? err.message : 'Failed to apply virtual background'; setError(errorMessage); - console.error('Virtual background error:', err); + logger.error('Virtual background error', { error: err }); return stream; } finally { setIsProcessing(false); diff --git a/src/hooks/useWeb3Wallet.ts b/src/hooks/useWeb3Wallet.ts index 8d63341f..0be27d77 100644 --- a/src/hooks/useWeb3Wallet.ts +++ b/src/hooks/useWeb3Wallet.ts @@ -6,6 +6,9 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { validateWalletInteraction, safeWalletCall } from '@/utils/web3/walletValidation'; import { walletCache, walletCacheKeys, CACHE_TTL } from '@/utils/web3/walletCache'; import { walletConnectionQueue } from '@/utils/web3/walletQueue'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('use-web3-wallet'); /** * Supported wallet providers @@ -313,7 +316,7 @@ export function useWeb3Wallet() { walletCache.set(cacheKey, balances, CACHE_TTL.BALANCE); setState((prev) => ({ ...prev, balances })); } catch (error) { - console.warn('[useWeb3Wallet] Balance fetch failed:', error); + logger.warn('[useWeb3Wallet] Balance fetch failed', { error }); } }, [state.address, state.chainId, state.provider]); @@ -459,7 +462,7 @@ export function useWeb3Wallet() { try { await connect(savedProvider); } catch (error) { - console.warn('[useWeb3Wallet] Auto-connect failed:', error); + logger.warn('[useWeb3Wallet] Auto-connect failed', { error }); localStorage.removeItem('wallet_connected'); } } diff --git a/src/lib/dataWarehouse.ts b/src/lib/dataWarehouse.ts index abafa743..60b10d65 100644 --- a/src/lib/dataWarehouse.ts +++ b/src/lib/dataWarehouse.ts @@ -6,6 +6,10 @@ * for providers like BigQuery, Snowflake, Mixpanel, etc. */ +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('data-warehouse'); + export interface TrackEventPayload { eventName: string; timestamp: string; @@ -33,7 +37,7 @@ class DataWarehouseService { // Mock implementation for the data warehouse integration if (process.env.NODE_ENV !== 'test') { - console.log('[DataWarehouse] Event tracked:', JSON.stringify(payload, null, 2)); + logger.info('[DataWarehouse] Event tracked', { payload }); } // In a real implementation, you would do something like: diff --git a/src/lib/db/pool.ts b/src/lib/db/pool.ts index f1fc9dd8..0f4c86ad 100644 --- a/src/lib/db/pool.ts +++ b/src/lib/db/pool.ts @@ -1,7 +1,10 @@ import { Pool, PoolConfig, QueryResult } from 'pg'; +import { createLogger } from '@/lib/logging'; import { logContextStorage } from '@/lib/logging/context'; import { retryWithBackoff } from '@/utils/errorUtils'; +const logger = createLogger('db-pool'); + /** * Database Connection Pool Management * Configures and maintains a singleton PostgreSQL connection pool @@ -49,7 +52,9 @@ class DatabasePool { DatabasePool.instance.on('connect', () => { if (process.env.NODE_ENV === 'development') { const traceId = logContextStorage.getStore()?.traceId ?? ''; - console.log('[DB Pool] New client connected to database', traceId ? { traceId } : ''); + logger.info('[DB Pool] New client connected to database', { + context: { traceId }, + }); } DatabasePool.consecutiveFailures = 0; @@ -60,7 +65,10 @@ class DatabasePool { DatabasePool.instance.on('error', (err) => { const traceId = logContextStorage.getStore()?.traceId ?? ''; - console.error('[DB Pool] Unexpected error on idle client', err, traceId ? { traceId } : ''); + logger.error('[DB Pool] Unexpected error on idle client', { + error: err, + context: { traceId }, + }); DatabasePool.consecutiveFailures++; DatabasePool.lastFailureTime = Date.now(); @@ -166,9 +174,6 @@ class DatabasePool { } } - /** - * Get current pool metrics for monitoring - */ public static getMetrics() { if (!DatabasePool.instance) { return { @@ -191,9 +196,6 @@ class DatabasePool { }; } - /** - * Gracefully shutdown the pool - */ public static async end(): Promise { if (DatabasePool.instance) { await DatabasePool.instance.end(); @@ -205,7 +207,7 @@ export const dbPool = DatabasePool; export const query = (text: string, params?: unknown[]) => { const traceId = logContextStorage.getStore()?.traceId ?? ''; if (traceId && process.env.NODE_ENV === 'development') { - console.log('[DB Query]', { text: text.slice(0, 100), traceId }); + logger.debug('[DB Query]', { context: { text: text.slice(0, 100), traceId } }); } return DatabasePool.queryWithRetry(text, params); -}; +}; \ No newline at end of file diff --git a/src/lib/errors/index.ts b/src/lib/errors/index.ts index 1f8a1995..54484062 100644 --- a/src/lib/errors/index.ts +++ b/src/lib/errors/index.ts @@ -7,6 +7,8 @@ */ import { errorReportingService, BreadcrumbEntry } from '@/services/errorReporting'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('ErrorTracking'); // ── Types ──────────────────────────────────────────────────────────────────── @@ -39,7 +41,7 @@ export function init(dsn?: string): void { // TODO: replace with Sentry.init({ dsn, ... }) when SDK is installed. if (dsn) { - console.info('[ErrorTracking] Initialised with DSN:', dsn); + logger.info('[ErrorTracking] Initialised with DSN', { context: { dsn } }); } // Forward global unhandled errors to the reporting service automatically. diff --git a/src/lib/export-scheduler/notification-service.ts b/src/lib/export-scheduler/notification-service.ts index 896a0ca1..77cc0ff4 100644 --- a/src/lib/export-scheduler/notification-service.ts +++ b/src/lib/export-scheduler/notification-service.ts @@ -5,6 +5,9 @@ import { EmailMessage } from '@/lib/email/types'; import { ExportNotification } from './types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('export-notification-service'); export class ExportNotificationService { /** @@ -15,7 +18,7 @@ export class ExportNotificationService { // In production, this would use the email queue // For now, we'll log it - console.log('Export notification:', message); + logger.info('Export notification', { message }); // Uncomment when email service is configured: // const { EmailQueue } = await import('@/lib/email/queue'); diff --git a/src/lib/export-scheduler/scheduler-service.ts b/src/lib/export-scheduler/scheduler-service.ts index 6d24c6fe..e84ee718 100644 --- a/src/lib/export-scheduler/scheduler-service.ts +++ b/src/lib/export-scheduler/scheduler-service.ts @@ -22,6 +22,9 @@ import { import { getNextRunTime, frequencyToCron } from './cron-parser'; import { exportData, fetchDataForTemplate } from './exporter'; import { notificationService } from './notification-service'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('export-scheduler-service'); interface ExportJobPayload { scheduleId?: string; @@ -47,7 +50,7 @@ export class ExportSchedulerService { if (this.isRunning) return; this.isRunning = true; - console.log('Export scheduler started'); + logger.info('Export scheduler started'); // Check for due schedules periodically this.checkInterval = setInterval(() => { @@ -69,7 +72,7 @@ export class ExportSchedulerService { clearInterval(this.checkInterval); this.checkInterval = null; } - console.log('Export scheduler stopped'); + logger.info('Export scheduler stopped'); } /** @@ -83,7 +86,7 @@ export class ExportSchedulerService { await this.queueExportJob(schedule); } } catch (error) { - console.error('Error checking due schedules:', error); + logger.error('Error checking due schedules', { error }); } } @@ -192,7 +195,7 @@ export class ExportSchedulerService { } } - console.log(`Export completed: ${fileName}`); + logger.info('Export completed', { fileName }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; diff --git a/src/lib/featureFlags/tipCanary.ts b/src/lib/featureFlags/tipCanary.ts index 0e83edce..33a6d8a2 100644 --- a/src/lib/featureFlags/tipCanary.ts +++ b/src/lib/featureFlags/tipCanary.ts @@ -1,4 +1,6 @@ import { NextRequest } from 'next/server'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('TipCanary'); interface CanaryResult { enabled: boolean; @@ -60,7 +62,7 @@ export function evaluateTipCanary(request: NextRequest): CanaryResult { try { // Logging minimal data: percent, bucket, enabled // Consumers can hook into logs to build metrics - console.info(JSON.stringify({ event: 'tip_canary_evaluation', percent, bucket, enabled })); + logger.info('tip_canary_evaluation', { context: JSON.parse(JSON.stringify({ event: 'tip_canary_evaluation' })) }); } catch {} return { enabled, bucket, percent, identifier: setAnonId ? undefined : identifier, setAnonId }; diff --git a/src/lib/graphql/subscriptions.ts b/src/lib/graphql/subscriptions.ts index 99386931..2caf4875 100644 --- a/src/lib/graphql/subscriptions.ts +++ b/src/lib/graphql/subscriptions.ts @@ -9,6 +9,9 @@ import { ApolloClient, InMemoryCache, ApolloLink, split, HttpLink } from '@apoll import { getMainDefinition } from '@apollo/client/utilities'; import { DocumentNode } from 'graphql'; import { flagStore, evaluateFlag } from '@/lib/feature-flags'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('graphql-subscriptions'); /** * WebSocket subscription configuration options @@ -139,7 +142,7 @@ class SubscriptionConnectionManager { try { listener(event); } catch (err) { - console.error('Error notifying subscription listener:', err); + logger.error('Error notifying subscription listener', { error: err }); } }); } diff --git a/src/lib/monitoring/provider.ts b/src/lib/monitoring/provider.ts index ee5888f1..841b9ee2 100644 --- a/src/lib/monitoring/provider.ts +++ b/src/lib/monitoring/provider.ts @@ -1,4 +1,7 @@ import { getRecordedMetrics } from '@/lib/logging/performance'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('monitoring-provider'); export type Metric = { name: string; @@ -35,7 +38,7 @@ export class LocalMonitoringProvider implements MonitoringProvider { return [...baseMetrics, ...result.data]; } } catch (error) { - console.warn('[Monitoring] Failed to fetch DB metrics:', error); + logger.warn('[Monitoring] Failed to fetch DB metrics', { error }); } return baseMetrics; diff --git a/src/lib/notifications/socket.ts b/src/lib/notifications/socket.ts index 23c4884b..4cf008d1 100644 --- a/src/lib/notifications/socket.ts +++ b/src/lib/notifications/socket.ts @@ -1,4 +1,7 @@ import type { BaseNotification, NotificationEvent } from './types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('notification-socket'); export type NotificationEventType = NotificationEvent['event']; @@ -162,7 +165,7 @@ export class NotificationSocketService { this.listeners.forEach((listener) => listener(notification)); } } catch { - console.warn('[NotificationSocket] Failed to parse message', event.data); + logger.warn('[NotificationSocket] Failed to parse message', { data: event.data }); } }; @@ -187,7 +190,7 @@ export class NotificationSocketService { }; } catch (error) { const message = error instanceof Error ? error.message : 'Failed to open WebSocket'; - console.error('[NotificationSocket] Failed to open', error); + logger.error('[NotificationSocket] Failed to open', { error }); this.updateConnectionState({ status: 'disconnected', reconnectAttempts: this.reconnectAttempts, diff --git a/src/lib/redirectManagement.ts b/src/lib/redirectManagement.ts index 877e154f..a5a8e5ec 100644 --- a/src/lib/redirectManagement.ts +++ b/src/lib/redirectManagement.ts @@ -5,6 +5,8 @@ */ import type { ReadonlyURLSearchParams } from 'next/navigation'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('RedirectManagement'); /** * Redirect rule configuration @@ -262,14 +264,14 @@ export interface RedirectLog { * This is for server-side logging */ export async function logRedirect(entry: RedirectLog): Promise { - // This would be implemented to send logs to your analytics service - // For now, it's a no-op that can be extended if (process.env.NODE_ENV === 'development') { - console.debug('[Redirect Log]', { - from: entry.from, - to: entry.to, - locale: entry.locale, - statusCode: entry.statusCode, + logger.debug('[Redirect Log]', { + context: { + from: entry.from, + to: entry.to, + locale: entry.locale, + statusCode: entry.statusCode, + }, }); } } diff --git a/src/lib/validation/validator.ts b/src/lib/validation/validator.ts index dbfe8c23..295154ec 100644 --- a/src/lib/validation/validator.ts +++ b/src/lib/validation/validator.ts @@ -1,4 +1,7 @@ import { z } from 'zod'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('validator'); export class ValidationError extends Error { constructor(public errors: z.ZodIssue[]) { @@ -16,7 +19,7 @@ export function validateData(schema: z.ZodSchema, data: unknown): T { if (!result.success) { if (process.env.NODE_ENV !== 'production') { - console.error('Validation Error Details:', result.error.format()); + logger.error('Validation Error Details', { details: result.error.format() }); } throw new ValidationError(result.error.issues); } diff --git a/src/locales/translationManager.ts b/src/locales/translationManager.ts index 262ce9f2..7f685ecd 100644 --- a/src/locales/translationManager.ts +++ b/src/locales/translationManager.ts @@ -3,6 +3,9 @@ */ import type { LanguageCode, Translations } from './types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('translation-manager'); // Cache for loaded translations const translationCache = new Map(); @@ -35,7 +38,7 @@ export async function loadTranslations(language: LanguageCode): Promise { - console.error('[Redirect Log Error]', err); + logger.error('[Redirect Log Error]', { error: err }); }); // Perform the redirect diff --git a/src/pages/admin/audit.tsx b/src/pages/admin/audit.tsx index 87b5ca5c..c455661e 100644 --- a/src/pages/admin/audit.tsx +++ b/src/pages/admin/audit.tsx @@ -1,6 +1,9 @@ import { useEffect, useMemo, useState } from 'react'; import type { AuditAction, AuditLogEntry } from '@/lib/audit'; import AdminThemeToggle from '@/components/admin/AdminThemeToggle'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('admin-audit'); type AuditApiResponse = { entries: AuditLogEntry[]; @@ -35,7 +38,7 @@ export default function AdminAuditPage() { setEntries(data.entries); setTotal(data.total); } catch (error) { - console.error(error); + logger.error('Failed to load audit logs', { error }); setEntries([]); setTotal(0); } finally { diff --git a/src/pages/exports/index.tsx b/src/pages/exports/index.tsx index 3b0f0c28..8bdbdad9 100644 --- a/src/pages/exports/index.tsx +++ b/src/pages/exports/index.tsx @@ -9,6 +9,9 @@ import ExportButton from '@/components/ExportButton'; import { apiClient } from '@/lib/api'; import { defaultSort, normalizeFilters } from '@/lib/export'; import { ExportHistory, ExportSchedule, ExportTemplate } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('exports-dashboard'); export default function ExportsPage() { const router = useRouter(); @@ -40,7 +43,7 @@ export default function ExportsPage() { setHistory(response.history); } } catch (error) { - console.error('Error loading data:', error); + logger.error('Error loading data', { error }); } finally { setLoading(false); } @@ -52,7 +55,7 @@ export default function ExportsPage() { await apiClient.delete(`/api/exports/templates/${id}`); void loadData(); } catch (error) { - console.error('Error deleting template:', error); + logger.error('Error deleting template', { error }); } }; @@ -62,7 +65,7 @@ export default function ExportsPage() { await apiClient.delete(`/api/exports/schedules/${id}`); void loadData(); } catch (error) { - console.error('Error deleting schedule:', error); + logger.error('Error deleting schedule', { error }); } }; @@ -71,7 +74,7 @@ export default function ExportsPage() { await apiClient.patch(`/api/exports/schedules/${scheduleId}`, { enabled }); void loadData(); } catch (error) { - console.error('Error toggling schedule:', error); + logger.error('Error toggling schedule', { error }); } }; diff --git a/src/pages/exports/schedules/new.tsx b/src/pages/exports/schedules/new.tsx index 595a0b80..73dce3bc 100644 --- a/src/pages/exports/schedules/new.tsx +++ b/src/pages/exports/schedules/new.tsx @@ -6,6 +6,9 @@ import React, { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import { apiClient } from '@/lib/api'; import { ExportTemplate, ScheduleFrequency } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('export-schedules-new'); export default function NewSchedulePage() { const router = useRouter(); @@ -31,7 +34,7 @@ export default function NewSchedulePage() { ); setTemplates(response.templates); } catch (error) { - console.error('Error loading templates:', error); + logger.error('Error loading templates', { error }); } }; @@ -53,7 +56,7 @@ export default function NewSchedulePage() { router.push('/exports'); } catch (error) { - console.error('Error creating schedule:', error); + logger.error('Error creating schedule', { error }); alert('Failed to create schedule'); } finally { setLoading(false); diff --git a/src/pages/exports/templates/new.tsx b/src/pages/exports/templates/new.tsx index aa094263..4e486da1 100644 --- a/src/pages/exports/templates/new.tsx +++ b/src/pages/exports/templates/new.tsx @@ -6,6 +6,9 @@ import React, { useState } from 'react'; import { useRouter } from 'next/router'; import { apiClient } from '@/lib/api'; import { ExportFormat } from '@/lib/export-scheduler'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('export-templates-new'); export default function NewTemplatePage() { const router = useRouter(); @@ -35,7 +38,7 @@ export default function NewTemplatePage() { router.push('/exports'); } catch (error) { - console.error('Error creating template:', error); + logger.error('Error creating template', { error }); alert('Failed to create template'); } finally { setLoading(false); diff --git a/src/providers/WalletProvider.tsx b/src/providers/WalletProvider.tsx index f56fec91..ef9d064a 100644 --- a/src/providers/WalletProvider.tsx +++ b/src/providers/WalletProvider.tsx @@ -9,6 +9,9 @@ import React, { ReactNode, } from 'react'; import { walletConnectionQueue } from '@/utils/web3/walletQueue'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('wallet-provider'); // Environment validation for wallet config const validateWalletEnv = () => { @@ -23,7 +26,7 @@ const validateWalletEnv = () => { process.env.NODE_ENV === 'development' && warnings.length > 0 ) { - console.warn('[WalletProvider] Environment warnings:', warnings); + logger.warn('[WalletProvider] Environment warnings', { warnings }); } return { @@ -112,7 +115,7 @@ export function WalletProvider({ children }: WalletProviderProps) { isConnecting: false, error: message, })); - console.error('[WalletProvider] Connection failed:', message); + logger.error('[WalletProvider] Connection failed', { error: message }); throw error; } }); @@ -169,7 +172,7 @@ export function useWallet(): WalletContextType { return { ...initialState, connect: async () => { - console.warn('WalletProvider not found'); + logger.warn('WalletProvider not found'); }, disconnect: async () => {}, clearError: () => {}, diff --git a/src/services/bundleOptimizer.ts b/src/services/bundleOptimizer.ts index 8a284a4a..180114cf 100644 --- a/src/services/bundleOptimizer.ts +++ b/src/services/bundleOptimizer.ts @@ -7,6 +7,9 @@ */ import { bundleSecurityContext, type ChunkSecurityMetadata } from './bundleSecurityContext'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('bundle-optimizer'); export interface BundleChunk { id: string; @@ -65,11 +68,11 @@ class BundleOptimizer { ); if (heavyChunks.length > 0) { - console.warn( + logger.warn( `[Bundle Analysis] Found ${heavyChunks.length} heavy chunks (> ${threshold}KB). Consider further code splitting.`, ); heavyChunks.forEach((chunk) => { - console.warn(` - Chunk: ${chunk.name} (${chunk.size}KB)`); + logger.warn(` - Chunk: ${chunk.name} (${chunk.size}KB)`); }); } diff --git a/src/services/errorReporting.ts b/src/services/errorReporting.ts index e08b2f52..9e8cbbaa 100644 --- a/src/services/errorReporting.ts +++ b/src/services/errorReporting.ts @@ -4,6 +4,9 @@ */ import { formatErrorForLogging } from '@/utils/errorUtils'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('error-reporting'); export interface ErrorReport { id: string; @@ -95,7 +98,7 @@ class ErrorReportingService { // Log to console in development if (!this.isProduction) { - console.error('Error Report:', report); + logger.error('Error Report', { error: report }); } // Send to error tracking service (e.g., Sentry, LogRocket) @@ -142,10 +145,10 @@ class ErrorReportingService { }); if (!response.ok) { - console.error('Failed to send error report:', response.statusText); + logger.error('Failed to send error report', { status: response.statusText }); } } catch (err) { - console.error('Error sending error report:', err); + logger.error('Error sending error report', { error: err }); } } diff --git a/src/store/devTools.ts b/src/store/devTools.ts index 71a8d31a..d3e92fc7 100644 --- a/src/store/devTools.ts +++ b/src/store/devTools.ts @@ -6,6 +6,8 @@ * Middleware or utility to log state transitions in development. */ import { StateCreator } from 'zustand'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('DevTools'); /** * Middleware or utility to log state transitions in development. @@ -16,10 +18,8 @@ export const stateLogger = config( (args) => { if (process.env.NODE_ENV === 'development') { - console.group('%c [State Action]', 'color: #00bcd4; font-weight: bold;'); - + logger.debug('[State Action]', { context: { nextState: args } }); set(args); - console.groupEnd(); } else { set(args); } diff --git a/src/store/persistenceLayer.ts b/src/store/persistenceLayer.ts index aa0c4b6c..552a487f 100644 --- a/src/store/persistenceLayer.ts +++ b/src/store/persistenceLayer.ts @@ -1,4 +1,7 @@ import { openDB } from 'idb'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('persistence-layer'); const DB_NAME = 'teachlink_state_v1'; const STORE_NAME = 'app_state'; @@ -21,7 +24,7 @@ export const persistenceLayer = { const data = await db.get(STORE_NAME, name); return data ? JSON.stringify(data) : null; } catch (error) { - console.error('[Persistence] Error loading state:', error); + logger.error('[Persistence] Error loading state', { error }); return null; } }, @@ -39,7 +42,7 @@ export const persistenceLayer = { }); await db.put(STORE_NAME, JSON.parse(value), name); } catch (error) { - console.error('[Persistence] Error saving state:', error); + logger.error('[Persistence] Error saving state', { error }); } }, diff --git a/src/store/synchronizationEngine.ts b/src/store/synchronizationEngine.ts index 9ba7b329..78b7cdb2 100644 --- a/src/store/synchronizationEngine.ts +++ b/src/store/synchronizationEngine.ts @@ -1,4 +1,7 @@ import { useStore } from './stateManager'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('synchronization-engine'); const CHANNEL_NAME = 'teachlink_state_sync'; @@ -56,6 +59,9 @@ export class SynchronizationEngine { private broadcastState(state: any) { if (!this.channel) return; +<<<<<<< HEAD + logger.debug('[SyncEngine] Broadcasting state update to other tabs'); +======= // Only send the synced slice const syncedState = SYNC_KEYS.reduce((acc, key) => { acc[key] = state[key as keyof typeof state]; @@ -63,6 +69,7 @@ export class SynchronizationEngine { }, {} as any); console.log('[SyncEngine] Broadcasting synced state slice'); +>>>>>>> 02548c6ab26096252d529c7be8ea762478efef06 this.channel.postMessage({ type: 'STATE_UPDATE', payload: syncedState, diff --git a/src/utils/analytics.ts b/src/utils/analytics.ts index 6f888cbb..d11f1c9e 100644 --- a/src/utils/analytics.ts +++ b/src/utils/analytics.ts @@ -11,6 +11,9 @@ // track("button_clicked", { label: "Enroll" }); // ────────────────────────────────────────────────────────────────────────────── +import { createLogger } from '@/lib/logging'; +const logger = createLogger('Analytics'); + export type EventName = // Navigation | 'page_view' @@ -86,7 +89,7 @@ export type AnalyticsAdapter = (event: AnalyticsEvent) => void | Promise; /** Logs to console in development */ export const consoleAdapter: AnalyticsAdapter = (event) => { if (process.env.NODE_ENV !== 'production') { - console.info(`[Analytics] ${event.name}`, event.properties); + logger.info(`[Analytics] ${event.name}`, { context: { properties: event.properties } }); } }; @@ -101,7 +104,7 @@ export function createApiAdapter(endpoint: string): AnalyticsAdapter { keepalive: true, // survive page unload }); } catch (err) { - console.warn('[Analytics] Failed to send event', err); + logger.warn('[Analytics] Failed to send event', { error: err }); } }; } @@ -199,7 +202,7 @@ class Analytics { try { adapter(event); } catch (err) { - console.warn(`[Analytics] Adapter error for "${name}"`, err); + logger.warn(`[Analytics] Adapter error for "${name}"`, { error: err }); } } } diff --git a/src/utils/generate-qr.ts b/src/utils/generate-qr.ts index 22618240..6a9cd27b 100644 --- a/src/utils/generate-qr.ts +++ b/src/utils/generate-qr.ts @@ -3,6 +3,10 @@ * Provides functions for generating and styling QR codes, with support for custom colors and sizes. */ +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('qr-code'); + export interface QRCodeOptions { /** Size of the QR code in pixels */ size?: number; @@ -80,7 +84,7 @@ export async function downloadQRCode(canvas: HTMLCanvasElement, filename: string link.click(); document.body.removeChild(link); } catch (error) { - console.error('Failed to download QR code:', error); + logger.error('Failed to download QR code', { error }); throw new Error('Failed to download QR code'); } } @@ -100,7 +104,7 @@ export async function printQRCode(canvas: HTMLCanvasElement) { printWindow.document.close(); printWindow.print(); } catch (error) { - console.error('Failed to print QR code:', error); + logger.error('Failed to print QR code', { error }); throw new Error('Failed to print QR code'); } } @@ -119,7 +123,7 @@ export async function copyQRCodeToClipboard(canvas: HTMLCanvasElement) { }), ]); } catch (error) { - console.error('Failed to copy QR code to clipboard:', error); + logger.error('Failed to copy QR code to clipboard', { error }); throw new Error('Failed to copy QR code to clipboard'); } } diff --git a/src/utils/i18nUtils.ts b/src/utils/i18nUtils.ts index 5980722c..4da2c476 100644 --- a/src/utils/i18nUtils.ts +++ b/src/utils/i18nUtils.ts @@ -7,6 +7,8 @@ import { getLocaleConfig } from '@/locales/config'; import { format, formatDistanceToNow, type Locale } from 'date-fns'; import { enUS, es, fr, de, ar, he, ja, zhCN, ptBR, ru, it, ko } from 'date-fns/locale'; import { getNumberFormat } from './intlCache'; +import { createLogger } from '@/lib/logging'; +const logger = createLogger('i18nUtils'); // Date-fns locale mapping const dateFnsLocales: Record = { @@ -86,7 +88,7 @@ export function formatDate( try { return format(dateObj, formatPattern, { locale }); } catch (error) { - console.warn('Date formatting error:', error); + logger.warn('Date formatting error', { error }); return format(dateObj, 'PP', { locale: enUS }); } } @@ -102,7 +104,7 @@ export function formatRelativeTime(date: Date | string | number, language: Langu try { return formatDistanceToNow(dateObj, { addSuffix: true, locale }); } catch (error) { - console.warn('Relative time formatting error:', error); + logger.warn('Relative time formatting error', { error }); return formatDistanceToNow(dateObj, { addSuffix: true, locale: enUS }); } } @@ -120,7 +122,7 @@ export function formatNumber( try { return getNumberFormat(config.numberFormat, options).format(value); } catch (error) { - console.warn('Number formatting error:', error); + logger.warn('Number formatting error', { error }); return value.toString(); } } @@ -138,7 +140,7 @@ export function formatCurrency(amount: number, language: LanguageCode, currency? currency: currencyCode, }).format(amount); } catch (error) { - console.warn('Currency formatting error:', error); + logger.warn('Currency formatting error', { error }); return `${currencyCode} ${amount.toFixed(2)}`; } } @@ -156,7 +158,7 @@ export function formatPercentage(value: number, language: LanguageCode, decimals maximumFractionDigits: decimals, }).format(value / 100); } catch (error) { - console.warn('Percentage formatting error:', error); + logger.warn('Percentage formatting error', { error }); return `${value.toFixed(decimals)}%`; } } diff --git a/src/utils/performanceUtils.ts b/src/utils/performanceUtils.ts index 8a01730b..3261fb70 100644 --- a/src/utils/performanceUtils.ts +++ b/src/utils/performanceUtils.ts @@ -4,6 +4,9 @@ import { STORAGE_KEYS, MAX_TREND_POINTS, DEFAULT_IDLE_TIMEOUT_MS } from '@/constants/app.constants'; import { onCLS, onFCP, onINP, onLCP, onTTFB, type Metric } from 'web-vitals'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('performance-utils'); export interface PerformanceMetric { name: string; @@ -337,7 +340,7 @@ export async function reportVitalToAnalytics(metric: PerformanceMetric): Promise process.env.NEXT_PUBLIC_ENABLE_PERF_ANALYTICS === 'true'; if (!shouldReport) { - console.debug(`[Performance Analytics] Skipping report for ${metric.name} in development`); + logger.debug(`[Performance Analytics] Skipping report for ${metric.name} in development`); return; } @@ -359,12 +362,12 @@ export async function reportVitalToAnalytics(metric: PerformanceMetric): Promise }); if (!response.ok) { - console.warn( + logger.warn( `[Performance Analytics] Failed to send ${metric.name}: ${response.statusText}`, ); } } catch (err) { - console.error(`[Performance Analytics] Error sending ${metric.name}:`, err); + logger.error(`[Performance Analytics] Error sending ${metric.name}`, { error: err }); } }); } diff --git a/src/utils/pwaUtils.ts b/src/utils/pwaUtils.ts index 5ba43f23..8fcd0e95 100644 --- a/src/utils/pwaUtils.ts +++ b/src/utils/pwaUtils.ts @@ -1,16 +1,20 @@ /** * Registers the service worker for PWA offline capabilities */ + +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('pwa-utils'); export async function registerServiceWorker(): Promise { if (typeof window !== 'undefined' && 'serviceWorker' in navigator) { try { const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/', }); - console.log('ServiceWorker registration successful with scope: ', registration.scope); + logger.info('ServiceWorker registration successful', { scope: registration.scope }); return registration; } catch (error) { - console.error('ServiceWorker registration failed: ', error); + logger.error('ServiceWorker registration failed', { error }); return undefined; } } @@ -36,7 +40,7 @@ export async function promptPWAInstall(installEvent: any): Promise { const { outcome } = await installEvent.userChoice; return outcome === 'accepted'; } catch (err) { - console.error('Error prompting PWA install:', err); + logger.error('Error prompting PWA install', { error: err }); return false; } } diff --git a/src/utils/registerSW.ts b/src/utils/registerSW.ts index f78d570f..fd91d8b3 100644 --- a/src/utils/registerSW.ts +++ b/src/utils/registerSW.ts @@ -4,6 +4,10 @@ export type UpdateCallback = (registration: ServiceWorkerRegistration) => void; * Registers /sw.js and calls `onUpdate` whenever a new service worker is * waiting to activate (i.e. an update is available). */ + +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('service-worker'); export async function registerSW( onUpdate?: UpdateCallback, ): Promise { @@ -39,7 +43,7 @@ export async function registerSW( return registration; } catch (err) { - console.error('[SW] Registration failed:', err); + logger.error('[SW] Registration failed', { error: err }); return null; } } diff --git a/src/utils/searchUtils.ts b/src/utils/searchUtils.ts index 838b9c0e..12a42705 100644 --- a/src/utils/searchUtils.ts +++ b/src/utils/searchUtils.ts @@ -2,6 +2,10 @@ * Search Utilities for Advanced Search Interface */ +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('search-utils'); + const VALID_DIFFICULTIES = ['beginner', 'intermediate', 'advanced'] as const; const VALID_SORT_OPTIONS = ['relevance', 'newest', 'rating', 'price'] as const; const MAX_STRING_LENGTH = 100; @@ -144,7 +148,7 @@ export const trackSearch = (analytics: SearchAnalytics) => { // Keep last 100 entries for analysis localStorage.setItem('search_analytics', JSON.stringify(history.slice(0, 100))); } catch (error) { - console.error('Failed to track search analytics', error); + logger.error('Failed to track search analytics', { error }); } }; @@ -169,7 +173,7 @@ export const getPopularQueries = (): { query: string; count: number }[] => { .sort((a, b) => b.count - a.count) .slice(0, 10); } catch (error) { - console.error('Failed to get popular queries', error); + logger.error('Failed to get popular queries', { error }); return []; } }; @@ -192,7 +196,7 @@ export const getSearchGaps = (): string[] => { ), ).slice(0, 10); } catch (error) { - console.error('Failed to get search gaps', error); + logger.error('Failed to get search gaps', { error }); return []; } }; diff --git a/src/utils/virtualBackgroundUtils.ts b/src/utils/virtualBackgroundUtils.ts index a94865fe..24189a3e 100644 --- a/src/utils/virtualBackgroundUtils.ts +++ b/src/utils/virtualBackgroundUtils.ts @@ -6,6 +6,9 @@ */ import type { AppSettings } from '@/lib/settings/types'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('virtual-background'); export interface VirtualBackgroundConfig { enabled: boolean; @@ -147,7 +150,7 @@ async function applyImageBackground( ctx.drawImage(img, x, y, img.width * scale, img.height * scale); } catch (error) { - console.error('Failed to load background image:', error); + logger.error('Failed to load background image', { error }); } } diff --git a/src/utils/web3/security.ts b/src/utils/web3/security.ts index 3f6fda3c..f7a5b2c2 100644 --- a/src/utils/web3/security.ts +++ b/src/utils/web3/security.ts @@ -10,6 +10,9 @@ */ import { z } from 'zod'; +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('web3-security'); /** * Address validation schema @@ -220,7 +223,7 @@ export function decodeContractData(data: string): { params: { encoded: params }, }; } catch (error) { - console.error('[Web3Utils] Error decoding contract data:', error); + logger.error('[Web3Utils] Error decoding contract data', { error }); return null; } } diff --git a/src/utils/web3/walletValidation.ts b/src/utils/web3/walletValidation.ts index 575fe829..be3b6615 100644 --- a/src/utils/web3/walletValidation.ts +++ b/src/utils/web3/walletValidation.ts @@ -3,6 +3,10 @@ * Safe checks for wallet operations that won't break builds */ +import { createLogger } from '@/lib/logging'; + +const logger = createLogger('wallet-validation'); + export interface WalletInteractionResult { canInteract: boolean; reason: string | null; @@ -51,7 +55,7 @@ export async function safeWalletCall( return { success: true, data, error: null }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown wallet error'; - console.error('[safeWalletCall]', message); + logger.error('[safeWalletCall] failed', { error }); return { success: false, data: fallback, error: message }; } } diff --git a/src/workers/sms-cluster-worker.ts b/src/workers/sms-cluster-worker.ts index c6c789dc..1289e3a6 100644 --- a/src/workers/sms-cluster-worker.ts +++ b/src/workers/sms-cluster-worker.ts @@ -1,6 +1,9 @@ import cluster from 'cluster'; import os from 'os'; import { sendSMS } from '../utils/notificationUtils'; // Assuming we have this or we'll mock it +import { createLogger } from '../lib/logging'; + +const logger = createLogger('sms-cluster-worker'); const numCPUs = os.cpus().length; @@ -30,8 +33,8 @@ for (let i = 0; i < 50; i++) { export const startSMSClusterWorker = () => { if (cluster.isPrimary) { - console.log(`Primary ${process.pid} is running`); - console.log(`Setting up cluster with ${numCPUs} workers for SMS processing...`); + logger.info(`Primary ${process.pid} is running`); + logger.info(`Setting up cluster with ${numCPUs} workers for SMS processing...`); // Fork workers. for (let i = 0; i < numCPUs; i++) { @@ -39,27 +42,27 @@ export const startSMSClusterWorker = () => { } cluster.on('exit', (worker, code, signal) => { - console.log(`Worker ${worker.process.pid} died with code: ${code}, and signal: ${signal}`); - console.log('Starting a new worker...'); + logger.warn(`Worker ${worker.process.pid} died with code: ${code}, and signal: ${signal}`); + logger.info('Starting a new worker...'); cluster.fork(); // Auto-heal workers }); } else { // Workers can share any TCP connection // In this case it is an HTTP server or a message queue listener - console.log(`Worker ${process.pid} started for SMS processing`); + logger.info(`Worker ${process.pid} started for SMS processing`); const processQueue = async () => { // Basic mock of polling a queue const messageJob = smsQueue.dequeue(); if (messageJob) { try { - console.log(`[Worker ${process.pid}] Processing SMS for ${messageJob.to}...`); + logger.debug(`[Worker ${process.pid}] Processing SMS for ${messageJob.to}...`); // Note: In a real app we'd use sendSMS(messageJob.to, messageJob.message) // For now we just mock the delay await new Promise((resolve) => setTimeout(resolve, Math.random() * 500 + 100)); - console.log(`[Worker ${process.pid}] Successfully sent SMS to ${messageJob.to}`); + logger.info(`[Worker ${process.pid}] Successfully sent SMS to ${messageJob.to}`); } catch (error) { - console.error(`[Worker ${process.pid}] Failed to send SMS:`, error); + logger.error(`[Worker ${process.pid}] Failed to send SMS`, { error }); } }