You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2019/10/08 22:45:24 UTC
[qpid-dispatch] branch eallen-DISPATCH-1385 updated: Working on log
details
This is an automated email from the ASF dual-hosted git repository.
eallen pushed a commit to branch eallen-DISPATCH-1385
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/eallen-DISPATCH-1385 by this push:
new 23bfb0c Working on log details
23bfb0c is described below
commit 23bfb0cbb780c628ee5a923048afa6f7deb2c148
Author: Ernest Allen <ea...@redhat.com>
AuthorDate: Tue Oct 8 18:45:05 2019 -0400
Working on log details
---
console/react/src/App.css | 5 +
console/react/src/connect-form.js | 2 +-
console/react/src/layout.js | 92 ++++++------
console/react/src/overview/dataSources/logsData.js | 115 +++++++++++++++
console/react/src/overview/detailsTablePage.js | 2 +
console/react/src/overview/entityData.js | 6 +-
.../{detailsTablePage.js => logDetails.js} | 156 ++++++++-------------
console/react/src/overview/overviewTable.js | 68 +++++----
console/react/src/qdrService.js | 15 +-
9 files changed, 261 insertions(+), 200 deletions(-)
diff --git a/console/react/src/App.css b/console/react/src/App.css
index 5601c16..eb0bfb2 100644
--- a/console/react/src/App.css
+++ b/console/react/src/App.css
@@ -954,6 +954,7 @@ div.qdrChord .legend-text {
.link-dir-in,
.link-dir-out {
padding-right: 0.5em;
+ font-size: 16px;
}
.link-dir-in {
color: red;
@@ -962,3 +963,7 @@ div.qdrChord .legend-text {
.link-dir-out {
color: blue;
}
+
+.overview-table tr {
+ border-bottom-color: #eaeaea !important;
+}
diff --git a/console/react/src/connect-form.js b/console/react/src/connect-form.js
index 6ef9a74..7f0073a 100644
--- a/console/react/src/connect-form.js
+++ b/console/react/src/connect-form.js
@@ -129,7 +129,7 @@ class ConnectForm extends React.Component {
</FormGroup>
<ActionGroup>
<Button variant="primary" onClick={this.handleConnect}>
- Connect
+ {this.props.isConnected ? "Disconnect" : "Connect"}
</Button>
<Button variant="secondary" onClick={this.toggleDrawerHide}>
Cancel
diff --git a/console/react/src/layout.js b/console/react/src/layout.js
index e81ee11..13d198f 100644
--- a/console/react/src/layout.js
+++ b/console/react/src/layout.js
@@ -25,7 +25,6 @@ import {
Dropdown,
DropdownToggle,
DropdownItem,
- DropdownSeparator,
Page,
PageHeader,
SkipToContent,
@@ -48,9 +47,8 @@ import {
} from "react-router-dom";
import accessibleStyles from "@patternfly/patternfly/utilities/Accessibility/accessibility.css";
-import spacingStyles from "@patternfly/patternfly/utilities/Spacing/spacing.css";
import { css } from "@patternfly/react-styles";
-import { BellIcon, CogIcon, PowerOffIcon } from "@patternfly/react-icons";
+import { BellIcon, PowerOffIcon } from "@patternfly/react-icons";
//import ConnectForm from "./connect-form";
import ConnectPage from "./connectPage";
import DashboardPage from "./overview/dashboard/dashboardPage";
@@ -58,7 +56,9 @@ import OverviewTablePage from "./overview/overviewTablePage";
import DetailsTablePage from "./overview/detailsTablePage";
import TopologyPage from "./topology/qdrTopology";
import MessageFlowPage from "./chord/qdrChord";
+import LogDetails from "./overview/logDetails";
import { QDRService } from "./qdrService";
+import ConnectForm from "./connect-form";
const avatarImg = require("./assets/img_avatar.svg");
class PageLayout extends React.Component {
@@ -70,23 +70,9 @@ class PageLayout extends React.Component {
isDropdownOpen: false,
activeGroup: "overview",
activeItem: "dashboard",
- detailInfo: null,
- detailMeta: null
+ isConnectFormOpen: false
};
this.tables = ["routers", "addresses", "links", "connections", "logs"];
-
- /*
- logs: [
- { title: "Module" },
- { title: "Notice" },
- { title: "Info" },
- { title: "Trace" },
- { title: "Debug" },
- { title: "Warning" },
- { title: "Error" },
- { title: "Critical" }
- ]
- */
this.hooks = { setLocation: this.setLocation };
this.service = new QDRService(this.hooks);
}
@@ -108,22 +94,26 @@ class PageLayout extends React.Component {
};
handleConnect = connectPath => {
- this.service
- .connect({ address: "localhost", port: 5673, reconnect: true })
- .then(
- r => {
- this.setState({
- connected: true,
- connectPath
- });
- },
- e => {
- console.log(e);
- }
- );
+ if (this.state.connected) {
+ this.service.disconnect();
+ this.setState({ connected: false });
+ } else {
+ this.service
+ .connect({ address: "localhost", port: 5673, reconnect: true })
+ .then(
+ r => {
+ this.setState({
+ connected: true,
+ connectPath
+ });
+ },
+ e => {
+ console.log(e);
+ }
+ );
+ }
};
- handleConnectCancel = () => {};
onNavSelect = result => {
this.setState({
activeItem: result.itemId,
@@ -133,25 +123,12 @@ class PageLayout extends React.Component {
};
icap = s => s.charAt(0).toUpperCase() + s.slice(1);
- showDetailTable = (_value, detailInfo, activeItem, detailMeta) => {
- this.setState({
- activeGroup: "detailsTable",
- activeItem,
- detailInfo,
- detailMeta,
- connectPath: "/details"
- });
- };
-
- BreadcrumbSelected = connectPath => {
- this.setState({
- connectPath
- });
+ toggleConnectForm = event => {
+ this.setState({ isConnectFormOpen: !this.state.isConnectFormOpen });
};
- toggleConnectForm = event => {
- console.log("taggleConnectForm called with event.target");
- console.log(event.target);
+ handleConnectCancel = () => {
+ this.setState({ isConnectFormOpen: false });
};
render() {
@@ -335,6 +312,19 @@ class PageLayout extends React.Component {
return <React.Fragment />;
};
+ const connectForm = () => {
+ if (this.state.isConnectFormOpen) {
+ return (
+ <ConnectForm
+ handleConnect={this.handleConnect}
+ handleConnectCancel={this.handleConnectCancel}
+ isConnected={this.state.connected}
+ />
+ );
+ }
+ return <React.Fragment />;
+ };
+
return (
<Router>
{redirectAfterConnect()}
@@ -344,9 +334,10 @@ class PageLayout extends React.Component {
isManagedSidebar
skipToContent={PageSkipToContent}
>
+ {connectForm()}
<Switch>
<PrivateRoute path="/" exact component={DashboardPage} />
- <PrivateRoute path="/dashboard" exact component={DashboardPage} />
+ <PrivateRoute path="/dashboard" component={DashboardPage} />
<PrivateRoute
path="/overview/:entity"
component={OverviewTablePage}
@@ -354,6 +345,7 @@ class PageLayout extends React.Component {
<PrivateRoute path="/details" component={DetailsTablePage} />
<PrivateRoute path="/topology" component={TopologyPage} />
<PrivateRoute path="/flow" component={MessageFlowPage} />
+ <PrivateRoute path="/logs" component={LogDetails} />
<Route
path="/login"
render={props => (
diff --git a/console/react/src/overview/dataSources/logsData.js b/console/react/src/overview/dataSources/logsData.js
new file mode 100644
index 0000000..d54769b
--- /dev/null
+++ b/console/react/src/overview/dataSources/logsData.js
@@ -0,0 +1,115 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+class LogsData {
+ constructor(service) {
+ this.service = service;
+ this.fields = [
+ { title: "Router", field: "node" },
+ { title: "Enable", field: "enable" },
+ { title: "Module", field: "name" },
+ { title: "Trace", field: "traceCount", numeric: true },
+ { title: "Info", field: "infoCount", numeric: true },
+ { title: "Debug", field: "debugCount", numeric: true },
+ { title: "Notice", field: "noticeCount", numeric: true },
+ { title: "Warning", field: "warningCount", numeric: true },
+ { title: "Error", field: "errorCount", numeric: true },
+ { title: "Critical", field: "criticalCount", numeric: true }
+ ];
+ this.detailEntity = "log";
+ this.detailName = "Log";
+ this.detailPath = "/logs";
+ }
+
+ fetchRecord = (currentRecord, schema) => {
+ return new Promise(resolve => {
+ this.service.management.topology.fetchEntities(
+ currentRecord.nodeId,
+ { entity: "logStats" },
+ data => {
+ const record = data[currentRecord.nodeId]["logStats"];
+ const identityIndex = record.attributeNames.indexOf("name");
+ const result = record.results.find(
+ r => r[identityIndex] === currentRecord.name
+ );
+ let obj = this.service.utilities.flatten(
+ record.attributeNames,
+ result
+ );
+ obj = this.service.utilities.formatAttributes(
+ obj,
+ schema.entityTypes["logStats"]
+ );
+ resolve(obj);
+ }
+ );
+ });
+ };
+
+ doFetch = (page, perPage) => {
+ return new Promise(resolve => {
+ // an array of logStat records that have router name and log.enable added
+ let logModules = [];
+ const insertEnable = (record, logData) => {
+ // find the logData result for this record
+ const moduleIndex = logData.attributeNames.indexOf("module");
+ const enableIndex = logData.attributeNames.indexOf("enable");
+ const logRec = logData.results.find(
+ r => r[moduleIndex] === record.name
+ );
+ if (logRec) {
+ record.enable =
+ logRec[enableIndex] === null ? "" : String(logRec[enableIndex]);
+ } else {
+ record.enable = "";
+ }
+ };
+ this.service.management.topology.fetchAllEntities(
+ [{ entity: "log" }, { entity: "logStats" }],
+ nodes => {
+ // each router is a node in nodes
+ for (let node in nodes) {
+ const nodeName = this.service.utilities.nameFromId(node);
+ let response = nodes[node]["logStats"];
+ // response is an array of records for this node/router
+ response.results.forEach(result => {
+ // result is a single log record for this router
+ let logStat = this.service.utilities.flatten(
+ response.attributeNames,
+ result
+ );
+
+ logStat.node = nodeName;
+ logStat.nodeId = node;
+ insertEnable(logStat, nodes[node]["log"]);
+ logModules.push(logStat);
+ });
+ }
+ resolve({
+ data: logModules,
+ page,
+ perPage
+ });
+ }
+ );
+ });
+ };
+}
+
+export default LogsData;
diff --git a/console/react/src/overview/detailsTablePage.js b/console/react/src/overview/detailsTablePage.js
index d945455..8ae0be4 100644
--- a/console/react/src/overview/detailsTablePage.js
+++ b/console/react/src/overview/detailsTablePage.js
@@ -66,6 +66,7 @@ class DetailTablesPage extends React.Component {
this.props.location &&
this.props.location.state &&
this.props.location.state.entity;
+ console.log(`detailsTablePage entity is ${this.entity}`);
if (!dataMap[this.entity]) {
this.state.redirect = true;
} else {
@@ -94,6 +95,7 @@ class DetailTablesPage extends React.Component {
},
error => {
console.log(`detailsTablePage: ${error}`);
+ if (this.timer) clearInterval(this.timer);
}
);
};
diff --git a/console/react/src/overview/entityData.js b/console/react/src/overview/entityData.js
index f625328..badca4d 100644
--- a/console/react/src/overview/entityData.js
+++ b/console/react/src/overview/entityData.js
@@ -21,12 +21,14 @@ import RouterData from "./dataSources/routerData";
import AddressData from "./dataSources/addressData";
import LinkData from "./dataSources/linkData";
import ConnectionData from "./dataSources/connectionData";
+import LogsData from "./dataSources/logsData";
const dataMap = {
routers: RouterData,
connections: ConnectionData,
links: LinkData,
- addresses: AddressData
+ addresses: AddressData,
+ logs: LogsData
};
-export { RouterData, AddressData, LinkData, ConnectionData, dataMap };
+export { dataMap };
diff --git a/console/react/src/overview/detailsTablePage.js b/console/react/src/overview/logDetails.js
similarity index 55%
copy from console/react/src/overview/detailsTablePage.js
copy to console/react/src/overview/logDetails.js
index d945455..22dff64 100644
--- a/console/react/src/overview/detailsTablePage.js
+++ b/console/react/src/overview/logDetails.js
@@ -19,6 +19,15 @@ under the License.
import React from "react";
import { PageSection, PageSectionVariants } from "@patternfly/react-core";
+
+import {
+ DataList,
+ DataListItem,
+ DataListItemRow,
+ DataListItemCells,
+ DataListCell,
+ DataListContent
+} from "@patternfly/react-core";
import {
Stack,
StackItem,
@@ -29,32 +38,14 @@ import {
BreadcrumbItem
} from "@patternfly/react-core";
-import {
- cellWidth,
- Table,
- TableHeader,
- TableBody,
- TableVariant
-} from "@patternfly/react-table";
-import { Card, CardBody } from "@patternfly/react-core";
import { Redirect } from "react-router-dom";
import { dataMap } from "./entityData";
-class DetailTablesPage extends React.Component {
+class LogDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
- columns: [
- { title: "Attribute", transforms: [cellWidth(20)] },
- {
- title: "Value",
- transforms: [cellWidth("max")],
- props: { className: "pf-u-text-align-left" }
- }
- ],
- rows: [],
redirect: false,
- redirectState: { page: 1 },
redirectPath: "/dashboard",
lastUpdated: new Date()
};
@@ -66,19 +57,14 @@ class DetailTablesPage extends React.Component {
this.props.location &&
this.props.location.state &&
this.props.location.state.entity;
- if (!dataMap[this.entity]) {
+ if (!this.entity) {
this.state.redirect = true;
- } else {
- this.dataSource = new dataMap[this.entity](this.props.service);
}
}
componentDidMount = () => {
- this.props.service.management.getSchema().then(schema => {
- this.schema = schema;
- this.timer = setInterval(this.update, 5000);
- this.update();
- });
+ this.timer = setInterval(this.update, 5000);
+ this.update();
};
componentWillUnmount = () => {
@@ -87,67 +73,13 @@ class DetailTablesPage extends React.Component {
}
};
- update = () => {
- this.mapRows().then(
- rows => {
- this.setState({ rows, lastUpdated: new Date() });
- },
- error => {
- console.log(`detailsTablePage: ${error}`);
- }
- );
- };
-
- toString = val => {
- return val === null ? "" : String(val);
- };
-
- mapRows = () => {
- return new Promise((resolve, reject) => {
- const rows = [];
- if (!this.dataSource) {
- reject("no data source");
- }
- this.dataSource
- .fetchRecord(this.props.location.state.currentRecord, this.schema)
- .then(data => {
- for (const attribute in data) {
- if (
- !this.dataSource.hideFields ||
- this.dataSource.hideFields.indexOf(attribute) === -1
- ) {
- rows.push({
- cells: [attribute, this.toString(data[attribute])]
- });
- }
- }
- resolve(rows);
- });
- });
- };
-
+ update = () => {};
icap = s => s.charAt(0).toUpperCase() + s.slice(1);
parentItem = () => {
- // if we have a specific field that should be used
- // as the record's title, return it
- if (this.dataSource.detailField) {
- return this.props.location.state.currentRecord[
- this.dataSource.detailField
- ];
- }
// otherwise return the 1st field
return this.props.location.state.value;
};
-
- breadcrumbSelected = () => {
- this.setState({
- redirect: true,
- redirectPath: `/overview/${this.entity}`,
- redirectState: this.props.location.state
- });
- };
-
render() {
if (this.state.redirect) {
return (
@@ -166,6 +98,7 @@ class DetailTablesPage extends React.Component {
variant={PageSectionVariants.light}
className="overview-table-page"
>
+ {" "}
<Stack>
<StackItem className="overview-header details">
<Breadcrumb>
@@ -182,9 +115,7 @@ class DetailTablesPage extends React.Component {
<TextContent>
<Text className="overview-title" component={TextVariants.h1}>
- {`${
- this.dataSource.detailName
- } ${this.parentItem()} attributes`}
+ {`Logs ${this.parentItem()} attributes`}
</Text>
<Text className="overview-loading" component={TextVariants.pre}>
{`Updated ${this.props.service.utilities.strDate(
@@ -193,20 +124,47 @@ class DetailTablesPage extends React.Component {
</Text>
</TextContent>
</StackItem>
+
<StackItem className="overview-table">
- <Card>
- <CardBody>
- <Table
- cells={this.state.columns}
- rows={this.state.rows}
- variant={TableVariant.compact}
- aria-label={this.entity}
- >
- <TableHeader />
- <TableBody />
- </Table>
- </CardBody>
- </Card>
+ <DataList aria-label="Simple data list example">
+ <DataListItem aria-labelledby="simple-item1">
+ <DataListItemRow>
+ <DataListItemCells
+ dataListCells={[
+ <DataListCell key="primary content">
+ <span id="simple-item1">Primary content</span>
+ </DataListCell>,
+ <DataListCell key="secondary content">
+ Secondary content
+ </DataListCell>
+ ]}
+ />
+ </DataListItemRow>
+ </DataListItem>
+ <DataListItem aria-labelledby="simple-item2">
+ <DataListItemRow>
+ <DataListItemCells
+ dataListCells={[
+ <DataListCell
+ isFilled={false}
+ key="secondary content fill"
+ >
+ <span id="simple-item2">
+ Secondary content (pf-m-no-fill)
+ </span>
+ </DataListCell>,
+ <DataListCell
+ isFilled={false}
+ alignRight
+ key="secondary content align"
+ >
+ Secondary content (pf-m-align-right pf-m-no-fill)
+ </DataListCell>
+ ]}
+ />
+ </DataListItemRow>
+ </DataListItem>
+ </DataList>
</StackItem>
</Stack>
</PageSection>
@@ -215,4 +173,4 @@ class DetailTablesPage extends React.Component {
}
}
-export default DetailTablesPage;
+export default LogDetails;
diff --git a/console/react/src/overview/overviewTable.js b/console/react/src/overview/overviewTable.js
index 2d77f20..6555199 100644
--- a/console/react/src/overview/overviewTable.js
+++ b/console/react/src/overview/overviewTable.js
@@ -72,8 +72,9 @@ class OverviewTable extends React.Component {
if (!this.dataSource) return;
// initialize the columns and get the data
this.dataSource.fields.forEach(f => {
- if (!f.noSort) f.transforms = [sortable];
+ f.transforms = [];
f.cellFormatters = [];
+ if (!f.noSort) f.transforms.push(sortable);
if (f.numeric) {
f.cellFormatters.push(this.prettier);
}
@@ -86,8 +87,6 @@ class OverviewTable extends React.Component {
);
}
});
- if (!this.dataSource.fields[0].cellFormatters)
- this.dataSource.fields[0].cellFormatters = [];
this.dataSource.fields[0].cellFormatters.push(this.detailLink);
this.setState({ columns: this.dataSource.fields }, () => {
@@ -136,16 +135,34 @@ class OverviewTable extends React.Component {
);
};
+ detailClick = (value, extraInfo) => {
+ this.setState({
+ redirect: true,
+ redirectState: {
+ value: extraInfo.rowData.cells[0],
+ currentRecord: extraInfo.rowData.data,
+ entity: this.entity,
+ page: this.state.page,
+ sortBy: this.state.sortBy,
+ filterBy: this.state.filterBy,
+ perPage: this.state.perPage
+ }
+ });
+ };
+
+ // cell formatter
noWrap = (value, extraInfo) => {
return <span className="noWrap">{value}</span>;
};
+ // cell formatter
prettier = (value, extraInfo) => {
return typeof value === "undefined"
? "-"
: this.props.service.utilities.pretty(value);
};
+ // cell formatter, display a component instead of this cell's data
formatter = (Component, value, extraInfo) => {
return (
<Component
@@ -156,21 +173,6 @@ class OverviewTable extends React.Component {
);
};
- detailClick = (value, extraInfo) => {
- this.setState({
- redirect: true,
- redirectState: {
- value: extraInfo.rowData.cells[0],
- currentRecord: extraInfo.rowData.data,
- entity: this.entity,
- page: this.state.page,
- sortBy: this.state.sortBy,
- filterBy: this.state.filterBy,
- perPage: this.state.perPage
- }
- });
- };
-
onSort = (_event, index, direction) => {
this.setState({ sortBy: { index, direction } }, () => {
const { allRows, page, perPage } = this.state;
@@ -255,24 +257,18 @@ class OverviewTable extends React.Component {
return rows;
}
- if (this.dataSource.fields[index].numeric) {
- rows.sort((a, b) => {
- if (direction === SortByDirection.desc)
- return a > b ? -1 : a < b ? 1 : 0;
- return a < b ? -1 : a > b ? 1 : 0;
- });
- } else {
- rows.sort((a, b) => {
- return a.cells[index] < b.cells[index]
- ? -1
- : a.cells[index] > b.cells[index]
- ? 1
- : 0;
- });
- if (direction === SortByDirection.desc) {
- rows = rows.reverse();
+ const less = direction === SortByDirection.desc ? 1 : -1;
+ const more = -1 * less;
+ rows.sort((a, b) => {
+ if (a.cells[index] < b.cells[index]) return less;
+ if (a.cells[index] > b.cells[index]) return more;
+ // the values matched, sort by 1st column
+ if (index !== 0) {
+ if (a.cells[0] < b.cells[0]) return less;
+ if (a.cells[0] > b.cells[0]) return more;
}
- }
+ return 0;
+ });
return rows;
};
@@ -281,7 +277,7 @@ class OverviewTable extends React.Component {
return (
<Redirect
to={{
- pathname: "/details",
+ pathname: this.dataSource.detailPath || "/details",
state: this.state.redirectState
}}
/>
diff --git a/console/react/src/qdrService.js b/console/react/src/qdrService.js
index 1d3fbd9..6ca41c7 100644
--- a/console/react/src/qdrService.js
+++ b/console/react/src/qdrService.js
@@ -53,25 +53,16 @@ export class QDRService {
);
self.management.getSchema().then(schema => {
- console.log("got schema after connection");
- console.log(schema);
+ //console.log("got schema after connection");
+ //console.log(schema);
self.management.topology.setUpdateEntities([]);
//console.log("requesting a topology");
self.management.topology
.get() // gets the list of routers
.then(t => {
- //console.log("got topology");
- const url = utils.getUrlParts(window.location);
- let curPath = url.pathname;
- let parts = curPath.split("/");
- let org = parts[parts.length - 1];
- if (org === "" || org === "connect") {
- org = localStorage[QDR_LAST_LOCATION] || "/overview";
- }
- self.hooks.setLocation(org);
+ resolve(r);
});
});
- resolve(r);
},
e => {
reject(e);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org