Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 98 additions & 98 deletions portal-ui/bindata_assetfs.go

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion portal-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@types/recharts": "^1.8.9",
"@types/superagent": "^4.1.4",
"@types/webpack-env": "^1.14.1",
"@types/websocket": "^1.0.0",
"codemirror": "^5.52.2",
"history": "^4.10.1",
"local-storage-fallback": "^4.1.1",
Expand All @@ -37,7 +38,8 @@
"redux-thunk": "^2.3.0",
"superagent": "^5.1.0",
"typeface-roboto": "^0.0.75",
"typescript": "3.6.4"
"typescript": "3.6.4",
"websocket": "^1.0.31"
},
"scripts": {
"start": "PORT=5000 react-scripts start",
Expand Down
2 changes: 2 additions & 0 deletions portal-ui/src/screens/Console/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import ListNotificationEndpoints from "./NotificationEndopoints/ListNotification
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
import { Button, LinearProgress } from "@material-ui/core";
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
import Trace from "./Trace/Trace";

function Copyright() {
return (
Expand Down Expand Up @@ -302,6 +303,7 @@ class Console extends React.Component<
/>
<Route exact path="/webhook/logger" component={WebhookPanel} />
<Route exact path="/webhook/audit" component={WebhookPanel} />
<Route exct path="/trace" component={Trace} />
<Route exact path="/">
<Redirect to="/dashboard" />
</Route>
Expand Down
7 changes: 7 additions & 0 deletions portal-ui/src/screens/Console/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import PersonIcon from "@material-ui/icons/Person";
import api from "../../common/api";
import NotificationsIcon from "@material-ui/icons/Notifications";
import ListAltIcon from "@material-ui/icons/ListAlt";
import LoopIcon from "@material-ui/icons/Loop";

const styles = (theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -150,6 +151,12 @@ class Menu extends React.Component<MenuProps> {
</ListItemIcon>
<ListItemText primary="IAM Policies" />
</ListItem>
<ListItem button component={NavLink} to="/trace">
<ListItemIcon>
<LoopIcon />
</ListItemIcon>
<ListItemText primary="Trace" />
</ListItem>
<ListItem component={Typography}>Configuration</ListItem>
<ListItem button component={NavLink} to="/notification-endpoints">
<ListItemIcon>
Expand Down
157 changes: 157 additions & 0 deletions portal-ui/src/screens/Console/Trace/Trace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useEffect } from "react";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import storage from "local-storage-fallback";
import { AppState } from "../../../store";
import { connect } from "react-redux";
import { traceMessageReceived, traceResetMessages } from "./actions";
import { TraceMessage } from "./types";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";

const styles = (theme: Theme) =>
createStyles({
logList: {
background: "white",
maxHeight: "400px",
overflow: "auto",
"& ul": {
margin: "4px",
padding: "0px"
},
"& ul li": {
listStyle: "none",
margin: "0px",
padding: "0px",
borderBottom: "1px solid #dedede"
}
}
});

interface ITrace {
classes: any;
traceMessageReceived: typeof traceMessageReceived;
traceResetMessages: typeof traceResetMessages;
messages: TraceMessage[];
}

const Trace = ({
classes,
traceMessageReceived,
traceResetMessages,
messages
}: ITrace) => {
useEffect(() => {
traceResetMessages();
const token: string = storage.getItem("token")!;
const url = new URL(window.location.toString());
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;

const setCookie = (name: string, val: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be set in utils file too

const date = new Date();
const value = val;

// Set it expire in 45 minutes
date.setTime(date.getTime() + 45 * 60 * 1000);

// Set it
document.cookie =
name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
};

setCookie("token", token);

const c = new W3CWebSocket(`ws://${url.hostname}:${port}/ws/trace`);

let interval: any | null = null;
if (c !== null) {
c.onopen = () => {
console.log("WebSocket Client Connected");
c.send("ok");
interval = setInterval(() => {
c.send("ok");
}, 10 * 1000);
};
c.onmessage = (message: IMessageEvent) => {
let m: TraceMessage = JSON.parse(message.data.toString());
m.time = new Date(m.time.toString());
m.key = Math.random();
traceMessageReceived(m);
};
c.onclose = () => {
clearInterval(interval);
console.log("connection closed by server");
};
return () => {
c.close(1000);
clearInterval(interval);
console.log("closing websockets");
};
}
}, [traceMessageReceived]);

const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const niceBytes = (x: string) => {
let l = 0,
n = parseInt(x, 10) || 0;

while (n >= 1024 && ++l) {
n = n / 1024;
}
//include a decimal point and a tenths-place digit if presenting
//less than ten of KB or greater units
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l];
};
const timeFromdate = (d: Date) => {
let h = d.getHours() < 10 ? `0${d.getHours()}` : `${d.getHours()}`;
let m = d.getMinutes() < 10 ? `0${d.getMinutes()}` : `${d.getMinutes()}`;
let s = d.getSeconds() < 10 ? `0${d.getSeconds()}` : `${d.getSeconds()}`;

return `${h}:${m}:${s}:${d.getMilliseconds()}`;
};
Comment on lines +107 to +125
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be placed in utils file


return (
<div>
<h1>Trace</h1>
<div className={classes.logList}>
<ul>
{messages.map(m => {
return (
<li key={m.key}>
{timeFromdate(m.time)} - {m.api}[{m.statusCode} {m.statusMsg}]{" "}
{m.api} {m.host} {m.client} {m.callStats.duration} ↑{" "}
{niceBytes(m.callStats.rx + "")} ↓{" "}
{niceBytes(m.callStats.tx + "")}
</li>
);
})}
</ul>
</div>
</div>
);
};

const mapState = (state: AppState) => ({
messages: state.trace.messages
});

const connector = connect(mapState, {
traceMessageReceived: traceMessageReceived,
traceResetMessages: traceResetMessages
});

export default connector(withStyles(styles)(Trace));
46 changes: 46 additions & 0 deletions portal-ui/src/screens/Console/Trace/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import { TraceMessage } from "./types";

export const TRACE_MESSAGE_RECEIVED = "TRACE_MESSAGE_RECEIVED";
export const TRACE_RESET_MESSAGES = "TRACE_RESET_MESSAGES";

interface TraceMessageReceivedAction {
type: typeof TRACE_MESSAGE_RECEIVED;
message: TraceMessage;
}

interface TraceResetMessagesAction {
type: typeof TRACE_RESET_MESSAGES;
}

export type TraceActionTypes =
| TraceMessageReceivedAction
| TraceResetMessagesAction;

export function traceMessageReceived(message: TraceMessage) {
return {
type: TRACE_MESSAGE_RECEIVED,
message: message
};
}

export function traceResetMessages() {
return {
type: TRACE_RESET_MESSAGES
};
}
50 changes: 50 additions & 0 deletions portal-ui/src/screens/Console/Trace/reducers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import {
TRACE_MESSAGE_RECEIVED,
TRACE_RESET_MESSAGES,
TraceActionTypes
} from "./actions";
import { TraceMessage } from "./types";

export interface TraceState {
messages: TraceMessage[];
}

const initialState: TraceState = {
messages: []
};

export function traceReducer(
state = initialState,
action: TraceActionTypes
): TraceState {
switch (action.type) {
case TRACE_MESSAGE_RECEIVED:
return {
...state,
messages: [...state.messages, action.message]
};
case TRACE_RESET_MESSAGES:
return {
...state,
messages: []
};
default:
return state;
}
}
35 changes: 35 additions & 0 deletions portal-ui/src/screens/Console/Trace/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

export interface CallStats {
timeToFirstByte: string;
rx: number;
tx: number;
duration: string;
}

export interface TraceMessage {
client: string;
time: Date;
statusCode: number;
api: string;
query: string;
host: string;
callStats: CallStats;
path: string;
statusMsg: string;
key: number;
}
4 changes: 3 additions & 1 deletion portal-ui/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import { applyMiddleware, combineReducers, compose, createStore } from "redux";
import thunk from "redux-thunk";
import { systemReducer } from "./reducer";
import { traceReducer } from "./screens/Console/Trace/reducers";

const globalReducer = combineReducers({
system: systemReducer
system: systemReducer,
trace: traceReducer
});

declare global {
Expand Down
Loading