diff --git a/app/composables/useMeilisearch.ts b/app/composables/useMeilisearch.ts index 05ffae6..ebf62fe 100644 --- a/app/composables/useMeilisearch.ts +++ b/app/composables/useMeilisearch.ts @@ -37,7 +37,7 @@ export const useMeilisearch = () => { const host = runtimeConfig.public?.meili?.host; if (!host) { - console.warn('Meilisearch host is not configured.'); + logger.warn('Meilisearch host is not configured.'); return null; } const apiKey = runtimeConfig.public?.meili?.searchKey; @@ -76,7 +76,7 @@ export const useMeilisearch = () => { const activeIndexes = indexes.value as K[]; if (!activeIndexes.length) { - console.warn('No Meilisearch indexes configured.'); + logger.warn('No Meilisearch indexes configured.'); return []; } const rawIndexMap = Object.fromEntries( @@ -105,7 +105,7 @@ export const useMeilisearch = () => { result.status === 'rejected' ) .forEach((result) => { - console.error('Meilisearch query failed', result.reason); + logger.error('Meilisearch query failed', result.reason); }); return settled diff --git a/app/pages/about/index.vue b/app/pages/about/index.vue index afd6b69..64dd05f 100644 --- a/app/pages/about/index.vue +++ b/app/pages/about/index.vue @@ -38,7 +38,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/index.vue b/app/pages/index.vue index d6556bc..f2eb413 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -23,7 +23,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/products/[...slug].vue b/app/pages/products/[...slug].vue index 0ecfd3a..cca08a3 100644 --- a/app/pages/products/[...slug].vue +++ b/app/pages/products/[...slug].vue @@ -55,7 +55,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/products/index.vue b/app/pages/products/index.vue index bb5aa99..28babe7 100644 --- a/app/pages/products/index.vue +++ b/app/pages/products/index.vue @@ -83,7 +83,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/search.vue b/app/pages/search.vue index f824f41..c29ac62 100644 --- a/app/pages/search.vue +++ b/app/pages/search.vue @@ -86,7 +86,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/solutions/[...slug].vue b/app/pages/solutions/[...slug].vue index cab1949..e3ec505 100644 --- a/app/pages/solutions/[...slug].vue +++ b/app/pages/solutions/[...slug].vue @@ -46,7 +46,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/solutions/index.vue b/app/pages/solutions/index.vue index f060d43..24f293d 100644 --- a/app/pages/solutions/index.vue +++ b/app/pages/solutions/index.vue @@ -83,7 +83,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/support/contact-us.vue b/app/pages/support/contact-us.vue index 1868f63..d6d09e2 100644 --- a/app/pages/support/contact-us.vue +++ b/app/pages/support/contact-us.vue @@ -28,7 +28,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/support/documents.vue b/app/pages/support/documents.vue index e340188..e67f386 100644 --- a/app/pages/support/documents.vue +++ b/app/pages/support/documents.vue @@ -104,7 +104,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/app/pages/support/faq.vue b/app/pages/support/faq.vue index 2c472d8..b47e058 100644 --- a/app/pages/support/faq.vue +++ b/app/pages/support/faq.vue @@ -105,7 +105,7 @@ watch(error, (value) => { if (value) { - console.error('数据获取失败: ', value); + logger.error('数据获取失败: ', value); } }); diff --git a/eslint.config.mjs b/eslint.config.mjs index 410eb6a..35af3ce 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,7 @@ export default withNuxt( }, }, ], + 'no-console': 'warn', }, } ); diff --git a/server/utils/file.ts b/server/utils/file.ts index 5d4ae83..117d151 100644 --- a/server/utils/file.ts +++ b/server/utils/file.ts @@ -46,7 +46,7 @@ export async function getFileMeta(id: string): Promise { }; } catch (error) { if (error instanceof Error) { - console.error('Error fetching file metadata:', error.message); + logger.error('Error fetching file metadata:', error.message); } return null; } diff --git a/shared/utils/logger.ts b/shared/utils/logger.ts new file mode 100644 index 0000000..4a6dfd4 --- /dev/null +++ b/shared/utils/logger.ts @@ -0,0 +1,112 @@ +/* eslint-disable no-console -- console is allowed inside logger implementation */ + +/** + * 日志等级枚举 + * + * - `debug` : 开发调试日志(最低) + * - `info` : 普通信息输出 + * - `warn` : 警告信息 + * - `error` : 错误信息(最高级别) + * - `silent` : 屏蔽所有日志 + */ +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; + +/** + * 每个日志级别的优先级数值 + * 数值越小,级别越低 + */ +const levelPriority: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3, + silent: 4, +}; + +/** + * 检测当前运行环境应使用的日志等级: + * + * - 开发环境:默认 `debug` + * - 生产环境:默认 `warn` + * - 浏览器允许通过 `localStorage.DEBUG = 'true'` 强制开启 `debug` + */ +function detectInitialLevel(): LogLevel { + const isProduction = process.env.NODE_ENV === 'production'; + if (import.meta.client && localStorage.getItem('DEBUG') === 'true') { + return 'debug'; + } + + return isProduction ? 'warn' : 'debug'; +} + +/** + * 核心日志类 + * + * 提供 `debug / info / warn / error` 四个级别 + * 并支持通过 `setLevel` 动态调整输出等级 + * + * @example + * ```ts + * logger.debug('User data:', user) + * logger.error(new Error('Something broke')) + * logger.setLevel('silent') + * ``` + */ +class Logger { + /** 当前日志等级(默认由 detectInitialLevel 判断) */ + private level: LogLevel = detectInitialLevel(); + + /** + * 手动设置日志输出级别 + * + * @param level 目标日志级别 + * + * @example + * ```ts + * logger.setLevel('silent') // 全部禁用 + * logger.setLevel('debug') // 强制打印全部日志 + * ``` + */ + setLevel(newLevel: LogLevel) { + this.level = newLevel; + } + + /** + * 判断当前等级是否允许输出指定日志 + */ + private canLog(level: LogLevel): boolean { + return levelPriority[level] >= levelPriority[this.level]; + } + + /** 输出调试日志(开发阶段使用) */ + debug(...args: unknown[]) { + if (this.canLog('debug')) console.debug('[DEBUG]', ...args); + } + + /** 输出普通信息日志 */ + info(...args: unknown[]) { + if (this.canLog('info')) console.info('[INFO]', ...args); + } + + /** 输出警告日志 */ + warn(...args: unknown[]) { + if (this.canLog('warn')) console.warn('[WARN]', ...args); + } + + /** 输出错误日志 */ + error(...args: unknown[]) { + if (this.canLog('error')) console.error('[ERROR]', ...args); + } +} + +/** + * 默认导出的单例 logger + * + * @example + * ```ts + * import { logger } from '~/utils/logger' + * + * logger.info('App started') + * ``` + */ +export const logger = new Logger();