102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { router } from 'expo-router'
|
|
import * as SecureStore from 'expo-secure-store'
|
|
|
|
interface FetchLoggerOptions {
|
|
enableLogging?: boolean;
|
|
logRequest?: boolean;
|
|
logResponse?: boolean;
|
|
logError?: boolean;
|
|
}
|
|
|
|
const defaultOptions: FetchLoggerOptions = {
|
|
enableLogging: __DEV__,
|
|
logRequest: true,
|
|
logResponse: true,
|
|
logError: true,
|
|
};
|
|
|
|
const originalFetch = global.fetch;
|
|
|
|
export const createFetchWithLogger = (options: FetchLoggerOptions = {}) => {
|
|
const config = { ...defaultOptions, ...options };
|
|
|
|
return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
|
const startTime = Date.now();
|
|
const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
|
|
const method = init?.method || 'GET';
|
|
|
|
try {
|
|
const token = await SecureStore.getItemAsync('token');
|
|
|
|
if (token) {
|
|
init = {
|
|
...init,
|
|
headers: {
|
|
...init?.headers,
|
|
'authorization': `Bearer ${token}`,
|
|
},
|
|
};
|
|
}
|
|
|
|
const response = await originalFetch(input, init);
|
|
|
|
if (response.status === 401) {
|
|
console.warn('🔐 401 未授权,跳转到登录页')
|
|
router.replace('/auth')
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
|
|
if (config.enableLogging && config.logError) {
|
|
console.group(`❌ [FETCH 错误] ${method} ${url}`);
|
|
console.error('📍 URL:', url);
|
|
console.error('🔧 Method:', method);
|
|
console.error('⏱️ Duration:', `${duration}ms`);
|
|
|
|
if (init?.headers) {
|
|
console.error('📋 Request Headers:', JSON.stringify(init.headers, null, 2));
|
|
}
|
|
|
|
if (init?.body) {
|
|
try {
|
|
const bodyContent = typeof init.body === 'string'
|
|
? init.body
|
|
: JSON.stringify(init.body);
|
|
console.error('📦 Request Body:', bodyContent);
|
|
} catch (e) {
|
|
console.error('📦 Request Body: [无法序列化]');
|
|
}
|
|
}
|
|
|
|
console.error('💥 Error Type:', error?.constructor?.name || 'Unknown');
|
|
console.error('💥 Error Message:', error instanceof Error ? error.message : String(error));
|
|
|
|
if (error instanceof Error && error.stack) {
|
|
console.error('📚 Stack Trace:', error.stack);
|
|
}
|
|
|
|
if (error && typeof error === 'object') {
|
|
console.error('🔍 Error Details:', JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
|
|
}
|
|
|
|
console.groupEnd();
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
};
|
|
};
|
|
|
|
export const fetchWithLogger = createFetchWithLogger();
|
|
|
|
export const setupGlobalFetchLogger = (options?: FetchLoggerOptions) => {
|
|
const loggedFetch = createFetchWithLogger(options);
|
|
global.fetch = loggedFetch as typeof fetch;
|
|
|
|
return () => {
|
|
global.fetch = originalFetch;
|
|
};
|
|
};
|