From beb2a614b76ebc754706250906bd11c39d51a950 Mon Sep 17 00:00:00 2001 From: Florian Keller Date: Thu, 24 Oct 2019 15:52:39 +0200 Subject: [PATCH] feat: Add developer option to send logs via email (WEBAPP-6493) (backport) (#3155) --- electron/src/logging/getLogger.ts | 5 ++- electron/src/logging/loggerUtils.ts | 49 +++++++++++++++++++++++ electron/src/main.ts | 60 ++++++++++++----------------- electron/src/menu/developer.ts | 15 +++++++- package.json | 1 + 5 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 electron/src/logging/loggerUtils.ts diff --git a/electron/src/logging/getLogger.ts b/electron/src/logging/getLogger.ts index a92f263259b..393d20761c8 100644 --- a/electron/src/logging/getLogger.ts +++ b/electron/src/logging/getLogger.ts @@ -18,12 +18,14 @@ */ import {LogFactory, LoggerOptions} from '@wireapp/commons'; -import {remote} from 'electron'; +import {app, remote} from 'electron'; import * as logdown from 'logdown'; import {config} from '../settings/config'; const mainProcess = remote ? remote.process : process; +const logDir = path.join((remote ? remote.app : app).getPath('userData'), 'logs'); +const logFile = path.join(logDir, 'electron.log'); const isDevelopment = config.environment !== 'production'; const forceLogging = mainProcess.argv.includes('--enable-logging'); @@ -33,6 +35,7 @@ export const ENABLE_LOGGING = isDevelopment || forceLogging; export function getLogger(name: string): logdown.Logger { const options: LoggerOptions = { + logFilePath: logFile, namespace: LOGGER_NAMESPACE, separator: '/', }; diff --git a/electron/src/logging/loggerUtils.ts b/electron/src/logging/loggerUtils.ts new file mode 100644 index 00000000000..fbfa4542f5d --- /dev/null +++ b/electron/src/logging/loggerUtils.ts @@ -0,0 +1,49 @@ +/* + * Wire + * Copyright (C) 2019 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {app} from 'electron'; +import * as fs from 'fs-extra'; +import * as globby from 'globby'; +import * as path from 'path'; + +const logDir = path.join(app.getPath('userData'), 'logs'); + +export function getLogFiles(base: string = logDir, absolute: boolean = false): Promise { + return globby('**/*.{log,old}', {cwd: base, followSymbolicLinks: false, onlyFiles: true, absolute}); +} + +export async function gatherLogs(): Promise { + let log = ''; + + const relativeFilePaths = await getLogFiles(); + + for (const relativeFilePath of relativeFilePaths) { + const resolvedPath = path.join(logDir, relativeFilePath); + log += `\n\n+++++++ ${relativeFilePath} +++++++\n`; + try { + const fileContent = await fs.readFile(resolvedPath, 'utf-8'); + log += fileContent || '(no content)\n'; + } catch (error) { + log += '(fle not readable)\n'; + } + log += '++++++++++++++++++++++++++++\n'; + } + + return log || 'No log files found.'; +} diff --git a/electron/src/main.ts b/electron/src/main.ts index 3ad4397c95b..f7ad3355354 100644 --- a/electron/src/main.ts +++ b/electron/src/main.ts @@ -39,6 +39,7 @@ import {WebViewFocus} from './lib/webViewFocus'; import * as locale from './locale/locale'; import {ENABLE_LOGGING, getLogger} from './logging/getLogger'; import {Raygun} from './logging/initRaygun'; +import {getLogFiles} from './logging/loggerUtils'; import {menuItem as developerMenu} from './menu/developer'; import * as systemMenu from './menu/system'; import {TrayHandler} from './menu/TrayHandler'; @@ -276,9 +277,9 @@ const showMainWindow = (mainWindowState: WindowStateKeeper.State) => { // App Events const handleAppEvents = () => { - app.on('window-all-closed', async () => { + app.on('window-all-closed', () => { if (!EnvironmentUtil.platform.IS_MAC_OS) { - await lifecycle.quit(); + lifecycle.quit(); } }); @@ -310,41 +311,27 @@ const handleAppEvents = () => { }); }; -const renameFileExtensions = (files: string[], oldExtension: string, newExtension: string): void => { - files - .filter(file => { - try { - return fs.statSync(file).isFile(); - } catch (statError) { - return false; +const renameFileExtensions = async (files: string[], oldExtension: string, newExtension: string): Promise => { + for (const file of files) { + try { + const fileStat = await fs.stat(file); + if (fileStat.isFile() && file.endsWith(oldExtension)) { + await fs.rename(file, file.replace(oldExtension, newExtension)); } - }) - .forEach(file => { - if (file.endsWith(oldExtension)) { - try { - fs.renameSync(file, file.replace(oldExtension, newExtension)); - } catch (error) { - logger.error(`Failed to rename log file: "${error.message}"`); - } - } - }); -}; - -const renameWebViewLogFiles = (): void => { - // Rename "console.log" to "console.old" (for every log directory of every account) - fs.readdir(LOG_DIR, (readError, contents) => { - if (readError) { - return logger.log(`Failed to read log directory with error: ${readError.message}`); + } catch (error) { + logger.error(`Failed to rename log file: "${error.message}"`); } - - const logFiles = contents.map(file => path.join(LOG_DIR, file, config.logFileName)); - renameFileExtensions(logFiles, '.log', '.old'); - }); + } }; -const initElectronLogFile = (): void => { - renameFileExtensions([LOG_FILE], '.log', '.old'); - fs.ensureFileSync(LOG_FILE); +const renameWebViewLogFiles = async (): Promise => { + // Rename "console.log" to "console.old" (for every log directory of every account) + try { + const logFiles = await getLogFiles(LOG_DIR, true); + await renameFileExtensions(logFiles, '.log', '.old'); + } catch (error) { + logger.log(`Failed to read log directory with error: ${error.message}`); + } }; const addLinuxWorkarounds = () => { @@ -519,7 +506,8 @@ if (lifecycle.isFirstInstance) { addLinuxWorkarounds(); bindIpcEvents(); handleAppEvents(); - renameWebViewLogFiles(); - initElectronLogFile(); - new ElectronWrapperInit().run().catch(logger.error); + renameWebViewLogFiles() + .then(() => fs.ensureFile(LOG_FILE)) + .then(() => new ElectronWrapperInit().run()) + .catch(error => logger.error(error)); } diff --git a/electron/src/menu/developer.ts b/electron/src/menu/developer.ts index 1217355151f..9a59c0ec072 100644 --- a/electron/src/menu/developer.ts +++ b/electron/src/menu/developer.ts @@ -17,8 +17,9 @@ * */ -import {MenuItem, app} from 'electron'; +import {MenuItem, app, shell} from 'electron'; +import {gatherLogs} from '../logging/loggerUtils'; import * as EnvironmentUtil from '../runtime/EnvironmentUtil'; import {config} from '../settings/config'; import {settings} from '../settings/ConfigurationPersistence'; @@ -33,6 +34,17 @@ const reloadTemplate: Electron.MenuItemConstructorOptions = { label: 'Reload', }; +const sendLogTemplate: Electron.MenuItemConstructorOptions = { + click: async () => { + const logText = await gatherLogs(); + const subject = encodeURIComponent('Wire Desktop Log'); + const body = encodeURIComponent(logText); + const mailToLink = `mailto:support+web@wire.com?subject=${subject}&body=${body}`; + await shell.openExternal(mailToLink); + }, + label: 'Send Debug Logs', +}; + const devToolsTemplate: Electron.MenuItemConstructorOptions = { label: 'Toggle DevTools', submenu: [ @@ -109,6 +121,7 @@ const menuTemplate: Electron.MenuItemConstructorOptions = { submenu: [ devToolsTemplate, reloadTemplate, + sendLogTemplate, separatorTemplate, ...createEnvironmentTemplates(), separatorTemplate, diff --git a/package.json b/package.json index bf97fae90f8..8cbd0ec3f5b 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "electron-window-state": "5.0.3", "file-url": "3.0.0", "fs-extra": "8.0.1", + "globby": "10.0.1", "iconv-lite": "0.5.0", "image-type": "4.1.0", "lodash": "4.17.15",