From bfce9b525a7c4064ea22689ba34cd1491d01a7ca Mon Sep 17 00:00:00 2001 From: Gabe Date: Sun, 12 Oct 2025 23:17:50 +0800 Subject: [PATCH] feat: support dynamically set log level --- src/background.js | 19 +++++++--- src/common.js | 10 +++-- src/config/i18n.js | 5 +++ src/config/setting.js | 2 + src/hooks/Setting.js | 19 +++++++++- src/libs/log.js | 51 ++++++++++++++++++++++---- src/subtitle/YouTubeCaptionProvider.js | 11 ++++++ src/views/Options/Setting.js | 20 +++++++++- 8 files changed, 119 insertions(+), 18 deletions(-) diff --git a/src/background.js b/src/background.js index af87b43..d31d2b9 100644 --- a/src/background.js +++ b/src/background.js @@ -32,7 +32,7 @@ import { trySyncAllSubRules } from "./libs/subRules"; import { saveRule } from "./libs/rules"; import { getCurTabId } from "./libs/msg"; import { injectInlineJs, injectInternalCss } from "./libs/injector"; -import { kissLog } from "./libs/log"; +import { kissLog, logger } from "./libs/log"; import { chromeDetect, chromeTranslate } from "./libs/builtinAI"; globalThis.ContextType = "BACKGROUND"; @@ -210,11 +210,17 @@ browser.runtime.onInstalled.addListener(() => { * 浏览器启动 */ browser.runtime.onStartup.addListener(async () => { - // 同步数据 - await trySyncSettingAndRules(); + const { + clearCache, + contextMenuType, + subrulesList, + csplist, + orilist, + logLevel, + } = await getSettingWithDefault(); - const { clearCache, contextMenuType, subrulesList, csplist, orilist } = - await getSettingWithDefault(); + // 设置日志 + logger.setLevel(logLevel); // 清除缓存 if (clearCache) { @@ -233,6 +239,9 @@ browser.runtime.onStartup.addListener(async () => { // 禁用CSP updateCspRules({ csplist, orilist }); + // 同步数据 + trySyncSettingAndRules(); + // 同步订阅规则 trySyncAllSubRules({ subrulesList }); }); diff --git a/src/common.js b/src/common.js index 043af27..5a1c91c 100644 --- a/src/common.js +++ b/src/common.js @@ -19,6 +19,7 @@ import { matchRule } from "./libs/rules"; import { trySyncAllSubRules } from "./libs/subRules"; import { isInBlacklist } from "./libs/blacklist"; import { runSubtitle } from "./subtitle/subtitle"; +import { logger } from "./libs/log"; /** * 油猴脚本设置页面 @@ -180,6 +181,12 @@ function touchOperation(translator) { */ export async function run(isUserscript = false) { try { + // 读取设置信息 + const setting = await getSettingWithDefault(); + + // 日志 + logger.setLevel(setting.logLevel); + const href = document.location.href; // 设置页面 @@ -192,9 +199,6 @@ export async function run(isUserscript = false) { return; } - // 读取设置信息 - const setting = await getSettingWithDefault(); - // 黑名单 if (isInBlacklist(href, setting)) { return; diff --git a/src/config/i18n.js b/src/config/i18n.js index efba8bf..f2f6a33 100644 --- a/src/config/i18n.js +++ b/src/config/i18n.js @@ -1628,6 +1628,11 @@ export const I18N = { en: `The subtitle data is ready, please click the KT button to load it`, zh_TW: `字幕資料已準備就緒,請點擊KT按鈕加載`, }, + log_level: { + zh: `日志级别`, + en: `Log Level`, + zh_TW: `日誌等級`, + }, }; export const i18n = (lang) => (key) => I18N[key]?.[lang] || ""; diff --git a/src/config/setting.js b/src/config/setting.js index 2671fed..726a768 100644 --- a/src/config/setting.js +++ b/src/config/setting.js @@ -1,3 +1,4 @@ +import { LogLevel } from "../libs/log"; import { OPT_DICT_BING, OPT_SUG_YOUDAO, @@ -181,4 +182,5 @@ export const DEFAULT_SETTING = { preInit: true, // 是否预加载脚本 transAllnow: false, // 是否立即全部翻译 subtitleSetting: DEFAULT_SUBTITLE_SETTING, // 字幕设置 + logLevel: LogLevel.INFO.value, // 日志级别 }; diff --git a/src/hooks/Setting.js b/src/hooks/Setting.js index f9b2957..7a822d3 100644 --- a/src/hooks/Setting.js +++ b/src/hooks/Setting.js @@ -1,9 +1,16 @@ -import { createContext, useCallback, useContext, useMemo } from "react"; +import { + createContext, + useCallback, + useContext, + useMemo, + useEffect, +} from "react"; import Alert from "@mui/material/Alert"; import { STOKEY_SETTING, DEFAULT_SETTING, KV_SETTING_KEY } from "../config"; import { useStorage } from "./Storage"; import { debounceSyncMeta } from "../libs/storage"; import Loading from "./Loading"; +import { logger } from "../libs/log"; const SettingContext = createContext({ setting: DEFAULT_SETTING, @@ -19,6 +26,16 @@ export function SettingProvider({ children }) { reload, } = useStorage(STOKEY_SETTING, DEFAULT_SETTING, KV_SETTING_KEY); + useEffect(() => { + (async () => { + try { + logger.setLevel(setting?.logLevel); + } catch (error) { + logger.error("Failed to fetch log level, using default.", error); + } + })(); + }, [setting]); + const updateSetting = useCallback( (objOrFn) => { update(objOrFn); diff --git a/src/libs/log.js b/src/libs/log.js index 0b04577..515fcc2 100644 --- a/src/libs/log.js +++ b/src/libs/log.js @@ -7,6 +7,16 @@ export const LogLevel = { SILENT: { value: 4, name: "SILENT" }, // 特殊级别,用于关闭所有日志 }; +function findLogLevelByValue(value) { + return Object.values(LogLevel).find((level) => level.value === value); +} + +function findLogLevelByName(name) { + if (typeof name !== "string" || name.length === 0) return undefined; + const upperCaseName = name.toUpperCase(); + return Object.values(LogLevel).find((level) => level.name === upperCaseName); +} + class Logger { /** * @param {object} [options={}] 配置选项 @@ -25,10 +35,37 @@ class Logger { * @param {LogLevel} level - 新的日志级别 */ setLevel(level) { - if (level && typeof level.value === "number") { - this.config.level = level; - console.log(`[${this.config.prefix}] Log level set to ${level.name}`); + let newLevelObject; + + if (typeof level === "string") { + newLevelObject = findLogLevelByName(level); + if (!newLevelObject) { + this.warn( + `Invalid log level name provided: "${level}". Keeping current level.` + ); + return; + } + } else if (typeof level === "number") { + newLevelObject = findLogLevelByValue(level); + if (!newLevelObject) { + this.warn( + `Invalid log level value provided: ${level}. Keeping current level.` + ); + return; + } + } else if (level && typeof level.value === "number") { + newLevelObject = level; + } else { + this.warn( + "Invalid argument passed to setLevel. Must be a LogLevel object, number, or string." + ); + return; } + + this.config.level = newLevelObject; + console.log( + `[${this.config.prefix}] Log level dynamically set to ${this.config.level.name}` + ); } /** @@ -118,9 +155,7 @@ class Logger { } } -const isDevelopment = - typeof process === "undefined" || process.env.NODE_ENV !== "development"; -const defaultLevel = isDevelopment ? LogLevel.DEBUG : LogLevel.INFO; - -export const logger = new Logger({ level: defaultLevel }); +export const logger = new Logger(); export const kissLog = logger.info.bind(logger); + +// todo:debug日志埋点 diff --git a/src/subtitle/YouTubeCaptionProvider.js b/src/subtitle/YouTubeCaptionProvider.js index da7e9bc..27cb10a 100644 --- a/src/subtitle/YouTubeCaptionProvider.js +++ b/src/subtitle/YouTubeCaptionProvider.js @@ -231,6 +231,13 @@ class YouTubeCaptionProvider { try { const events = chunkEvents.filter((item) => item.text); const chunkSign = `${events[0].start} --> ${events[events.length - 1].end}`; + logger.debug("Youtube Provider: aiSegment events", { + videoId, + chunkSign, + fromLang, + toLang, + events, + }); const subtitles = await apiSubtitle({ videoId, chunkSign, @@ -239,6 +246,7 @@ class YouTubeCaptionProvider { events, apiSetting: segApiSetting, }); + logger.debug("Youtube Provider: aiSegment subtitles", subtitles); if (Array.isArray(subtitles)) { return subtitles; } @@ -300,6 +308,9 @@ class YouTubeCaptionProvider { OPT_LANGS_TO_CODE[OPT_TRANS_MICROSOFT].get(lang.slice(0, 2)) || "auto"; + logger.debug( + `Youtube Provider: fromLang: ${fromLang}, toLang: ${toLang}` + ); if (this.#isSameLang(fromLang, toLang)) { logger.info("Youtube Provider: skip same lang", fromLang, toLang); return; diff --git a/src/views/Options/Setting.js b/src/views/Options/Setting.js index 5ae228e..6cfa5e6 100644 --- a/src/views/Options/Setting.js +++ b/src/views/Options/Setting.js @@ -29,7 +29,7 @@ import { useShortcut } from "../../hooks/Shortcut"; import ShortcutInput from "./ShortcutInput"; import { useFab } from "../../hooks/Fab"; import { sendBgMsg } from "../../libs/msg"; -import { kissLog } from "../../libs/log"; +import { kissLog, LogLevel } from "../../libs/log"; import UploadButton from "./UploadButton"; import DownloadButton from "./DownloadButton"; import { getSettingOld } from "../../libs/storage"; @@ -99,6 +99,7 @@ export default function Settings() { orilist = DEFAULT_ORILIST.join(",\n"), transInterval = 100, langDetector = "-", + logLevel = 1, preInit = true, skipLangs = [], // detectRemote = true, @@ -337,6 +338,23 @@ export default function Settings() { ))} + + + {Object.values(LogLevel).map(({ value, name }) => ( + + {name} + + ))} + +