mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 08:39:01 +08:00
ability to filter by deployment environment service map (#2506)
This commit is contained in:
parent
99ed314fc9
commit
3f96325ad8
@ -17,6 +17,7 @@ import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
|||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
import {
|
import {
|
||||||
dbSystemTags,
|
dbSystemTags,
|
||||||
|
handleNonInQueryRange,
|
||||||
onGraphClickHandler,
|
onGraphClickHandler,
|
||||||
onViewTracePopupClick,
|
onViewTracePopupClick,
|
||||||
} from './util';
|
} from './util';
|
||||||
@ -25,10 +26,13 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
|
|||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||||
const { queries } = useResourceAttribute();
|
const { queries } = useResourceAttribute();
|
||||||
|
|
||||||
const tagFilterItems = useMemo(
|
const tagFilterItems = useMemo(
|
||||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
() =>
|
||||||
|
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||||
[queries],
|
[queries],
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedTraceTags: string = useMemo(
|
const selectedTraceTags: string = useMemo(
|
||||||
() =>
|
() =>
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
|
@ -18,7 +18,11 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||||
import { legend } from './constant';
|
import { legend } from './constant';
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
import { onGraphClickHandler, onViewTracePopupClick } from './util';
|
import {
|
||||||
|
handleNonInQueryRange,
|
||||||
|
onGraphClickHandler,
|
||||||
|
onViewTracePopupClick,
|
||||||
|
} from './util';
|
||||||
|
|
||||||
function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||||
@ -27,7 +31,8 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
|||||||
const { queries } = useResourceAttribute();
|
const { queries } = useResourceAttribute();
|
||||||
|
|
||||||
const tagFilterItems = useMemo(
|
const tagFilterItems = useMemo(
|
||||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
() =>
|
||||||
|
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||||
[queries],
|
[queries],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -26,7 +26,11 @@ import {
|
|||||||
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
||||||
import TopOperationsTable from '../TopOperationsTable';
|
import TopOperationsTable from '../TopOperationsTable';
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
import { onGraphClickHandler, onViewTracePopupClick } from './util';
|
import {
|
||||||
|
handleNonInQueryRange,
|
||||||
|
onGraphClickHandler,
|
||||||
|
onViewTracePopupClick,
|
||||||
|
} from './util';
|
||||||
|
|
||||||
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
@ -67,7 +71,8 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tagFilterItems = useMemo(
|
const tagFilterItems = useMemo(
|
||||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
() =>
|
||||||
|
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||||
[queries],
|
[queries],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
|||||||
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
|
||||||
import { Tags } from 'types/reducer/trace';
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export const dbSystemTags: Tags[] = [
|
export const dbSystemTags: Tags[] = [
|
||||||
@ -84,3 +85,16 @@ export function onGraphClickHandler(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleNonInQueryRange = (
|
||||||
|
tags: IQueryBuilderTagFilterItems[],
|
||||||
|
): IQueryBuilderTagFilterItems[] =>
|
||||||
|
tags.map((tag) => {
|
||||||
|
if (tag.op === 'Not IN') {
|
||||||
|
return {
|
||||||
|
...tag,
|
||||||
|
op: 'NIN',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
});
|
||||||
|
@ -17,7 +17,6 @@ import {
|
|||||||
GetTagKeys,
|
GetTagKeys,
|
||||||
GetTagValues,
|
GetTagValues,
|
||||||
OperatorSchema,
|
OperatorSchema,
|
||||||
resourceAttributesQueryToPromQL,
|
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
function ResourceProvider({ children }: Props): JSX.Element {
|
function ResourceProvider({ children }: Props): JSX.Element {
|
||||||
@ -29,10 +28,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
|||||||
getResourceAttributeQueriesFromURL(),
|
getResourceAttributeQueriesFromURL(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [promQLQuery, setpromQLQuery] = useState<string>(
|
|
||||||
resourceAttributesQueryToPromQL(queries),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [optionsData, setOptionsData] = useState<OptionsData>({
|
const [optionsData, setOptionsData] = useState<OptionsData>({
|
||||||
mode: undefined,
|
mode: undefined,
|
||||||
options: [],
|
options: [],
|
||||||
@ -55,7 +50,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
|||||||
: '',
|
: '',
|
||||||
});
|
});
|
||||||
setQueries(queries);
|
setQueries(queries);
|
||||||
setpromQLQuery(resourceAttributesQueryToPromQL(queries));
|
|
||||||
},
|
},
|
||||||
[pathname],
|
[pathname],
|
||||||
);
|
);
|
||||||
@ -144,7 +138,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
|||||||
() => ({
|
() => ({
|
||||||
queries,
|
queries,
|
||||||
staging,
|
staging,
|
||||||
promQLQuery,
|
|
||||||
handleClearAll,
|
handleClearAll,
|
||||||
handleClose,
|
handleClose,
|
||||||
handleBlur,
|
handleBlur,
|
||||||
@ -161,7 +154,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
|||||||
handleClose,
|
handleClose,
|
||||||
handleFocus,
|
handleFocus,
|
||||||
loading,
|
loading,
|
||||||
promQLQuery,
|
|
||||||
queries,
|
queries,
|
||||||
staging,
|
staging,
|
||||||
selectedQuery,
|
selectedQuery,
|
||||||
|
@ -20,7 +20,6 @@ export interface OptionsData {
|
|||||||
export interface IResourceAttributeProps {
|
export interface IResourceAttributeProps {
|
||||||
queries: IResourceAttribute[];
|
queries: IResourceAttribute[];
|
||||||
staging: string[];
|
staging: string[];
|
||||||
promQLQuery: string;
|
|
||||||
handleClearAll: VoidFunction;
|
handleClearAll: VoidFunction;
|
||||||
handleClose: (id: string) => void;
|
handleClose: (id: string) => void;
|
||||||
handleBlur: VoidFunction;
|
handleBlur: VoidFunction;
|
||||||
|
@ -57,26 +57,6 @@ export const convertRawQueriesToTraceSelectedTags = (
|
|||||||
TagType: tagType,
|
TagType: tagType,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts Resource Attribute Queries to PromQL query string
|
|
||||||
*/
|
|
||||||
export const resourceAttributesQueryToPromQL = (
|
|
||||||
queries: IResourceAttribute[],
|
|
||||||
): string => {
|
|
||||||
let parsedQueryString = '';
|
|
||||||
|
|
||||||
if (Array.isArray(queries))
|
|
||||||
queries.forEach((query) => {
|
|
||||||
parsedQueryString += `, ${
|
|
||||||
query.tagKey
|
|
||||||
}${convertOperatorLabelToMetricOperator(
|
|
||||||
query.operator,
|
|
||||||
)}"${query.tagValue.join('|')}"`;
|
|
||||||
});
|
|
||||||
|
|
||||||
return parsedQueryString;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Convert resource attributes to tagFilter items for queryBuilder */
|
/* Convert resource attributes to tagFilter items for queryBuilder */
|
||||||
export const resourceAttributesToTagFilterItems = (
|
export const resourceAttributesToTagFilterItems = (
|
||||||
queries: IResourceAttribute[],
|
queries: IResourceAttribute[],
|
||||||
|
56
frontend/src/modules/Servicemap/Map.tsx
Normal file
56
frontend/src/modules/Servicemap/Map.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
//@ts-nocheck
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { ForceGraph2D } from 'react-force-graph';
|
||||||
|
|
||||||
|
import { getGraphData, getTooltip, transformLabel } from './utils';
|
||||||
|
|
||||||
|
function Map({ fgRef, serviceMap }: any): JSX.Element {
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
|
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
|
||||||
|
|
||||||
|
const graphData = { nodes, links };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ForceGraph2D
|
||||||
|
ref={fgRef}
|
||||||
|
cooldownTicks={100}
|
||||||
|
graphData={graphData}
|
||||||
|
linkLabel={getTooltip}
|
||||||
|
linkAutoColorBy={(d) => d.target}
|
||||||
|
linkDirectionalParticles="value"
|
||||||
|
linkDirectionalParticleSpeed={(d) => d.value}
|
||||||
|
nodeCanvasObject={(node, ctx) => {
|
||||||
|
const label = transformLabel(node.id);
|
||||||
|
const { fontSize } = node;
|
||||||
|
ctx.font = `${fontSize}px Roboto`;
|
||||||
|
const { width } = node;
|
||||||
|
|
||||||
|
ctx.fillStyle = node.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
|
||||||
|
ctx.fillText(label, node.x, node.y);
|
||||||
|
}}
|
||||||
|
onLinkHover={(node) => {
|
||||||
|
const tooltip = document.querySelector('.graph-tooltip');
|
||||||
|
if (tooltip && node) {
|
||||||
|
tooltip.innerHTML = getTooltip(node);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
nodePointerAreaPaint={(node, color, ctx) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fill();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Map);
|
@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
|
||||||
|
import useResourceAttribute from 'hooks/useResourceAttribute';
|
||||||
|
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { ForceGraph2D } from 'react-force-graph';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||||
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
|
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
|
||||||
@ -13,7 +14,7 @@ import { AppState } from 'store/reducers';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { GlobalTime } from 'types/actions/globalTime';
|
import { GlobalTime } from 'types/actions/globalTime';
|
||||||
|
|
||||||
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils';
|
import Map from './Map';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
.force-graph-container {
|
.force-graph-container {
|
||||||
@ -38,7 +39,10 @@ const Container = styled.div`
|
|||||||
interface ServiceMapProps extends RouteComponentProps<any> {
|
interface ServiceMapProps extends RouteComponentProps<any> {
|
||||||
serviceMap: ServiceMapStore;
|
serviceMap: ServiceMapStore;
|
||||||
globalTime: GlobalTime;
|
globalTime: GlobalTime;
|
||||||
getDetailedServiceMapItems: (time: GlobalTime) => void;
|
getDetailedServiceMapItems: (
|
||||||
|
time: GlobalTime,
|
||||||
|
queries: IResourceAttribute[],
|
||||||
|
) => void;
|
||||||
}
|
}
|
||||||
interface graphNode {
|
interface graphNode {
|
||||||
id: string;
|
id: string;
|
||||||
@ -60,17 +64,17 @@ export interface graphDataType {
|
|||||||
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||||
const fgRef = useRef();
|
const fgRef = useRef();
|
||||||
|
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
|
|
||||||
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
||||||
|
|
||||||
|
const { queries } = useResourceAttribute();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
/*
|
/*
|
||||||
Call the apis only when the route is loaded.
|
Call the apis only when the route is loaded.
|
||||||
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
||||||
*/
|
*/
|
||||||
getDetailedServiceMapItems(globalTime);
|
getDetailedServiceMapItems(globalTime, queries);
|
||||||
}, [globalTime, getDetailedServiceMapItems]);
|
}, [globalTime, getDetailedServiceMapItems, queries]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
|
fgRef.current && fgRef.current.d3Force('charge').strength(-400);
|
||||||
@ -83,51 +87,15 @@ function ServiceMap(props: ServiceMapProps): JSX.Element {
|
|||||||
if (!serviceMap.loading && serviceMap.items.length === 0) {
|
if (!serviceMap.loading && serviceMap.items.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
<ResourceAttributesFilter />
|
||||||
<Card>No Service Found</Card>
|
<Card>No Service Found</Card>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
|
|
||||||
const graphData = { nodes, links };
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<ForceGraph2D
|
<ResourceAttributesFilter />
|
||||||
ref={fgRef}
|
<Map fgRef={fgRef} serviceMap={serviceMap} />
|
||||||
cooldownTicks={100}
|
|
||||||
graphData={graphData}
|
|
||||||
linkLabel={getTooltip}
|
|
||||||
linkAutoColorBy={(d) => d.target}
|
|
||||||
linkDirectionalParticles="value"
|
|
||||||
linkDirectionalParticleSpeed={(d) => d.value}
|
|
||||||
nodeCanvasObject={(node, ctx) => {
|
|
||||||
const label = transformLabel(node.id);
|
|
||||||
const { fontSize } = node;
|
|
||||||
ctx.font = `${fontSize}px Roboto`;
|
|
||||||
const { width } = node;
|
|
||||||
|
|
||||||
ctx.fillStyle = node.color;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.fillStyle = isDarkMode ? '#ffffff' : '#000000';
|
|
||||||
ctx.fillText(label, node.x, node.y);
|
|
||||||
}}
|
|
||||||
onLinkHover={(node) => {
|
|
||||||
const tooltip = document.querySelector('.graph-tooltip');
|
|
||||||
if (tooltip && node) {
|
|
||||||
tooltip.innerHTML = getTooltip(node);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
nodePointerAreaPaint={(node, color, ctx) => {
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
|
|
||||||
ctx.fill();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import api from 'api';
|
import api from 'api';
|
||||||
|
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
|
||||||
|
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { GlobalTime } from 'types/actions/globalTime';
|
import { GlobalTime } from 'types/actions/globalTime';
|
||||||
|
|
||||||
@ -30,16 +32,17 @@ export interface ServiceMapLoading {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => async (
|
export const getDetailedServiceMapItems = (
|
||||||
dispatch: Dispatch,
|
globalTime: GlobalTime,
|
||||||
): Promise<void> => {
|
queries: IResourceAttribute[],
|
||||||
|
) => async (dispatch: Dispatch): Promise<void> => {
|
||||||
const start = `${globalTime.minTime}`;
|
const start = `${globalTime.minTime}`;
|
||||||
const end = `${globalTime.maxTime}`;
|
const end = `${globalTime.maxTime}`;
|
||||||
|
|
||||||
const serviceMapPayload = {
|
const serviceMapPayload = {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
tags: [],
|
tags: convertRawQueriesToTraceSelectedTags(queries),
|
||||||
};
|
};
|
||||||
const [dependencyGraphResponse] = await Promise.all([
|
const [dependencyGraphResponse] = await Promise.all([
|
||||||
api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload),
|
api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user