| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- // electron/modules/logger.ts - 日志模块 (基于 electron-log)
- import log from 'electron-log/main'
- import * as path from 'path'
- import * as fs from 'fs'
- import { app } from 'electron'
- import type { LogEntry, LogCategory } from './types'
- // 日志配置常量
- const MAX_LOG_FILE_SIZE = 5 * 1024 * 1024 // 5MB
- const DEFAULT_MAX_FILES = 3
- const DEFAULT_MAX_RECENT_LOGS = 500
- // 日志实例
- let appLog: ReturnType<typeof log.create> | null = null
- let installLog: ReturnType<typeof log.create> | null = null
- class Logger {
- private recentLogs: LogEntry[] = []
- private maxRecentLogs = DEFAULT_MAX_RECENT_LOGS
- private logDir: string = ''
- private initialized = false
- init(): void {
- if (this.initialized) return
- this.initialized = true
- // 日志目录设置 - 统一使用 Electron 推荐的用户数据目录
- // Windows: C:\Users\用户名\AppData\Roaming\claude-ai-installer\logs
- const userDataDir = app.getPath('userData')
- console.log('[Logger] 使用用户数据目录:', userDataDir)
- this.logDir = path.join(userDataDir, 'logs')
- // 确保日志目录存在
- console.log('[Logger] 尝试创建日志目录:', this.logDir)
- try {
- if (!fs.existsSync(this.logDir)) {
- fs.mkdirSync(this.logDir, { recursive: true })
- console.log('[Logger] 日志目录创建成功')
- } else {
- console.log('[Logger] 日志目录已存在')
- }
- } catch (err) {
- console.error('[Logger] 创建日志目录失败:', err)
- }
- // 保存日志目录路径供 resolvePathFn 使用
- const logDir = this.logDir
- // 创建应用日志实例
- console.log('[Logger] 创建 appLog 实例...')
- appLog = log.create({ logId: 'app' })
- const appLogPath = path.join(logDir, 'app.log')
- appLog.transports.file.resolvePathFn = () => appLogPath
- appLog.transports.file.level = 'debug'
- appLog.transports.file.maxSize = MAX_LOG_FILE_SIZE
- appLog.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'
- appLog.transports.file.archiveLogFn = (file) => this.archiveLog(file)
- appLog.transports.console.level = 'debug'
- appLog.transports.console.format = '[{y}-{m}-{d} {h}:{i}:{s}] [{level}] {text}'
- console.log('[Logger] appLog 配置完成,路径:', appLogPath)
- // 创建安装日志实例
- console.log('[Logger] 创建 installLog 实例...')
- installLog = log.create({ logId: 'install' })
- const installLogPath = path.join(logDir, 'install.log')
- installLog.transports.file.resolvePathFn = () => installLogPath
- installLog.transports.file.level = 'debug'
- installLog.transports.file.maxSize = MAX_LOG_FILE_SIZE
- installLog.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'
- installLog.transports.file.archiveLogFn = (file) => this.archiveLog(file)
- installLog.transports.console.level = 'debug'
- installLog.transports.console.format = '[{y}-{m}-{d} {h}:{i}:{s}] [{level}] {text}'
- console.log('[Logger] installLog 配置完成,路径:', installLogPath)
- // 写入初始化日志
- this.info('日志系统初始化完成', { logDir: this.logDir })
- this.installInfo('安装日志系统初始化完成', { logDir: this.logDir })
- }
- /**
- * 日志文件轮转
- */
- private archiveLog(file: { path: string; toString: () => string }): void {
- const filePath = file.toString()
- const info = path.parse(filePath)
- // 删除最旧的轮转文件
- for (let i = DEFAULT_MAX_FILES; i >= 1; i--) {
- const oldFile = path.join(info.dir, `${info.name}.${i}${info.ext}`)
- const newFile = path.join(info.dir, `${info.name}.${i + 1}${info.ext}`)
- try {
- if (fs.existsSync(oldFile)) {
- if (i === DEFAULT_MAX_FILES) {
- fs.unlinkSync(oldFile)
- } else {
- fs.renameSync(oldFile, newFile)
- }
- }
- } catch {
- // 忽略轮转错误
- }
- }
- // 重命名当前日志文件
- try {
- fs.renameSync(filePath, path.join(info.dir, `${info.name}.1${info.ext}`))
- } catch {
- // 忽略重命名错误
- }
- }
- getLogDir(): string {
- return this.logDir
- }
- getAppLogPath(): string {
- return path.join(this.logDir, 'app.log')
- }
- getInstallLogPath(): string {
- return path.join(this.logDir, 'install.log')
- }
- private addToRecent(level: LogEntry['level'], message: string, category: LogCategory, data?: unknown): void {
- const entry: LogEntry = {
- level,
- message,
- timestamp: new Date().toISOString(),
- category,
- data
- }
- this.recentLogs.push(entry)
- if (this.recentLogs.length > this.maxRecentLogs) {
- this.recentLogs.shift()
- }
- }
- // ==================== 应用日志方法 ====================
- debug(message: string, data?: unknown): void {
- if (appLog) {
- if (data !== undefined) {
- appLog.debug(message, data)
- } else {
- appLog.debug(message)
- }
- } else {
- console.debug('[Logger]', message, data ?? '')
- }
- this.addToRecent('DEBUG', message, 'app', data)
- }
- info(message: string, data?: unknown): void {
- if (appLog) {
- if (data !== undefined) {
- appLog.info(message, data)
- } else {
- appLog.info(message)
- }
- } else {
- console.info('[Logger]', message, data ?? '')
- }
- this.addToRecent('INFO', message, 'app', data)
- }
- warn(message: string, data?: unknown): void {
- if (appLog) {
- if (data !== undefined) {
- appLog.warn(message, data)
- } else {
- appLog.warn(message)
- }
- } else {
- console.warn('[Logger]', message, data ?? '')
- }
- this.addToRecent('WARN', message, 'app', data)
- }
- error(message: string, data?: unknown): void {
- if (appLog) {
- if (data !== undefined) {
- appLog.error(message, data)
- } else {
- appLog.error(message)
- }
- } else {
- console.error('[Logger]', message, data ?? '')
- }
- this.addToRecent('ERROR', message, 'app', data)
- }
- // ==================== 安装日志方法 ====================
- installDebug(message: string, data?: unknown): void {
- if (installLog) {
- if (data !== undefined) {
- installLog.debug(message, data)
- } else {
- installLog.debug(message)
- }
- } else {
- console.debug('[InstallLog]', message, data ?? '')
- }
- this.addToRecent('DEBUG', message, 'install', data)
- }
- installInfo(message: string, data?: unknown): void {
- if (installLog) {
- if (data !== undefined) {
- installLog.info(message, data)
- } else {
- installLog.info(message)
- }
- } else {
- console.info('[InstallLog]', message, data ?? '')
- }
- this.addToRecent('INFO', message, 'install', data)
- }
- installWarn(message: string, data?: unknown): void {
- if (installLog) {
- if (data !== undefined) {
- installLog.warn(message, data)
- } else {
- installLog.warn(message)
- }
- } else {
- console.warn('[InstallLog]', message, data ?? '')
- }
- this.addToRecent('WARN', message, 'install', data)
- }
- installError(message: string, data?: unknown): void {
- if (installLog) {
- if (data !== undefined) {
- installLog.error(message, data)
- } else {
- installLog.error(message)
- }
- } else {
- console.error('[InstallLog]', message, data ?? '')
- }
- this.addToRecent('ERROR', message, 'install', data)
- }
- // ==================== 日志查询方法 ====================
- getRecentLogs(limit = 200, category?: LogCategory): LogEntry[] {
- if (category) {
- return this.recentLogs.filter(log => log.category === category).slice(-limit)
- }
- return this.recentLogs.slice(-limit)
- }
- close(): void {
- this.recentLogs = []
- }
- }
- export const logger = new Logger()
- export default logger
|