Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
Write logs to file that can be opened by user
Browse files Browse the repository at this point in the history
* Main thread writes all logs to file
* Main thread sends the filepath of the logs to the browser window
* Added 'GetRawLogsButton' to server monitor component
* When button is clicked, opens the file externally
* Delete logfile when browser closed
* If logfile not present, show an alert to indicate that logs can't be accessed
  • Loading branch information
dpgraham committed Aug 4, 2017
1 parent 53d31c3 commit 0c93577
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 12 deletions.
30 changes: 28 additions & 2 deletions app/main/appium.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { main as appiumServer } from 'appium';
import { getDefaultArgs, getParser } from 'appium/build/lib/parser';
import path from 'path';
import wd from 'wd';
import { fs, tempDir } from 'appium-support';
import settings from '../settings';
import autoUpdaterController from './auto-updater';
import AppiumMethodHandler from './appium-method-handler';
Expand All @@ -20,10 +21,19 @@ var batchedLogs = [];
let sessionDrivers = {};

let appiumHandlers = {};
let logFile;

// Delete saved server args, don't start until a server has been started
settings.deleteSync('SERVER_ARGS');

async function deleteLogfile () {
if (logFile) {
try {
await fs.rimraf(logFile);
} catch (ign) { }
}
}

/**
* Kill session associated with session browser window
*/
Expand All @@ -46,6 +56,14 @@ async function killSession (sessionWinID) {

function connectStartServer (win) {
ipcMain.on('start-server', async (event, args) => {
// log the server logs to a file
try {
const dir = await tempDir.openDir();
logFile = path.resolve(dir, 'appium-server-logs.txt');
win.webContents.send('path-to-logs', logFile);
win.on('close', deleteLogfile);
} catch (ign) { }

// clean up args object for appium log purposes (so it doesn't show in
// non-default args list
if (args.defaultCapabilities &&
Expand All @@ -60,9 +78,16 @@ function connectStartServer (win) {
args.throwInsteadOfExit = true;

// set up our log watcher
logWatcher = setInterval(() => {
logWatcher = setInterval(async () => {
if (batchedLogs.length) {
win.webContents.send('appium-log-line', batchedLogs);
try {
await fs.writeFile(
logFile,
batchedLogs.map((log) => `[${log.level}] ${log.msg}`).join('\n'),
{flag: 'a'}
);
win.webContents.send('appium-log-line', batchedLogs);
} catch (ign) { }
batchedLogs = [];
}
}, LOG_SEND_INTERVAL_MS);
Expand Down Expand Up @@ -90,6 +115,7 @@ function connectStopServer (win) {
} catch (e) {
win.webContents.send('appium-stop-error', e.message);
}

clearInterval(logWatcher);
await settings.delete('SERVER_ARGS');
});
Expand Down
13 changes: 12 additions & 1 deletion app/renderer/actions/ServerMonitor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, shell } from 'electron';
import { push } from 'react-router-redux';

export const SERVER_STOP_REQ = 'SERVER_STOP_REQ';
Expand Down Expand Up @@ -86,4 +86,15 @@ export function startSession () {
dispatch({type: START_SESSION_REQUEST});
ipcRenderer.send('create-new-session-window');
};
}

export function getRawLogs () {
return (dispatch, getState) => {
const logfilePath = getState().startServer.logfilePath;
if (logfilePath) {
shell.openExternal(`file://${logfilePath}`);
} else {
alert('An error has occurred: Logs not available');
}
};
}
4 changes: 3 additions & 1 deletion app/renderer/actions/StartServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const PRESET_SAVE_OK = 'PRESET_SAVE_OK';
export const GET_PRESETS = 'GET_PRESETS';
export const PRESET_DELETE_REQ = 'PRESET_DELETE_REQ';
export const PRESET_DELETE_OK = 'PRESET_DELETE_OK';
export const SET_LOGFILE_PATH = 'SET_LOGFILE_PATH';

export const PRESETS = 'presets';

Expand Down Expand Up @@ -47,6 +48,7 @@ export function startServer (evt) {
});

dispatch(clearLogs());
ipcRenderer.once('path-to-logs', (event, logfilePath) => dispatch({type: SET_LOGFILE_PATH, logfilePath}));
ipcRenderer.send('start-server', serverArgs);
};
}
Expand Down Expand Up @@ -104,4 +106,4 @@ export function deletePreset (name) {
}
dispatch({type: PRESET_DELETE_OK, presets});
};
}
}
9 changes: 8 additions & 1 deletion app/renderer/components/ServerMonitor/ServerMonitor.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,20 @@
margin-right: 10px;
}

.stopButton {

.serverButton {
color: #a0a0a0 !important;
background-image: none !important;
background-color: #252525 !important;
border-color: #a0a0a0 !important;
}

.getRawLogsButton {
position: absolute;
right: 0;
margin: 0 1em 0 0;
}

.term {
white-space: pre-wrap;
width: 100%;
Expand Down
17 changes: 13 additions & 4 deletions app/renderer/components/ServerMonitor/ServerMonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ class StopButton extends Component {

render () {
const {serverStatus, stopServer, closeMonitor} = this.props;
let btn = <Button icon="pause-circle-o" className={styles.stopButton}
let btn = <Button icon="pause-circle-o" className={styles.serverButton}
onClick={stopServer}>Stop Server</Button>;
if (serverStatus === STATUS_STOPPED) {
btn = <Button className={styles.stopButton}
btn = <Button className={styles.serverButton}
icon="close-circle-o"
onClick={closeMonitor}>Close Logs</Button>;
} else if (serverStatus === STATUS_STOPPING) {
btn = <Button icon="pause-circle-o"
className={styles.stopButton} type="disabled">Stopping...</Button>;
className={styles.serverButton} type="disabled">Stopping...</Button>;
}
return btn;
}
Expand All @@ -54,7 +54,7 @@ class StartSessionButton extends Component {
render () {
const {serverStatus, startSession} = this.props;
if (serverStatus !== STATUS_STOPPED && serverStatus !== STATUS_STOPPING) {
return <Button className={styles.stopButton} id='startNewSessionBtn'
return <Button className={styles.serverButton} id='startNewSessionBtn'
icon="play-circle-o"
onClick={startSession}>Start New Session</Button>;
} else {
Expand All @@ -63,6 +63,14 @@ class StartSessionButton extends Component {
}
}

class GetRawLogsButton extends Component {
render () {
return <Button className={`${styles.getRawLogsButton} ${styles.serverButton}`}
icon="file-text"
onClick={() => this.props.getRawLogs()}>Get raw logs</Button>;
}
}

export default class ServerMonitor extends Component {

static propTypes = {
Expand Down Expand Up @@ -149,6 +157,7 @@ export default class ServerMonitor extends Component {
</div>
</div>
<div className={termClass} ref={(c) => this._term = c}>
<GetRawLogsButton {...this.props} />
{logLineSection}
{lastSection}
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/renderer/reducers/ServerMonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const initialState = {
};

export default function serverMonitor (state = initialState, action) {
let logLines;
switch (action.type) {
case SERVER_STOP_REQ:
return {...state, serverStatus: STATUS_STOPPING};
Expand All @@ -40,8 +41,7 @@ export default function serverMonitor (state = initialState, action) {
sessionId: action.sessionUUID,
};
case LOGS_RECEIVED:
// TODO: We should dump logs to a txt file that can be exported
var logLines = state.logLines.concat(action.logs.map((l) => {
logLines = state.logLines.concat(action.logs.map((l) => {
// attach a timestamp to each log line here when it comes in
l.timestamp = moment().format('YYYY-MM-DD hh:mm:ss');
return l;
Expand Down
4 changes: 3 additions & 1 deletion app/renderer/reducers/StartServer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SERVER_START_REQ, SERVER_START_OK, SERVER_START_ERR, GET_PRESETS,
UPDATE_ARGS, SWITCH_TAB, PRESET_SAVE_REQ, PRESET_SAVE_OK,
PRESET_DELETE_REQ, PRESET_DELETE_OK
PRESET_DELETE_REQ, PRESET_DELETE_OK, SET_LOGFILE_PATH,
} from '../actions/StartServer';

import { ipcRenderer } from 'electron';
Expand Down Expand Up @@ -50,6 +50,8 @@ export default function startServer (state = initialState, action) {
return {...state, presetDeleting: true};
case PRESET_DELETE_OK:
return {...state, presetDeleting: false, presets: action.presets};
case SET_LOGFILE_PATH:
return {...state, logfilePath: action.logfilePath};
default:
return state;
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"ansi-to-html": "0.5",
"antd": "2.10.1",
"appium": "1.6.5",
"appium-support": "^2.8.2",
"bluebird": "3.x",
"css-modules-require-hook": "4.x",
"electron-debug": "1.x",
Expand Down

0 comments on commit 0c93577

Please sign in to comment.