diff --git a/src/backend/__tests__/utils.test.ts b/src/backend/__tests__/utils.test.ts index 49a1c2fe76..d3a44b7059 100644 --- a/src/backend/__tests__/utils.test.ts +++ b/src/backend/__tests__/utils.test.ts @@ -4,7 +4,7 @@ jest.mock('../logger/logger', () => { const original = jest.requireActual('../logger/logger') return { ...original, - createNewLogFileAndClearOldOnces: jest.fn().mockReturnValue('') + createNewLogFileAndClearOldOnes: jest.fn().mockReturnValue('') } }) diff --git a/src/backend/constants.ts b/src/backend/constants.ts index 73445c2b7b..c231afc67f 100644 --- a/src/backend/constants.ts +++ b/src/backend/constants.ts @@ -6,7 +6,7 @@ import { parse } from '@node-steam/vdf' import { GameConfigVersion, GlobalConfigVersion } from 'common/types' import { logDebug, LogPrefix } from './logger/logger' -import { createNewLogFileAndClearOldOnces } from './logger/logfile' +import { createNewLogFileAndClearOldOnes } from './logger/logfile' import { env } from 'process' import { app } from 'electron' import { existsSync, readFileSync } from 'graceful-fs' @@ -54,8 +54,8 @@ const heroicDefaultWinePrefix = join(homedir(), 'Games', 'Heroic', 'Prefixes') const heroicAnticheatDataPath = join(heroicFolder, 'areweanticheatyet.json') const imagesCachePath = join(heroicFolder, 'images-cache') -const { currentLogFile: currentLogFile, lastLogFile: lastLogFile } = - createNewLogFileAndClearOldOnces() +const { currentLogFile, lastLogFile, legendaryLogFile, gogdlLogFile } = + createNewLogFileAndClearOldOnes() const publicDir = resolve(__dirname, '..', app.isPackaged ? '' : '../public') const icon = fixAsarPath(join(publicDir, 'icon.png')) @@ -180,6 +180,8 @@ export { currentGlobalConfigVersion, currentLogFile, lastLogFile, + legendaryLogFile, + gogdlLogFile, discordLink, execOptions, fixAsarPath, diff --git a/src/backend/gog/library.ts b/src/backend/gog/library.ts index cc691061b0..6f52c90b32 100644 --- a/src/backend/gog/library.ts +++ b/src/backend/gog/library.ts @@ -20,7 +20,7 @@ import { existsSync, readFileSync } from 'graceful-fs' import { logError, logInfo, LogPrefix, logWarning } from '../logger/logger' import { getGOGdlBin, getFileSize } from '../utils' -import { fallBackImage } from '../constants' +import { fallBackImage, gogdlLogFile } from '../constants' import { apiInfoCache, libraryStore, @@ -948,6 +948,9 @@ export async function runGogdlCommand( commandParts, { name: 'gog', logPrefix: LogPrefix.Gog, bin, dir }, abortController, - options + { + ...options, + verboseLogFile: gogdlLogFile + } ) } diff --git a/src/backend/launcher.ts b/src/backend/launcher.ts index acbcb228e7..5b574b0f4a 100644 --- a/src/backend/launcher.ts +++ b/src/backend/launcher.ts @@ -674,6 +674,13 @@ async function callRunner( }) } + if (options?.verboseLogFile) { + appendFileSync( + options.verboseLogFile, + `[${new Date().toLocaleString()}] ${safeCommand}\n` + ) + } + if (options?.logFile && existsSync(options.logFile)) { writeFileSync(options.logFile, '') } @@ -705,6 +712,10 @@ async function callRunner( appendFileSync(options.logFile, data) } + if (options?.verboseLogFile) { + appendFileSync(options.verboseLogFile, data) + } + if (options?.onOutput) { options.onOutput(data, child) } @@ -718,6 +729,10 @@ async function callRunner( appendFileSync(options.logFile, data) } + if (options?.verboseLogFile) { + appendFileSync(options.verboseLogFile, data) + } + if (options?.onOutput) { options.onOutput(data, child) } diff --git a/src/backend/legendary/library.ts b/src/backend/legendary/library.ts index 53ebef1e88..7188011a0f 100644 --- a/src/backend/legendary/library.ts +++ b/src/backend/legendary/library.ts @@ -27,6 +27,7 @@ import { import { fallBackImage, legendaryConfigPath, + legendaryLogFile, legendaryMetadata } from '../constants' import { @@ -610,6 +611,9 @@ export async function runLegendaryCommand( commandParts, { name: 'legendary', logPrefix: LogPrefix.Legendary, bin, dir }, abortController, - options + { + ...options, + verboseLogFile: legendaryLogFile + } ) } diff --git a/src/backend/logger/__mocks__/logfile.ts b/src/backend/logger/__mocks__/logfile.ts index c5dbca9a4e..a39408dbf8 100644 --- a/src/backend/logger/__mocks__/logfile.ts +++ b/src/backend/logger/__mocks__/logfile.ts @@ -1,6 +1,6 @@ const logfile = jest.requireActual('../logfile') -logfile.createNewLogFileAndClearOldOnces = jest.fn().mockReturnValue('') +logfile.createNewLogFileAndClearOldOnes = jest.fn().mockReturnValue('') logfile.appendMessageToLogFile = jest.fn() module.exports = logfile diff --git a/src/backend/logger/__tests__/logfile.test.ts b/src/backend/logger/__tests__/logfile.test.ts index 700aa735ef..b6c573624d 100644 --- a/src/backend/logger/__tests__/logfile.test.ts +++ b/src/backend/logger/__tests__/logfile.test.ts @@ -34,11 +34,11 @@ describe('logger/logfile.ts', () => { tmpDir.removeCallback() }) - test('createNewLogFileAndClearOldOnces fails because logDir does not exist', () => { + test('createNewLogFileAndClearOldOnes fails because logDir does not exist', () => { const spyAppGetPath = jest.spyOn(app, 'getPath').mockReturnValue('invalid') const spyOpenSync = jest.spyOn(graceful_fs, 'openSync') - logfile.createNewLogFileAndClearOldOnces() + logfile.createNewLogFileAndClearOldOnes() expect(spyOpenSync).toBeCalledWith( expect.stringContaining('invalid/heroic-'), @@ -56,7 +56,7 @@ describe('logger/logfile.ts', () => { ) }) - test('createNewLogFileAndClearOldOnces success', () => { + test('createNewLogFileAndClearOldOnes success', () => { jest.spyOn(app, 'getPath').mockReturnValue(tmpDir.name) jest.spyOn(configStore, 'has').mockReturnValue(true) jest.spyOn(configStore, 'get').mockReturnValue({ @@ -64,16 +64,18 @@ describe('logger/logfile.ts', () => { lastLogFile: undefined }) - const data = logfile.createNewLogFileAndClearOldOnces() + const data = logfile.createNewLogFileAndClearOldOnes() expect(logError).not.toBeCalled() expect(data).toStrictEqual({ currentLogFile: expect.any(String), - lastLogFile: 'old/log/path/file.log' + lastLogFile: 'old/log/path/file.log', + legendaryLogFile: expect.any(String), + gogdlLogFile: expect.any(String) }) }) - test('createNewLogFileAndClearOldOnces removing old logs fails', () => { + test('createNewLogFileAndClearOldOnes removing old logs fails', () => { jest.spyOn(app, 'getPath').mockReturnValue(tmpDir.name) const spyUnlinkSync = jest .spyOn(graceful_fs, 'unlinkSync') @@ -81,7 +83,7 @@ describe('logger/logfile.ts', () => { throw Error('unlink failed') }) const date = new Date() - date.setMonth(date.getMonth() > 0 ? date.getMonth() - 1 : 11) + date.setMonth(date.getMonth() - 1) const monthOutdatedLogFile = join( tmpDir.name, // @ts-ignore replaceAll error @@ -92,7 +94,7 @@ describe('logger/logfile.ts', () => { expect(graceful_fs.existsSync(monthOutdatedLogFile)).toBeTruthy() - const data = logfile.createNewLogFileAndClearOldOnces() + const data = logfile.createNewLogFileAndClearOldOnes() expect(logError).toBeCalledWith( [ @@ -104,7 +106,7 @@ describe('logger/logfile.ts', () => { expect(graceful_fs.existsSync(monthOutdatedLogFile)).toBeTruthy() }) - test('createNewLogFileAndClearOldOnces removing old logs successful', () => { + test('createNewLogFileAndClearOldOnes removing old logs successful', () => { jest.spyOn(app, 'getPath').mockReturnValue(tmpDir.name) const date = new Date() date.setMonth(date.getMonth() > 0 ? date.getMonth() - 1 : 11) @@ -126,7 +128,7 @@ describe('logger/logfile.ts', () => { expect(graceful_fs.existsSync(monthOutdatedLogFile)).toBeTruthy() expect(graceful_fs.existsSync(yearOutdatedLogFile)).toBeTruthy() - const data = logfile.createNewLogFileAndClearOldOnces() + const data = logfile.createNewLogFileAndClearOldOnes() expect(logError).not.toBeCalled() expect(graceful_fs.existsSync(monthOutdatedLogFile)).toBeFalsy() diff --git a/src/backend/logger/logfile.ts b/src/backend/logger/logfile.ts index 46e5f17baa..39b746fed4 100644 --- a/src/backend/logger/logfile.ts +++ b/src/backend/logger/logfile.ts @@ -19,33 +19,47 @@ import { logError, LogPrefix } from './logger' interface createLogFileReturn { currentLogFile: string lastLogFile: string + legendaryLogFile: string + gogdlLogFile: string } let longestPrefix = 0 export const getLongestPrefix = (): number => longestPrefix +const createLogFile = (filePath: string) => { + try { + openSync(filePath, 'w') + } catch (error) { + logError([`Open ${filePath} failed with`, error], { + prefix: LogPrefix.Backend, + skipLogToFile: true + }) + } +} + /** * Creates a new log file in heroic config path under folder Logs. * It also removes old logs every new month. * @returns path to current log file */ -export function createNewLogFileAndClearOldOnces(): createLogFileReturn { +export function createNewLogFileAndClearOldOnes(): createLogFileReturn { const date = new Date() const logDir = app.getPath('logs') const fmtDate = date.toISOString().replaceAll(':', '_') const newLogFile = join(logDir, `heroic-${fmtDate}.log`) - try { - openSync(newLogFile, 'w') - } catch (error) { - logError([`Open ${newLogFile} failed with`, error], { - prefix: LogPrefix.Backend, - skipLogToFile: true - }) - } + const newLegendaryLogFile = join(logDir, `legendary-${fmtDate}.log`) + const newGogdlLogFile = join(logDir, `gogdl-${fmtDate}.log`) + + createLogFile(newLogFile) + createLogFile(newLegendaryLogFile) + createLogFile(newGogdlLogFile) // Clean out logs that are more than a month old if (existsSync(logDir)) { try { + const oneMonthAgo = new Date() + oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1) + const logs = readdirSync(logDir, { withFileTypes: true }) @@ -53,16 +67,13 @@ export function createNewLogFileAndClearOldOnces(): createLogFileReturn { .map((dirent) => dirent.name) logs.forEach((log) => { - if (log.includes('heroic-')) { + if (log.match(/(heroic|legendary|gogdl)-/)) { const dateString = log - .replace('heroic-', '') + .replace(/(heroic|legendary|gogdl)-/, '') .replace('.log', '') .replaceAll('_', ':') const logDate = new Date(dateString) - if ( - logDate.getFullYear() < date.getFullYear() || - logDate.getMonth() < date.getMonth() - ) { + if (logDate <= oneMonthAgo) { unlinkSync(`${logDir}/${log}`) } } @@ -77,7 +88,9 @@ export function createNewLogFileAndClearOldOnces(): createLogFileReturn { let logs: createLogFileReturn = { currentLogFile: '', - lastLogFile: '' + lastLogFile: '', + legendaryLogFile: '', + gogdlLogFile: '' } if (configStore.has('general-logs')) { logs = configStore.get('general-logs') as createLogFileReturn @@ -85,6 +98,8 @@ export function createNewLogFileAndClearOldOnces(): createLogFileReturn { logs.lastLogFile = logs.currentLogFile logs.currentLogFile = newLogFile + logs.legendaryLogFile = newLegendaryLogFile + logs.gogdlLogFile = newGogdlLogFile configStore.set('general-logs', logs) diff --git a/src/common/types.ts b/src/common/types.ts index 348cd86f75..1cfc539509 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -338,6 +338,7 @@ export interface RpcClient { export interface CallRunnerOptions { logMessagePrefix?: string logFile?: string + verboseLogFile?: string env?: Record | NodeJS.ProcessEnv wrappers?: string[] onOutput?: (output: string, child: ChildProcess) => void