mirror of
https://github.com/status-im/embark-area-51.git
synced 2025-02-12 23:26:39 +00:00
Merge pull request #47 from status-im/features/improve-process-logs
Improve process logs
This commit is contained in:
commit
e0343deabf
@ -4,6 +4,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"ace-mode-solidity": "^0.1.0",
|
||||
"ansi-to-html": "^0.6.6",
|
||||
"autoscroll-react": "^3.2.0",
|
||||
"axios": "^0.18.0",
|
||||
"classnames": "^2.2.6",
|
||||
"connected-react-router": "^4.3.0",
|
||||
|
@ -64,7 +64,7 @@ export const processes = {
|
||||
|
||||
export const COMMANDS = createRequestTypes('COMMANDS');
|
||||
export const commands = {
|
||||
post: (command) => action(COMMANDS[REQUEST], {command}),
|
||||
post: (command) => action(COMMANDS[REQUEST], {command, noLoading: true}),
|
||||
success: (command) => action(COMMANDS[SUCCESS], {commands: [{timestamp: new Date().getTime(), ...command}]}),
|
||||
failure: (error) => action(COMMANDS[FAILURE], {error})
|
||||
};
|
||||
|
@ -1,6 +0,0 @@
|
||||
.console--results {
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
import PropTypes from "prop-types";
|
||||
import React, {Component} from 'react';
|
||||
import {Grid, Card, Form} from 'tabler-react';
|
||||
import {Grid, Card, Form, Tab, TabbedHeader, TabbedContainer} from 'tabler-react';
|
||||
import Logs from "./Logs";
|
||||
import Convert from 'ansi-to-html';
|
||||
|
||||
require('./Console.css');
|
||||
const convert = new Convert();
|
||||
|
||||
const CommandResult = ({result}) => (
|
||||
<p className="text__new-line">{result}</p>
|
||||
@ -15,7 +17,10 @@ CommandResult.propTypes = {
|
||||
class Console extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: ''};
|
||||
this.state = {
|
||||
value: '',
|
||||
selectedProcess: 'Embark'
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
@ -29,27 +34,56 @@ class Console extends Component {
|
||||
this.setState({value: event.target.value});
|
||||
}
|
||||
|
||||
renderTabs() {
|
||||
const {processLogs, processes, commands} = this.props;
|
||||
return [
|
||||
(<Tab title="Embark" key="Embark">
|
||||
<Logs>
|
||||
{commands.map((command, index) => <CommandResult key={index} result={command.result}/>)}
|
||||
</Logs>
|
||||
</Tab>)
|
||||
].concat(processes.map(process => (
|
||||
<Tab title={process.name} key={process.name} onClick={(e, x) => this.clickTab(e, x)}>
|
||||
<Logs>
|
||||
{
|
||||
processLogs.filter((item) => item.name === process.name)
|
||||
.map((item, i) => <p key={i} className={item.logLevel}
|
||||
dangerouslySetInnerHTML={{__html: convert.toHtml(item.msg)}}></p>)
|
||||
}
|
||||
</Logs>
|
||||
</Tab>
|
||||
)));
|
||||
}
|
||||
|
||||
render() {
|
||||
const tabs = this.renderTabs();
|
||||
const {selectedProcess, value} = this.state;
|
||||
|
||||
return (
|
||||
<Grid.Row cards className="console">
|
||||
<Grid.Col>
|
||||
<Card>
|
||||
<Card.Header>
|
||||
<Card.Title>Embark console</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Body className="console--results">
|
||||
<div>
|
||||
{this.props.commands.map((command, index) => <CommandResult key={index} result={command.result} />)}
|
||||
</div>
|
||||
<Card.Body className="console-container">
|
||||
<React.Fragment>
|
||||
<TabbedHeader
|
||||
selectedTitle={selectedProcess}
|
||||
stateCallback={newProcess => this.setState({selectedProcess: newProcess})}
|
||||
>
|
||||
{tabs}
|
||||
</TabbedHeader>
|
||||
<TabbedContainer selectedTitle={selectedProcess}>
|
||||
{tabs}
|
||||
</TabbedContainer>
|
||||
</React.Fragment>
|
||||
</Card.Body>
|
||||
<Card.Footer>
|
||||
<Form onSubmit={(event) => this.handleSubmit(event)}>
|
||||
<Form.Input value={this.state.value}
|
||||
{selectedProcess === 'Embark' && <Card.Footer>
|
||||
<form onSubmit={(event) => this.handleSubmit(event)} autoComplete="off">
|
||||
<Form.Input value={value}
|
||||
onChange={(event) => this.handleChange(event)}
|
||||
name="command"
|
||||
placeholder="Type a command (e.g help)"/>
|
||||
</Form>
|
||||
</Card.Footer>
|
||||
</form>
|
||||
</Card.Footer>}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid.Row>
|
||||
@ -59,7 +93,9 @@ class Console extends Component {
|
||||
|
||||
Console.propTypes = {
|
||||
postCommand: PropTypes.func,
|
||||
commands: PropTypes.arrayOf(PropTypes.object)
|
||||
commands: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
processes: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
processLogs: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
|
||||
export default Console;
|
||||
|
@ -9,7 +9,6 @@ const navBarItems = [
|
||||
{value: "Home", to: "/embark", icon: "home", LinkComponent: NavLink},
|
||||
{value: "Contracts", to: "/embark/contracts", icon: "box", LinkComponent: NavLink},
|
||||
{value: "Explorer", to: "/embark/explorer/accounts", icon: "activity", LinkComponent: NavLink},
|
||||
{value: "Processes", to: "/embark/processes", icon: "cpu", LinkComponent: NavLink},
|
||||
{value: "Fiddle", to: "/embark/fiddle", icon: "codepen", LinkComponent: NavLink},
|
||||
{value: "Documentation", to: "/embark/documentation", icon: "file-text", LinkComponent: NavLink}
|
||||
];
|
||||
|
14
embark-ui/src/components/Logs.js
Normal file
14
embark-ui/src/components/Logs.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import autoscroll from 'autoscroll-react';
|
||||
|
||||
class Logs extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="logs" {...this.props}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default autoscroll(Logs);
|
@ -1,26 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Page} from "tabler-react";
|
||||
|
||||
class Process extends Component {
|
||||
render() {
|
||||
const {processLogs, process}= this.props;
|
||||
return (
|
||||
<Page.Content className="text-capitalize" title={process.name}>
|
||||
<p className="text-capitalize">State: {process.state}</p>
|
||||
<div className="logs">
|
||||
{
|
||||
processLogs.map((item, i) => <p key={i} className={item.logLevel}>{item.msg_clear || item.msg}</p>)
|
||||
}
|
||||
</div>
|
||||
</Page.Content>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Process.propTypes = {
|
||||
process: PropTypes.object,
|
||||
processLogs: PropTypes.arrayOf(PropTypes.object)
|
||||
};
|
||||
|
||||
export default Process;
|
@ -1,64 +0,0 @@
|
||||
import PropTypes from "prop-types";
|
||||
import React, {Component} from 'react';
|
||||
import connect from "react-redux/es/connect/connect";
|
||||
import {NavLink, Route, Switch, Redirect} from 'react-router-dom';
|
||||
import {
|
||||
Page,
|
||||
Grid,
|
||||
List
|
||||
} from "tabler-react";
|
||||
|
||||
import ProcessContainer from '../containers/ProcessContainer';
|
||||
import {getProcesses} from "../reducers/selectors";
|
||||
import Loading from "./Loading";
|
||||
|
||||
const routePrefix = '/embark/processes';
|
||||
|
||||
class ProcessesLayout extends Component {
|
||||
render() {
|
||||
if (this.props.processes.length === 0) {
|
||||
return <Loading />;
|
||||
}
|
||||
return (<Grid.Row>
|
||||
<Grid.Col md={3}>
|
||||
<Page.Title className="my-5">Processes</Page.Title>
|
||||
<div>
|
||||
<List.Group transparent={true}>
|
||||
{this.props.processes.map((process, index) => {
|
||||
return (<List.GroupItem
|
||||
className="d-flex align-items-center text-capitalize"
|
||||
to={`${routePrefix}/${process.name}`}
|
||||
key={'process-' + process.name}
|
||||
active={index === 0 && this.props.match.isExact === true}
|
||||
RootComponent={NavLink}
|
||||
>
|
||||
{process.name}
|
||||
</List.GroupItem>);
|
||||
})}
|
||||
|
||||
</List.Group>
|
||||
</div>
|
||||
</Grid.Col>
|
||||
<Grid.Col md={9}>
|
||||
<Switch>
|
||||
<Route exact path={`${routePrefix}/:processName`} component={() => <ProcessContainer />} />
|
||||
<Redirect exact from={`${routePrefix}/`} to={`${routePrefix}/${this.props.processes[0].name}`} />
|
||||
</Switch>
|
||||
</Grid.Col>
|
||||
</Grid.Row>);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessesLayout.propTypes = {
|
||||
processes: PropTypes.arrayOf(PropTypes.object),
|
||||
match: PropTypes.object
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {processes: getProcesses(state)};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(ProcessesLayout);
|
||||
|
@ -3,14 +3,26 @@ import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {Page} from "tabler-react";
|
||||
|
||||
import {commands as commandsAction} from "../actions";
|
||||
import {commands as commandsAction, listenToProcessLogs, processLogs as processLogsAction} from "../actions";
|
||||
import DataWrapper from "../components/DataWrapper";
|
||||
import Processes from '../components/Processes';
|
||||
import Versions from '../components/Versions';
|
||||
import Console from '../components/Console';
|
||||
import {getProcesses, getCommands, getVersions} from "../reducers/selectors";
|
||||
import {getProcesses, getCommands, getVersions, getProcessLogs} from "../reducers/selectors";
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
class HomeContainer extends Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!deepEqual(this.props.processes, prevProps.processes)) {
|
||||
this.props.processes.forEach(process => {
|
||||
this.props.fetchProcessLogs(process.name);
|
||||
if (!prevProps.processes.length) {
|
||||
this.props.listenToProcessLogs(process.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
@ -21,7 +33,10 @@ class HomeContainer extends Component {
|
||||
<DataWrapper shouldRender={this.props.versions.length > 0 } {...this.props} render={({versions}) => (
|
||||
<Versions versions={versions} />
|
||||
)} />
|
||||
<Console postCommand={this.props.postCommand} commands={this.props.commands} />
|
||||
|
||||
<DataWrapper shouldRender={this.props.processes.length > 0 } {...this.props} render={({processes, postCommand, processLogs}) => (
|
||||
<Console postCommand={postCommand} commands={this.props.commands} processes={processes} processLogs={processLogs} />
|
||||
)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@ -42,6 +57,7 @@ function mapStateToProps(state) {
|
||||
processes: getProcesses(state),
|
||||
commands: getCommands(state),
|
||||
error: state.errorMessage,
|
||||
processLogs: getProcessLogs(state),
|
||||
loading: state.loading
|
||||
};
|
||||
}
|
||||
@ -49,6 +65,8 @@ function mapStateToProps(state) {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
postCommand: commandsAction.post
|
||||
postCommand: commandsAction.post,
|
||||
fetchProcessLogs: processLogsAction.request,
|
||||
listenToProcessLogs
|
||||
}
|
||||
)(HomeContainer);
|
||||
|
@ -1,55 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import {withRouter} from "react-router-dom";
|
||||
import {processLogs as processLogsAction, listenToProcessLogs} from '../actions';
|
||||
import DataWrapper from "../components/DataWrapper";
|
||||
import Process from "../components/Process";
|
||||
import {getProcess, getProcessLogsByProcess} from "../reducers/selectors";
|
||||
|
||||
class ProcessContainer extends Component {
|
||||
componentDidMount() {
|
||||
if (this.props.process.state === 'running' && this.props.processLogs.length === 0) {
|
||||
this.props.fetchProcessLogs(this.props.match.params.processName);
|
||||
this.props.listenToProcessLogs(this.props.match.params.processName);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<DataWrapper shouldRender={this.props.process !== undefined } {...this.props} render={({process, processLogs}) => (
|
||||
<div className="processes-container">
|
||||
<Process process={process}
|
||||
processLogs={processLogs}/>
|
||||
</div>
|
||||
)} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessContainer.propTypes = {
|
||||
fetchProcessLogs: PropTypes.func,
|
||||
listenToProcessLogs: PropTypes.func,
|
||||
process: PropTypes.object,
|
||||
processLogs: PropTypes.arrayOf(PropTypes.object),
|
||||
error: PropTypes.string,
|
||||
loading: PropTypes.bool,
|
||||
match: PropTypes.object
|
||||
};
|
||||
|
||||
function mapStateToProps(state, props) {
|
||||
return {
|
||||
process: getProcess(state, props.match.params.processName),
|
||||
processLogs: getProcessLogsByProcess(state, props.match.params.processName),
|
||||
error: state.errorMessage,
|
||||
loading: state.loading
|
||||
};
|
||||
}
|
||||
|
||||
export default withRouter(connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
fetchProcessLogs: processLogsAction.request,
|
||||
listenToProcessLogs
|
||||
}
|
||||
)(ProcessContainer));
|
@ -6,56 +6,80 @@
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.logs .error {
|
||||
color: #dc3546;
|
||||
}
|
||||
|
||||
.logs .warn {
|
||||
color: #fec107;
|
||||
}
|
||||
|
||||
.logs .debug {
|
||||
color: #b7c1cc;
|
||||
}
|
||||
|
||||
.logs .trace {
|
||||
color: #8f98a2;
|
||||
}
|
||||
|
||||
.console-container {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.console-container .nav-link {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.text__new-line, .card.warnings-card .list-group-item, .card.errors-card .list-group-item {
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.card.card-fullscreen {
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.card.warnings-card, .card.errors-card {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.card.warnings-card .card-options a, .card.errors-card .card-options a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.compilation-summary {
|
||||
float: right;
|
||||
margin-bottom: 3px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.compilation-summary.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.compilation-summary .badge-link:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.ace_editor {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.loader, .loader:before, .loader:after {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.loader:before, .loader:after {
|
||||
margin: -0.6rem 0 0 -0.6rem;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.loader, .loader-text {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
@ -44,12 +44,8 @@ export function getProcesses(state) {
|
||||
return state.entities.processes;
|
||||
}
|
||||
|
||||
export function getProcess(state, name) {
|
||||
return state.entities.processes.find((process) => process.name === name);
|
||||
}
|
||||
|
||||
export function getProcessLogsByProcess(state, processName) {
|
||||
return state.entities.processLogs.filter((processLog => processLog.name === processName));
|
||||
export function getProcessLogs(state) {
|
||||
return state.entities.processLogs;
|
||||
}
|
||||
|
||||
export function getContractLogsByContract(state, contractName) {
|
||||
|
@ -6,7 +6,6 @@ import ContractsContainer from './containers/ContractsContainer';
|
||||
import ContractContainer from './containers/ContractLayoutContainer';
|
||||
import NoMatch from './components/NoMatch';
|
||||
import ExplorerLayout from './components/ExplorerLayout';
|
||||
import ProcessesLayout from './components/ProcessesLayout';
|
||||
import FiddleLayout from './components/FiddleLayout';
|
||||
|
||||
const routes = (
|
||||
@ -14,7 +13,6 @@ const routes = (
|
||||
<Switch>
|
||||
<Route exact path="/embark/" component={HomeContainer} />
|
||||
<Route path="/embark/explorer/" component={ExplorerLayout} />
|
||||
<Route path="/embark/processes/" component={ProcessesLayout} />
|
||||
<Route path="/embark/contracts/:contractName" component={ContractContainer} />
|
||||
<Route path="/embark/contracts" component={ContractsContainer} />
|
||||
<Route path="/embark/fiddle" component={FiddleLayout} />
|
||||
|
@ -11,7 +11,7 @@ class ContractSource {
|
||||
this.lineCount = this.lineLengths.length;
|
||||
|
||||
this.lineOffsets = this.lineLengths.reduce((sum, _elt, i) => {
|
||||
sum[i] = (i == 0) ? 0 : self.lineLengths[i-1] + sum[i-1] + 1;
|
||||
sum[i] = (i === 0) ? 0 : self.lineLengths[i-1] + sum[i-1] + 1;
|
||||
return sum;
|
||||
}, []);
|
||||
|
||||
@ -245,7 +245,7 @@ class ContractSource {
|
||||
|
||||
trace.structLogs.forEach((step) => {
|
||||
step = bytecode[step.pc];
|
||||
if(!step.sourceMap || step.sourceMap == '') return;
|
||||
if(!step.sourceMap || step.sourceMap === '') return;
|
||||
|
||||
var nodes = sourceMapToNodeType[step.sourceMap];
|
||||
|
||||
@ -262,7 +262,7 @@ class ContractSource {
|
||||
recordedLineHit = true;
|
||||
}
|
||||
|
||||
if(node.type != 'b') coverage[node.type][node.id]++;
|
||||
if(node.type !== 'b') coverage[node.type][node.id]++;
|
||||
|
||||
if(!node.parent) return;
|
||||
|
||||
@ -282,7 +282,7 @@ class ContractSource {
|
||||
}
|
||||
|
||||
_instructionLength(instruction) {
|
||||
if(instruction.indexOf('PUSH') == -1) return 1;
|
||||
if(instruction.indexOf('PUSH') === -1) return 1;
|
||||
return parseInt(instruction.match(/PUSH(\d+)/m)[1], 10) + 1;
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class CodeCoverage {
|
||||
|
||||
async runSolc(receipt) {
|
||||
let block = await web3.eth.getBlock(receipt.number);
|
||||
if(block.transactions.length == 0) return;
|
||||
if(block.transactions.length === 0) return;
|
||||
|
||||
let requests = [];
|
||||
for(let i in block.transactions) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
class SourceMap {
|
||||
constructor(sourceMapStringOrOffset, length, id) {
|
||||
if(typeof sourceMapStringOrOffset == 'string') {
|
||||
if(typeof sourceMapStringOrOffset === 'string') {
|
||||
let [offset, length, id, ..._rest] = sourceMapStringOrOffset.split(":");
|
||||
|
||||
this.offset = parseInt(offset, 10);
|
||||
|
@ -129,14 +129,6 @@ class IPFS {
|
||||
this.embark.addCodeToEmbarkJS(code);
|
||||
}
|
||||
|
||||
addNamesystemProviderToEmbarkJS() {
|
||||
let code = "";
|
||||
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs', 'name.js')).toString();
|
||||
code += "\nEmbarkJS.Names.registerProvider('ipns', __embarkIPFS);";
|
||||
|
||||
this.embark.addCodeToEmbarkJS(code);
|
||||
}
|
||||
|
||||
addObjectToConsole() {
|
||||
let ipfs = IpfsApi(this.host, this.port);
|
||||
this.events.emit("runcode:register", "ipfs", ipfs);
|
||||
|
@ -50,7 +50,7 @@ class ContractFuzzer {
|
||||
}
|
||||
case kind === "bool":
|
||||
return self.generateRandomBool();
|
||||
case kind == "uint" || kind == "int":
|
||||
case kind === "uint" || kind === "int":
|
||||
return self.generateRandomInt(size || 256);
|
||||
case kind === "bytes":
|
||||
return self.generateRandomStaticBytes(size || 32);
|
||||
|
Loading…
x
Reference in New Issue
Block a user