refactor(logging): implement unified Logger system to replace console calls

- Add Logger utility with tagged output and environment-based control
- Configure Babel to remove console calls in production builds
- Replace all console.* calls across stores, services, and components with Logger
- Enable development-only logging with formatted output and component tags
- Optimize production builds by eliminating all logging code
This commit is contained in:
zimplexing
2025-08-15 22:57:38 +08:00
parent 836285dbd5
commit e57466c8c1
25 changed files with 404 additions and 200 deletions

View File

@@ -1,4 +1,8 @@
import Logger from '@/utils/Logger';
const logger = Logger.withTag('API');
// region: --- Interface Definitions ---
export interface DoubanItem {
title: string;
@@ -215,13 +219,13 @@ export class API {
// 添加安全检查
if (!config || !config.Config.SourceConfig) {
console.warn('API response missing SourceConfig:', config);
logger.warn('API response missing SourceConfig:', config);
return [];
}
// 确保 SourceConfig 是数组
if (!Array.isArray(config.Config.SourceConfig)) {
console.warn('SourceConfig is not an array:', config.Config.SourceConfig);
logger.warn('SourceConfig is not an array:', config.Config.SourceConfig);
return [];
}

View File

@@ -1,3 +1,7 @@
import Logger from '@/utils/Logger';
const logger = Logger.withTag('M3U');
export interface Channel {
id: string;
name: string;
@@ -59,7 +63,7 @@ export const fetchAndParseM3u = async (m3uUrl: string): Promise<Channel[]> => {
const m3uText = await response.text();
return parseM3U(m3uText);
} catch (error) {
console.info("Error fetching or parsing M3U:", error);
logger.info("Error fetching or parsing M3U:", error);
return []; // Return empty array on error
}
};

View File

@@ -1,3 +1,7 @@
import Logger from '@/utils/Logger';
const logger = Logger.withTag('M3U8');
interface CacheEntry {
resolution: string | null;
timestamp: number;
@@ -11,18 +15,18 @@ export const getResolutionFromM3U8 = async (
signal?: AbortSignal
): Promise<string | null> => {
const perfStart = performance.now();
console.info(`[PERF] M3U8 resolution detection START - url: ${url.substring(0, 100)}...`);
logger.info(`[PERF] M3U8 resolution detection START - url: ${url.substring(0, 100)}...`);
// 1. Check cache first
const cachedEntry = resolutionCache[url];
if (cachedEntry && Date.now() - cachedEntry.timestamp < CACHE_DURATION) {
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection CACHED - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${cachedEntry.resolution}`);
logger.info(`[PERF] M3U8 resolution detection CACHED - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${cachedEntry.resolution}`);
return cachedEntry.resolution;
}
if (!url.toLowerCase().endsWith(".m3u8")) {
console.info(`[PERF] M3U8 resolution detection SKIPPED - not M3U8 file`);
logger.info(`[PERF] M3U8 resolution detection SKIPPED - not M3U8 file`);
return null;
}
@@ -30,7 +34,7 @@ export const getResolutionFromM3U8 = async (
const fetchStart = performance.now();
const response = await fetch(url, { signal });
const fetchEnd = performance.now();
console.info(`[PERF] M3U8 fetch took ${(fetchEnd - fetchStart).toFixed(2)}ms, status: ${response.status}`);
logger.info(`[PERF] M3U8 fetch took ${(fetchEnd - fetchStart).toFixed(2)}ms, status: ${response.status}`);
if (!response.ok) {
return null;
@@ -56,7 +60,7 @@ export const getResolutionFromM3U8 = async (
}
const parseEnd = performance.now();
console.info(`[PERF] M3U8 parsing took ${(parseEnd - parseStart).toFixed(2)}ms, lines: ${lines.length}`);
logger.info(`[PERF] M3U8 parsing took ${(parseEnd - parseStart).toFixed(2)}ms, lines: ${lines.length}`);
// 2. Store result in cache
resolutionCache[url] = {
@@ -65,12 +69,12 @@ export const getResolutionFromM3U8 = async (
};
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection COMPLETE - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${resolutionString}`);
logger.info(`[PERF] M3U8 resolution detection COMPLETE - took ${(perfEnd - perfStart).toFixed(2)}ms, resolution: ${resolutionString}`);
return resolutionString;
} catch (error) {
const perfEnd = performance.now();
console.info(`[PERF] M3U8 resolution detection ERROR - took ${(perfEnd - perfStart).toFixed(2)}ms, error: ${error}`);
logger.info(`[PERF] M3U8 resolution detection ERROR - took ${(perfEnd - perfStart).toFixed(2)}ms, error: ${error}`);
return null;
}
};

View File

@@ -1,4 +1,7 @@
import TCPHttpServer from "./tcpHttpServer";
import Logger from '@/utils/Logger';
const logger = Logger.withTag('RemoteControl');
const getRemotePageHTML = () => {
return `
@@ -25,7 +28,7 @@ const getRemotePageHTML = () => {
</div>
<script>
window.addEventListener('DOMContentLoaded', () => {
fetch('/handshake', { method: 'POST' }).catch(console.info);
fetch('/handshake', { method: 'POST' }).catch(err => logger.info('Handshake failed:', err));
});
function send() {
const input = document.getElementById("text");
@@ -36,7 +39,7 @@ const getRemotePageHTML = () => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: value })
})
.catch(err => console.info(err));
.catch(err => logger.info('Message send failed:', err));
input.value = '';
}
}
@@ -58,7 +61,7 @@ class RemoteControlService {
private setupRequestHandler() {
this.httpServer.setRequestHandler((request) => {
console.log("[RemoteControl] Received request:", request.method, request.url);
logger.debug("[RemoteControl] Received request:", request.method, request.url);
try {
if (request.method === "GET" && request.url === "/") {
@@ -80,7 +83,7 @@ class RemoteControlService {
body: JSON.stringify({ status: "ok" }),
};
} catch (parseError) {
console.info("[RemoteControl] Failed to parse message body:", parseError);
logger.info("[RemoteControl] Failed to parse message body:", parseError);
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
@@ -102,7 +105,7 @@ class RemoteControlService {
};
}
} catch (error) {
console.info("[RemoteControl] Request handler error:", error);
logger.info("[RemoteControl] Request handler error:", error);
return {
statusCode: 500,
headers: { "Content-Type": "application/json" },
@@ -118,20 +121,20 @@ class RemoteControlService {
}
public async startServer(): Promise<string> {
console.log("[RemoteControl] Attempting to start server...");
logger.debug("[RemoteControl] Attempting to start server...");
try {
const url = await this.httpServer.start();
console.log(`[RemoteControl] Server started successfully at: ${url}`);
logger.debug(`[RemoteControl] Server started successfully at: ${url}`);
return url;
} catch (error) {
console.info("[RemoteControl] Failed to start server:", error);
logger.info("[RemoteControl] Failed to start server:", error);
throw new Error(error instanceof Error ? error.message : "Failed to start server");
}
}
public stopServer() {
console.log("[RemoteControl] Stopping server...");
logger.debug("[RemoteControl] Stopping server...");
this.httpServer.stop();
}

View File

@@ -1,6 +1,9 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { api, PlayRecord as ApiPlayRecord, Favorite as ApiFavorite } from "./api";
import { storageConfig } from "./storageConfig";
import Logger from '@/utils/Logger';
const logger = Logger.withTag('Storage');
// --- Storage Keys ---
const STORAGE_KEYS = {
@@ -53,20 +56,20 @@ export class PlayerSettingsManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.PLAYER_SETTINGS);
return data ? JSON.parse(data) : {};
} catch (error) {
console.info("Failed to get all player settings:", error);
logger.info("Failed to get all player settings:", error);
return {};
}
}
static async get(source: string, id: string): Promise<PlayerSettings | null> {
const perfStart = performance.now();
console.info(`[PERF] PlayerSettingsManager.get START - source: ${source}, id: ${id}`);
logger.info(`[PERF] PlayerSettingsManager.get START - source: ${source}, id: ${id}`);
const allSettings = await this.getAll();
const result = allSettings[generateKey(source, id)] || null;
const perfEnd = performance.now();
console.info(`[PERF] PlayerSettingsManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
logger.info(`[PERF] PlayerSettingsManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
return result;
}
@@ -107,7 +110,7 @@ export class FavoriteManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.FAVORITES);
return data ? JSON.parse(data) : {};
} catch (error) {
console.info("Failed to get all local favorites:", error);
logger.info("Failed to get all local favorites:", error);
return {};
}
}
@@ -175,7 +178,7 @@ export class PlayRecordManager {
static async getAll(): Promise<Record<string, PlayRecord>> {
const perfStart = performance.now();
const storageType = this.getStorageType();
console.info(`[PERF] PlayRecordManager.getAll START - storageType: ${storageType}`);
logger.info(`[PERF] PlayRecordManager.getAll START - storageType: ${storageType}`);
let apiRecords: Record<string, PlayRecord> = {};
if (storageType === "localstorage") {
@@ -183,17 +186,17 @@ export class PlayRecordManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.PLAY_RECORDS);
apiRecords = data ? JSON.parse(data) : {};
} catch (error) {
console.info("Failed to get all local play records:", error);
logger.info("Failed to get all local play records:", error);
return {};
}
} else {
const apiStart = performance.now();
console.info(`[PERF] API getPlayRecords START`);
logger.info(`[PERF] API getPlayRecords START`);
apiRecords = await api.getPlayRecords();
const apiEnd = performance.now();
console.info(`[PERF] API getPlayRecords END - took ${(apiEnd - apiStart).toFixed(2)}ms, records: ${Object.keys(apiRecords).length}`);
logger.info(`[PERF] API getPlayRecords END - took ${(apiEnd - apiStart).toFixed(2)}ms, records: ${Object.keys(apiRecords).length}`);
}
const localSettings = await PlayerSettingsManager.getAll();
@@ -206,7 +209,7 @@ export class PlayRecordManager {
}
const perfEnd = performance.now();
console.info(`[PERF] PlayRecordManager.getAll END - took ${(perfEnd - perfStart).toFixed(2)}ms, total records: ${Object.keys(mergedRecords).length}`);
logger.info(`[PERF] PlayRecordManager.getAll END - took ${(perfEnd - perfStart).toFixed(2)}ms, total records: ${Object.keys(mergedRecords).length}`);
return mergedRecords;
}
@@ -232,13 +235,13 @@ export class PlayRecordManager {
const perfStart = performance.now();
const key = generateKey(source, id);
const storageType = this.getStorageType();
console.info(`[PERF] PlayRecordManager.get START - source: ${source}, id: ${id}, storageType: ${storageType}`);
logger.info(`[PERF] PlayRecordManager.get START - source: ${source}, id: ${id}, storageType: ${storageType}`);
const records = await this.getAll();
const result = records[key] || null;
const perfEnd = performance.now();
console.info(`[PERF] PlayRecordManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
logger.info(`[PERF] PlayRecordManager.get END - took ${(perfEnd - perfStart).toFixed(2)}ms, found: ${!!result}`);
return result;
}
@@ -279,7 +282,7 @@ export class SearchHistoryManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.SEARCH_HISTORY);
return data ? JSON.parse(data) : [];
} catch (error) {
console.info("Failed to get local search history:", error);
logger.info("Failed to get local search history:", error);
return [];
}
}
@@ -324,7 +327,7 @@ export class SettingsManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.SETTINGS);
return data ? { ...defaultSettings, ...JSON.parse(data) } : defaultSettings;
} catch (error) {
console.info("Failed to get settings:", error);
logger.info("Failed to get settings:", error);
return defaultSettings;
}
}
@@ -347,7 +350,7 @@ export class LoginCredentialsManager {
const data = await AsyncStorage.getItem(STORAGE_KEYS.LOGIN_CREDENTIALS);
return data ? JSON.parse(data) : null;
} catch (error) {
console.info("Failed to get login credentials:", error);
logger.info("Failed to get login credentials:", error);
return null;
}
}
@@ -356,7 +359,7 @@ export class LoginCredentialsManager {
try {
await AsyncStorage.setItem(STORAGE_KEYS.LOGIN_CREDENTIALS, JSON.stringify(credentials));
} catch (error) {
console.error("Failed to save login credentials:", error);
logger.error("Failed to save login credentials:", error);
}
}
@@ -364,7 +367,7 @@ export class LoginCredentialsManager {
try {
await AsyncStorage.removeItem(STORAGE_KEYS.LOGIN_CREDENTIALS);
} catch (error) {
console.error("Failed to clear login credentials:", error);
logger.error("Failed to clear login credentials:", error);
}
}
}

View File

@@ -1,5 +1,8 @@
import TcpSocket from 'react-native-tcp-socket';
import NetInfo from '@react-native-community/netinfo';
import Logger from '@/utils/Logger';
const logger = Logger.withTag('TCPHttpServer');
const PORT = 12346;
@@ -59,7 +62,7 @@ class TCPHttpServer {
return { method, url, headers, body };
} catch (error) {
console.info('[TCPHttpServer] Error parsing HTTP request:', error);
logger.info('[TCPHttpServer] Error parsing HTTP request:', error);
return null;
}
}
@@ -108,14 +111,14 @@ class TCPHttpServer {
}
if (this.isRunning) {
console.log('[TCPHttpServer] Server is already running.');
logger.debug('[TCPHttpServer] Server is already running.');
return `http://${ipAddress}:${PORT}`;
}
return new Promise((resolve, reject) => {
try {
this.server = TcpSocket.createServer((socket: TcpSocket.Socket) => {
console.log('[TCPHttpServer] Client connected');
logger.debug('[TCPHttpServer] Client connected');
let requestData = '';
@@ -140,7 +143,7 @@ class TCPHttpServer {
socket.write(errorResponse);
}
} catch (error) {
console.info('[TCPHttpServer] Error handling request:', error);
logger.info('[TCPHttpServer] Error handling request:', error);
const errorResponse = this.formatHttpResponse({
statusCode: 500,
headers: { 'Content-Type': 'text/plain' },
@@ -155,28 +158,28 @@ class TCPHttpServer {
});
socket.on('error', (error: Error) => {
console.info('[TCPHttpServer] Socket error:', error);
logger.info('[TCPHttpServer] Socket error:', error);
});
socket.on('close', () => {
console.log('[TCPHttpServer] Client disconnected');
logger.debug('[TCPHttpServer] Client disconnected');
});
});
this.server.listen({ port: PORT, host: '0.0.0.0' }, () => {
console.log(`[TCPHttpServer] Server listening on ${ipAddress}:${PORT}`);
logger.debug(`[TCPHttpServer] Server listening on ${ipAddress}:${PORT}`);
this.isRunning = true;
resolve(`http://${ipAddress}:${PORT}`);
});
this.server.on('error', (error: Error) => {
console.info('[TCPHttpServer] Server error:', error);
logger.info('[TCPHttpServer] Server error:', error);
this.isRunning = false;
reject(error);
});
} catch (error) {
console.info('[TCPHttpServer] Failed to start server:', error);
logger.info('[TCPHttpServer] Failed to start server:', error);
reject(error);
}
});
@@ -187,7 +190,7 @@ class TCPHttpServer {
this.server.close();
this.server = null;
this.isRunning = false;
console.log('[TCPHttpServer] Server stopped');
logger.debug('[TCPHttpServer] Server stopped');
}
}

View File

@@ -3,6 +3,9 @@ import FileViewer from "react-native-file-viewer";
import Toast from "react-native-toast-message";
import { version as currentVersion } from "../package.json";
import { UPDATE_CONFIG } from "../constants/UpdateConfig";
import Logger from '@/utils/Logger';
const logger = Logger.withTag('UpdateService');
interface VersionInfo {
version: string;
@@ -47,7 +50,7 @@ class UpdateService {
};
} catch (error) {
retries++;
console.info(`Error checking version (attempt ${retries}/${maxRetries}):`, error);
logger.info(`Error checking version (attempt ${retries}/${maxRetries}):`, error);
if (retries === maxRetries) {
Toast.show({ type: "error", text1: "检查更新失败", text2: "无法获取版本信息,请检查网络连接" });
@@ -86,14 +89,14 @@ class UpdateService {
for (const file of filesToDelete) {
try {
await ReactNativeBlobUtil.fs.unlink(`${dirs.DocumentDir}/${file}`);
console.log(`Cleaned old APK file: ${file}`);
logger.debug(`Cleaned old APK file: ${file}`);
} catch (deleteError) {
console.warn(`Failed to delete old APK file ${file}:`, deleteError);
logger.warn(`Failed to delete old APK file ${file}:`, deleteError);
}
}
}
} catch (error) {
console.warn('Failed to clean old APK files:', error);
logger.warn('Failed to clean old APK files:', error);
}
}
@@ -138,11 +141,11 @@ class UpdateService {
}
const res = await task;
console.log(`APK downloaded successfully: ${filePath}`);
logger.debug(`APK downloaded successfully: ${filePath}`);
return res.path();
} catch (error) {
retries++;
console.info(`Error downloading APK (attempt ${retries}/${maxRetries}):`, error);
logger.info(`Error downloading APK (attempt ${retries}/${maxRetries}):`, error);
if (retries === maxRetries) {
Toast.show({ type: "error", text1: "下载失败", text2: "APK下载失败请检查网络连接" });
@@ -173,7 +176,7 @@ class UpdateService {
displayName: "OrionTV Update",
});
} catch (error) {
console.info("Error installing APK:", error);
logger.info("Error installing APK:", error);
// 提供更详细的错误信息
if (error instanceof Error) {