mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-10-26 16:26:12 +08:00
Add redux actions/reducer for metrics
This commit is contained in:
45
ui/src/actions/metricsActions.ts
Normal file
45
ui/src/actions/metricsActions.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Dispatch } from "redux";
|
||||
import { getMetrics, MetricsResponse } from "../api";
|
||||
import { toErrorString, toErrorStringWithHttpStatus } from "../utils";
|
||||
|
||||
// List of metrics related action types.
|
||||
export const GET_METRICS_BEGIN = "GET_METRICS_BEGIN";
|
||||
export const GET_METRICS_SUCCESS = "GET_METRICS_SUCCESS";
|
||||
export const GET_METRICS_ERROR = "GET_METRICS_ERROR";
|
||||
|
||||
interface GetMetricsBeginAction {
|
||||
type: typeof GET_METRICS_BEGIN;
|
||||
}
|
||||
|
||||
interface GetMetricsSuccessAction {
|
||||
type: typeof GET_METRICS_SUCCESS;
|
||||
payload: MetricsResponse;
|
||||
}
|
||||
|
||||
interface GetMetricsErrorAction {
|
||||
type: typeof GET_METRICS_ERROR;
|
||||
error: string;
|
||||
}
|
||||
|
||||
// Union of all metrics related actions.
|
||||
export type MetricsActionTypes =
|
||||
| GetMetricsBeginAction
|
||||
| GetMetricsSuccessAction
|
||||
| GetMetricsErrorAction;
|
||||
|
||||
export function getMetricsAsync() {
|
||||
return async (dispatch: Dispatch<MetricsActionTypes>) => {
|
||||
dispatch({ type: GET_METRICS_BEGIN });
|
||||
try {
|
||||
const response = await getMetrics();
|
||||
console.log("DEBUG: got metrics: ", response);
|
||||
dispatch({ type: GET_METRICS_SUCCESS, payload: response });
|
||||
} catch (error) {
|
||||
console.error(`getMetricsAsync: ${toErrorStringWithHttpStatus(error)}`);
|
||||
dispatch({
|
||||
type: GET_METRICS_ERROR,
|
||||
error: toErrorString(error),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -76,6 +76,27 @@ export interface QueueLocation {
|
||||
nodes: string[]; // node addresses
|
||||
}
|
||||
|
||||
export interface MetricsResponse {
|
||||
data: MetricsResult;
|
||||
}
|
||||
|
||||
export interface MetricsResult {
|
||||
result: Metrics[];
|
||||
}
|
||||
|
||||
export interface Metrics {
|
||||
metric: MetricsInfo;
|
||||
values: [number, string]; // [unixtime, value]
|
||||
}
|
||||
|
||||
export interface MetricsInfo {
|
||||
__name__: string;
|
||||
instance: string;
|
||||
job: string;
|
||||
queue: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
// Return value from redis INFO command.
|
||||
// See https://redis.io/commands/info#return-value.
|
||||
export interface RedisInfo {
|
||||
@@ -854,3 +875,11 @@ export async function getRedisInfo(): Promise<RedisInfoResponse> {
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function getMetrics(): Promise<MetricsResponse> {
|
||||
const resp = await axios({
|
||||
method: "get",
|
||||
url: `${BASE_URL}/metrics`,
|
||||
});
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
49
ui/src/reducers/metricsReducer.ts
Normal file
49
ui/src/reducers/metricsReducer.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
GET_METRICS_BEGIN,
|
||||
GET_METRICS_ERROR,
|
||||
GET_METRICS_SUCCESS,
|
||||
MetricsActionTypes,
|
||||
} from "../actions/metricsActions";
|
||||
import { MetricsResponse } from "../api";
|
||||
|
||||
interface MetricsState {
|
||||
loading: boolean;
|
||||
error: string;
|
||||
data: MetricsResponse | null;
|
||||
}
|
||||
|
||||
const initialState: MetricsState = {
|
||||
loading: false,
|
||||
error: "",
|
||||
data: null,
|
||||
};
|
||||
|
||||
export default function metricsReducer(
|
||||
state = initialState,
|
||||
action: MetricsActionTypes
|
||||
): MetricsState {
|
||||
switch (action.type) {
|
||||
case GET_METRICS_BEGIN:
|
||||
return {
|
||||
...state,
|
||||
loading: true,
|
||||
};
|
||||
|
||||
case GET_METRICS_ERROR:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
error: action.error,
|
||||
};
|
||||
|
||||
case GET_METRICS_SUCCESS:
|
||||
return {
|
||||
loading: false,
|
||||
error: "",
|
||||
data: action.payload,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import schedulerEntriesReducer from "./reducers/schedulerEntriesReducer";
|
||||
import snackbarReducer from "./reducers/snackbarReducer";
|
||||
import queueStatsReducer from "./reducers/queueStatsReducer";
|
||||
import redisInfoReducer from "./reducers/redisInfoReducer";
|
||||
import metricsReducer from "./reducers/metricsReducer";
|
||||
import { loadState } from "./localStorage";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
@@ -18,6 +19,7 @@ const rootReducer = combineReducers({
|
||||
snackbar: snackbarReducer,
|
||||
queueStats: queueStatsReducer,
|
||||
redis: redisInfoReducer,
|
||||
metrics: metricsReducer,
|
||||
});
|
||||
|
||||
const preloadedState = loadState();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createMuiTheme, Theme } from "@material-ui/core/styles";
|
||||
import { createTheme, Theme } from "@material-ui/core/styles";
|
||||
import { ThemePreference } from "./reducers/settingsReducer";
|
||||
import useMediaQuery from "@material-ui/core/useMediaQuery";
|
||||
|
||||
@@ -9,7 +9,7 @@ export function useTheme(themePreference: ThemePreference): Theme {
|
||||
} else if (themePreference === ThemePreference.Never) {
|
||||
prefersDarkMode = false;
|
||||
}
|
||||
return createMuiTheme({
|
||||
return createTheme({
|
||||
// Got color palette from https://htmlcolors.com/palette/31/stripe
|
||||
palette: {
|
||||
primary: {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { connect, ConnectedProps } from "react-redux";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { getMetricsAsync } from "../actions/metricsActions";
|
||||
import { AppState } from "../store";
|
||||
import { usePolling } from "../hooks";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
container: {
|
||||
@@ -10,12 +14,23 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function MetricsView() {
|
||||
const classes = useStyles();
|
||||
function mapStateToProps(state: AppState) {
|
||||
return {
|
||||
loading: state.metrics.loading,
|
||||
error: state.metrics.error,
|
||||
data: state.metrics.data,
|
||||
pollInterval: state.settings.pollInterval,
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("TODO: Get metrics data!");
|
||||
}, []);
|
||||
const connector = connect(mapStateToProps, { getMetricsAsync });
|
||||
type Props = ConnectedProps<typeof connector>;
|
||||
|
||||
function MetricsView(props: Props) {
|
||||
const classes = useStyles();
|
||||
const { pollInterval, getMetricsAsync } = props;
|
||||
|
||||
usePolling(getMetricsAsync, pollInterval);
|
||||
|
||||
return (
|
||||
<Container maxWidth="lg" className={classes.container}>
|
||||
@@ -28,4 +43,4 @@ function MetricsView() {
|
||||
);
|
||||
}
|
||||
|
||||
export default MetricsView;
|
||||
export default connector(MetricsView);
|
||||
|
||||
Reference in New Issue
Block a user