|  |  |  | /* eslint-env node */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* eslint strict: ['error', 'never'] */ | 
					
						
							|  |  |  | /* eslint-disable no-console */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { ipcRenderer } from 'electron'; | 
					
						
							|  |  |  | import _ from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { redactAll } from './privacy'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ipc = ipcRenderer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Default Bunyan levels: https://github.com/trentm/node-bunyan#levels
 | 
					
						
							|  |  |  | // To make it easier to visually scan logs, we make all levels the same length
 | 
					
						
							|  |  |  | const BLANK_LEVEL = '     '; | 
					
						
							|  |  |  | const LEVELS: Record<number, string> = { | 
					
						
							|  |  |  |   60: 'fatal', | 
					
						
							|  |  |  |   50: 'error', | 
					
						
							|  |  |  |   40: 'warn ', | 
					
						
							|  |  |  |   30: 'info ', | 
					
						
							|  |  |  |   20: 'debug', | 
					
						
							|  |  |  |   10: 'trace', | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Backwards-compatible logging, simple strings and no level (defaulted to INFO)
 | 
					
						
							|  |  |  | function now() { | 
					
						
							|  |  |  |   const current = Date.now(); | 
					
						
							|  |  |  |   return `${current}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // To avoid [Object object] in our log since console.log handles non-strings smoothly
 | 
					
						
							|  |  |  | function cleanArgsForIPC(args: any) { | 
					
						
							|  |  |  |   const str = args.map((item: any) => { | 
					
						
							|  |  |  |     if (typeof item !== 'string') { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         return JSON.stringify(item); | 
					
						
							|  |  |  |       } catch (error) { | 
					
						
							|  |  |  |         return item; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return item; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return str.join(' '); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function log(...args: any) { | 
					
						
							|  |  |  |   logAtLevel('info', 'INFO ', ...args); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (window.console) { | 
					
						
							|  |  |  |   (console as any)._log = (console as any).log; | 
					
						
							|  |  |  |   (console as any).log = log; | 
					
						
							|  |  |  |   (console as any)._trace = (console as any).trace; | 
					
						
							|  |  |  |   (console as any)._debug = (console as any).debug; | 
					
						
							|  |  |  |   (console as any)._info = (console as any).info; | 
					
						
							|  |  |  |   (console as any)._warn = (console as any).warn; | 
					
						
							|  |  |  |   (console as any)._error = (console as any).error; | 
					
						
							|  |  |  |   (console as any)._fatal = (console as any).error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The mechanics of preparing a log for publish
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getHeader() { | 
					
						
							| 
									
										
											  
											
												Beta versions support: SxS support, in-app env/instance display (#1606)
* Script for beta config; unique data dir, in-app env/type display
To release a beta build, increment the version and add -beta-N to the
end, then go through all the standard release activities.
The prepare-build npm script then updates key bits of the package.json
to ensure that the beta build can be installed alongside a production
build. This includes a new name ('Signal Beta') and a different location
for application data.
Note: Beta builds can be installed alongside production builds.
As part of this, a couple new bits of data are shown across the app:
- Environment (development or test, not shown if production)
- App Instance (disabled in production; used for multiple accounts)
These are shown in:
- The window title - both environment and app instance. You can tell
  beta builds because the app name, preceding these data bits, is
  different.
- The about window - both environment and app instance. You can tell
  beta builds from the version number.
- The header added to the debug log - just environment. The version
  number will tell us if it's a beta build, and app instance isn't
  helpful.
* Turn on single-window mode in non-production modes
Because it's really frightening when you see 'unable to read from db'
errors in the console.
* aply.sh: More instructions for initial setup and testing
* Gruntfile: Get consistent with use of package.json datas
* Linux: manually update desktop keys, since macros not available
											
										 
											8 years ago
										 |  |  |   let header = window.navigator.userAgent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   header += ` node/${window?.getNodeVersion()}`; | 
					
						
							|  |  |  |   header += ` env/${window?.getEnvironment()}`; | 
					
						
							| 
									
										
											  
											
												Beta versions support: SxS support, in-app env/instance display (#1606)
* Script for beta config; unique data dir, in-app env/type display
To release a beta build, increment the version and add -beta-N to the
end, then go through all the standard release activities.
The prepare-build npm script then updates key bits of the package.json
to ensure that the beta build can be installed alongside a production
build. This includes a new name ('Signal Beta') and a different location
for application data.
Note: Beta builds can be installed alongside production builds.
As part of this, a couple new bits of data are shown across the app:
- Environment (development or test, not shown if production)
- App Instance (disabled in production; used for multiple accounts)
These are shown in:
- The window title - both environment and app instance. You can tell
  beta builds because the app name, preceding these data bits, is
  different.
- The about window - both environment and app instance. You can tell
  beta builds from the version number.
- The header added to the debug log - just environment. The version
  number will tell us if it's a beta build, and app instance isn't
  helpful.
* Turn on single-window mode in non-production modes
Because it's really frightening when you see 'unable to read from db'
errors in the console.
* aply.sh: More instructions for initial setup and testing
* Gruntfile: Get consistent with use of package.json datas
* Linux: manually update desktop keys, since macros not available
											
										 
											8 years ago
										 |  |  | 
 | 
					
						
							|  |  |  |   return header; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getLevel(level: number) { | 
					
						
							|  |  |  |   const text = LEVELS[level]; | 
					
						
							|  |  |  |   if (!text) { | 
					
						
							|  |  |  |     return BLANK_LEVEL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return text.toUpperCase(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type EntryType = { | 
					
						
							|  |  |  |   level: number; | 
					
						
							|  |  |  |   time: number; | 
					
						
							|  |  |  |   msg: string; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function formatLine(entry: EntryType) { | 
					
						
							|  |  |  |   return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function format(entries: Array<EntryType>) { | 
					
						
							|  |  |  |   return redactAll(entries.map(formatLine).join('\n')); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export async function fetchNodeLog() { | 
					
						
							|  |  |  |   return new Promise(resolve => { | 
					
						
							|  |  |  |     ipc.on('fetched-log', (_event, text) => { | 
					
						
							|  |  |  |       const result = `${getHeader()}\n${format(text)}`; | 
					
						
							|  |  |  |       resolve(result); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     ipc.send('fetch-log'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const development = window && window?.getEnvironment && window?.getEnvironment() !== 'production'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A modern logging interface for the browser
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
 | 
					
						
							|  |  |  | function logAtLevel(level: string, prefix: string, ...args: any) { | 
					
						
							|  |  |  |   if (prefix === 'DEBUG' && !window.sessionFeatureFlags.debug.debugLogging) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (development) { | 
					
						
							|  |  |  |     const fn = `_${level}`; | 
					
						
							|  |  |  |     (console as any)[fn](prefix, now(), ...args); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     (console as any)._log(prefix, now(), ...args); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const str = cleanArgsForIPC(args); | 
					
						
							|  |  |  |   const logText = redactAll(str); | 
					
						
							|  |  |  |   ipc.send(`log-${level}`, logText); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | window.log = { | 
					
						
							|  |  |  |   fatal: _.partial(logAtLevel, 'fatal', 'FATAL'), | 
					
						
							|  |  |  |   error: _.partial(logAtLevel, 'error', 'ERROR'), | 
					
						
							|  |  |  |   warn: _.partial(logAtLevel, 'warn', 'WARN '), | 
					
						
							|  |  |  |   info: _.partial(logAtLevel, 'info', 'INFO '), | 
					
						
							|  |  |  |   debug: _.partial(logAtLevel, 'debug', 'DEBUG'), | 
					
						
							|  |  |  |   trace: _.partial(logAtLevel, 'trace', 'TRACE'), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | window.onerror = (_message, _script, _line, _col, error) => { | 
					
						
							|  |  |  |   const errorInfo = JSON.stringify(error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   window.log.error( | 
					
						
							|  |  |  |     `Top-level unhandled error: "${_message}";"${_script}";"${_line}";"${_col}" ${errorInfo}`, | 
					
						
							|  |  |  |     error | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | window.addEventListener('unhandledrejection', rejectionEvent => { | 
					
						
							|  |  |  |   const error = rejectionEvent.reason; | 
					
						
							|  |  |  |   const errorInfo = error && error.stack ? error.stack : error; | 
					
						
							|  |  |  |   window.log.error('Top-level unhandled promise rejection:', errorInfo); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function saveLogToDesktop() { | 
					
						
							|  |  |  |   const versionInfo = `v${window.getVersion()}`; | 
					
						
							|  |  |  |   const systemInfo = `System Information: ${window.getOSRelease()}`; | 
					
						
							|  |  |  |   const commitInfo = `Commit Hash: ${window.getCommitHash()}` || 'Unknown'; | 
					
						
							|  |  |  |   const debugLogWithSystemInfo = `
 | 
					
						
							|  |  |  | ****************************************************************************** | 
					
						
							|  |  |  | # Application Info | 
					
						
							|  |  |  | ${versionInfo} | 
					
						
							|  |  |  | ${systemInfo} | 
					
						
							|  |  |  | ${commitInfo} | 
					
						
							|  |  |  | ******************************************************************************`;
 | 
					
						
							|  |  |  |   window.saveLog(debugLogWithSystemInfo); | 
					
						
							|  |  |  | } |