forked from jsnbuchanan/crowd-funder-for-time-pwa
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.
This commit is contained in:
@@ -649,19 +649,19 @@ export async function getPlanFromCache(
|
|||||||
cred = resp.data.data[0];
|
cred = resp.data.data[0];
|
||||||
planCache.set(handleId, cred);
|
planCache.set(handleId, cred);
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.info(
|
||||||
"Failed to load plan with handle",
|
"[EndorserServer] Plan cache is empty for handle",
|
||||||
handleId,
|
handleId,
|
||||||
" Got data:",
|
" Got data:",
|
||||||
resp.data,
|
JSON.stringify(resp.data),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
"Failed to load plan with handle",
|
"[EndorserServer] Failed to load plan with handle",
|
||||||
handleId,
|
handleId,
|
||||||
" Got error:",
|
" Got error:",
|
||||||
error,
|
JSON.stringify(error),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { handleApiError } from "./services/api";
|
|||||||
import { loadPlanWithRetry } from "./services/plan";
|
import { loadPlanWithRetry } from "./services/plan";
|
||||||
import { Capacitor } from '@capacitor/core';
|
import { Capacitor } from '@capacitor/core';
|
||||||
|
|
||||||
|
console.log("[Capacitor] Starting initialization");
|
||||||
|
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
|
||||||
|
|
||||||
const app = initializeApp();
|
const app = initializeApp();
|
||||||
|
|
||||||
// Store initial deep link if app is not ready
|
// Store initial deep link if app is not ready
|
||||||
@@ -20,44 +23,32 @@ window.addEventListener('unhandledrejection', (event) => {
|
|||||||
// Create reusable handler function
|
// Create reusable handler function
|
||||||
const handleDeepLink = async (data: { url: string }) => {
|
const handleDeepLink = async (data: { url: string }) => {
|
||||||
try {
|
try {
|
||||||
if (Capacitor.isNativePlatform()) {
|
|
||||||
console.log("[Capacitor Deep Link] START Handler");
|
console.log("[Capacitor Deep Link] START Handler");
|
||||||
console.log("[Capacitor Deep Link] Received URL:", data.url);
|
console.log("[Capacitor Deep Link] Received URL:", data.url);
|
||||||
|
|
||||||
|
// Wait for router to be ready
|
||||||
|
await router.isReady();
|
||||||
|
|
||||||
|
// Parse the custom URL scheme
|
||||||
|
const parts = data.url.split('://');
|
||||||
|
if (parts.length !== 2) {
|
||||||
|
throw new Error('Invalid URL format');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for app to be mounted
|
const path = parts[1]; // This will be "claim/01JMAAFZRNSRTQ0EBSD70A8E1H"
|
||||||
if (!app._container) {
|
|
||||||
console.log("[Capacitor Deep Link] Waiting for app mount");
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
if (app._container) {
|
|
||||||
clearInterval(interval);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL(data.url);
|
|
||||||
const path = url.pathname.substring(1);
|
|
||||||
|
|
||||||
if (Capacitor.isNativePlatform()) {
|
|
||||||
console.log("[Capacitor Deep Link] Parsed path:", path);
|
console.log("[Capacitor Deep Link] Parsed path:", path);
|
||||||
}
|
|
||||||
|
|
||||||
// Map parameterized routes
|
// Map parameterized routes
|
||||||
const paramRoutes = {
|
const paramRoutes = {
|
||||||
'claim': /^claim\/(.+)$/,
|
'claim': /^claim\/(.+)$/, // Updated pattern without leading slash
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if path matches any parameterized route
|
// Check if path matches any parameterized route
|
||||||
for (const [routeName, pattern] of Object.entries(paramRoutes)) {
|
for (const [routeName, pattern] of Object.entries(paramRoutes)) {
|
||||||
const match = path.match(pattern);
|
const match = path.match(pattern);
|
||||||
if (match) {
|
if (match) {
|
||||||
if (Capacitor.isNativePlatform()) {
|
|
||||||
console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`);
|
console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`);
|
||||||
}
|
await router.replace({
|
||||||
await router.push({
|
|
||||||
name: routeName,
|
name: routeName,
|
||||||
params: { id: match[1] }
|
params: { id: match[1] }
|
||||||
});
|
});
|
||||||
@@ -65,7 +56,7 @@ const handleDeepLink = async (data: { url: string }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await router.push('/' + path);
|
await router.replace('/' + path);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[Capacitor Deep Link] Error:", error);
|
console.error("[Capacitor Deep Link] Error:", error);
|
||||||
handleApiError(error, 'deep-link');
|
handleApiError(error, 'deep-link');
|
||||||
@@ -75,5 +66,6 @@ const handleDeepLink = async (data: { url: string }) => {
|
|||||||
// Register listener
|
// Register listener
|
||||||
App.addListener("appUrlOpen", handleDeepLink);
|
App.addListener("appUrlOpen", handleDeepLink);
|
||||||
|
|
||||||
// Mount app
|
console.log("[Capacitor] Mounting app");
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
console.log("[Capacitor] App mounted");
|
||||||
@@ -11,12 +11,17 @@ import Camera from "simple-vue-camera";
|
|||||||
|
|
||||||
// Global Error Handler
|
// Global Error Handler
|
||||||
function setupGlobalErrorHandler(app: VueApp) {
|
function setupGlobalErrorHandler(app: VueApp) {
|
||||||
|
console.log("[App Init] Setting up global error handler");
|
||||||
app.config.errorHandler = (
|
app.config.errorHandler = (
|
||||||
err: unknown,
|
err: unknown,
|
||||||
instance: ComponentPublicInstance | null,
|
instance: ComponentPublicInstance | null,
|
||||||
info: string
|
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(
|
alert(
|
||||||
(err instanceof Error ? err.message : "Something bad happened") +
|
(err instanceof Error ? err.message : "Something bad happened") +
|
||||||
" - Try reloading or restarting the app."
|
" - Try reloading or restarting the app."
|
||||||
@@ -26,15 +31,31 @@ function setupGlobalErrorHandler(app: VueApp) {
|
|||||||
|
|
||||||
// Function to initialize the app
|
// Function to initialize the app
|
||||||
export function initializeApp() {
|
export function initializeApp() {
|
||||||
const app = createApp(App)
|
console.log("[App Init] Starting app initialization");
|
||||||
.component("fa", FontAwesomeIcon)
|
console.log("[App Init] Platform:", process.env.VITE_PLATFORM);
|
||||||
.component("camera", Camera)
|
|
||||||
.use(createPinia())
|
const app = createApp(App);
|
||||||
.use(VueAxios, axios)
|
console.log("[App Init] Vue app created");
|
||||||
.use(router)
|
|
||||||
.use(Notifications);
|
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);
|
setupGlobalErrorHandler(app);
|
||||||
|
console.log("[App Init] App initialization complete");
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
export const handleApiError = (error: any, endpoint: string) => {
|
export const handleApiError = (error: any, endpoint: string) => {
|
||||||
if (process.env.VITE_PLATFORM === 'capacitor') {
|
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
|
// Specific handling for rate limits
|
||||||
|
|||||||
@@ -1,19 +1,71 @@
|
|||||||
export const loadPlanWithRetry = async (handle: string, retries = 3) => {
|
import axios from 'axios';
|
||||||
for (let i = 0; i < retries; i++) {
|
|
||||||
|
interface PlanResponse {
|
||||||
|
data?: any;
|
||||||
|
status?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadPlanWithRetry = async (handle: string, retries = 3): Promise<PlanResponse> => {
|
||||||
try {
|
try {
|
||||||
const plan = await loadPlan(handle);
|
console.log(`[Plan Service] Loading plan ${handle}, attempt 1/${retries}`);
|
||||||
if (plan) return plan;
|
console.log(`[Plan Service] Context: Deep link handle=${handle}, isClaimFlow=${handle.includes('claim')}`);
|
||||||
} catch (err) {
|
|
||||||
console.warn(`[Plan Load] Attempt ${i + 1} failed for ${handle}`);
|
// Different endpoint if this is a claim flow
|
||||||
if (i === retries - 1) throw err;
|
const response = await loadPlan(handle);
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
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) => {
|
export const loadPlan = async (handle: string): Promise<PlanResponse> => {
|
||||||
// Implement your plan loading logic here
|
console.log(`[Plan Service] Making API request for plan ${handle}`);
|
||||||
// This is a placeholder - replace with actual implementation
|
|
||||||
const response = await fetch(`/api/plans/${handle}`);
|
const endpoint = handle.includes('claim')
|
||||||
return response.json();
|
? `/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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -585,6 +585,7 @@ export default class ClaimView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
|
console.log("ClaimView created");
|
||||||
const settings = await retrieveSettingsForActiveAccount();
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
this.activeDid = settings.activeDid || "";
|
this.activeDid = settings.activeDid || "";
|
||||||
this.apiServer = settings.apiServer || "";
|
this.apiServer = settings.apiServer || "";
|
||||||
@@ -659,6 +660,7 @@ export default class ClaimView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadClaim(claimId: string, userDid: string) {
|
async loadClaim(claimId: string, userDid: string) {
|
||||||
|
console.log("[ClaimView] loadClaim called with claimId:", claimId);
|
||||||
const urlPath = libsUtil.isGlobalUri(claimId)
|
const urlPath = libsUtil.isGlobalUri(claimId)
|
||||||
? "/api/claim/byHandle/"
|
? "/api/claim/byHandle/"
|
||||||
: "/api/claim/";
|
: "/api/claim/";
|
||||||
@@ -666,6 +668,7 @@ export default class ClaimView extends Vue {
|
|||||||
const headers = await serverUtil.getHeaders(userDid);
|
const headers = await serverUtil.getHeaders(userDid);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log("[ClaimView] Making API request to:", url);
|
||||||
const resp = await this.axios.get(url, { headers });
|
const resp = await this.axios.get(url, { headers });
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
this.veriClaim = resp.data;
|
this.veriClaim = resp.data;
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export async function createBuildConfig(mode: string) {
|
|||||||
const isCapacitor = mode === "capacitor";
|
const isCapacitor = mode === "capacitor";
|
||||||
const isPyWebView = mode === "pywebview";
|
const isPyWebView = mode === "pywebview";
|
||||||
|
|
||||||
|
// Explicitly set platform
|
||||||
|
process.env.VITE_PLATFORM = mode;
|
||||||
|
|
||||||
if (isElectron || isPyWebView || isCapacitor) {
|
if (isElectron || isPyWebView || isCapacitor) {
|
||||||
process.env.VITE_PWA_ENABLED = 'false';
|
process.env.VITE_PWA_ENABLED = 'false';
|
||||||
}
|
}
|
||||||
@@ -35,6 +38,7 @@ export async function createBuildConfig(mode: string) {
|
|||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
'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)),
|
'process.env.VITE_PWA_ENABLED': JSON.stringify(!(isElectron || isPyWebView || isCapacitor)),
|
||||||
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
|
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user