mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:48:59 +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 {
|
||||
dbSystemTags,
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
onViewTracePopupClick,
|
||||
} from './util';
|
||||
@ -25,10 +26,13 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
const tagFilterItems = useMemo(
|
||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
||||
() =>
|
||||
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||
[queries],
|
||||
);
|
||||
|
||||
const selectedTraceTags: string = useMemo(
|
||||
() =>
|
||||
JSON.stringify(
|
||||
|
@ -18,7 +18,11 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||
import { legend } from './constant';
|
||||
import { Button } from './styles';
|
||||
import { onGraphClickHandler, onViewTracePopupClick } from './util';
|
||||
import {
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
onViewTracePopupClick,
|
||||
} from './util';
|
||||
|
||||
function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
@ -27,7 +31,8 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
const tagFilterItems = useMemo(
|
||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
||||
() =>
|
||||
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||
[queries],
|
||||
);
|
||||
|
||||
|
@ -26,7 +26,11 @@ import {
|
||||
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
||||
import TopOperationsTable from '../TopOperationsTable';
|
||||
import { Button } from './styles';
|
||||
import { onGraphClickHandler, onViewTracePopupClick } from './util';
|
||||
import {
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
onViewTracePopupClick,
|
||||
} from './util';
|
||||
|
||||
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
||||
const { servicename } = useParams<{ servicename?: string }>();
|
||||
@ -67,7 +71,8 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
||||
);
|
||||
|
||||
const tagFilterItems = useMemo(
|
||||
() => resourceAttributesToTagFilterItems(queries) || [],
|
||||
() =>
|
||||
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||
[queries],
|
||||
);
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
||||
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll';
|
||||
import { Tags } from 'types/reducer/trace';
|
||||
|
||||
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,
|
||||
GetTagValues,
|
||||
OperatorSchema,
|
||||
resourceAttributesQueryToPromQL,
|
||||
} from './utils';
|
||||
|
||||
function ResourceProvider({ children }: Props): JSX.Element {
|
||||
@ -29,10 +28,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
getResourceAttributeQueriesFromURL(),
|
||||
);
|
||||
|
||||
const [promQLQuery, setpromQLQuery] = useState<string>(
|
||||
resourceAttributesQueryToPromQL(queries),
|
||||
);
|
||||
|
||||
const [optionsData, setOptionsData] = useState<OptionsData>({
|
||||
mode: undefined,
|
||||
options: [],
|
||||
@ -55,7 +50,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
: '',
|
||||
});
|
||||
setQueries(queries);
|
||||
setpromQLQuery(resourceAttributesQueryToPromQL(queries));
|
||||
},
|
||||
[pathname],
|
||||
);
|
||||
@ -144,7 +138,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
() => ({
|
||||
queries,
|
||||
staging,
|
||||
promQLQuery,
|
||||
handleClearAll,
|
||||
handleClose,
|
||||
handleBlur,
|
||||
@ -161,7 +154,6 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
handleClose,
|
||||
handleFocus,
|
||||
loading,
|
||||
promQLQuery,
|
||||
queries,
|
||||
staging,
|
||||
selectedQuery,
|
||||
|
@ -20,7 +20,6 @@ export interface OptionsData {
|
||||
export interface IResourceAttributeProps {
|
||||
queries: IResourceAttribute[];
|
||||
staging: string[];
|
||||
promQLQuery: string;
|
||||
handleClearAll: VoidFunction;
|
||||
handleClose: (id: string) => void;
|
||||
handleBlur: VoidFunction;
|
||||
|
@ -57,26 +57,6 @@ export const convertRawQueriesToTraceSelectedTags = (
|
||||
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 */
|
||||
export const resourceAttributesToTagFilterItems = (
|
||||
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 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 { ForceGraph2D } from 'react-force-graph';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { getDetailedServiceMapItems, ServiceMapStore } from 'store/actions';
|
||||
@ -13,7 +14,7 @@ import { AppState } from 'store/reducers';
|
||||
import styled from 'styled-components';
|
||||
import { GlobalTime } from 'types/actions/globalTime';
|
||||
|
||||
import { getGraphData, getTooltip, getZoomPx, transformLabel } from './utils';
|
||||
import Map from './Map';
|
||||
|
||||
const Container = styled.div`
|
||||
.force-graph-container {
|
||||
@ -38,7 +39,10 @@ const Container = styled.div`
|
||||
interface ServiceMapProps extends RouteComponentProps<any> {
|
||||
serviceMap: ServiceMapStore;
|
||||
globalTime: GlobalTime;
|
||||
getDetailedServiceMapItems: (time: GlobalTime) => void;
|
||||
getDetailedServiceMapItems: (
|
||||
time: GlobalTime,
|
||||
queries: IResourceAttribute[],
|
||||
) => void;
|
||||
}
|
||||
interface graphNode {
|
||||
id: string;
|
||||
@ -60,17 +64,17 @@ export interface graphDataType {
|
||||
function ServiceMap(props: ServiceMapProps): JSX.Element {
|
||||
const fgRef = useRef();
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const { getDetailedServiceMapItems, globalTime, serviceMap } = props;
|
||||
|
||||
const { queries } = useResourceAttribute();
|
||||
|
||||
useEffect(() => {
|
||||
/*
|
||||
Call the apis only when the route is loaded.
|
||||
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
||||
*/
|
||||
getDetailedServiceMapItems(globalTime);
|
||||
}, [globalTime, getDetailedServiceMapItems]);
|
||||
getDetailedServiceMapItems(globalTime, queries);
|
||||
}, [globalTime, getDetailedServiceMapItems, queries]);
|
||||
|
||||
useEffect(() => {
|
||||
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) {
|
||||
return (
|
||||
<Container>
|
||||
<ResourceAttributesFilter />
|
||||
<Card>No Service Found</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const { nodes, links } = getGraphData(serviceMap, isDarkMode);
|
||||
const graphData = { nodes, links };
|
||||
return (
|
||||
<Container>
|
||||
<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();
|
||||
}}
|
||||
/>
|
||||
<ResourceAttributesFilter />
|
||||
<Map fgRef={fgRef} serviceMap={serviceMap} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import api from 'api';
|
||||
import { IResourceAttribute } from 'hooks/useResourceAttribute/types';
|
||||
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
|
||||
import { Dispatch } from 'redux';
|
||||
import { GlobalTime } from 'types/actions/globalTime';
|
||||
|
||||
@ -30,16 +32,17 @@ export interface ServiceMapLoading {
|
||||
};
|
||||
}
|
||||
|
||||
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => async (
|
||||
dispatch: Dispatch,
|
||||
): Promise<void> => {
|
||||
export const getDetailedServiceMapItems = (
|
||||
globalTime: GlobalTime,
|
||||
queries: IResourceAttribute[],
|
||||
) => async (dispatch: Dispatch): Promise<void> => {
|
||||
const start = `${globalTime.minTime}`;
|
||||
const end = `${globalTime.maxTime}`;
|
||||
|
||||
const serviceMapPayload = {
|
||||
start,
|
||||
end,
|
||||
tags: [],
|
||||
tags: convertRawQueriesToTraceSelectedTags(queries),
|
||||
};
|
||||
const [dependencyGraphResponse] = await Promise.all([
|
||||
api.post<ServicesMapItem[]>(`/dependency_graph`, serviceMapPayload),
|
||||
|
Loading…
x
Reference in New Issue
Block a user