Skip to content

Commit

Permalink
Merge pull request #50 from metrico/alexey-log-label-filters
Browse files Browse the repository at this point in the history
Alexey log label filters
  • Loading branch information
lmangani authored Mar 1, 2022
2 parents 133bda2 + 155fb81 commit e329226
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"plugins": [
"transform-class-properties",
"react-hot-loader/babel",
"syntax-dynamic-import"
"syntax-dynamic-import",
"@babel/plugin-transform-runtime"
],
"presets": [

Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@babel/core": "^7.16.12",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"babel-loader": "^8.2.3",
Expand Down
7 changes: 3 additions & 4 deletions src/actions/LoadLabels.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from "axios";
import setLabels from "./setLabels";
import { setLabels } from "./setLabels";
import setLoading from "./setLoading";
import { setApiError } from "./setApiError";
import { errorHandler } from "./errorHandler";
Expand All @@ -21,11 +21,11 @@ export default function loadLabels(apiUrl) {
mode: "cors",
};

return function (dispatch) {
return async (dispatch) => {



axios.get(`${url.trim()}/loki/api/v1/labels`, options)
await axios.get(`${url.trim()}/loki/api/v1/labels`, options)
?.then((response) => {
if(response){
if(response?.data?.data === []) console.log('no labels found')
Expand All @@ -39,7 +39,6 @@ export default function loadLabels(apiUrl) {
hidden: false,
facets: 0,
}));

dispatch(setLabels(labels || []));

dispatch(setApiError(''))
Expand Down
1 change: 1 addition & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from "./setApiUrl";
export * from "./setQuery";
export * from "./setIsSubmit";
export * from "./setMatrixData";
export * from "./setLabels";
16 changes: 8 additions & 8 deletions src/actions/loadLabelValues.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import axios from "axios";
import { errorHandler } from "./errorHandler";
import { setApiError } from "./setApiError";
import setLabels from "./setLabels";
import { setLabels } from "./setLabels";
import setLabelValues from "./setLabelValues";
import setLoading from "./setLoading";


export default function loadLaebelValues(label, labelList, apiUrl) {
if (label?.length <= 0 && label.lsList.length <= 0) {
return;
export default function loadLabelValues(label, labelList, apiUrl) {
if (!label || (label?.length <= 0 && label.lsList.length <= 0)) {
return () => {};
};

const url = apiUrl;
Expand All @@ -28,18 +28,18 @@ export default function loadLaebelValues(label, labelList, apiUrl) {

};

return function (dispatch) {

return async (dispatch) => {
dispatch(setLoading(true))

axios.get(`${url}/loki/api/v1/label/${label.name}/values`, options)
await axios.get(`${url}/loki/api/v1/label/${label.name}/values`, options)
?.then(response => {
if (response?.data?.data) {
const values = response?.data?.data?.map?.((value) => ({
name: value,
selected: false,
loading: false,
hidden: false,
inverted: false
}));

const lsList = [...labelList];
Expand All @@ -62,7 +62,7 @@ export default function loadLaebelValues(label, labelList, apiUrl) {
const { message } = errorHandler(url, error)
dispatch(setApiError(message))

console.log(error)
console.err(error)
})
}

Expand Down
3 changes: 2 additions & 1 deletion src/actions/setLabelValues.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default (labelValues) => (dispatch) => {
const setLabelValues = (labelValues) => (dispatch) => {
dispatch({
type: 'SET_LABEL_VALUES',
labelValues
});
}
export default setLabelValues;
4 changes: 2 additions & 2 deletions src/actions/setLabels.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default (labels) => (dispatch) => {
export const setLabels = (labels) => (dispatch) => {
dispatch({
type: 'SET_LABELS',
labels: labels
});
}
};
1 change: 1 addition & 0 deletions src/components/LabelBrowser/ValuesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const ValuesList = (props) => {
const onLabelValueClick = (e, value) => {
e.preventDefault()
value.selected = !value.selected;
value.inverted = false;
onLabelValueChange();
};

Expand Down
18 changes: 17 additions & 1 deletion src/components/LabelBrowser/helpers/querybuilder.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import { setQuery } from "../../../actions";
import store from "../../../store/store";

export function queryBuilder(labels) {
const selectedLabels = [];
for (const label of labels) {
if (label.selected && label.values && label.values.length > 0) {
const selectedValues = label.values
.filter((value) => value.selected)
.filter((value) => value.selected && !value.inverted)
.map((value) => value.name);
const invertedSelectedValues = label.values
.filter((value) => value.selected && value.inverted)
.map((value) => value.name);

if (selectedValues.length > 1) {
selectedLabels.push(
`${label.name}=~"${selectedValues.join("|")}"`
);
} else if (selectedValues.length === 1) {
selectedLabels.push(`${label.name}="${selectedValues[0]}"`);
}
invertedSelectedValues.forEach(value => {
selectedLabels.push(`${label.name}!="${value}"`)
});

}
}
return ["{", selectedLabels.join(","), "}"].join("");
}
export function queryBuilderWithLabels() {
const labels = store.getState().labels;
console.log(labels)
const query = queryBuilder(labels)
store.dispatch(setQuery(query));
}
43 changes: 40 additions & 3 deletions src/components/LogView.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { ZoomIn, ZoomOut } from "@mui/icons-material/";
import { CircularProgress, IconButton } from "@mui/material";
import * as moment from "moment";
import React, { Component } from "react";
import { connect } from "react-redux";
import { CircularProgress } from "@mui/material";
import * as moment from "moment";
import { setLabels } from "../actions";
import loadLabelValues from '../actions/loadLabelValues';
import ClokiChart from "../plugins/charts";
import store from "../store/store";
import { queryBuilderWithLabels } from "./LabelBrowser/helpers/querybuilder";

import loadLogs from "../actions/loadLogs"
const TAGS_LEVEL = {
critical: ['emerg', 'fatal', 'alert', 'crit', 'critical'],
error: ['err', 'eror', 'error', 'warning'],
Expand All @@ -13,11 +19,42 @@ const TAGS_LEVEL = {
trace: ['trace']
}
export const ValueTags = (props) => {

const addLabel = async (e, key, value, isInverted = false) => {
e.preventDefault();
e.stopPropagation();
const {labels, apiUrl} = store.getState();
const label = labels.find(label => label.name === key);
if (label) {
const labelValue = label.values.find(tag => tag.name === value);
if (labelValue) {
labelValue.selected = !labelValue.selected || (labelValue.inverted !== isInverted);
labelValue.inverted = !labelValue.inverted && isInverted;
label.selected = label.values.some(value => value.selected);
store.dispatch(setLabels(labels));
} else {
await store.dispatch(loadLabelValues(label,labels,apiUrl));
const updatedLabels = store.getState().labels;
const updatedLabel = updatedLabels.find(label => label.name === key);
const labelValue = updatedLabel.values.find(tag => tag.name === value);
labelValue.selected = !labelValue.selected || (labelValue.inverted !== isInverted);
labelValue.inverted = !labelValue.inverted && isInverted;
updatedLabel.selected = updatedLabel.values.some(value => value.selected);
store.dispatch(setLabels(updatedLabels));
}
queryBuilderWithLabels()
store.dispatch(loadLogs())
}
}
const getTags = (tags) => {
return Object.entries(tags).map(
([key, value], k) => (
<div className={"value-tags"} key={k}>
<IconButton onClick={(e) => addLabel(e, key, value)} aria-label="Filter for value" size="small" color="primary">
<ZoomIn />
</IconButton>
<IconButton onClick={(e) => addLabel(e, key, value, true)} aria-label="Filter out value" size="small" color="primary">
<ZoomOut />
</IconButton>
<span>{key}</span>
<span>{value}</span>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/MainView.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import StatusBar from "./StatusBar/StatusBar";
import LabelBrowser from "./LabelBrowser/LabelBrowser"
import { UpdateStateFromQueryParams } from "./UpdateStateFromQueryParams";
export default function MainView() {

UpdateStateFromQueryParams()
return (
<div className={"log-search"}>
Expand Down
94 changes: 89 additions & 5 deletions src/components/UpdateStateFromQueryParams.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as moment from 'moment'
import { useDispatch, useSelector } from 'react-redux';
import * as moment from 'moment';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { setUrlQueryParams } from '../actions/setUrlQueryParams';
import { setApiUrl, setIsSubmit, setLabels, setQuery, setQueryLimit, setQueryStep, setStartTime, setStopTime } from '../actions';
import loadLabels from '../actions/LoadLabels';
import loadLabelValues from '../actions/loadLabelValues';
import { setUrlLocation } from '../actions/setUrlLocation';
import { setQuery, setStopTime, setStartTime, setQueryLimit, setQueryStep, setApiUrl, setIsSubmit} from '../actions';
import { setUrlQueryParams } from '../actions/setUrlQueryParams';
import { environment } from '../environment/env.dev';
import store from '../store/store';
export function UpdateStateFromQueryParams() {
const { hash } = useLocation()

Expand Down Expand Up @@ -79,6 +82,7 @@ export function UpdateStateFromQueryParams() {
dispatch(STORE_ACTIONS[param](startParams[param]))
} else if (QUERY_VALUE === param && startParams[param] !== '') {
const parsedQuery = decodeURIComponent(startParams[param])
decodeQuery(parsedQuery, apiUrl)
dispatch(STORE_ACTIONS[param](parsedQuery))
} else if (TIME_VALUES.includes(param) && startParams[param] !== '') {
const croppedTime = ((startParams[param])) / 1000000
Expand All @@ -87,7 +91,9 @@ export function UpdateStateFromQueryParams() {
} else if (BOOLEAN_VALUES.includes(param) && typeof param === 'boolean') {
dispatch(STORE_ACTIONS[param](startParams[param]))
}
if (QUERY_VALUE === param) {

}
})

}
Expand All @@ -109,7 +115,8 @@ export function UpdateStateFromQueryParams() {

urlFromHash.set(param, time_value.toString())
} else if (QUERY_VALUE === param) {
const parsed = encodeURIComponent(STORE_KEYS[param]).toString()
const parsed = encodeURIComponent(STORE_KEYS[param]).toString();
decodeQuery(parsed, apiUrl)
urlFromHash.set(param, parsed.toString())
} else if(BOOLEAN_VALUES === param && typeof param === 'boolean') {
urlFromHash.set(param,param.toString())
Expand Down Expand Up @@ -168,3 +175,80 @@ export function UpdateStateFromQueryParams() {
}, [STORE_KEYS])

}
async function decodeQuery(query, apiUrl) {
await store.dispatch(loadLabels(apiUrl))
const queryArr = query.replaceAll(/[{}]/g,'').split(',');
const labelsFromQuery = [];
queryArr.forEach(label => {
const regexQuery = label.match(/([^{}=,~!]+)/gm);
if (!regexQuery) {
return;
}
if (label.includes("!=")) {
const labelObj = {
name: regexQuery[0],
values: []
}
const valueObj = {
name: regexQuery[1].replaceAll('"', ''),
selected: true,
inverted: true
}
labelObj.values.push(valueObj);
labelsFromQuery.push(labelObj);
} else if(label.includes("=~")) {
const values = regexQuery[1].split('|')
console.log(values)
const labelObj = {
name: regexQuery[0],
values: []
}
values.forEach(value => {
const valueObj = {
name: value.replaceAll('"', ''),
selected: true,
inverted: false
}
labelObj.values.push(valueObj);

});
labelsFromQuery.push(labelObj);
} else {
const labelObj = {
name: regexQuery[0],
values: []
}
const valueObj = {
name: regexQuery[1].replaceAll('"', ''),
selected: true,
inverted: false
}
labelObj.values.push(valueObj);
labelsFromQuery.push(labelObj);
}
});
const newLabels = store.getState().labels;
labelsFromQuery.forEach(async (label) => {

const cleanLabel = newLabels?.find(item => item?.name === label?.name);
if (!cleanLabel) {
return
}

await store.dispatch(loadLabelValues(cleanLabel,newLabels,apiUrl));
const labelsWithValues = store.getState().labels;
const labelWithValues = labelsWithValues.find(item => item?.name === label?.name);
let values = labelWithValues.values;
values = label.values.concat(values);
values = values
.sort((a, b) => a.name.localeCompare(b.name))
.filter((i, k, a) => {
console.log(a[k-1])
return i.name !== a[k - 1]?.name})
.filter((i) => !!i);
labelWithValues.values = values;
labelWithValues.selected = true;
store.dispatch(setLabels(labelsWithValues))
})

}
4 changes: 2 additions & 2 deletions src/scss/modules/log-view.scss
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@
justify-content: space-between;

span {
padding: 4px;
padding: 0px 4px;
display: flex;
flex: 1;

line-height: 34px;
color: #cccccd;
}
&:hover {
Expand Down

0 comments on commit e329226

Please sign in to comment.