diff --git a/package.json b/package.json index fc554d4..be49663 100644 --- a/package.json +++ b/package.json @@ -44,14 +44,15 @@ "keycode": "^2.2.0", "lodash-id": "^0.14.0", "lowdb": "^1.0.0", - "picgo": "^1.5.0-alpha.4", + "picgo": "^1.5.0-alpha.5", "qrcode.vue": "^1.7.0", "shell-path": "2.1.0", "uuidv4": "^6.2.11", "vue": "^2.6.10", "vue-gallery": "^2.0.1", "vue-lazyload": "^1.2.6", - "vue-router": "^3.1.3" + "vue-router": "^3.1.3", + "write-file-atomic": "^4.0.1" }, "devDependencies": { "@babel/plugin-proposal-optional-chaining": "^7.16.7", @@ -62,6 +63,7 @@ "@types/node": "^16.10.2", "@types/request-promise-native": "^1.0.17", "@types/semver": "^7.3.8", + "@types/write-file-atomic": "^4.0.0", "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "@vue/cli-plugin-babel": "^4.0.0", diff --git a/src/main/apis/app/uploader/index.ts b/src/main/apis/app/uploader/index.ts index 6f66e41..197ccb1 100644 --- a/src/main/apis/app/uploader/index.ts +++ b/src/main/apis/app/uploader/index.ts @@ -19,6 +19,8 @@ import { T } from '~/universal/i18n' import fse from 'fs-extra' import path from 'path' import { privacyManager } from '~/main/utils/privacyManager' +import writeFile from 'write-file-atomic' +import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' const waitForShow = (webcontent: WebContents) => { return new Promise((resolve) => { @@ -126,8 +128,8 @@ class Uploader { const buffer = nativeImage.toPNG() const baseDir = picgo.baseDir const fileName = `${dayjs().format('YYYYMMDDHHmmSSS')}.png` - filePath = path.join(baseDir, fileName) - await fse.writeFile(filePath, buffer) + filePath = path.join(baseDir, CLIPBOARD_IMAGE_FOLDER, fileName) + await writeFile(filePath, buffer) return await this.upload([filePath]) } catch (e: any) { logger.error(e) diff --git a/src/main/apis/core/datastore/dbChecker.ts b/src/main/apis/core/datastore/dbChecker.ts index 30966fb..da5b7ff 100644 --- a/src/main/apis/core/datastore/dbChecker.ts +++ b/src/main/apis/core/datastore/dbChecker.ts @@ -1,4 +1,5 @@ import fs from 'fs-extra' +import writeFile from 'write-file-atomic' import path from 'path' import { app as APP } from 'electron' import { getLogger } from '@core/utils/localLogger' @@ -50,7 +51,7 @@ function dbChecker () { try { configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' }) JSON.parse(configFile) - fs.writeFileSync(configFilePath, configFile, { encoding: 'utf-8' }) + writeFile.sync(configFilePath, configFile, { encoding: 'utf-8' }) const stats = fs.statSync(configFileBackupPath) optionsTpl.body = `${errorMsg.brokenButBackup}\n${T('TIPS_PICGO_BACKUP_FILE_VERSION', { v: dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss') @@ -67,7 +68,7 @@ function dbChecker () { global.notificationList?.push(optionsTpl) return } - fs.writeFileSync(configFileBackupPath, configFile, { encoding: 'utf-8' }) + writeFile.sync(configFileBackupPath, configFile, { encoding: 'utf-8' }) } } @@ -97,7 +98,7 @@ function dbPathChecker (): string { } return _configFilePath } catch (e) { - const picgoLogPath = path.join(STORE_PATH, 'picgo.log') + const picgoLogPath = path.join(STORE_PATH, 'picgo-gui-local.log') const logger = getLogger(picgoLogPath) if (!hasCheckPath) { const optionsTpl = { @@ -108,7 +109,6 @@ function dbPathChecker (): string { hasCheckPath = true } logger('error', e) - console.error(e) _configFilePath = defaultConfigPath return _configFilePath } diff --git a/src/main/apis/core/utils/localLogger.ts b/src/main/apis/core/utils/localLogger.ts index a29e0b5..2e325f4 100644 --- a/src/main/apis/core/utils/localLogger.ts +++ b/src/main/apis/core/utils/localLogger.ts @@ -2,6 +2,32 @@ import fs from 'fs-extra' import dayjs from 'dayjs' import util from 'util' +const checkLogFileIsLarge = (logPath: string): { + isLarge: boolean + logFileSize?: number + logFileSizeLimit?: number +} => { + if (fs.existsSync(logPath)) { + const logFileSize = fs.statSync(logPath).size + const logFileSizeLimit = 10 * 1024 * 1024 // 10 MB default + return { + isLarge: logFileSize > logFileSizeLimit, + logFileSize, + logFileSizeLimit + } + } + return { + isLarge: false + } +} + +const recreateLogFile = (logPath: string): void => { + if (fs.existsSync(logPath)) { + fs.unlinkSync(logPath) + fs.createFileSync(logPath) + } +} + /** * for local log before picgo inited */ @@ -9,21 +35,29 @@ const getLogger = (logPath: string) => { if (!fs.existsSync(logPath)) { fs.ensureFileSync(logPath) } + if (checkLogFileIsLarge(logPath).isLarge) { + recreateLogFile(logPath) + } return (type: string, ...msg: any[]) => { - let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] ` - msg.forEach((item: ILogArgvTypeWithError) => { - if (typeof item === 'object' && type === 'error') { - log += `\n------Error Stack Begin------\n${util.format(item.stack)}\n-------Error Stack End------- ` - } else { - if (typeof item === 'object') { - item = JSON.stringify(item) + try { + let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] ` + msg.forEach((item: ILogArgvTypeWithError) => { + if (typeof item === 'object' && type === 'error') { + log += `\n------Error Stack Begin------\n${util.format(item.stack)}\n-------Error Stack End------- ` + } else { + if (typeof item === 'object') { + item = JSON.stringify(item) + } + log += `${item} ` } - log += `${item} ` - } - }) - log += '\n' - // A synchronized approach to avoid log msg sequence errors - fs.appendFileSync(logPath, log) + }) + log += '\n' + console.log(log) + // A synchronized approach to avoid log msg sequence errors + fs.appendFileSync(logPath, log) + } catch (e) { + console.error(e) + } } } diff --git a/src/main/lifeCycle/errorHandler.ts b/src/main/lifeCycle/errorHandler.ts index d19e258..09dbd77 100644 --- a/src/main/lifeCycle/errorHandler.ts +++ b/src/main/lifeCycle/errorHandler.ts @@ -1,36 +1,17 @@ -import fse from 'fs-extra' import path from 'path' -import dayjs from 'dayjs' -import util from 'util' import { dbPathDir } from 'apis/core/datastore/dbChecker' +import { getLogger } from 'apis/core/utils/localLogger' const STORE_PATH = dbPathDir() -const LOG_PATH = path.join(STORE_PATH, '/picgo.log') +const LOG_PATH = path.join(STORE_PATH, 'picgo-gui-local.log') + +const logger = getLogger(LOG_PATH) // since the error may occur in picgo-core // so we can't use the log from picgo -export const loggerWriter = (error: Error) => { - try { - const time = dayjs().format('YYYY-MM-DD HH:mm:ss') - let log = `${time} [PicGo ERROR] process error begin` - if (error?.stack) { - log += `\n------Error Stack Begin------\n${util.format(error.stack)}\n-------Error Stack End-------\n` - } else { - const msg = JSON.stringify(error) - log += `${msg}\n` - } - log += `${time} [PicGo ERROR] process error end` - if (!fse.existsSync(LOG_PATH)) { - fse.ensureFileSync(LOG_PATH) - } - fse.appendFileSync(LOG_PATH, log) - } catch (e) { - console.error(e) - } -} const handleProcessError = (error: Error) => { console.error(error) - loggerWriter(error) + logger('error', error) } process.on('uncaughtException', error => { diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index b23ee3a..4f3d2da 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -285,10 +285,11 @@ :title="$T('SETTINGS_SET_LOG_FILE')" :visible.sync="logFileVisible" :modal-append-to-body="false" + width="500px" > + + + {{ $T('CANCEL') }} @@ -379,6 +392,7 @@ import { } from 'electron' import { Component, Vue } from 'vue-property-decorator' import { T, languageList } from '~/universal/i18n' +import { enforceNumber } from '~/universal/utils/common' // import db from '#/datastore' const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest' const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json' @@ -407,7 +421,8 @@ export default class extends Vue { autoCopyUrl: true, checkBetaUpdate: true, useBuiltinClipboard: false, - language: 'zh-CN' + language: 'zh-CN', + logFileSizeLimit: 10 } languageList = languageList.map(item => ({ @@ -501,6 +516,7 @@ export default class extends Vue { host: '127.0.0.1', enable: true } + this.form.logFileSizeLimit = enforceNumber(settings.logFileSizeLimit) || 10 } } @@ -686,7 +702,8 @@ export default class extends Vue { return this.$message.error(this.$T('TIPS_PLEASE_CHOOSE_LOG_LEVEL')) } this.saveConfig({ - 'settings.logLevel': this.form.logLevel + 'settings.logLevel': this.form.logLevel, + 'settings.logFileSizeLimit': this.form.logFileSizeLimit }) const successNotification = new Notification(this.$T('SETTINGS_SET_LOG_FILE'), { body: this.$T('TIPS_SET_SUCCEED') @@ -700,6 +717,7 @@ export default class extends Vue { async cancelLogLevelSetting () { this.logFileVisible = false let logLevel = await this.getConfig('settings.logLevel') + const logFileSizeLimit = await this.getConfig('settings.logFileSizeLimit') || 10 if (!Array.isArray(logLevel)) { if (logLevel && logLevel.length > 0) { logLevel = [logLevel] @@ -708,6 +726,7 @@ export default class extends Vue { } } this.form.logLevel = logLevel + this.form.logFileSizeLimit = logFileSizeLimit } confirmServerSetting () { diff --git a/src/universal/i18n/en.ts b/src/universal/i18n/en.ts index 9747122..dbc6b8d 100644 --- a/src/universal/i18n/en.ts +++ b/src/universal/i18n/en.ts @@ -88,6 +88,7 @@ export const EN: ILocales = { SETTINGS_TIPS_HAS_NEW_VERSION: 'PicGo has a new version, please click confirm to open download page', SETTINGS_LOG_FILE: 'Log File', SETTINGS_LOG_LEVEL: 'Log Level', + SETTINGS_LOG_FILE_SIZE: 'Log File Size', SETTINGS_SET_PICGO_SERVER: 'Set PicGo Server', SETTINGS_TIPS_SERVER_NOTICE: 'If you don\'t know what is the server\'s function, please read the document, or don\'t modify the configuration.', SETTINGS_ENABLE_SERVER: 'Enable Server', diff --git a/src/universal/i18n/zh-CN.ts b/src/universal/i18n/zh-CN.ts index 53b1af4..52d8de9 100644 --- a/src/universal/i18n/zh-CN.ts +++ b/src/universal/i18n/zh-CN.ts @@ -87,6 +87,7 @@ export const ZH_CN = { SETTINGS_TIPS_HAS_NEW_VERSION: 'PicGo更新啦,请点击确定打开下载页面', SETTINGS_LOG_FILE: '日志文件', SETTINGS_LOG_LEVEL: '日志记录等级', + SETTINGS_LOG_FILE_SIZE: '日志文件大小', SETTINGS_SET_PICGO_SERVER: '设置PicGo-Server', SETTINGS_TIPS_SERVER_NOTICE: '如果你不知道Server的作用,请阅读文档,或者不用修改配置。', SETTINGS_ENABLE_SERVER: '是否开启Server', diff --git a/src/universal/types/view.d.ts b/src/universal/types/view.d.ts index 6b7b80a..575cd8b 100644 --- a/src/universal/types/view.d.ts +++ b/src/universal/types/view.d.ts @@ -11,6 +11,7 @@ interface ISettingForm { checkBetaUpdate: boolean useBuiltinClipboard: boolean language: string + logFileSizeLimit: number } interface IShortKeyMap { diff --git a/src/universal/utils/common.ts b/src/universal/utils/common.ts index ce50836..357b0a3 100644 --- a/src/universal/utils/common.ts +++ b/src/universal/utils/common.ts @@ -37,3 +37,7 @@ export const handleStreamlinePluginName = (name: string) => { export const simpleClone = (obj: any) => { return JSON.parse(JSON.stringify(obj)) } + +export const enforceNumber = (num: number | string) => { + return isNaN(Number(num)) ? 0 : Number(num) +} diff --git a/src/universal/utils/static.ts b/src/universal/utils/static.ts new file mode 100644 index 0000000..382945b --- /dev/null +++ b/src/universal/utils/static.ts @@ -0,0 +1 @@ +export const CLIPBOARD_IMAGE_FOLDER = 'picgo-clipboard-images' diff --git a/yarn.lock b/yarn.lock index 486db32..2b8e852 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1360,6 +1360,22 @@ lodash-id "^0.14.0" write-file-atomic "^4.0.1" +"@picgo/store@^2.0.2": + version "2.0.2" + resolved "https://registry.npmmirror.com/@picgo/store/-/store-2.0.2.tgz#0b5050f5e8cef7043cf5463fa81ef3c3a19fffc7" + integrity sha512-/nZr6zeLNtlTG+g8iUd5xy5Vtl7iu7SHI3aY9a/+AIlBSs7Io/06MrxGyoAHSWVg9BsB80kJyrNeGyOWiOO5jw== + dependencies: + "@commonify/lowdb" "^3.0.0" + "@commonify/steno" "^2.1.0" + "@types/bson" "^4.0.1" + "@types/graceful-fs" "^4.1.3" + "@types/lodash" "^4.14.182" + comment-json "^4.1.0" + fflate "^0.7.3" + lodash "^4.17.21" + lodash-id "^0.14.0" + write-file-atomic "^4.0.1" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1706,6 +1722,13 @@ anymatch "^3.0.0" source-map "^0.6.0" +"@types/write-file-atomic@^4.0.0": + version "4.0.0" + resolved "https://registry.npmmirror.com/@types/write-file-atomic/-/write-file-atomic-4.0.0.tgz#ffcedcb1ae027e0a28ddfe218b72b3573797b5bc" + integrity sha512-piEKt2KKBUtye+feTlfdPjtW7uPFsAaLNX3/f6AJD+Y1T1YPTFwnqtlO9Y+gy9qGshrvxKa/Kay9vqbyVIuhwQ== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" @@ -9808,12 +9831,13 @@ performance-now@^2.1.0: resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picgo@^1.5.0-alpha.4: - version "1.5.0-alpha.4" - resolved "https://registry.npmmirror.com/picgo/-/picgo-1.5.0-alpha.4.tgz#bca46bf6124a2855222c8536bd4e3522ebf2699c" - integrity sha512-igNNBHSZywwTvtA28TtwoXJtO8omgDu+8TX3em2c4F1e4yHSmPsTJdWbHSf69KujULs0J0SV11eLp8gyI3B7nw== +picgo@^1.5.0-alpha.5: + version "1.5.0-alpha.5" + resolved "https://registry.npmmirror.com/picgo/-/picgo-1.5.0-alpha.5.tgz#3258efa1aecdb9392405dc77fdb3273d3703b003" + integrity sha512-62F1GoctoHG4lIak91TNls5cw/DeHRt7PGh3SU/vKMacjSaKuIA9eU6FOyXSKtXqBgZFIpEQC6JYEvlTf/aMQA== dependencies: "@picgo/i18n" "^1.0.0" + "@picgo/store" "^2.0.2" chalk "^2.4.1" commander "^8.1.0" comment-json "^2.3.1" @@ -9827,8 +9851,6 @@ picgo@^1.5.0-alpha.4: inquirer "^6.0.0" is-wsl "^2.2.0" lodash "^4.17.21" - lodash-id "^0.14.0" - lowdb "^1.0.0" md5 "^2.2.1" mime-types "2.1.33" minimatch "^3.0.4"