Add content to RedisInfoView

This commit is contained in:
Ken Hibino 2021-01-04 12:05:37 -08:00
parent 0c3cd80728
commit 14dc7de869
5 changed files with 141 additions and 10 deletions

View File

@ -15,8 +15,9 @@ import (
// **************************************************************************** // ****************************************************************************
type RedisInfoResponse struct { type RedisInfoResponse struct {
Addr string `json:"address"` Addr string `json:"address"`
Info map[string]string `json:"info"` Info map[string]string `json:"info"`
RawInfo string `json:"raw_info"`
} }
func newRedisInfoHandlerFunc(rdb *redis.Client) http.HandlerFunc { func newRedisInfoHandlerFunc(rdb *redis.Client) http.HandlerFunc {
@ -29,8 +30,9 @@ func newRedisInfoHandlerFunc(rdb *redis.Client) http.HandlerFunc {
} }
info := parseRedisInfo(res) info := parseRedisInfo(res)
resp := RedisInfoResponse{ resp := RedisInfoResponse{
Addr: redisAddr, Addr: redisAddr,
Info: info, Info: info,
RawInfo: res,
} }
if err := json.NewEncoder(w).Encode(resp); err != nil { if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -71,6 +71,7 @@ export interface ListQueueStatsResponse {
export interface RedisInfoResponse { export interface RedisInfoResponse {
address: string; address: string;
info: RedisInfo; info: RedisInfo;
raw_info: string;
} }
// Return value from redis INFO command. // Return value from redis INFO command.

View File

@ -10,12 +10,14 @@ interface RedisInfoState {
loading: boolean; loading: boolean;
address: string; address: string;
data: RedisInfo | null; data: RedisInfo | null;
rawData: string | null;
} }
const initialState: RedisInfoState = { const initialState: RedisInfoState = {
loading: false, loading: false,
address: "", address: "",
data: null, data: null,
rawData: null,
}; };
export default function redisInfoReducer( export default function redisInfoReducer(
@ -40,6 +42,7 @@ export default function redisInfoReducer(
loading: false, loading: false,
address: action.payload.address, address: action.payload.address,
data: action.payload.info, data: action.payload.info,
rawData: action.payload.raw_info,
}; };
default: default:

View File

@ -37,13 +37,18 @@ export function durationBefore(timestamp: string): string {
export function timeAgo(timestamp: string): string { export function timeAgo(timestamp: string): string {
try { try {
const duration = durationBetween(Date.now(), Date.parse(timestamp)); return timeAgoUnix(Date.parse(timestamp) / 1000);
return stringifyDuration(duration) + " ago"; } catch (error) {
} catch { console.error("Could not parse timestamp: ", timestamp, error);
return "-"; return "-";
} }
} }
export function timeAgoUnix(unixtime: number) {
const duration = durationBetween(Date.now(), unixtime * 1000);
return stringifyDuration(duration) + " ago";
}
export function getCurrentUTCDate(): string { export function getCurrentUTCDate(): string {
const today = new Date(); const today = new Date();
const dd = today.getUTCDate().toString().padStart(2, "0"); const dd = today.getUTCDate().toString().padStart(2, "0");

View File

@ -4,9 +4,14 @@ import Container from "@material-ui/core/Container";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import SyntaxHighlighter from "react-syntax-highlighter";
import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/github";
import { getRedisInfoAsync } from "../actions/redisInfoActions"; import { getRedisInfoAsync } from "../actions/redisInfoActions";
import { usePolling } from "../hooks"; import { usePolling } from "../hooks";
import { AppState } from "../store"; import { AppState } from "../store";
import { timeAgoUnix } from "../utils";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
container: { container: {
@ -26,6 +31,7 @@ function mapStateToProps(state: AppState) {
loading: state.redis.loading, loading: state.redis.loading,
redisInfo: state.redis.data, redisInfo: state.redis.data,
redisAddress: state.redis.address, redisAddress: state.redis.address,
redisInfoRaw: state.redis.rawData,
pollInterval: state.settings.pollInterval, pollInterval: state.settings.pollInterval,
}; };
} }
@ -35,12 +41,10 @@ type Props = ConnectedProps<typeof connector>;
function RedisInfoView(props: Props) { function RedisInfoView(props: Props) {
const classes = useStyles(); const classes = useStyles();
const { pollInterval, getRedisInfoAsync } = props; const { pollInterval, getRedisInfoAsync, redisInfo, redisInfoRaw } = props;
usePolling(getRedisInfoAsync, pollInterval); usePolling(getRedisInfoAsync, pollInterval);
console.log("DEBUG: redisInfo", props.redisInfo);
// Metrics to show // Metrics to show
// - Used Memory // - Used Memory
// - Memory Fragmentation Ratio // - Memory Fragmentation Ratio
@ -58,9 +62,125 @@ function RedisInfoView(props: Props) {
Connected to: {props.redisAddress} Connected to: {props.redisAddress}
</Typography> </Typography>
</Grid> </Grid>
{redisInfo !== null && (
<>
<Grid item xs={12}>
<Typography variant="h6" color="textSecondary">
Server
</Typography>
</Grid>
<Grid item xs={3}>
<MetricCard title="Version" content={redisInfo.redis_version} />
</Grid>
<Grid item xs={3}>
<MetricCard
title="Uptime"
content={`${redisInfo.uptime_in_days} days`}
/>
</Grid>
<Grid item xs={6} />
<Grid item xs={12}>
<Typography variant="h6" color="textSecondary">
Memory
</Typography>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Used Memory"
content={redisInfo.used_memory_human}
/>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Peak Memory Used"
content={redisInfo.used_memory_peak_human}
/>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Memory Fragmentation Ratio"
content={redisInfo.mem_fragmentation_ratio}
/>
</Grid>
<Grid item xs={3} />
<Grid item xs={12}>
<Typography variant="h6" color="textSecondary">
Connections
</Typography>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Connected Clients"
content={redisInfo.connected_clients}
/>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Connected Replicas"
content={redisInfo.connected_slaves}
/>
</Grid>
<Grid item xs={6} />
<Grid item xs={12}>
<Typography variant="h6" color="textSecondary">
Persistence
</Typography>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Last Save to Disk"
content={timeAgoUnix(parseInt(redisInfo.rdb_last_save_time))}
/>
</Grid>
<Grid item xs={3}>
<MetricCard
title="Number of Changes Since Last Dump"
content={redisInfo.rdb_changes_since_last_save}
/>
</Grid>
<Grid item xs={6} />
</>
)}
{redisInfoRaw !== null && (
<>
<Grid item xs={6}>
<Typography variant="h6" color="textSecondary">
INFO Command Output
</Typography>
<SyntaxHighlighter language="yaml" style={syntaxHighlightStyle}>
{redisInfoRaw}
</SyntaxHighlighter>
</Grid>
</>
)}
</Grid> </Grid>
</Container> </Container>
); );
} }
interface MetricCardProps {
title: string;
content: string;
}
function MetricCard(props: MetricCardProps) {
return (
<Card variant="outlined">
<CardContent>
<Typography
gutterBottom
color="textPrimary"
variant="h5"
align="center"
>
{props.content}
</Typography>
<Typography color="textSecondary" variant="subtitle2" align="center">
{props.title}
</Typography>
</CardContent>
</Card>
);
}
export default connector(RedisInfoView); export default connector(RedisInfoView);