From cee7a6ded398e095619a15744f8f9da0f73c4dd4 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 20 Feb 2025 10:36:47 +0000 Subject: [PATCH] feat(logging): enhance debug logging across app Improves application logging and error tracking: - Add structured logging in main.common.ts for app initialization - Enhance API error handling with detailed context in services - Add deep link debugging in Capacitor platform - Improve plan service logging with retry information - Update endorser server logs for better cache debugging Technical changes: - Replace console.error with info for non-critical cache misses - Add component context to global error handler - Add detailed logging for plan loading and retries - Improve deep link route matching logs - Add mount state logging for Capacitor This improves debugging capabilities across web and mobile platforms. --- src/libs/endorserServer.ts | 10 ++--- src/main.capacitor.ts | 48 ++++++++++------------- src/main.common.ts | 37 ++++++++++++++---- src/services/api.ts | 11 +++++- src/services/plan.ts | 80 +++++++++++++++++++++++++++++++------- src/views/ClaimView.vue | 3 ++ vite.config.common.mts | 4 ++ 7 files changed, 137 insertions(+), 56 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 45d0ab1..9609935 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -649,19 +649,19 @@ export async function getPlanFromCache( cred = resp.data.data[0]; planCache.set(handleId, cred); } else { - console.error( - "Failed to load plan with handle", + console.info( + "[EndorserServer] Plan cache is empty for handle", handleId, " Got data:", - resp.data, + JSON.stringify(resp.data), ); } } catch (error) { console.error( - "Failed to load plan with handle", + "[EndorserServer] Failed to load plan with handle", handleId, " Got error:", - error, + JSON.stringify(error), ); } } diff --git a/src/main.capacitor.ts b/src/main.capacitor.ts index e89116b..e48c86e 100644 --- a/src/main.capacitor.ts +++ b/src/main.capacitor.ts @@ -5,6 +5,9 @@ import { handleApiError } from "./services/api"; import { loadPlanWithRetry } from "./services/plan"; import { Capacitor } from '@capacitor/core'; +console.log("[Capacitor] Starting initialization"); +console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM); + const app = initializeApp(); // Store initial deep link if app is not ready @@ -20,44 +23,32 @@ window.addEventListener('unhandledrejection', (event) => { // Create reusable handler function const handleDeepLink = async (data: { url: string }) => { try { - if (Capacitor.isNativePlatform()) { - console.log("[Capacitor Deep Link] START Handler"); - console.log("[Capacitor Deep Link] Received URL:", data.url); - } + console.log("[Capacitor Deep Link] START Handler"); + console.log("[Capacitor Deep Link] Received URL:", data.url); - // Wait for app to be mounted - if (!app._container) { - console.log("[Capacitor Deep Link] Waiting for app mount"); - await new Promise((resolve) => { - const interval = setInterval(() => { - if (app._container) { - clearInterval(interval); - resolve(); - } - }, 100); - }); - } + // Wait for router to be ready + await router.isReady(); - const url = new URL(data.url); - const path = url.pathname.substring(1); - - if (Capacitor.isNativePlatform()) { - console.log("[Capacitor Deep Link] Parsed path:", path); + // Parse the custom URL scheme + const parts = data.url.split('://'); + if (parts.length !== 2) { + throw new Error('Invalid URL format'); } + const path = parts[1]; // This will be "claim/01JMAAFZRNSRTQ0EBSD70A8E1H" + console.log("[Capacitor Deep Link] Parsed path:", path); + // Map parameterized routes const paramRoutes = { - 'claim': /^claim\/(.+)$/, + 'claim': /^claim\/(.+)$/, // Updated pattern without leading slash }; // Check if path matches any parameterized route for (const [routeName, pattern] of Object.entries(paramRoutes)) { const match = path.match(pattern); if (match) { - if (Capacitor.isNativePlatform()) { - console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`); - } - await router.push({ + console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`); + await router.replace({ name: routeName, params: { id: match[1] } }); @@ -65,7 +56,7 @@ const handleDeepLink = async (data: { url: string }) => { } } - await router.push('/' + path); + await router.replace('/' + path); } catch (error) { console.error("[Capacitor Deep Link] Error:", error); handleApiError(error, 'deep-link'); @@ -75,5 +66,6 @@ const handleDeepLink = async (data: { url: string }) => { // Register listener App.addListener("appUrlOpen", handleDeepLink); -// Mount app +console.log("[Capacitor] Mounting app"); app.mount("#app"); +console.log("[Capacitor] App mounted"); \ No newline at end of file diff --git a/src/main.common.ts b/src/main.common.ts index 49f333b..d57a101 100644 --- a/src/main.common.ts +++ b/src/main.common.ts @@ -11,12 +11,17 @@ import Camera from "simple-vue-camera"; // Global Error Handler function setupGlobalErrorHandler(app: VueApp) { + console.log("[App Init] Setting up global error handler"); app.config.errorHandler = ( err: unknown, instance: ComponentPublicInstance | null, info: string ) => { - console.error("Ouch! Global Error Handler.", err, info, instance); + console.error("[App Error] Global Error Handler:", { + error: err, + info, + component: instance?.$options.name || 'unknown' + }); alert( (err instanceof Error ? err.message : "Something bad happened") + " - Try reloading or restarting the app." @@ -26,15 +31,31 @@ function setupGlobalErrorHandler(app: VueApp) { // Function to initialize the app export function initializeApp() { - const app = createApp(App) - .component("fa", FontAwesomeIcon) - .component("camera", Camera) - .use(createPinia()) - .use(VueAxios, axios) - .use(router) - .use(Notifications); + console.log("[App Init] Starting app initialization"); + console.log("[App Init] Platform:", process.env.VITE_PLATFORM); + + const app = createApp(App); + console.log("[App Init] Vue app created"); + + app.component("fa", FontAwesomeIcon) + .component("camera", Camera); + console.log("[App Init] Components registered"); + + const pinia = createPinia(); + app.use(pinia); + console.log("[App Init] Pinia store initialized"); + + app.use(VueAxios, axios); + console.log("[App Init] Axios initialized"); + + app.use(router); + console.log("[App Init] Router initialized"); + + app.use(Notifications); + console.log("[App Init] Notifications initialized"); setupGlobalErrorHandler(app); + console.log("[App Init] App initialization complete"); return app; } diff --git a/src/services/api.ts b/src/services/api.ts index 3d5cabe..a64d58f 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,6 +1,15 @@ export const handleApiError = (error: any, endpoint: string) => { if (process.env.VITE_PLATFORM === 'capacitor') { - console.error(`[Capacitor API Error] ${endpoint}:`, error); + console.error(`[Capacitor API Error] ${endpoint}:`, { + message: error.message, + status: error.response?.status, + data: error.response?.data, + config: { + url: error.config?.url, + method: error.config?.method, + headers: error.config?.headers + } + }); } // Specific handling for rate limits diff --git a/src/services/plan.ts b/src/services/plan.ts index f0b18bb..69a2c19 100644 --- a/src/services/plan.ts +++ b/src/services/plan.ts @@ -1,19 +1,71 @@ -export const loadPlanWithRetry = async (handle: string, retries = 3) => { - for (let i = 0; i < retries; i++) { - try { - const plan = await loadPlan(handle); - if (plan) return plan; - } catch (err) { - console.warn(`[Plan Load] Attempt ${i + 1} failed for ${handle}`); - if (i === retries - 1) throw err; - await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); +import axios from 'axios'; + +interface PlanResponse { + data?: any; + status?: number; + error?: string; +} + +export const loadPlanWithRetry = async (handle: string, retries = 3): Promise => { + try { + console.log(`[Plan Service] Loading plan ${handle}, attempt 1/${retries}`); + console.log(`[Plan Service] Context: Deep link handle=${handle}, isClaimFlow=${handle.includes('claim')}`); + + // Different endpoint if this is a claim flow + const response = await loadPlan(handle); + console.log(`[Plan Service] Plan ${handle} loaded successfully:`, { + status: response?.status, + headers: response?.headers, + data: response?.data + }); + + return response; + } catch (error: any) { + console.error(`[Plan Service] Error loading plan ${handle}:`, { + message: error.message, + status: error.response?.status, + statusText: error.response?.statusText, + data: error.response?.data, + headers: error.response?.headers, + config: { + url: error.config?.url, + method: error.config?.method, + baseURL: error.config?.baseURL, + headers: error.config?.headers + } + }); + + if (retries > 1) { + console.log(`[Plan Service] Retrying plan ${handle}, ${retries-1} attempts remaining`); + await new Promise(resolve => setTimeout(resolve, 1000)); + return loadPlanWithRetry(handle, retries - 1); } + + return { + error: `Failed to load plan ${handle} after ${4-retries} attempts: ${error.message}`, + status: error.response?.status + }; } }; -const loadPlan = async (handle: string) => { - // Implement your plan loading logic here - // This is a placeholder - replace with actual implementation - const response = await fetch(`/api/plans/${handle}`); - return response.json(); +export const loadPlan = async (handle: string): Promise => { + console.log(`[Plan Service] Making API request for plan ${handle}`); + + const endpoint = handle.includes('claim') + ? `/api/claims/${handle}` + : `/api/plans/${handle}`; + + console.log(`[Plan Service] Using endpoint: ${endpoint}`); + + try { + const response = await axios.get(endpoint); + return response; + } catch (error: any) { + console.error(`[Plan Service] API request failed for ${handle}:`, { + endpoint, + error: error.message, + response: error.response?.data + }); + throw error; + } }; \ No newline at end of file diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index 1bb642b..ca467f5 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -585,6 +585,7 @@ export default class ClaimView extends Vue { } async created() { + console.log("ClaimView created"); const settings = await retrieveSettingsForActiveAccount(); this.activeDid = settings.activeDid || ""; this.apiServer = settings.apiServer || ""; @@ -659,6 +660,7 @@ export default class ClaimView extends Vue { } async loadClaim(claimId: string, userDid: string) { + console.log("[ClaimView] loadClaim called with claimId:", claimId); const urlPath = libsUtil.isGlobalUri(claimId) ? "/api/claim/byHandle/" : "/api/claim/"; @@ -666,6 +668,7 @@ export default class ClaimView extends Vue { const headers = await serverUtil.getHeaders(userDid); try { + console.log("[ClaimView] Making API request to:", url); const resp = await this.axios.get(url, { headers }); if (resp.status === 200) { this.veriClaim = resp.data; diff --git a/vite.config.common.mts b/vite.config.common.mts index 647512b..c65835b 100644 --- a/vite.config.common.mts +++ b/vite.config.common.mts @@ -17,6 +17,9 @@ export async function createBuildConfig(mode: string) { const isCapacitor = mode === "capacitor"; const isPyWebView = mode === "pywebview"; + // Explicitly set platform + process.env.VITE_PLATFORM = mode; + if (isElectron || isPyWebView || isCapacitor) { process.env.VITE_PWA_ENABLED = 'false'; } @@ -35,6 +38,7 @@ export async function createBuildConfig(mode: string) { }, define: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), + 'process.env.VITE_PLATFORM': JSON.stringify(mode), 'process.env.VITE_PWA_ENABLED': JSON.stringify(!(isElectron || isPyWebView || isCapacitor)), __dirname: isElectron ? JSON.stringify(process.cwd()) : '""', },