beef up event log

This commit is contained in:
Maximilian Hils 2014-09-22 03:06:19 +02:00
parent 84e2a028c2
commit 45f39c2380
11 changed files with 291 additions and 56 deletions

View File

@ -60,6 +60,7 @@ body,
flex: 1 1 auto;
display: flex;
flex-direction: row;
outline: 0;
}
.main-view.vertical {
flex-direction: column;
@ -212,27 +213,61 @@ header .menu {
table-layout: fixed;
word-break: break-all;
}
.flow-detail table tr {
.flow-detail table tr:not(:first-child) {
border-top: 1px solid #f7f7f7;
}
.flow-detail table td {
vertical-align: top;
}
.connection-table td:first-child {
width: 33%;
width: 50%;
padding-right: 1em;
}
.header-table .header-name {
width: 33%;
padding-right: 1em;
}
.connection-table td,
.timing-table td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.eventlog {
height: 200px;
flex: 0 0 auto;
display: flex;
flex-direction: column;
}
.eventlog > div {
background-color: #F2F2F2;
padding: 0 5px;
flex: 0 0 auto;
}
.eventlog > pre {
flex: 1 1 auto;
margin: 0;
border-radius: 0;
height: 200px;
overflow: auto;
overflow-x: auto;
overflow-y: scroll;
background-color: #fcfcfc;
}
.eventlog .fa-close {
cursor: pointer;
float: right;
color: grey;
padding: 3px 0;
padding-left: 10px;
}
.eventlog .fa-close:hover {
color: black;
}
.eventlog .label {
cursor: pointer;
vertical-align: middle;
display: inline-block;
margin-top: -2px;
margin-left: 3px;
}
footer {
box-shadow: 0 -1px 3px #d3d3d3;

View File

@ -121,14 +121,15 @@ var SettingsActions = {
}
};
var event_id = 0;
var EventLogActions = {
add_event: function(message, level){
add_event: function(message){
AppDispatcher.dispatchViewAction({
type: ActionTypes.ADD_EVENT,
data: {
message: message,
level: level || "info",
source: "ui"
level: "web",
id: "viewAction-"+event_id++
}
});
}
@ -263,7 +264,7 @@ _.extend(EventLogView.prototype, EventEmitter.prototype, {
},
add: function (entry) {
this.log.push(entry);
if(this.log.length > 50){
if(this.log.length > 200){
this.log.shift();
}
this.emit("change");
@ -334,7 +335,6 @@ _.extend(_EventLogStore.prototype, EventEmitter.prototype, {
var EventLogStore = new _EventLogStore();
AppDispatcher.register(EventLogStore.handle.bind(EventLogStore));
function FlowView(store, live) {
EventEmitter.call(this);
this._store = store;
@ -443,7 +443,7 @@ _Connection.prototype.openWebSocketConnection = function () {
ws.onclose = this.onclose.bind(this);
};
_Connection.prototype.onopen = function (open) {
console.log("onopen", this, arguments);
console.debug("onopen", this, arguments);
};
_Connection.prototype.onmessage = function (message) {
//AppDispatcher.dispatchServerAction(...);
@ -452,11 +452,11 @@ _Connection.prototype.onmessage = function (message) {
};
_Connection.prototype.onerror = function (error) {
EventLogActions.add_event("WebSocket Connection Error.");
console.log("onerror", this, arguments);
console.debug("onerror", this, arguments);
};
_Connection.prototype.onclose = function (close) {
EventLogActions.add_event("WebSocket Connection closed.");
console.log("onclose", this, arguments);
console.debug("onclose", this, arguments);
};
var Connection = new _Connection(location.origin + "/updates");
@ -1139,7 +1139,7 @@ var Timing = React.createClass({displayName: 'Timing',
return (
React.DOM.div(null,
React.DOM.h4(null, "Timing"),
React.DOM.table(null,
React.DOM.table({className: "timing-table"},
React.DOM.tbody(null,
rows
)
@ -1214,7 +1214,6 @@ var MainView = React.createClass({displayName: 'MainView',
};
},
componentDidMount: function () {
console.log("get view");
this.flowStore = FlowStore.getView();
this.flowStore.addListener("change",this.onFlowChange);
},
@ -1334,7 +1333,32 @@ var MainView = React.createClass({displayName: 'MainView',
});
/** @jsx React.DOM */
var EventLog = React.createClass({displayName: 'EventLog',
var LogMessage = React.createClass({displayName: 'LogMessage',
render: function(){
var entry = this.props.entry;
var indicator;
switch(entry.level){
case "web":
indicator = React.DOM.i({className: "fa fa-fw fa-html5"});
break;
case "debug":
indicator = React.DOM.i({className: "fa fa-fw fa-bug"});
break;
default:
indicator = React.DOM.i({className: "fa fa-fw fa-info"});
}
return (
React.DOM.div(null,
indicator, " ", entry.message
)
);
},
shouldComponentUpdate: function(){
return false; // log entries are immutable.
}
});
var EventLogContents = React.createClass({displayName: 'EventLogContents',
mixins:[AutoScrollMixin],
getInitialState: function () {
return {
@ -1354,23 +1378,76 @@ var EventLog = React.createClass({displayName: 'EventLog',
log: this.log.getAll()
});
},
render: function () {
var messages = this.state.log.map(function(row) {
if(!this.props.filter[row.level]){
return null;
}
return LogMessage({key: row.id, entry: row});
}.bind(this));
return React.DOM.pre(null, messages);
}
});
var ToggleFilter = React.createClass({displayName: 'ToggleFilter',
toggle: function(){
return this.props.toggleLevel(this.props.name);
},
render: function(){
var className = "label ";
if (this.props.active) {
className += "label-primary";
} else {
className += "label-default";
}
return (
React.DOM.a({
href: "#",
className: className,
onClick: this.toggle},
this.props.name
)
);
}
});
var EventLog = React.createClass({displayName: 'EventLog',
getInitialState: function(){
return {
filter: {
"debug": false,
"info": true,
"web": true
}
};
},
close: function () {
SettingsActions.update({
showEventLog: false
});
},
toggleLevel: function(level){
var filter = this.state.filter;
filter[level] = !filter[level];
this.setState({filter: filter});
return false;
},
render: function () {
var messages = this.state.log.map(function(row) {
var indicator = null;
if(row.source === "ui"){
indicator = React.DOM.i({className: "fa fa-html5"});
}
return (
React.DOM.div({key: row.id},
indicator, " ", row.message
));
});
return React.DOM.pre({className: "eventlog"}, messages);
return (
React.DOM.div({className: "eventlog"},
React.DOM.div(null,
"Eventlog",
React.DOM.div({className: "pull-right"},
ToggleFilter({name: "debug", active: this.state.filter.debug, toggleLevel: this.toggleLevel}),
ToggleFilter({name: "info", active: this.state.filter.info, toggleLevel: this.toggleLevel}),
ToggleFilter({name: "web", active: this.state.filter.web, toggleLevel: this.toggleLevel}),
React.DOM.i({onClick: this.close, className: "fa fa-close"})
)
),
EventLogContents({filter: this.state.filter})
)
);
}
});
/** @jsx React.DOM */

View File

@ -1,10 +1,45 @@
.eventlog {
height: 200px;
flex: 0 0 auto;
margin: 0;
border-radius: 0;
height: 200px;
overflow: auto;
overflow-y: scroll;
display: flex;
flex-direction: column;
> div {
background-color: #F2F2F2;
padding: 0 5px;
flex: 0 0 auto;
}
> pre {
flex: 1 1 auto;
margin: 0;
border-radius: 0;
overflow-x: auto;
overflow-y: scroll;
background-color: #fcfcfc;
}
.fa-close {
cursor: pointer;
float: right;
color: grey;
padding: 3px 0;
padding-left: 10px;
&:hover {
color: black;
}
}
.label {
cursor: pointer;
vertical-align: middle;
display: inline-block;
margin-top: -2px;
margin-left: 3px;
}
}

View File

@ -35,9 +35,9 @@
word-break: break-all;
tr {
//&:not(:first-child){
&:not(:first-child){
border-top: 1px solid #f7f7f7;
//}
}
}
td {
@ -51,7 +51,7 @@
.connection-table {
td:first-child {
width: 33%;
width: 50%;
padding-right: 1em;
}
}
@ -64,4 +64,12 @@
.header-value {
}
}
.connection-table, .timing-table {
td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

View File

@ -19,6 +19,8 @@ html, body, #container {
display: flex;
flex-direction: row;
outline: 0;
&.vertical {
flex-direction: column;
}

View File

@ -23,14 +23,15 @@ var SettingsActions = {
}
};
var event_id = 0;
var EventLogActions = {
add_event: function(message, level){
add_event: function(message){
AppDispatcher.dispatchViewAction({
type: ActionTypes.ADD_EVENT,
data: {
message: message,
level: level || "info",
source: "ui"
level: "web",
id: "viewAction-"+event_id++
}
});
}

View File

@ -1,6 +1,31 @@
/** @jsx React.DOM */
var EventLog = React.createClass({
var LogMessage = React.createClass({
render: function(){
var entry = this.props.entry;
var indicator;
switch(entry.level){
case "web":
indicator = <i className="fa fa-fw fa-html5"></i>;
break;
case "debug":
indicator = <i className="fa fa-fw fa-bug"></i>;
break;
default:
indicator = <i className="fa fa-fw fa-info"></i>;
}
return (
<div>
{ indicator } {entry.message}
</div>
);
},
shouldComponentUpdate: function(){
return false; // log entries are immutable.
}
});
var EventLogContents = React.createClass({
mixins:[AutoScrollMixin],
getInitialState: function () {
return {
@ -20,22 +45,75 @@ var EventLog = React.createClass({
log: this.log.getAll()
});
},
render: function () {
var messages = this.state.log.map(function(row) {
if(!this.props.filter[row.level]){
return null;
}
return <LogMessage key={row.id} entry={row}/>;
}.bind(this));
return <pre>{messages}</pre>;
}
});
var ToggleFilter = React.createClass({
toggle: function(){
return this.props.toggleLevel(this.props.name);
},
render: function(){
var className = "label ";
if (this.props.active) {
className += "label-primary";
} else {
className += "label-default";
}
return (
<a
href="#"
className={className}
onClick={this.toggle}>
{this.props.name}
</a>
);
}
});
var EventLog = React.createClass({
getInitialState: function(){
return {
filter: {
"debug": false,
"info": true,
"web": true
}
};
},
close: function () {
SettingsActions.update({
showEventLog: false
});
},
toggleLevel: function(level){
var filter = this.state.filter;
filter[level] = !filter[level];
this.setState({filter: filter});
return false;
},
render: function () {
var messages = this.state.log.map(function(row) {
var indicator = null;
if(row.source === "ui"){
indicator = <i className="fa fa-html5"></i>;
}
return (
<div key={row.id}>
{ indicator } {row.message}
</div>);
});
return <pre className="eventlog">{messages}</pre>;
return (
<div className="eventlog">
<div>
Eventlog
<div className="pull-right">
<ToggleFilter name="debug" active={this.state.filter.debug} toggleLevel={this.toggleLevel}/>
<ToggleFilter name="info" active={this.state.filter.info} toggleLevel={this.toggleLevel}/>
<ToggleFilter name="web" active={this.state.filter.web} toggleLevel={this.toggleLevel}/>
<i onClick={this.close} className="fa fa-close"></i>
</div>
</div>
<EventLogContents filter={this.state.filter}/>
</div>
);
}
});

View File

@ -231,7 +231,7 @@ var Timing = React.createClass({
return (
<div>
<h4>Timing</h4>
<table>
<table className="timing-table">
<tbody>
{rows}
</tbody>

View File

@ -7,7 +7,6 @@ var MainView = React.createClass({
};
},
componentDidMount: function () {
console.log("get view");
this.flowStore = FlowStore.getView();
this.flowStore.addListener("change",this.onFlowChange);
},

View File

@ -14,7 +14,7 @@ _Connection.prototype.openWebSocketConnection = function () {
ws.onclose = this.onclose.bind(this);
};
_Connection.prototype.onopen = function (open) {
console.log("onopen", this, arguments);
console.debug("onopen", this, arguments);
};
_Connection.prototype.onmessage = function (message) {
//AppDispatcher.dispatchServerAction(...);
@ -23,11 +23,11 @@ _Connection.prototype.onmessage = function (message) {
};
_Connection.prototype.onerror = function (error) {
EventLogActions.add_event("WebSocket Connection Error.");
console.log("onerror", this, arguments);
console.debug("onerror", this, arguments);
};
_Connection.prototype.onclose = function (close) {
EventLogActions.add_event("WebSocket Connection closed.");
console.log("onclose", this, arguments);
console.debug("onclose", this, arguments);
};
var Connection = new _Connection(location.origin + "/updates");

View File

@ -26,7 +26,7 @@ _.extend(EventLogView.prototype, EventEmitter.prototype, {
},
add: function (entry) {
this.log.push(entry);
if(this.log.length > 50){
if(this.log.length > 200){
this.log.shift();
}
this.emit("change");
@ -96,4 +96,4 @@ _.extend(_EventLogStore.prototype, EventEmitter.prototype, {
var EventLogStore = new _EventLogStore();
AppDispatcher.register(EventLogStore.handle.bind(EventLogStore));
AppDispatcher.register(EventLogStore.handle.bind(EventLogStore));