import { app , WebContents } from 'electron' ; import electronLog , { LogLevel , transports } from 'electron-log' ; import * as fs from 'fs' ; import * as path from 'path' ; import * as util from 'util' ; import { isElectronQA , isWindowsOS } from './env' ; import { getCommandLineArgs } from './utils' ; export interface ILogMsg { level : LogLevel ; details : any ; showInConsole : boolean ; startTime : number ; } interface IClientLogMsg { msgs ? : ILogMsg [ ] ; logLevel ? : LogLevel ; showInConsole ? : boolean ; } const MAX_LOG_QUEUE_LENGTH = 100 ; if ( isWindowsOS && process . env . LOCALAPPDATA ) { app . setPath ( 'appData' , process . env . LOCALAPPDATA ) ; app . setPath ( 'userData' , path . join ( app . getPath ( 'appData' ) , app . getName ( ) ) ) ; } app . setAppLogsPath ( ) ; class Logger { private readonly showInConsole : boolean = false ; private readonly desiredLogLevel ? : LogLevel ; private readonly logQueue : ILogMsg [ ] ; private readonly logPath : string ; private loggerWindow : WebContents | null ; constructor ( ) { this . loggerWindow = null ; this . logQueue = [ ] ; const customLogPathArg = getCommandLineArgs ( process . argv , '--logPath=' , false ) ; const customLogsFolder = customLogPathArg && customLogPathArg . substring ( customLogPathArg . indexOf ( '=' ) + 1 ) ; if ( customLogsFolder ) { if ( ! fs . existsSync ( customLogsFolder ) ) { fs . mkdirSync ( customLogsFolder , { recursive : true } ) ; } app . setPath ( 'logs' , customLogsFolder ) ; } this . logPath = app . getPath ( 'logs' ) ; if ( app . isPackaged ) { transports . file . file = path . join ( this . logPath , `app_${ Date . now ( ) }.log` ) ; transports . file . level = 'debug' ; transports . file . format = '{y}-{m}-{d} {h}:{i}:{s}:{ms} {z} | {level} | {text}' ; transports . file . appName = 'Symphony' ; } const logLevel = getCommandLineArgs ( process . argv , '--logLevel=' , false ) ; if ( logLevel ) { const level = logLevel . split ( '=' ) [ 1 ] ; if ( level ) { this . desiredLogLevel = level as LogLevel ; } } if ( getCommandLineArgs ( process . argv , '--enableConsoleLogging' , false ) ) { this . showInConsole = true ; } if ( app . isPackaged ) { this . cleanupOldLogs ( ) ; } } /**
     * get instance of logQueue
     */ public getLogQueue ( ) { return this . logQueue ; } param message { string } param data { any } public error ( message , ... data ) { this . log ( 'error' , message , data ) ; } param message { string } param data { any } public warn ( message , ... data ) { this . log ( 'warn' , message , data ) ; } param message { string } param data { any } public info ( message , ... data ) { this . log ( 'info' , message , data ) ; } param message { string } param data { array } public verbose ( message , ... data ) { this . log ( 'verbose' , message , data ) ; } param message { string } param data { any } public debug ( message , ... data ) { this . log ( 'debug' , message , data ) ; } param message { string } param data { any } public silly ( message , ... data ) { this . log ( 'silly' , message , data ) ; } param window { WebContents } public setLoggerWindow ( window ) { this . loggerWindow = window ; if ( this . loggerWindow ) { const logMsgs = { } ; if ( this . logQueue . length ) { logMsgs . msgs = this . logQueue ; } if ( this . desiredLogLevel ) { logMsgs . logLevel = this . desiredLogLevel ; } if ( Object . keys ( logMsgs ) . length ) { this . loggerWindow . send ( 'log' , logMsgs ) ; } } } param logLevel { LogLevel } param message { string } param data { array } param sendToCloud { boolean } public log ( logLevel , message , data = [ ] , sendToCloud = true ) { if ( data && data . length > 0 ) { data . forEach ( ( param ) => { message += `, '${ param && typeof param }': ${ JSON . stringify ( param ) }` ; } ) ; } if ( ! isElectronQA ) { switch ( logLevel ) { case 'error' : electronLog . error ( message ) ; break ; case 'warn' : electronLog . warn ( message ) ; break ; case 'info' : electronLog . info ( message ) ; break ; case 'verbose' : electronLog . verbose ( message ) ; break ; case 'debug' : electronLog . debug ( message ) ; break ; case 'silly' : electronLog . silly ( message ) ; break ; default : electronLog . info ( message ) ; } } if ( sendToCloud ) { this . sendToCloud ( this . formatLogMsg ( logLevel , message ) ) ; } } param level { LogLevel } param details { any } private formatLogMsg ( level , details ) { return { details , level , showInConsole : this . showInConsole , startTime : Date . now ( ) , } ; } param logMsg { ILogMsg } private sendToCloud ( logMsg ) { if ( this . desiredLogLevel && this . desiredLogLevel !== logMsg . level ) { return ; } if ( this . loggerWindow && ! this . loggerWindow . isDestroyed ( ) ) { this . loggerWindow . send ( 'log' , { msgs : [ logMsg ] , logLevel : this . desiredLogLevel , showInConsole : this . showInConsole , } ) ; return ; } this . logQueue . push ( logMsg ) ; if ( this . logQueue . length > MAX_LOG_QUEUE_LENGTH ) { this . logQueue . shift ( ) ; } } /**
     * Cleans up logs older than a day
     */ private cleanupOldLogs ( ) { const files = fs . readdirSync ( this . logPath ) ; const deleteTimeStamp = new Date ( ) . getTime ( ) - 5 * 24 * 60 * 60 * 1000 ; files . forEach ( ( file ) => { const filePath = path . join ( this . logPath , file ) ; if ( fs . existsSync ( filePath ) ) { const stat = fs . statSync ( filePath ) ; const fileTimestamp = new Date ( util . inspect ( stat . mtime ) ) . getTime ( ) ; if ( fileTimestamp < deleteTimeStamp ) { fs . unlinkSync ( filePath ) ; } } } ) ; } } const logger = new Logger ( ) ; export { logger } ;