mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 05:25:57 +08:00
feat: resource attributes based filter for metrics (#1022)
* feat: resource attributes based filtering enabled
This commit is contained in:
parent
29c0b43481
commit
7948bca710
@ -1,4 +1,6 @@
|
|||||||
const apiV1 = '/api/v1/';
|
const apiV1 = '/api/v1/';
|
||||||
export const apiV2 = '/api/alertmanager';
|
|
||||||
|
export const apiV2 = '/api/v2/';
|
||||||
|
export const apiAlertManager = '/api/alertmanager';
|
||||||
|
|
||||||
export default apiV1;
|
export default apiV1;
|
||||||
|
@ -8,7 +8,7 @@ import { ENVIRONMENT } from 'constants/env';
|
|||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
import apiV1, { apiV2 } from './apiV1';
|
import apiV1, { apiAlertManager, apiV2 } from './apiV1';
|
||||||
import { Logout } from './utils';
|
import { Logout } from './utils';
|
||||||
|
|
||||||
const interceptorsResponse = (
|
const interceptorsResponse = (
|
||||||
@ -67,6 +67,10 @@ instance.interceptors.response.use(interceptorsResponse, interceptorRejected);
|
|||||||
instance.interceptors.request.use(interceptorsRequestResponse);
|
instance.interceptors.request.use(interceptorsRequestResponse);
|
||||||
|
|
||||||
export const AxiosAlertManagerInstance = axios.create({
|
export const AxiosAlertManagerInstance = axios.create({
|
||||||
|
baseURL: `${ENVIRONMENT.baseURL}${apiAlertManager}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ApiV2Instance = axios.create({
|
||||||
baseURL: `${ENVIRONMENT.baseURL}${apiV2}`,
|
baseURL: `${ENVIRONMENT.baseURL}${apiV2}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
47
frontend/src/api/metrics/getResourceAttributes.ts
Normal file
47
frontend/src/api/metrics/getResourceAttributes.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { ApiV2Instance as axios } from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import {
|
||||||
|
TagKeysPayloadProps,
|
||||||
|
TagValueProps,
|
||||||
|
TagValuesPayloadProps,
|
||||||
|
} from 'types/api/metrics/getResourceAttributes';
|
||||||
|
|
||||||
|
export const getResourceAttributesTagKeys = async (): Promise<
|
||||||
|
SuccessResponse<TagKeysPayloadProps> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
'/metrics/autocomplete/tagKey?metricName=signoz_calls_total&match=resource_',
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResourceAttributesTagValues = async (
|
||||||
|
props: TagValueProps,
|
||||||
|
): Promise<SuccessResponse<TagValuesPayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`/metrics/autocomplete/tagValue?metricName=signoz_calls_total&tagKey=${props}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
@ -8,9 +8,11 @@ const getService = async (
|
|||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.post(`/services`, {
|
||||||
`/services?&start=${props.start}&end=${props.end}`,
|
start: `${props.start}`,
|
||||||
);
|
end: `${props.end}`,
|
||||||
|
tags: props.selectedTags,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -8,9 +8,13 @@ const getServiceOverview = async (
|
|||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.post(`/service/overview`, {
|
||||||
`/service/overview?&start=${props.start}&end=${props.end}&service=${props.service}&step=${props.step}`,
|
start: `${props.start}`,
|
||||||
);
|
end: `${props.end}`,
|
||||||
|
service: props.service,
|
||||||
|
step: props.step,
|
||||||
|
tags: props.selectedTags,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -8,9 +8,12 @@ const getTopEndPoints = async (
|
|||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.post(`/service/top_endpoints`, {
|
||||||
`/service/top_endpoints?&start=${props.start}&end=${props.end}&service=${props.service}`,
|
start: `${props.start}`,
|
||||||
);
|
end: `${props.end}`,
|
||||||
|
service: props.service,
|
||||||
|
tags: props.selectedTags,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
18
frontend/src/constants/resourceAttributes.ts
Normal file
18
frontend/src/constants/resourceAttributes.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { OperatorValues } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
export const OperatorConversions: Array<{
|
||||||
|
label: string;
|
||||||
|
metricValue: string;
|
||||||
|
traceValue: OperatorValues;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
label: 'IN',
|
||||||
|
metricValue: '=~',
|
||||||
|
traceValue: 'in',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Not IN',
|
||||||
|
metricValue: '!~',
|
||||||
|
traceValue: 'not in',
|
||||||
|
},
|
||||||
|
];
|
@ -189,6 +189,7 @@ function SearchFilter({
|
|||||||
value={selectedValues}
|
value={selectedValues}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
|
showSearch
|
||||||
>
|
>
|
||||||
{optionsData.options &&
|
{optionsData.options &&
|
||||||
Array.isArray(optionsData.options) &&
|
Array.isArray(optionsData.options) &&
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { QueryChipContainer, QueryChipItem } from './styles';
|
||||||
|
import { IResourceAttributeQuery } from './types';
|
||||||
|
|
||||||
|
interface IQueryChipProps {
|
||||||
|
queryData: IResourceAttributeQuery;
|
||||||
|
onClose: (id: string) => void;
|
||||||
|
disabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function QueryChip({
|
||||||
|
queryData,
|
||||||
|
onClose,
|
||||||
|
disabled,
|
||||||
|
}: IQueryChipProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<QueryChipContainer>
|
||||||
|
<QueryChipItem>{convertMetricKeyToTrace(queryData.tagKey)}</QueryChipItem>
|
||||||
|
<QueryChipItem>{queryData.operator}</QueryChipItem>
|
||||||
|
<QueryChipItem
|
||||||
|
closable={!disabled}
|
||||||
|
onClose={(): void => {
|
||||||
|
if (!disabled) onClose(queryData.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{queryData.tagValue.join(', ')}
|
||||||
|
</QueryChipItem>
|
||||||
|
</QueryChipContainer>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
import { createMachine } from 'xstate';
|
||||||
|
|
||||||
|
export const ResourceAttributesFilterMachine =
|
||||||
|
/** @xstate-layout N4IgpgJg5mDOIC5QBECGsAWAjA9qgThAAQDKYBAxhkQIIB2xAYgJYA2ALmPgHQAqqUANJgAngGIAcgFEAGr0SgADjljN2zHHQUgAHogAcAFgAM3AOz6ATAEYAzJdsA2Y4cOWAnABoQIxAFpDR2tuQ319AFYTcKdbFycAX3jvNExcAmIySmp6JjZOHn4hUTFNACFWAFd8bWVVdU1tPQQzY1MXY2tDdzNHM3dHd0NvXwR7biMTa313S0i+63DE5PRsPEJScnwqWgYiFg4uPgFhcQAlKRIpeSQQWrUNLRumx3Czbg8TR0sbS31jfUcw38fW47gBHmm4XCVms3SWIBSq3SGyyO1yBx4AHlFFxUOwcPhJLJrkoVPcGk9ENYFuF3i5YR0wtEHECEAEgiEmV8zH1DLYzHZ4Yi0utMltsrt9vluNjcfjCWVKtUbnd6o9QE1rMYBtxbGFvsZ3NrZj1WdYOfotUZLX0XEFHEKViKMpttjk9nlDrL8HiCWJzpcSbcyWrGoh3NCQj0zK53P1ph1WeFLLqnJZ2s5vmZLA6kginWsXaj3VLDoUAGqoSpgEp0cpVGohh5hhDWDy0sz8zruakzamWVm-Qyg362V5-AZOayO1KFlHitEejFHKCV6v+i5XRt1ZuU1s52zjNOOaZfdOWIY+RDZ0Hc6ZmKEXqyLPPCudit2Sz08ACSEFYNbSHI27kuquiIOEjiONwjJgrM3RWJYZisgEIJgnYPTmuEdi2OaiR5nQOAQHA2hvsiH4Sui0qFCcIGhnuLSmP0YJuJ2xjJsmKELG8XZTK0tjdHG06vgW5GupRS7St6vrKqSO4UhqVL8TBWp8o4eqdl0A5Xmy3G6gK56-B4uERDOSKiuJi6lgUAhrhUYB0buimtrEKZBDYrxaS0OZca8+ltheybOI4hivGZzrzp+VGHH+AGOQp4EIHy+ghNYnawtG4TsbYvk8QKfHGAJfQ9uF76WSW37xWBTSGJ0qXpd0vRZdEKGPqC2YeO2-zfO4+HxEAA */
|
||||||
|
createMachine({
|
||||||
|
tsTypes: {} as import('./ResourceAttributesFilter.Machine.typegen').Typegen0,
|
||||||
|
initial: 'Idle',
|
||||||
|
states: {
|
||||||
|
TagKey: {
|
||||||
|
on: {
|
||||||
|
NEXT: {
|
||||||
|
actions: 'onSelectOperator',
|
||||||
|
target: 'Operator',
|
||||||
|
},
|
||||||
|
onBlur: {
|
||||||
|
actions: 'onBlurPurge',
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
RESET: {
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Operator: {
|
||||||
|
on: {
|
||||||
|
NEXT: {
|
||||||
|
actions: 'onSelectTagValue',
|
||||||
|
target: 'TagValue',
|
||||||
|
},
|
||||||
|
onBlur: {
|
||||||
|
actions: 'onBlurPurge',
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
RESET: {
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TagValue: {
|
||||||
|
on: {
|
||||||
|
onBlur: {
|
||||||
|
actions: ['onValidateQuery', 'onBlurPurge'],
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
RESET: {
|
||||||
|
target: 'Idle',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Idle: {
|
||||||
|
on: {
|
||||||
|
NEXT: {
|
||||||
|
actions: 'onSelectTagKey',
|
||||||
|
description: 'Select Category',
|
||||||
|
target: 'TagKey',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: 'Dashboard Search And Filter',
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
// This file was automatically generated. Edits will be overwritten
|
||||||
|
|
||||||
|
export interface Typegen0 {
|
||||||
|
'@@xstate/typegen': true;
|
||||||
|
eventsCausingActions: {
|
||||||
|
onSelectOperator: 'NEXT';
|
||||||
|
onBlurPurge: 'onBlur';
|
||||||
|
onSelectTagValue: 'NEXT';
|
||||||
|
onValidateQuery: 'onBlur';
|
||||||
|
onSelectTagKey: 'NEXT';
|
||||||
|
};
|
||||||
|
internalEvents: {
|
||||||
|
'xstate.init': { type: 'xstate.init' };
|
||||||
|
};
|
||||||
|
invokeSrcNameMap: {};
|
||||||
|
missingImplementations: {
|
||||||
|
actions:
|
||||||
|
| 'onSelectOperator'
|
||||||
|
| 'onBlurPurge'
|
||||||
|
| 'onSelectTagValue'
|
||||||
|
| 'onValidateQuery'
|
||||||
|
| 'onSelectTagKey';
|
||||||
|
services: never;
|
||||||
|
guards: never;
|
||||||
|
delays: never;
|
||||||
|
};
|
||||||
|
eventsCausingServices: {};
|
||||||
|
eventsCausingGuards: {};
|
||||||
|
eventsCausingDelays: {};
|
||||||
|
matchesStates: 'TagKey' | 'Operator' | 'TagValue' | 'Idle';
|
||||||
|
tags: never;
|
||||||
|
}
|
@ -0,0 +1,222 @@
|
|||||||
|
import { CloseCircleFilled } from '@ant-design/icons';
|
||||||
|
import { useMachine } from '@xstate/react';
|
||||||
|
import { Button, Select, Spin } from 'antd';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
|
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
|
||||||
|
import { map } from 'lodash-es';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { ResetInitialData } from 'store/actions/metrics/resetInitialData';
|
||||||
|
import { SetResourceAttributeQueries } from 'store/actions/metrics/setResourceAttributeQueries';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import QueryChip from './QueryChip';
|
||||||
|
import { ResourceAttributesFilterMachine } from './ResourceAttributesFilter.Machine';
|
||||||
|
import { QueryChipItem, SearchContainer } from './styles';
|
||||||
|
import { IOption, IResourceAttributeQuery } from './types';
|
||||||
|
import { createQuery, GetTagKeys, GetTagValues, OperatorSchema } from './utils';
|
||||||
|
|
||||||
|
function ResourceAttributesFilter(): JSX.Element | null {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [disabled, setDisabled] = useState(
|
||||||
|
!(history.location.pathname === ROUTES.APPLICATION),
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unListen = history.listen(({ pathname }) => {
|
||||||
|
setDisabled(!(pathname === ROUTES.APPLICATION));
|
||||||
|
});
|
||||||
|
return (): void => {
|
||||||
|
if (!history.location.pathname.startsWith(`${ROUTES.APPLICATION}/`)) {
|
||||||
|
dispatch(ResetInitialData());
|
||||||
|
}
|
||||||
|
unListen();
|
||||||
|
};
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
|
||||||
|
(state) => state.metrics,
|
||||||
|
);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
||||||
|
const [staging, setStaging] = useState<string[]>([]);
|
||||||
|
const [queries, setQueries] = useState<IResourceAttributeQuery[]>([]);
|
||||||
|
const [optionsData, setOptionsData] = useState<{
|
||||||
|
mode: undefined | 'tags' | 'multiple';
|
||||||
|
options: IOption[];
|
||||||
|
}>({
|
||||||
|
mode: undefined,
|
||||||
|
options: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const dispatchQueries = (updatedQueries: IResourceAttributeQuery[]): void => {
|
||||||
|
dispatch(SetResourceAttributeQueries(updatedQueries));
|
||||||
|
};
|
||||||
|
const handleLoading = (isLoading: boolean): void => {
|
||||||
|
setLoading(isLoading);
|
||||||
|
if (isLoading) {
|
||||||
|
setOptionsData({ mode: undefined, options: [] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const [state, send] = useMachine(ResourceAttributesFilterMachine, {
|
||||||
|
actions: {
|
||||||
|
onSelectTagKey: () => {
|
||||||
|
handleLoading(true);
|
||||||
|
GetTagKeys()
|
||||||
|
.then((tagKeys) => setOptionsData({ options: tagKeys, mode: undefined }))
|
||||||
|
.finally(() => {
|
||||||
|
handleLoading(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSelectOperator: () => {
|
||||||
|
setOptionsData({ options: OperatorSchema, mode: undefined });
|
||||||
|
},
|
||||||
|
onSelectTagValue: () => {
|
||||||
|
handleLoading(true);
|
||||||
|
|
||||||
|
GetTagValues(staging[0])
|
||||||
|
.then((tagValuesOptions) =>
|
||||||
|
setOptionsData({ options: tagValuesOptions, mode: 'multiple' }),
|
||||||
|
)
|
||||||
|
.finally(() => {
|
||||||
|
handleLoading(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onBlurPurge: () => {
|
||||||
|
setSelectedValues([]);
|
||||||
|
setStaging([]);
|
||||||
|
},
|
||||||
|
onValidateQuery: (): void => {
|
||||||
|
if (staging.length < 2 || selectedValues.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedQuery = createQuery([...staging, selectedValues]);
|
||||||
|
if (generatedQuery) {
|
||||||
|
dispatchQueries([...queries, generatedQuery]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setQueries(resourceAttributeQueries);
|
||||||
|
}, [resourceAttributeQueries]);
|
||||||
|
|
||||||
|
const handleFocus = (): void => {
|
||||||
|
if (state.value === 'Idle') {
|
||||||
|
send('NEXT');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = (): void => {
|
||||||
|
send('onBlur');
|
||||||
|
};
|
||||||
|
const handleChange = (value: never): void => {
|
||||||
|
if (!optionsData.mode) {
|
||||||
|
setStaging((prevStaging) => [...prevStaging, value]);
|
||||||
|
setSelectedValues([]);
|
||||||
|
send('NEXT');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedValues([...value]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = (id: string): void => {
|
||||||
|
dispatchQueries(queries.filter((queryData) => queryData.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAll = (): void => {
|
||||||
|
send('RESET');
|
||||||
|
dispatchQueries([]);
|
||||||
|
setStaging([]);
|
||||||
|
setSelectedValues([]);
|
||||||
|
};
|
||||||
|
const disabledAndEmpty = !!(
|
||||||
|
!queries.length &&
|
||||||
|
!staging.length &&
|
||||||
|
!selectedValues.length &&
|
||||||
|
disabled
|
||||||
|
);
|
||||||
|
const disabledOrEmpty = !!(
|
||||||
|
queries.length ||
|
||||||
|
staging.length ||
|
||||||
|
selectedValues.length ||
|
||||||
|
disabled
|
||||||
|
);
|
||||||
|
|
||||||
|
if (disabledAndEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SearchContainer isDarkMode={isDarkMode} disabled={disabled}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: disabled ? '100%' : '70%',
|
||||||
|
display: 'flex',
|
||||||
|
overflowX: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{map(
|
||||||
|
queries,
|
||||||
|
(query): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<QueryChip
|
||||||
|
disabled={disabled}
|
||||||
|
key={query.id}
|
||||||
|
queryData={query}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
{map(staging, (item, idx) => {
|
||||||
|
return (
|
||||||
|
<QueryChipItem key={uuid()}>
|
||||||
|
{idx === 0 ? convertMetricKeyToTrace(item) : item}
|
||||||
|
</QueryChipItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{!disabled && (
|
||||||
|
<Select
|
||||||
|
placeholder={
|
||||||
|
disabledOrEmpty ? '' : 'Search and Filter based on resource attributes.'
|
||||||
|
}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={handleChange}
|
||||||
|
bordered={false}
|
||||||
|
value={selectedValues as never}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
options={optionsData.options}
|
||||||
|
mode={optionsData?.mode}
|
||||||
|
showArrow={false}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
notFoundContent={
|
||||||
|
loading ? (
|
||||||
|
<span>
|
||||||
|
<Spin size="small" /> Loading...{' '}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span>No data available for {state.value}</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(queries.length || staging.length || selectedValues.length) && !disabled ? (
|
||||||
|
<Button onClick={handleClearAll} icon={<CloseCircleFilled />} type="text" />
|
||||||
|
) : null}
|
||||||
|
</SearchContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ResourceAttributesFilter;
|
@ -0,0 +1,32 @@
|
|||||||
|
import { grey } from '@ant-design/colors';
|
||||||
|
import { Tag } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const SearchContainer = styled.div<{
|
||||||
|
isDarkMode: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
}>`
|
||||||
|
background: ${({ isDarkMode }): string => (isDarkMode ? '#000' : '#fff')};
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.2rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
border: 1px solid #ccc5;
|
||||||
|
${({ disabled }): string => (disabled ? `cursor: not-allowed;` : '')}
|
||||||
|
`;
|
||||||
|
export const QueryChipContainer = styled.span`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
&:hover {
|
||||||
|
& > * {
|
||||||
|
background: ${grey.primary}44;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QueryChipItem = styled(Tag)`
|
||||||
|
margin-right: 0.1rem;
|
||||||
|
`;
|
@ -0,0 +1,11 @@
|
|||||||
|
export interface IOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IResourceAttributeQuery {
|
||||||
|
id: string;
|
||||||
|
tagKey: string;
|
||||||
|
operator: string;
|
||||||
|
tagValue: string[];
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
getResourceAttributesTagKeys,
|
||||||
|
getResourceAttributesTagValues,
|
||||||
|
} from 'api/metrics/getResourceAttributes';
|
||||||
|
import { OperatorConversions } from 'constants/resourceAttributes';
|
||||||
|
import { convertMetricKeyToTrace } from 'lib/resourceAttributes';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
import { IOption, IResourceAttributeQuery } from './types';
|
||||||
|
|
||||||
|
export const OperatorSchema: IOption[] = OperatorConversions.map(
|
||||||
|
(operator) => ({
|
||||||
|
label: operator.label,
|
||||||
|
value: operator.label,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const GetTagKeys = async (): Promise<IOption[]> => {
|
||||||
|
// if (TagKeysCache) {
|
||||||
|
// return new Promise((resolve) => {
|
||||||
|
// resolve(TagKeysCache);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
const { payload } = await getResourceAttributesTagKeys();
|
||||||
|
if (!payload || !payload?.data) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return payload.data.map((tagKey: string) => ({
|
||||||
|
label: convertMetricKeyToTrace(tagKey),
|
||||||
|
value: tagKey,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GetTagValues = async (tagKey: string): Promise<IOption[]> => {
|
||||||
|
const { payload } = await getResourceAttributesTagValues(tagKey);
|
||||||
|
|
||||||
|
if (!payload || !payload?.data) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return payload.data.filter(Boolean).map((tagValue: string) => ({
|
||||||
|
label: tagValue,
|
||||||
|
value: tagValue,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createQuery = (
|
||||||
|
selectedItems: Array<string | string[]> = [],
|
||||||
|
): IResourceAttributeQuery | null => {
|
||||||
|
if (selectedItems.length === 3) {
|
||||||
|
return {
|
||||||
|
id: uuid().slice(0, 8),
|
||||||
|
tagKey: selectedItems[0] as string,
|
||||||
|
operator: selectedItems[1] as string,
|
||||||
|
tagValue: selectedItems[2] as string[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
@ -6,6 +6,7 @@ import FullView from 'container/GridGraphLayout/Graph/FullView';
|
|||||||
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
|
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
|
||||||
import { colors } from 'lib/getRandomColor';
|
import { colors } from 'lib/getRandomColor';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@ -21,8 +22,15 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
const selectedTimeStamp = useRef(0);
|
const selectedTimeStamp = useRef(0);
|
||||||
|
|
||||||
const { topEndPoints, serviceOverview } = useSelector<AppState, MetricReducer>(
|
const {
|
||||||
(state) => state.metrics,
|
topEndPoints,
|
||||||
|
serviceOverview,
|
||||||
|
resourceAttributePromQLQuery,
|
||||||
|
resourceAttributeQueries,
|
||||||
|
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
|
||||||
|
|
||||||
|
const selectedTraceTags: string = JSON.stringify(
|
||||||
|
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries, 'array') || [],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTracePopupClick = (timestamp: number): void => {
|
const onTracePopupClick = (timestamp: number): void => {
|
||||||
@ -36,7 +44,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
history.replace(
|
history.replace(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,7 +96,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
history.replace(
|
history.replace(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=[]&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,7 +193,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_latency_count{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[2m]))`,
|
query: `sum(rate(signoz_latency_count{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"${resourceAttributePromQLQuery}}[2m]))`,
|
||||||
legend: 'Requests',
|
legend: 'Requests',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
@ -219,7 +227,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max(sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."}[1m]))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"}[1m]))) < 1000 OR vector(0)`,
|
query: `max(sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", status_code="STATUS_CODE_ERROR"${resourceAttributePromQLQuery}}[1m]) OR rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER", http_status_code=~"5.."${resourceAttributePromQLQuery}}[1m]))*100/sum(rate(signoz_calls_total{service_name="${servicename}", span_kind="SPAN_KIND_SERVER"${resourceAttributePromQLQuery}}[1m]))) < 1000 OR vector(0)`,
|
||||||
legend: 'Error Percentage',
|
legend: 'Error Percentage',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { Col } from 'antd';
|
import { Col } from 'antd';
|
||||||
import FullView from 'container/GridGraphLayout/Graph/FullView';
|
import FullView from 'container/GridGraphLayout/Graph/FullView';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
|
||||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||||
|
|
||||||
function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
|
const { resourceAttributePromQLQuery } = useSelector<AppState, MetricReducer>(
|
||||||
|
(state) => state.metrics,
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
@ -20,7 +25,7 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_db_latency_count{service_name="${servicename}"}[1m])) by (db_system)`,
|
query: `sum(rate(signoz_db_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[1m])) by (db_system)`,
|
||||||
legend: '{{db_system}}',
|
legend: '{{db_system}}',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
@ -39,7 +44,7 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_db_latency_sum{service_name="${servicename}"}[5m]))/sum(rate(signoz_db_latency_count{service_name="${servicename}"}[5m]))`,
|
query: `sum(rate(signoz_db_latency_sum{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m]))/sum(rate(signoz_db_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m]))`,
|
||||||
legend: '',
|
legend: '',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { Col } from 'antd';
|
import { Col } from 'antd';
|
||||||
import FullView from 'container/GridGraphLayout/Graph/FullView';
|
import FullView from 'container/GridGraphLayout/Graph/FullView';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
|
||||||
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
import { Card, GraphContainer, GraphTitle, Row } from '../styles';
|
||||||
|
|
||||||
function External({ getWidget }: ExternalProps): JSX.Element {
|
function External({ getWidget }: ExternalProps): JSX.Element {
|
||||||
const { servicename } = useParams<{ servicename?: string }>();
|
const { servicename } = useParams<{ servicename?: string }>();
|
||||||
|
const { resourceAttributePromQLQuery } = useSelector<AppState, MetricReducer>(
|
||||||
|
(state) => state.metrics,
|
||||||
|
);
|
||||||
const legend = '{{http_url}}';
|
const legend = '{{http_url}}';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -23,7 +28,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"${resourceAttributePromQLQuery}}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."${resourceAttributePromQLQuery}}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
||||||
legend,
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
@ -42,7 +47,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"}[5m]))/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m]))`,
|
query: `sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m]))/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m]))`,
|
||||||
legend: 'Average Duration',
|
legend: 'Average Duration',
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
@ -63,7 +68,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url)`,
|
query: `sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m])) by (http_url)`,
|
||||||
legend,
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
@ -82,7 +87,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `(sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"}[5m])) by (http_url))/(sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[5m])) by (http_url))`,
|
query: `(sum(rate(signoz_external_call_latency_sum{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m])) by (http_url))/(sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[5m])) by (http_url))`,
|
||||||
legend,
|
legend,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
|
@ -3,16 +3,25 @@ import { ColumnsType } from 'antd/lib/table';
|
|||||||
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 { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
|
||||||
function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
|
||||||
|
(state) => state.metrics,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedTraceTags: string = JSON.stringify(
|
||||||
|
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [],
|
||||||
|
);
|
||||||
|
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
|
|
||||||
@ -33,7 +42,7 @@ function TopEndpointsTable(props: TopEndpointsTableProps): JSX.Element {
|
|||||||
history.push(
|
history.push(
|
||||||
`${
|
`${
|
||||||
ROUTES.TRACE
|
ROUTES.TRACE
|
||||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=[]&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { Tabs } from 'antd';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
import ResourceAttributesFilter from './ResourceAttributesFilter';
|
||||||
import Application from './Tabs/Application';
|
import Application from './Tabs/Application';
|
||||||
import DBCall from './Tabs/DBCall';
|
import DBCall from './Tabs/DBCall';
|
||||||
import External from './Tabs/External';
|
import External from './Tabs/External';
|
||||||
@ -31,6 +32,8 @@ function ServiceMetrics(): JSX.Element {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<ResourceAttributesFilter />
|
||||||
<Tabs defaultActiveKey="1">
|
<Tabs defaultActiveKey="1">
|
||||||
<TabPane animated destroyInactiveTabPane tab="Application Metrics" key="1">
|
<TabPane animated destroyInactiveTabPane tab="Application Metrics" key="1">
|
||||||
<Application getWidget={getWidget} />
|
<Application getWidget={getWidget} />
|
||||||
@ -44,6 +47,7 @@ function ServiceMetrics(): JSX.Element {
|
|||||||
<DBCall getWidget={getWidget} />
|
<DBCall getWidget={getWidget} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { SKIP_ONBOARDING } from 'constants/onboarding';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { ServicesList } from 'types/api/metrics/getService';
|
import { ServicesList } from 'types/api/metrics/getService';
|
||||||
import MetricReducer from 'types/reducer/metrics';
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
@ -14,6 +14,7 @@ import SkipBoardModal from './SkipOnBoardModal';
|
|||||||
import { Container, Name } from './styles';
|
import { Container, Name } from './styles';
|
||||||
|
|
||||||
function Metrics(): JSX.Element {
|
function Metrics(): JSX.Element {
|
||||||
|
const { search } = useLocation();
|
||||||
const [skipOnboarding, setSkipOnboarding] = useState(
|
const [skipOnboarding, setSkipOnboarding] = useState(
|
||||||
localStorageGet(SKIP_ONBOARDING) === 'true',
|
localStorageGet(SKIP_ONBOARDING) === 'true',
|
||||||
);
|
);
|
||||||
@ -41,9 +42,8 @@ function Metrics(): JSX.Element {
|
|||||||
title: 'Application',
|
title: 'Application',
|
||||||
dataIndex: 'serviceName',
|
dataIndex: 'serviceName',
|
||||||
key: 'serviceName',
|
key: 'serviceName',
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
render: (text: string): JSX.Element => (
|
render: (text: string): JSX.Element => (
|
||||||
<Link to={`${ROUTES.APPLICATION}/${text}`}>
|
<Link to={`${ROUTES.APPLICATION}/${text}${search}`}>
|
||||||
<Name>{text}</Name>
|
<Name>{text}</Name>
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
|
71
frontend/src/lib/resourceAttributes.ts
Normal file
71
frontend/src/lib/resourceAttributes.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { OperatorConversions } from 'constants/resourceAttributes';
|
||||||
|
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
|
||||||
|
import { OperatorValues, Tags, TagsAPI } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resource_x_y -> x.y
|
||||||
|
*/
|
||||||
|
export const convertMetricKeyToTrace = (key: string): string => {
|
||||||
|
const splittedKey = key.split('_');
|
||||||
|
|
||||||
|
if (splittedKey.length <= 1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return splittedKey.splice(1).join('.');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x.y -> resource_x_y
|
||||||
|
*/
|
||||||
|
export const convertTraceKeyToMetric = (key: string): string => {
|
||||||
|
const splittedKey = key.split('.');
|
||||||
|
return `resource_${splittedKey.join('_')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertOperatorLabelToMetricOperator = (label: string): string => {
|
||||||
|
return (
|
||||||
|
OperatorConversions.find((operator) => operator.label === label)
|
||||||
|
?.metricValue || ''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertOperatorLabelToTraceOperator = (
|
||||||
|
label: string,
|
||||||
|
): OperatorValues => {
|
||||||
|
return OperatorConversions.find((operator) => operator.label === label)
|
||||||
|
?.traceValue as OperatorValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertRawQueriesToTraceSelectedTags = (
|
||||||
|
queries: IResourceAttributeQuery[],
|
||||||
|
keyType: 'string' | 'array' = 'string',
|
||||||
|
): Tags[] | TagsAPI[] => {
|
||||||
|
return queries.map((query) => ({
|
||||||
|
Key:
|
||||||
|
keyType === 'array'
|
||||||
|
? [convertMetricKeyToTrace(query.tagKey)]
|
||||||
|
: (convertMetricKeyToTrace(query.tagKey) as never),
|
||||||
|
Operator: convertOperatorLabelToTraceOperator(query.operator),
|
||||||
|
Values: query.tagValue,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts Resource Attribute Queries to PromQL query string
|
||||||
|
*/
|
||||||
|
export const resourceAttributesQueryToPromQL = (
|
||||||
|
queries: IResourceAttributeQuery[],
|
||||||
|
): string => {
|
||||||
|
let parsedQueryString = '';
|
||||||
|
|
||||||
|
if (Array.isArray(queries))
|
||||||
|
queries.forEach((query) => {
|
||||||
|
parsedQueryString += `, ${
|
||||||
|
query.tagKey
|
||||||
|
}${convertOperatorLabelToMetricOperator(
|
||||||
|
query.operator,
|
||||||
|
)}"${query.tagValue.join('|')}"`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return parsedQueryString;
|
||||||
|
};
|
@ -1,7 +1,8 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import MetricsApplicationContainer from 'container/MetricsApplication';
|
import MetricsApplicationContainer from 'container/MetricsApplication';
|
||||||
import React, { useEffect } from 'react';
|
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
|
||||||
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -10,16 +11,13 @@ import {
|
|||||||
GetInitialData,
|
GetInitialData,
|
||||||
GetInitialDataProps,
|
GetInitialDataProps,
|
||||||
} from 'store/actions/metrics/getInitialData';
|
} from 'store/actions/metrics/getInitialData';
|
||||||
import { ResetInitialData } from 'store/actions/metrics/resetInitialData';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import MetricReducer from 'types/reducer/metrics';
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
function MetricsApplication({
|
function MetricsApplication({ getInitialData }: MetricsProps): JSX.Element {
|
||||||
getInitialData,
|
|
||||||
resetInitialData,
|
|
||||||
}: MetricsProps): JSX.Element {
|
|
||||||
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
@ -30,19 +28,27 @@ function MetricsApplication({
|
|||||||
|
|
||||||
const { servicename } = useParams<ServiceProps>();
|
const { servicename } = useParams<ServiceProps>();
|
||||||
|
|
||||||
|
const { resourceAttributeQueries } = useSelector<AppState, MetricReducer>(
|
||||||
|
(state) => state.metrics,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedTags = useMemo(
|
||||||
|
() =>
|
||||||
|
(convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) as Tags[]) ||
|
||||||
|
[],
|
||||||
|
[resourceAttributeQueries],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (servicename !== undefined) {
|
if (servicename !== undefined) {
|
||||||
getInitialData({
|
getInitialData({
|
||||||
serviceName: servicename,
|
serviceName: servicename,
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
|
selectedTags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, [servicename, getInitialData, maxTime, minTime, selectedTags]);
|
||||||
return (): void => {
|
|
||||||
resetInitialData();
|
|
||||||
};
|
|
||||||
}, [servicename, getInitialData, resetInitialData, maxTime, minTime]);
|
|
||||||
|
|
||||||
if (metricsApplicationLoading) {
|
if (metricsApplicationLoading) {
|
||||||
return <Spinner tip="Loading..." />;
|
return <Spinner tip="Loading..." />;
|
||||||
@ -57,7 +63,6 @@ function MetricsApplication({
|
|||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getInitialData: (props: GetInitialDataProps) => void;
|
getInitialData: (props: GetInitialDataProps) => void;
|
||||||
resetInitialData: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceProps {
|
interface ServiceProps {
|
||||||
@ -68,7 +73,6 @@ const mapDispatchToProps = (
|
|||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||||
): DispatchProps => ({
|
): DispatchProps => ({
|
||||||
getInitialData: bindActionCreators(GetInitialData, dispatch),
|
getInitialData: bindActionCreators(GetInitialData, dispatch),
|
||||||
resetInitialData: bindActionCreators(ResetInitialData, dispatch),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type MetricsProps = DispatchProps;
|
type MetricsProps = DispatchProps;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||||
|
import ResourceAttributesFilter from 'container/MetricsApplication/ResourceAttributesFilter';
|
||||||
import MetricTable from 'container/MetricsTable';
|
import MetricTable from 'container/MetricsTable';
|
||||||
import React, { useEffect } from 'react';
|
import { convertRawQueriesToTraceSelectedTags } from 'lib/resourceAttributes';
|
||||||
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
@ -11,16 +13,24 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import MetricReducer from 'types/reducer/metrics';
|
import MetricReducer from 'types/reducer/metrics';
|
||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
function Metrics({ getService }: MetricsProps): JSX.Element {
|
function Metrics({ getService }: MetricsProps): JSX.Element {
|
||||||
const { minTime, maxTime, loading, selectedTime } = useSelector<
|
const { minTime, maxTime, loading, selectedTime } = useSelector<
|
||||||
AppState,
|
AppState,
|
||||||
GlobalReducer
|
GlobalReducer
|
||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
const { services } = useSelector<AppState, MetricReducer>(
|
const { services, resourceAttributeQueries } = useSelector<
|
||||||
(state) => state.metrics,
|
AppState,
|
||||||
);
|
MetricReducer
|
||||||
|
>((state) => state.metrics);
|
||||||
|
|
||||||
|
const selectedTags = useMemo(
|
||||||
|
() =>
|
||||||
|
(convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) as Tags[]) ||
|
||||||
|
[],
|
||||||
|
[resourceAttributeQueries],
|
||||||
|
);
|
||||||
const isSkipped = getLocalStorageKey(SKIP_ONBOARDING) === 'true';
|
const isSkipped = getLocalStorageKey(SKIP_ONBOARDING) === 'true';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -28,9 +38,10 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
|
|||||||
getService({
|
getService({
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
|
selectedTags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [getService, loading, maxTime, minTime]);
|
}, [getService, loading, maxTime, minTime, selectedTags]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timeInterval: NodeJS.Timeout;
|
let timeInterval: NodeJS.Timeout;
|
||||||
@ -40,6 +51,7 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
|
|||||||
getService({
|
getService({
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
|
selectedTags,
|
||||||
});
|
});
|
||||||
}, 50000);
|
}, 50000);
|
||||||
}
|
}
|
||||||
@ -47,13 +59,27 @@ function Metrics({ getService }: MetricsProps): JSX.Element {
|
|||||||
return (): void => {
|
return (): void => {
|
||||||
clearInterval(timeInterval);
|
clearInterval(timeInterval);
|
||||||
};
|
};
|
||||||
}, [getService, isSkipped, loading, maxTime, minTime, services, selectedTime]);
|
}, [
|
||||||
|
getService,
|
||||||
|
isSkipped,
|
||||||
|
loading,
|
||||||
|
maxTime,
|
||||||
|
minTime,
|
||||||
|
services,
|
||||||
|
selectedTime,
|
||||||
|
selectedTags,
|
||||||
|
]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spinner tip="Loading..." />;
|
return <Spinner tip="Loading..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <MetricTable />;
|
return (
|
||||||
|
<>
|
||||||
|
<ResourceAttributesFilter />
|
||||||
|
<MetricTable />
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
|
@ -12,6 +12,7 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { Props } from 'types/api/metrics/getDBOverview';
|
import { Props } from 'types/api/metrics/getDBOverview';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export const GetInitialData = (
|
export const GetInitialData = (
|
||||||
props: GetInitialDataProps,
|
props: GetInitialDataProps,
|
||||||
@ -64,11 +65,13 @@ export const GetInitialData = (
|
|||||||
service: props.serviceName,
|
service: props.serviceName,
|
||||||
start: minTime,
|
start: minTime,
|
||||||
step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }),
|
step: getStep({ start: minTime, end: maxTime, inputFormat: 'ns' }),
|
||||||
|
selectedTags: props.selectedTags,
|
||||||
}),
|
}),
|
||||||
getTopEndPoints({
|
getTopEndPoints({
|
||||||
end: maxTime,
|
end: maxTime,
|
||||||
service: props.serviceName,
|
service: props.serviceName,
|
||||||
start: minTime,
|
start: minTime,
|
||||||
|
selectedTags: props.selectedTags,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -121,4 +124,5 @@ export interface GetInitialDataProps {
|
|||||||
serviceName: Props['service'];
|
serviceName: Props['service'];
|
||||||
maxTime: GlobalReducer['maxTime'];
|
maxTime: GlobalReducer['maxTime'];
|
||||||
minTime: GlobalReducer['minTime'];
|
minTime: GlobalReducer['minTime'];
|
||||||
|
selectedTags: Tags[];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { Dispatch } from 'redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export const GetService = (
|
export const GetService = (
|
||||||
props: GetServiceProps,
|
props: GetServiceProps,
|
||||||
@ -32,6 +33,7 @@ export const GetService = (
|
|||||||
const response = await getService({
|
const response = await getService({
|
||||||
end: maxTime,
|
end: maxTime,
|
||||||
start: minTime,
|
start: minTime,
|
||||||
|
selectedTags: props.selectedTags,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
@ -61,4 +63,5 @@ export const GetService = (
|
|||||||
export type GetServiceProps = {
|
export type GetServiceProps = {
|
||||||
minTime: GlobalReducer['minTime'];
|
minTime: GlobalReducer['minTime'];
|
||||||
maxTime: GlobalReducer['maxTime'];
|
maxTime: GlobalReducer['maxTime'];
|
||||||
|
selectedTags: Tags[];
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
|
||||||
|
import { decode, encode } from 'js-base64';
|
||||||
|
import history from 'lib/history';
|
||||||
|
import { resourceAttributesQueryToPromQL } from 'lib/resourceAttributes';
|
||||||
|
import { SET_RESOURCE_ATTRIBUTE_QUERIES } from 'types/actions/metrics';
|
||||||
|
|
||||||
|
export function GetResourceAttributeQueriesFromURL():
|
||||||
|
| IResourceAttributeQuery[]
|
||||||
|
| null {
|
||||||
|
const resourceAttributeQuery = new URLSearchParams(
|
||||||
|
history.location.search,
|
||||||
|
).get('resourceAttribute');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (resourceAttributeQuery) {
|
||||||
|
return JSON.parse(
|
||||||
|
decode(resourceAttributeQuery),
|
||||||
|
) as IResourceAttributeQuery[];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SetResourceAttributeQueriesFromURL = (
|
||||||
|
queries: IResourceAttributeQuery[],
|
||||||
|
): void => {
|
||||||
|
history.push({
|
||||||
|
pathname: history.location.pathname,
|
||||||
|
search:
|
||||||
|
queries && queries.length
|
||||||
|
? `?resourceAttribute=${encode(JSON.stringify(queries))}`
|
||||||
|
: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const SetResourceAttributeQueries = (
|
||||||
|
queries: IResourceAttributeQuery[],
|
||||||
|
): {
|
||||||
|
type: typeof SET_RESOURCE_ATTRIBUTE_QUERIES;
|
||||||
|
payload: {
|
||||||
|
queries: IResourceAttributeQuery[];
|
||||||
|
promQLQuery: string;
|
||||||
|
};
|
||||||
|
} => {
|
||||||
|
SetResourceAttributeQueriesFromURL(queries);
|
||||||
|
return {
|
||||||
|
type: SET_RESOURCE_ATTRIBUTE_QUERIES,
|
||||||
|
payload: {
|
||||||
|
queries,
|
||||||
|
promQLQuery: resourceAttributesQueryToPromQL(queries),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -1,3 +1,5 @@
|
|||||||
|
import { resourceAttributesQueryToPromQL } from 'lib/resourceAttributes';
|
||||||
|
import { GetResourceAttributeQueriesFromURL } from 'store/actions/metrics/setResourceAttributeQueries';
|
||||||
import {
|
import {
|
||||||
GET_INITIAL_APPLICATION_ERROR,
|
GET_INITIAL_APPLICATION_ERROR,
|
||||||
GET_INITIAL_APPLICATION_LOADING,
|
GET_INITIAL_APPLICATION_LOADING,
|
||||||
@ -7,6 +9,7 @@ import {
|
|||||||
GET_SERVICE_LIST_SUCCESS,
|
GET_SERVICE_LIST_SUCCESS,
|
||||||
MetricsActions,
|
MetricsActions,
|
||||||
RESET_INITIAL_APPLICATION_DATA,
|
RESET_INITIAL_APPLICATION_DATA,
|
||||||
|
SET_RESOURCE_ATTRIBUTE_QUERIES,
|
||||||
} from 'types/actions/metrics';
|
} from 'types/actions/metrics';
|
||||||
import InitialValueTypes from 'types/reducer/metrics';
|
import InitialValueTypes from 'types/reducer/metrics';
|
||||||
|
|
||||||
@ -22,6 +25,10 @@ const InitialValue: InitialValueTypes = {
|
|||||||
externalAverageDuration: [],
|
externalAverageDuration: [],
|
||||||
externalError: [],
|
externalError: [],
|
||||||
serviceOverview: [],
|
serviceOverview: [],
|
||||||
|
resourceAttributeQueries: GetResourceAttributeQueriesFromURL() || [],
|
||||||
|
resourceAttributePromQLQuery: resourceAttributesQueryToPromQL(
|
||||||
|
GetResourceAttributeQueriesFromURL() || [],
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const metrics = (
|
const metrics = (
|
||||||
@ -73,6 +80,8 @@ const metrics = (
|
|||||||
case RESET_INITIAL_APPLICATION_DATA: {
|
case RESET_INITIAL_APPLICATION_DATA: {
|
||||||
return {
|
return {
|
||||||
...InitialValue,
|
...InitialValue,
|
||||||
|
// ...state.resourceAttributeQueries,
|
||||||
|
// resourceAttributeQueries: state.resourceAttributeQueries,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +106,16 @@ const metrics = (
|
|||||||
metricsApplicationLoading: false,
|
metricsApplicationLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SET_RESOURCE_ATTRIBUTE_QUERIES: {
|
||||||
|
const { queries, promQLQuery } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
resourceAttributeQueries: queries,
|
||||||
|
resourceAttributePromQLQuery: promQLQuery,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
|
// import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
|
||||||
// import { ExternalError } from 'types/api/metrics/getExternalError';
|
// import { ExternalError } from 'types/api/metrics/getExternalError';
|
||||||
// import { ExternalService } from 'types/api/metrics/getExternalService';
|
// import { ExternalService } from 'types/api/metrics/getExternalService';
|
||||||
|
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
|
||||||
import { ServicesList } from 'types/api/metrics/getService';
|
import { ServicesList } from 'types/api/metrics/getService';
|
||||||
import { ServiceOverview } from 'types/api/metrics/getServiceOverview';
|
import { ServiceOverview } from 'types/api/metrics/getServiceOverview';
|
||||||
import { TopEndPoints } from 'types/api/metrics/getTopEndPoints';
|
import { TopEndPoints } from 'types/api/metrics/getTopEndPoints';
|
||||||
@ -14,6 +15,8 @@ export const GET_INITIAL_APPLICATION_LOADING =
|
|||||||
export const GET_INITIAL_APPLICATION_ERROR = 'GET_INITIAL_APPLICATION_ERROR';
|
export const GET_INITIAL_APPLICATION_ERROR = 'GET_INITIAL_APPLICATION_ERROR';
|
||||||
export const GET_INTIAL_APPLICATION_DATA = 'GET_INTIAL_APPLICATION_DATA';
|
export const GET_INTIAL_APPLICATION_DATA = 'GET_INTIAL_APPLICATION_DATA';
|
||||||
export const RESET_INITIAL_APPLICATION_DATA = 'RESET_INITIAL_APPLICATION_DATA';
|
export const RESET_INITIAL_APPLICATION_DATA = 'RESET_INITIAL_APPLICATION_DATA';
|
||||||
|
export const SET_RESOURCE_ATTRIBUTE_QUERIES = 'SET_RESOURCE_ATTRIBUTE_QUERIES';
|
||||||
|
|
||||||
export interface GetServiceList {
|
export interface GetServiceList {
|
||||||
type: typeof GET_SERVICE_LIST_SUCCESS;
|
type: typeof GET_SERVICE_LIST_SUCCESS;
|
||||||
payload: ServicesList[];
|
payload: ServicesList[];
|
||||||
@ -48,9 +51,18 @@ export interface ResetInitialApplicationData {
|
|||||||
type: typeof RESET_INITIAL_APPLICATION_DATA;
|
type: typeof RESET_INITIAL_APPLICATION_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetResourceAttributeQueries {
|
||||||
|
type: typeof SET_RESOURCE_ATTRIBUTE_QUERIES;
|
||||||
|
payload: {
|
||||||
|
queries: IResourceAttributeQuery[];
|
||||||
|
promQLQuery: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type MetricsActions =
|
export type MetricsActions =
|
||||||
| GetServiceListError
|
| GetServiceListError
|
||||||
| GetServiceListLoading
|
| GetServiceListLoading
|
||||||
| GetServiceList
|
| GetServiceList
|
||||||
| GetInitialApplicationData
|
| GetInitialApplicationData
|
||||||
| ResetInitialApplicationData;
|
| ResetInitialApplicationData
|
||||||
|
| SetResourceAttributeQueries;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
service: string;
|
service: string;
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
step: number;
|
step: number;
|
||||||
|
selectedTags: Tags[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DBOverView {
|
export interface DBOverView {
|
||||||
|
8
frontend/src/types/api/metrics/getResourceAttributes.ts
Normal file
8
frontend/src/types/api/metrics/getResourceAttributes.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export type TagKeysPayloadProps = {
|
||||||
|
data: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TagValueProps = string;
|
||||||
|
export type TagValuesPayloadProps = {
|
||||||
|
data: string[];
|
||||||
|
};
|
@ -1,6 +1,9 @@
|
|||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
|
selectedTags: Tags[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServicesList {
|
export interface ServicesList {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Tags } from 'types/reducer/trace';
|
||||||
|
|
||||||
export interface TopEndPoints {
|
export interface TopEndPoints {
|
||||||
name: string;
|
name: string;
|
||||||
numCalls: number;
|
numCalls: number;
|
||||||
@ -10,6 +12,7 @@ export interface Props {
|
|||||||
service: string;
|
service: string;
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
|
selectedTags: Tags[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PayloadProps = TopEndPoints[];
|
export type PayloadProps = TopEndPoints[];
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
|
||||||
import { DBOverView } from 'types/api/metrics/getDBOverview';
|
import { DBOverView } from 'types/api/metrics/getDBOverview';
|
||||||
import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
|
import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
|
||||||
import { ExternalError } from 'types/api/metrics/getExternalError';
|
import { ExternalError } from 'types/api/metrics/getExternalError';
|
||||||
@ -18,6 +19,8 @@ interface MetricReducer {
|
|||||||
externalAverageDuration: ExternalAverageDuration[];
|
externalAverageDuration: ExternalAverageDuration[];
|
||||||
externalError: ExternalError[];
|
externalError: ExternalError[];
|
||||||
serviceOverview: ServiceOverview[];
|
serviceOverview: ServiceOverview[];
|
||||||
|
resourceAttributeQueries: IResourceAttributeQuery[];
|
||||||
|
resourceAttributePromQLQuery: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MetricReducer;
|
export default MetricReducer;
|
||||||
|
@ -50,7 +50,12 @@ export interface Tags {
|
|||||||
Values: string[];
|
Values: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type OperatorValues = 'not in' | 'in';
|
export interface TagsAPI {
|
||||||
|
Key: string;
|
||||||
|
Operator: OperatorValues;
|
||||||
|
Values: string[];
|
||||||
|
}
|
||||||
|
export type OperatorValues = 'not in' | 'in';
|
||||||
|
|
||||||
export type TraceFilterEnum =
|
export type TraceFilterEnum =
|
||||||
| 'component'
|
| 'component'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user