From 0e80cbc55fe9cc5420be72e337ac536e16fb3219 Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Mon, 25 Apr 2022 12:17:21 +0200 Subject: [PATCH 1/2] fix: labels and colors at logs --- src/actions/loadLabels.js | 29 ++-- src/actions/loadLogs.js | 15 +- src/components/DataView/DataView.js | 145 +++++++++--------- src/components/DataView/helpers/index.js | 49 +++--- src/components/LabelBrowser/QueryBar.js | 4 + src/components/LabelBrowser/ValuesList.js | 30 ++-- .../ShowLogsButton/ShowLogsButton.js | 62 ++++++-- .../LabelBrowser/components/styled/index.js | 6 +- src/components/LabelBrowser/index.js | 4 +- .../components/apiselector/ApiSelector.js | 12 +- src/helpers/error.interceptor.js | 14 +- src/plugins/notifications/index.js | 100 ++++++------ 12 files changed, 268 insertions(+), 202 deletions(-) diff --git a/src/actions/loadLabels.js b/src/actions/loadLabels.js index 764f8484..8649f907 100644 --- a/src/actions/loadLabels.js +++ b/src/actions/loadLabels.js @@ -2,6 +2,7 @@ import axios from "axios"; import { setLabels } from "./setLabels"; import setLoading from "./setLoading"; import { setApiError } from "./setApiError"; +import { createAlert } from "./createAlert"; export default function loadLabels(apiUrl) { const origin = window.location.origin; @@ -26,30 +27,34 @@ export default function loadLabels(apiUrl) { .get(`${url.trim()}/loki/api/v1/labels`, options) ?.then((response) => { if (response) { - if (response?.data?.data === []) if (response?.data?.data?.length > 0) { - const labels = response?.data?.data - .sort() - .map((label) => ({ + let labels = response?.data?.data + ?.sort() + ?.map((label) => ({ name: label, selected: false, values: [], })); - dispatch(setLabels(labels || [])); - - dispatch(setApiError("")); + if (labels) { + dispatch(setLabels(labels || [])); + dispatch(setApiError("")); + dispatch(setLoading(false)); + } + } else { + dispatch(createAlert({type:'info',message:'No labels available for this API'})) } } else { dispatch(setLoading(false)); - dispatch( - setApiError("") - ); + + dispatch(setApiError("")); dispatch(setLabels([])); - } }) .catch((error) => { - console.log(error); + dispatch(createAlert({ + type:"error", + message:'API NOT FOUND' + })) dispatch(setLoading(false)); dispatch(setLabels([])); }); diff --git a/src/actions/loadLogs.js b/src/actions/loadLogs.js index dda20106..58051e05 100644 --- a/src/actions/loadLogs.js +++ b/src/actions/loadLogs.js @@ -21,15 +21,21 @@ export default function loadLogs() { } = localStore; let { start: startTs, stop: stopTs } = localStore; + function adjustForTimezone(date){ + + var timeOffsetInMS = date.getTimezoneOffset() * 60000; + date.setTime(date.getTime() + timeOffsetInMS); + return date + } + function getTimeParsed(time) { return time.getTime() + "000000"; } + const timeZone = new Date().getTimezoneOffset() const parsedStart = getTimeParsed(startTs); const parsedStop = getTimeParsed(stopTs); - const parsedTime = "&start=" + (from || parsedStart) + "&end=" + (to || parsedStop); - if (findRangeByLabel(rangeLabel)) { ({ dateStart: startTs, dateEnd: stopTs } = findRangeByLabel(rangeLabel)); @@ -37,6 +43,7 @@ export default function loadLogs() { store.dispatch(setStartTime(startTs)); store.dispatch(setStopTime(stopTs)); + const origin = window.location.origin; const url = apiUrl; const queryStep = `&step=${step || 120}`; @@ -113,13 +120,13 @@ export default function loadLogs() { dispatch(setMatrixData(idResult || [])); dispatch(setLoading(false)); } - dispatch(setLoading(false)); + // dispatch(setLoading(false)); } else { dispatch(setLogs([])); dispatch(setMatrixData([])); dispatch(setLoading(false)); } - dispatch(setLoading(false)); + // dispatch(setLoading(false)); }) .catch((error) => { diff --git a/src/components/DataView/DataView.js b/src/components/DataView/DataView.js index 3eb86a2f..b5f9d1ab 100644 --- a/src/components/DataView/DataView.js +++ b/src/components/DataView/DataView.js @@ -1,72 +1,73 @@ -import React, { Component } from "react"; -import { connect } from "react-redux"; - -import { DataViewCont, DataViewStyled, Loader } from "./styled"; - -import ClokiChart from "../../plugins/charts"; -import QueryHistory from "../../plugins/queryhistory"; -import LogsRow from "./LogsRow"; -import EmptyView from "./EmptyView"; - -class DataView extends Component { - constructor(props) { - super(props); - this.state = { - limit: props.limit || 100, - messages: props.messages || [], - matrixData: props.matrixData || [], - loading: false, - }; - } - - getMatrixForChart = () => { - return this.props.matrixData; - }; - getLimit = () => { - return this.props.limit; - }; - - render() { - return ( - - - {this.props.messages.length > 0 && - this.getMatrixForChart().length < 1 - ? this.props.messages.map((message, key) => ( - - )) - : null} - - {this.getMatrixForChart().length > 0 ? ( - - ) : null} - {this.props.messages.length < 1 && - this.getMatrixForChart().length < 1 && - !this.props.loading && } - - {this.props.loading && } - - - ); - } -} - -const mapStateToProps = (state) => { - return { - messages: state.logs, - start: state.start, - stop: state.stop, - limit: state.limit, - loading: state.loading, - matrixData: state.matrixData, - }; -}; - -export default connect(mapStateToProps)(DataView); +import React, { Component } from "react"; +import { connect } from "react-redux"; + +import { DataViewCont, DataViewStyled, Loader } from "./styled"; +import ClokiChart from "../../plugins/charts"; +import QueryHistory from "../../plugins/queryhistory"; +import LogsRow from "./LogsRow"; +import EmptyView from "./EmptyView"; +// import Table from "../../plugins/tables/Table"; + +class DataView extends Component { + constructor(props) { + super(props); + this.state = { + limit: props.limit || 100, + messages: props.messages || [], + matrixData: props.matrixData || [], + loading: false, + }; + } + + getMatrixForChart = () => { + return this.props.matrixData; + }; + getLimit = () => { + return this.props.limit; + }; + + render() { + return ( + + + {/* */} + {this.props.messages.length > 0 && + this.getMatrixForChart().length < 1 + ? this.props.messages.map((message, key) => ( + + )) + : null} + + {this.getMatrixForChart().length > 0 ? ( + + ) : null} + {this.props.messages.length < 1 && + this.getMatrixForChart().length < 1 && + !this.props.loading && } + + {this.props.loading && } + + + ); + } +} + +const mapStateToProps = (state) => { + return { + messages: state.logs, + start: state.start, + stop: state.stop, + limit: state.limit, + loading: state.loading, + matrixData: state.matrixData, + }; +}; + +export default connect(mapStateToProps)(DataView); diff --git a/src/components/DataView/helpers/index.js b/src/components/DataView/helpers/index.js index a25dba0e..d70e87f4 100644 --- a/src/components/DataView/helpers/index.js +++ b/src/components/DataView/helpers/index.js @@ -1,24 +1,25 @@ -import { LEVEL_COLORS as lColors } from "../theme/theme"; -import { TAGS_LEVEL as tLevel } from "../consts/consts"; -import * as moment from "moment"; - -export function getRowColor(tags) { - if (tags?.["level"]) { - const level = Object.keys(tLevel).find((level) => - tLevel[level].includes(tags.level.toLowerCase()) - ); - return lColors[level]; - } else { - return lColors["unknown"]; - } -} - -export function toggleActiveStyles(idx) { - return idx.showLabels - ? "value-tags-container labelsActive" - : "value-tags-container labelsInactive"; -} - -export function formatDate(timestamp) { - return moment(parseInt(timestamp)).format("YYYY-MM-DD HH:mm:ss.SSS UTC"); -} +import { LEVEL_COLORS as lColors } from "../theme/theme"; +import { TAGS_LEVEL as tLevel } from "../consts/consts"; +import * as moment from "moment"; + +export function getRowColor(tags) { + const type = tags?.severity || tags?.level; + if (type) { + const level = Object.keys(tLevel).find((level) => + tLevel[level].includes(type.toLowerCase()) + ); + return lColors[level]; + } else { + return lColors["unknown"]; + } +} + +export function toggleActiveStyles(idx) { + return idx.showLabels + ? "value-tags-container labelsActive" + : "value-tags-container labelsInactive"; +} + +export function formatDate(timestamp) { + return moment(parseInt(timestamp)).format("YYYY-MM-DD HH:mm:ss.SSS UTC"); +} diff --git a/src/components/LabelBrowser/QueryBar.js b/src/components/LabelBrowser/QueryBar.js index 51242be7..d7bc36d9 100644 --- a/src/components/LabelBrowser/QueryBar.js +++ b/src/components/LabelBrowser/QueryBar.js @@ -39,10 +39,12 @@ export const QueryBar = () => { const labels = useSelector(store => store.labels) const queryHistory = useSelector((store) => store.queryHistory); const saveUrl = localUrl(); + // const loading = useSelector( store => store.loading) useEffect(() => { const dLog = debugLog(query); debug && dLog.logicQueryBar(); const labels = sendLabels(apiUrl) + if (isEmbed) dispatch(loadLogs()) if (query.length > 0) { debug && dLog.queryBarDispatch(); @@ -144,6 +146,7 @@ export const QueryBar = () => { @@ -165,6 +168,7 @@ export const QueryBar = () => { /> diff --git a/src/components/LabelBrowser/ValuesList.js b/src/components/LabelBrowser/ValuesList.js index 9b6f9bbf..5aa0e23a 100644 --- a/src/components/LabelBrowser/ValuesList.js +++ b/src/components/LabelBrowser/ValuesList.js @@ -49,13 +49,13 @@ export const ValuesList = (props) => { } return state.labels; }); - + //const labels = useSelector((store)=> store.labels) const [labelList, setLabelList] = useState(labels); const dispatch = useDispatch(); const debug = useSelector((store) => store.debugMode); const apiUrl = useSelector((store) => store.apiUrl); - if (debug) console.log('🚧 LOGIC/LabelBrowser/ValuesList', apiUrl) + if (debug) console.log("🚧 LOGIC/LabelBrowser/ValuesList", apiUrl); const labelsBrowserOpen = useSelector((store) => store.labelsBrowserOpen); const CLEAR = "clear"; useEffect(() => { @@ -110,8 +110,8 @@ export const ValuesList = (props) => { } else return {}; }; const isString = (value) => { - return typeof value === 'string' - } + return typeof value === "string"; + }; return ( labelsBrowserOpen && (
@@ -136,7 +136,7 @@ export const ValuesList = (props) => { {labelList && labelList?.map((value, key) => ( {
- {labelSelected.name} ( {labelSelected.values.length}) @@ -181,17 +180,17 @@ export const ValuesList = (props) => { "valuelist-content column" } > - {labelSelected?.values?.map( - (value, key) => ( - + (value, key) => isString(value.name) ? ( - { ) } > - {value.name} - ) : (unknown) - - ) + ) : ( + + unknown + + ) )}
diff --git a/src/components/LabelBrowser/components/ShowLogsButton/ShowLogsButton.js b/src/components/LabelBrowser/components/ShowLogsButton/ShowLogsButton.js index 4293f06f..658ff158 100644 --- a/src/components/LabelBrowser/components/ShowLogsButton/ShowLogsButton.js +++ b/src/components/LabelBrowser/components/ShowLogsButton/ShowLogsButton.js @@ -1,15 +1,47 @@ -import { ShowLogsBtn } from "../styled"; - -export default function ShowLogsButton({ disabled, onClick, isMobile }) { - const SHOW_LOGS = "Show Logs"; - return ( - - {SHOW_LOGS} - - ); -} +import { useState, useEffect } from "react"; +import { useSelector } from "react-redux"; +import { LoadingBtn, ShowLogsBtn } from "../styled"; + +export default function ShowLogsButton({ + disabled, + onClick, + isMobile, + +}) { + const SHOW_LOGS = "Show Logs"; + +const loading = useSelector(store => store.loading) + + const [isLoading,setIsLoading] = useState(loading) + + + + return ( + <> + {loading ? ( + + + +{SHOW_LOGS} + + + ) : ( + + {SHOW_LOGS} + + )} + + ); +} diff --git a/src/components/LabelBrowser/components/styled/index.js b/src/components/LabelBrowser/components/styled/index.js index c9218a7f..5635c072 100644 --- a/src/components/LabelBrowser/components/styled/index.js +++ b/src/components/LabelBrowser/components/styled/index.js @@ -58,7 +58,7 @@ export const QueryBarContainer = styled.div` border-radius: 3px; `; export const ShowLogsBtn = styled(BtnSmall)` - background: ${theme.primaryDark}; + background: ${theme.primaryDark}; color: ${theme.buttonText}; margin-left: 10px; transition: 0.25s all; @@ -77,6 +77,10 @@ export const ShowLogsBtn = styled(BtnSmall)` } `; +export const LoadingBtn = styled(ShowLogsBtn)` +background:green; +` + export const MobileTopQueryMenu = styled.div` display: none; @media screen and (max-width: 864px) { diff --git a/src/components/LabelBrowser/index.js b/src/components/LabelBrowser/index.js index 91ff6f7d..e4d8175f 100644 --- a/src/components/LabelBrowser/index.js +++ b/src/components/LabelBrowser/index.js @@ -7,9 +7,9 @@ export default function LabelBrowser() { return (
- {labelsBrowserOpen && ( + - )} +
); diff --git a/src/components/StatusBar/components/apiselector/ApiSelector.js b/src/components/StatusBar/components/apiselector/ApiSelector.js index 9b56455b..07e9224b 100644 --- a/src/components/StatusBar/components/apiselector/ApiSelector.js +++ b/src/components/StatusBar/components/apiselector/ApiSelector.js @@ -18,17 +18,18 @@ export function ApiSelector() { const apiUrl = useSelector((store) => store.apiUrl); const apiError = useSelector((store) => store.apiErrors); const [editedUrl, setEditedUrl] = useState(apiUrl); - const [apiSelectorOpen, setApiSelectorOpen] = useState(false); const dispatch = useDispatch(); const API_URL = "API URL"; + useEffect(() => { setEditedUrl(apiUrl); }, []); useEffect(() => { setEditedUrl(apiUrl); - dispatch(loadLabels(apiUrl)) + + dispatch(loadLabels(apiUrl)); }, [apiUrl]); useEffect(() => { @@ -51,12 +52,15 @@ export function ApiSelector() { e.preventDefault(); setEditedUrl(e.target.value); }; + const onUrlSubmit = (e) => { dispatch(setApiUrl(editedUrl)); + setEditedUrl(editedUrl); - dispatch(loadLabels(editedUrl)) - dispatch(setLabelsBrowserOpen(false)); + dispatch(loadLabels(editedUrl)); + + dispatch(setLabelsBrowserOpen(false)); }; return ( diff --git a/src/helpers/error.interceptor.js b/src/helpers/error.interceptor.js index 1a4cbf46..bd761a8e 100644 --- a/src/helpers/error.interceptor.js +++ b/src/helpers/error.interceptor.js @@ -12,7 +12,6 @@ const errorInterceptor = (axiosInstance) => { (error) => { if (error.response) { - const handler = errorHandler(error) if (error?.response?.status === 401) { @@ -49,6 +48,9 @@ const errorInterceptor = (axiosInstance) => { } } else { + + // 1- get error by parsing json + const error_parsed = JSON.parse(JSON.stringify(error)); const networkError = { url: error_parsed.config.url, @@ -56,16 +58,22 @@ const errorInterceptor = (axiosInstance) => { name: error_parsed.name } + // 2- store.dispatch(setApiWarning({ type: 'labels', message: 'Labels not available', })) const { url } = networkError - + const apiWarning = store.getState().apiWarning - if (apiWarning && url.includes('query')) { + if (apiWarning && url.includes('query') && store.getState().notifications.length < 1) { apiWarning.num++ store.dispatch(createAlert({ type: 'error', message: `API not found, please adjust API URL` })) + } else if( url.includes('labels') && store.getState().notifications.length < 1) { + store.dispatch(createAlert({ + type: 'error', + message: 'API not found, please adjust API URL' + })) } } diff --git a/src/plugins/notifications/index.js b/src/plugins/notifications/index.js index b00af526..b06389af 100644 --- a/src/plugins/notifications/index.js +++ b/src/plugins/notifications/index.js @@ -1,51 +1,51 @@ -import { useEffect } from "react"; - -import { useSelector, useDispatch } from "react-redux"; -import { Alert} from "@mui/material"; -import { removeAlert } from "../../actions"; - - - -export function Notification() { - const { notifications } = useSelector(state => state); - const dispatch = useDispatch(); - const handleClose = (index) => { - dispatch(removeAlert(index)) - }; - const Expire = props => { - useEffect(() => { - setTimeout(() => { - dispatch(removeAlert(props.index)) - }, props.delay); - }, [props]); - - return
{props.children}
; - }; - - return ( -
- { - notifications.map((notification, index) => { - if (notification.visible) { - return -
- handleClose(index)} - severity={notification.type} - sx={{ width: "100%" }} - > - {notification.message} - -
-
- } else { - return undefined; - } - } - ) - } -
- ); +import { useEffect } from "react"; + +import { useSelector, useDispatch } from "react-redux"; +import { Alert} from "@mui/material"; +import { removeAlert } from "../../actions"; + + + +export function Notification() { + const { notifications } = useSelector(state => state); + const dispatch = useDispatch(); + const handleClose = (index) => { + dispatch(removeAlert(index)) + }; + const Expire = props => { + useEffect(() => { + setTimeout(() => { + dispatch(removeAlert(props.index)) + }, props.delay); + }, [props]); + + return
{props.children}
; + }; + + return ( +
+ { + notifications.map((notification, index) => { + if (notification.visible) { + return +
+ handleClose(index)} + severity={notification.type} + sx={{ width: "100%" }} + > + {notification.message} + +
+
+ } else { + return undefined; + } + } + ) + } +
+ ); } \ No newline at end of file From 98b3df4b82e89a24c963f5d4a59b96a3e7b9455f Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Mon, 25 Apr 2022 16:34:21 +0200 Subject: [PATCH 2/2] fix logs row styles --- src/components/DataView/LogsRow.js | 77 ++++++++++++------------ src/components/DataView/consts/consts.js | 16 ++--- src/components/DataView/styled/index.js | 18 ++++-- src/components/DataView/theme/theme.js | 36 ++++++----- 4 files changed, 76 insertions(+), 71 deletions(-) diff --git a/src/components/DataView/LogsRow.js b/src/components/DataView/LogsRow.js index 0531f568..c5cbb756 100644 --- a/src/components/DataView/LogsRow.js +++ b/src/components/DataView/LogsRow.js @@ -1,38 +1,39 @@ -import { formatDate, getRowColor, toggleActiveStyles } from "./helpers"; -import { LogRow, RowLogContent, RowTimestamp } from "./styled"; -import ValueTags from "./ValueTags"; -import { useSelector, useDispatch } from "react-redux"; -import setLogs from "../../actions/setLogs"; - -export default function LogsRow({ message }) { - const dispatch = useDispatch(); - const messages = useSelector((store) => store.logs); - - function toggleTagsActive(idx) { - let arrCopy = [...messages]; - arrCopy.forEach((entry) => { - if (entry.id === idx) { - entry.showLabels = entry.showLabels ? false : true; - } - }); - dispatch(setLogs(arrCopy)); - } - - return ( - { - toggleTagsActive(message.id); - }} - > - {formatDate(message.timestamp)} - {message.text} - - {message.tags && ( -
- -
- )} -
- ); -} +import { formatDate, getRowColor, toggleActiveStyles } from "./helpers"; +import { LogRow, RowLogContent, RowTimestamp } from "./styled"; +import ValueTags from "./ValueTags"; +import { useSelector, useDispatch } from "react-redux"; +import setLogs from "../../actions/setLogs"; + +export default function LogsRow({ message }) { + const dispatch = useDispatch(); + const messages = useSelector((store) => store.logs); + + function toggleTagsActive(idx) { + let arrCopy = [...messages]; + arrCopy.forEach((entry) => { + if (entry.id === idx) { + entry.showLabels = entry.showLabels ? false : true; + } + }); + dispatch(setLogs(arrCopy)); + } + + return ( + { + toggleTagsActive(message.id); + }} + > +
+ {formatDate(message.timestamp)} + {message.text} +
+ {message.tags && ( +
+ +
+ )} +
+ ); +} diff --git a/src/components/DataView/consts/consts.js b/src/components/DataView/consts/consts.js index 936604e8..f2d24937 100644 --- a/src/components/DataView/consts/consts.js +++ b/src/components/DataView/consts/consts.js @@ -1,8 +1,8 @@ -export const TAGS_LEVEL = { - critical: ["emerg", "fatal", "alert", "crit", "critical"], - error: ["err", "eror", "error", "warning"], - warning: ["warn", "warning"], - info: ["info", "information", "notice"], - debug: ["dbug", "debug"], - trace: ["trace"], -}; \ No newline at end of file +export const TAGS_LEVEL = { + critical: ["emerg", "fatal", "alert", "crit", "critical"], + error: ["err", "eror", "error"], + warning: ["warn", "warning"], + info: ["info", "information", "notice"], + debug: ["dbug", "debug"], + trace: ["trace"], +}; diff --git a/src/components/DataView/styled/index.js b/src/components/DataView/styled/index.js index 7ed2ed64..17208569 100644 --- a/src/components/DataView/styled/index.js +++ b/src/components/DataView/styled/index.js @@ -1,5 +1,4 @@ import styled from "@emotion/styled"; -import { THEME_COLORS } from '../theme/theme'; import { CircularProgress } from "@mui/material"; import darkTheme from "../../../theme/dark"; @@ -44,14 +43,18 @@ export const DataViewCont = styled.div` `; export const LogRow = styled.div` - padding: 0.3rem; + font-family: monospace; color: white; font-size: 12px; cursor: pointer; - margin-bottom: 4px; padding-left: 0.5rem; margin-left: 0.25rem; transition: 0.2s all; + display: flex; + flex-direction: column; + border-left: 4px solid ${(props) => props.rowColor}; + margin-bottom: 2px; + margin-top: 2px; &:hover { background: black; } @@ -59,13 +62,14 @@ export const LogRow = styled.div` p { display: inline-block; } - - border-left: 4px solid ${(props) => props.rowColor}; + .log-ts-row { + display: flex; + } `; export const RowLogContent = styled.span` font-size: 12px; - font-family: monospace; + color: ${theme.textWhite}; line-height: 1.5; `; @@ -74,6 +78,8 @@ export const RowTimestamp = styled.span` position: relative; color: ${theme.textColor}; margin-right: 0.25rem; + white-space: nowrap; + line-height: 1.5; `; export const Loader = styled(CircularProgress)` diff --git a/src/components/DataView/theme/theme.js b/src/components/DataView/theme/theme.js index 0137ae4a..db2fab4c 100644 --- a/src/components/DataView/theme/theme.js +++ b/src/components/DataView/theme/theme.js @@ -1,19 +1,17 @@ - -export const THEME_COLORS = { - logsTimestamp: "#e8e8e8", - scrollbarThumb: "#616161", - logContent: "#ddd", - dataViewBackground: "#1a1a1a", - emptyViewMessage: "#aaa", -}; - - -export const LEVEL_COLORS = { - critical: "purple", - error: "red", - warning: "yellow", - info: "green", - debug: "blue", - trace: "lightblue", - unknown: "gray", -}; \ No newline at end of file +export const THEME_COLORS = { + logsTimestamp: "#e8e8e8", + scrollbarThumb: "#616161", + logContent: "#ddd", + dataViewBackground: "#1a1a1a", + emptyViewMessage: "#aaa", +}; + +export const LEVEL_COLORS = { + critical: "purple", + error: "red", + warning: "orange", + info: "green", + debug: "blue", + trace: "lightblue", + unknown: "gray", +};