Skip to content

Commit 96b2793

Browse files
committed
Admin Trace UI
1 parent 8e9bd87 commit 96b2793

File tree

10 files changed

+437
-101
lines changed

10 files changed

+437
-101
lines changed

portal-ui/bindata_assetfs.go

Lines changed: 98 additions & 98 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

portal-ui/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@types/recharts": "^1.8.9",
2020
"@types/superagent": "^4.1.4",
2121
"@types/webpack-env": "^1.14.1",
22+
"@types/websocket": "^1.0.0",
2223
"codemirror": "^5.52.2",
2324
"history": "^4.10.1",
2425
"local-storage-fallback": "^4.1.1",
@@ -37,7 +38,8 @@
3738
"redux-thunk": "^2.3.0",
3839
"superagent": "^5.1.0",
3940
"typeface-roboto": "^0.0.75",
40-
"typescript": "3.6.4"
41+
"typescript": "3.6.4",
42+
"websocket": "^1.0.31"
4143
},
4244
"scripts": {
4345
"start": "PORT=5000 react-scripts start",

portal-ui/src/screens/Console/Console.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import ListNotificationEndpoints from "./NotificationEndopoints/ListNotification
6161
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
6262
import { Button, LinearProgress } from "@material-ui/core";
6363
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
64+
import Trace from "./Trace/Trace";
6465

6566
function Copyright() {
6667
return (
@@ -302,6 +303,7 @@ class Console extends React.Component<
302303
/>
303304
<Route exact path="/webhook/logger" component={WebhookPanel} />
304305
<Route exact path="/webhook/audit" component={WebhookPanel} />
306+
<Route exct path="/trace" component={Trace} />
305307
<Route exact path="/">
306308
<Redirect to="/dashboard" />
307309
</Route>

portal-ui/src/screens/Console/Menu.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import PersonIcon from "@material-ui/icons/Person";
3939
import api from "../../common/api";
4040
import NotificationsIcon from "@material-ui/icons/Notifications";
4141
import ListAltIcon from "@material-ui/icons/ListAlt";
42+
import LoopIcon from "@material-ui/icons/Loop";
4243

4344
const styles = (theme: Theme) =>
4445
createStyles({
@@ -150,6 +151,12 @@ class Menu extends React.Component<MenuProps> {
150151
</ListItemIcon>
151152
<ListItemText primary="IAM Policies" />
152153
</ListItem>
154+
<ListItem button component={NavLink} to="/trace">
155+
<ListItemIcon>
156+
<LoopIcon />
157+
</ListItemIcon>
158+
<ListItemText primary="Trace" />
159+
</ListItem>
153160
<ListItem component={Typography}>Configuration</ListItem>
154161
<ListItem button component={NavLink} to="/notification-endpoints">
155162
<ListItemIcon>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
import React, { useEffect } from "react";
17+
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
18+
import storage from "local-storage-fallback";
19+
import { AppState } from "../../../store";
20+
import { connect } from "react-redux";
21+
import { traceMessageReceived, traceResetMessages } from "./actions";
22+
import { TraceMessage } from "./types";
23+
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
24+
25+
const styles = (theme: Theme) =>
26+
createStyles({
27+
logList: {
28+
background: "white",
29+
maxHeight: "400px",
30+
overflow: "auto",
31+
"& ul": {
32+
margin: "4px",
33+
padding: "0px"
34+
},
35+
"& ul li": {
36+
listStyle: "none",
37+
margin: "0px",
38+
padding: "0px",
39+
borderBottom: "1px solid #dedede"
40+
}
41+
}
42+
});
43+
44+
interface ITrace {
45+
classes: any;
46+
traceMessageReceived: typeof traceMessageReceived;
47+
traceResetMessages: typeof traceResetMessages;
48+
messages: TraceMessage[];
49+
}
50+
51+
const Trace = ({
52+
classes,
53+
traceMessageReceived,
54+
traceResetMessages,
55+
messages
56+
}: ITrace) => {
57+
useEffect(() => {
58+
traceResetMessages();
59+
const token: string = storage.getItem("token")!;
60+
const url = new URL(window.location.toString());
61+
const isDev = process.env.NODE_ENV === "development";
62+
const port = isDev ? "9090" : url.port;
63+
64+
const setCookie = (name: string, val: string) => {
65+
const date = new Date();
66+
const value = val;
67+
68+
// Set it expire in 45 minutes
69+
date.setTime(date.getTime() + 45 * 60 * 1000);
70+
71+
// Set it
72+
document.cookie =
73+
name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
74+
};
75+
76+
setCookie("token", token);
77+
78+
const c = new W3CWebSocket(`ws://${url.hostname}:${port}/ws/trace`);
79+
80+
let interval: any | null = null;
81+
if (c !== null) {
82+
c.onopen = () => {
83+
console.log("WebSocket Client Connected");
84+
c.send("ok");
85+
interval = setInterval(() => {
86+
c.send("ok");
87+
}, 10 * 1000);
88+
};
89+
c.onmessage = (message: IMessageEvent) => {
90+
let m: TraceMessage = JSON.parse(message.data.toString());
91+
m.time = new Date(m.time.toString());
92+
m.key = Math.random();
93+
traceMessageReceived(m);
94+
};
95+
c.onclose = () => {
96+
clearInterval(interval);
97+
console.log("connection closed by server");
98+
};
99+
return () => {
100+
c.close(1000);
101+
clearInterval(interval);
102+
console.log("closing websockets");
103+
};
104+
}
105+
}, [traceMessageReceived]);
106+
107+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
108+
const niceBytes = (x: string) => {
109+
let l = 0,
110+
n = parseInt(x, 10) || 0;
111+
112+
while (n >= 1024 && ++l) {
113+
n = n / 1024;
114+
}
115+
//include a decimal point and a tenths-place digit if presenting
116+
//less than ten of KB or greater units
117+
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l];
118+
};
119+
const timeFromdate = (d: Date) => {
120+
let h = d.getHours() < 10 ? `0${d.getHours()}` : `${d.getHours()}`;
121+
let m = d.getMinutes() < 10 ? `0${d.getMinutes()}` : `${d.getMinutes()}`;
122+
let s = d.getSeconds() < 10 ? `0${d.getSeconds()}` : `${d.getSeconds()}`;
123+
124+
return `${h}:${m}:${s}:${d.getMilliseconds()}`;
125+
};
126+
127+
return (
128+
<div>
129+
<h1>Trace</h1>
130+
<div className={classes.logList}>
131+
<ul>
132+
{messages.map(m => {
133+
return (
134+
<li key={m.key}>
135+
{timeFromdate(m.time)} - {m.api}[{m.statusCode} {m.statusMsg}]{" "}
136+
{m.api} {m.host} {m.client} {m.callStats.duration}{" "}
137+
{niceBytes(m.callStats.rx + "")}{" "}
138+
{niceBytes(m.callStats.tx + "")}
139+
</li>
140+
);
141+
})}
142+
</ul>
143+
</div>
144+
</div>
145+
);
146+
};
147+
148+
const mapState = (state: AppState) => ({
149+
messages: state.trace.messages
150+
});
151+
152+
const connector = connect(mapState, {
153+
traceMessageReceived: traceMessageReceived,
154+
traceResetMessages: traceResetMessages
155+
});
156+
157+
export default connector(withStyles(styles)(Trace));
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import { TraceMessage } from "./types";
18+
19+
export const TRACE_MESSAGE_RECEIVED = "TRACE_MESSAGE_RECEIVED";
20+
export const TRACE_RESET_MESSAGES = "TRACE_RESET_MESSAGES";
21+
22+
interface TraceMessageReceivedAction {
23+
type: typeof TRACE_MESSAGE_RECEIVED;
24+
message: TraceMessage;
25+
}
26+
27+
interface TraceResetMessagesAction {
28+
type: typeof TRACE_RESET_MESSAGES;
29+
}
30+
31+
export type TraceActionTypes =
32+
| TraceMessageReceivedAction
33+
| TraceResetMessagesAction;
34+
35+
export function traceMessageReceived(message: TraceMessage) {
36+
return {
37+
type: TRACE_MESSAGE_RECEIVED,
38+
message: message
39+
};
40+
}
41+
42+
export function traceResetMessages() {
43+
return {
44+
type: TRACE_RESET_MESSAGES
45+
};
46+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import {
18+
TRACE_MESSAGE_RECEIVED,
19+
TRACE_RESET_MESSAGES,
20+
TraceActionTypes
21+
} from "./actions";
22+
import { TraceMessage } from "./types";
23+
24+
export interface TraceState {
25+
messages: TraceMessage[];
26+
}
27+
28+
const initialState: TraceState = {
29+
messages: []
30+
};
31+
32+
export function traceReducer(
33+
state = initialState,
34+
action: TraceActionTypes
35+
): TraceState {
36+
switch (action.type) {
37+
case TRACE_MESSAGE_RECEIVED:
38+
return {
39+
...state,
40+
messages: [...state.messages, action.message]
41+
};
42+
case TRACE_RESET_MESSAGES:
43+
return {
44+
...state,
45+
messages: []
46+
};
47+
default:
48+
return state;
49+
}
50+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
export interface CallStats {
18+
timeToFirstByte: string;
19+
rx: number;
20+
tx: number;
21+
duration: string;
22+
}
23+
24+
export interface TraceMessage {
25+
client: string;
26+
time: Date;
27+
statusCode: number;
28+
api: string;
29+
query: string;
30+
host: string;
31+
callStats: CallStats;
32+
path: string;
33+
statusMsg: string;
34+
key: number;
35+
}

portal-ui/src/store.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
import { applyMiddleware, combineReducers, compose, createStore } from "redux";
1818
import thunk from "redux-thunk";
1919
import { systemReducer } from "./reducer";
20+
import { traceReducer } from "./screens/Console/Trace/reducers";
2021

2122
const globalReducer = combineReducers({
22-
system: systemReducer
23+
system: systemReducer,
24+
trace: traceReducer
2325
});
2426

2527
declare global {

0 commit comments

Comments
 (0)