mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-31 05:31:58 +08:00
feat: added view logs button for error and latency chart
This commit is contained in:
parent
16738ea7e3
commit
6bc2f9125c
@ -38,6 +38,7 @@ import {
|
||||
} from '../MetricsPageQueries/OverviewQueries';
|
||||
import { Col, ColApDexContainer, ColErrorContainer, Row } from '../styles';
|
||||
import ApDex from './Overview/ApDex';
|
||||
import GraphControlsPanel from './Overview/GraphControlsPanel/GraphControlsPanel';
|
||||
import ServiceOverview from './Overview/ServiceOverview';
|
||||
import TopLevelOperation from './Overview/TopLevelOperations';
|
||||
import TopOperation from './Overview/TopOperation';
|
||||
@ -48,6 +49,7 @@ import {
|
||||
handleNonInQueryRange,
|
||||
onGraphClickHandler,
|
||||
onViewTracePopupClick,
|
||||
useGetAPMToLogsQueries,
|
||||
useGetAPMToTracesQueries,
|
||||
} from './util';
|
||||
|
||||
@ -194,33 +196,57 @@ function Application(): JSX.Element {
|
||||
[dispatch, pathname, urlQuery],
|
||||
);
|
||||
|
||||
const onErrorTrackHandler = (
|
||||
timestamp: number,
|
||||
apmToTraceQuery: Query,
|
||||
): (() => void) => (): void => {
|
||||
const currentTime = timestamp;
|
||||
const tPlusOne = timestamp + 60 * 1000;
|
||||
const onErrorTrackHandler = useCallback(
|
||||
(
|
||||
timestamp: number,
|
||||
apmToTraceQuery: Query,
|
||||
isViewLogsClicked?: boolean,
|
||||
): (() => void) => (): void => {
|
||||
const currentTime = timestamp;
|
||||
const tPlusOne = timestamp + 60 * 1000;
|
||||
|
||||
const urlParams = new URLSearchParams(search);
|
||||
urlParams.set(QueryParams.startTime, currentTime.toString());
|
||||
urlParams.set(QueryParams.endTime, tPlusOne.toString());
|
||||
const urlParams = new URLSearchParams(search);
|
||||
urlParams.set(QueryParams.startTime, currentTime.toString());
|
||||
urlParams.set(QueryParams.endTime, tPlusOne.toString());
|
||||
|
||||
const avialableParams = routeConfig[ROUTES.TRACE];
|
||||
const queryString = getQueryString(avialableParams, urlParams);
|
||||
const avialableParams = routeConfig[ROUTES.TRACE];
|
||||
const queryString = getQueryString(avialableParams, urlParams);
|
||||
|
||||
const JSONCompositeQuery = encodeURIComponent(
|
||||
JSON.stringify(apmToTraceQuery),
|
||||
);
|
||||
const JSONCompositeQuery = encodeURIComponent(
|
||||
JSON.stringify(apmToTraceQuery),
|
||||
);
|
||||
|
||||
const newTraceExplorerPath = `${
|
||||
ROUTES.TRACES_EXPLORER
|
||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${
|
||||
QueryParams.compositeQuery
|
||||
}=${JSONCompositeQuery}&${queryString.join('&')}`;
|
||||
const basePath = isViewLogsClicked
|
||||
? ROUTES.LOGS_EXPLORER
|
||||
: ROUTES.TRACES_EXPLORER;
|
||||
const newPath = `${basePath}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${
|
||||
QueryParams.compositeQuery
|
||||
}=${JSONCompositeQuery}&${queryString.join('&')}`;
|
||||
|
||||
history.push(newTraceExplorerPath);
|
||||
};
|
||||
history.push(newPath);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
);
|
||||
|
||||
const logErrorQuery = useGetAPMToLogsQueries({
|
||||
servicename,
|
||||
filters: [
|
||||
{
|
||||
id: uuid().slice(0, 8),
|
||||
key: {
|
||||
key: 'hasError',
|
||||
dataType: DataTypes.bool,
|
||||
type: 'tag',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
id: 'hasError--bool--tag--true',
|
||||
},
|
||||
op: '=',
|
||||
value: ['true'],
|
||||
},
|
||||
],
|
||||
});
|
||||
const errorTrackQuery = useGetAPMToTracesQueries({
|
||||
servicename,
|
||||
filters: [
|
||||
@ -304,14 +330,18 @@ function Application(): JSX.Element {
|
||||
/>
|
||||
</ColApDexContainer>
|
||||
<ColErrorContainer>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
<GraphControlsPanel
|
||||
id="Error_button"
|
||||
onClick={onErrorTrackHandler(selectedTimeStamp, errorTrackQuery)}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
onViewLogsClick={onErrorTrackHandler(
|
||||
selectedTimeStamp,
|
||||
logErrorQuery,
|
||||
true,
|
||||
)}
|
||||
onViewTracesClick={onErrorTrackHandler(
|
||||
selectedTimeStamp,
|
||||
errorTrackQuery,
|
||||
)}
|
||||
/>
|
||||
|
||||
<TopLevelOperation
|
||||
handleGraphClick={handleGraphClick}
|
||||
|
@ -0,0 +1,26 @@
|
||||
.graph-controls-panel {
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
width: 110px;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background: var(--bg-slate-400);
|
||||
|
||||
.ant-btn-link {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
color: var(--bg-vanilla-100);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ant-btn-link:hover {
|
||||
color: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import './GraphControlsPanel.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button } from 'antd';
|
||||
import { DraftingCompass, ScrollText } from 'lucide-react';
|
||||
|
||||
interface GraphControlsPanelProps {
|
||||
id: string;
|
||||
onViewLogsClick: () => void;
|
||||
onViewTracesClick: () => void;
|
||||
}
|
||||
|
||||
function GraphControlsPanel({
|
||||
id,
|
||||
onViewLogsClick,
|
||||
onViewTracesClick,
|
||||
}: GraphControlsPanelProps): JSX.Element {
|
||||
return (
|
||||
<div id={id} className="graph-controls-panel">
|
||||
<Button
|
||||
type="link"
|
||||
icon={<DraftingCompass size={14} />}
|
||||
size="small"
|
||||
onClick={onViewTracesClick}
|
||||
style={{ color: Color.BG_VANILLA_100 }}
|
||||
>
|
||||
View traces
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<ScrollText size={14} />}
|
||||
size="small"
|
||||
onClick={onViewLogsClick}
|
||||
style={{ color: Color.BG_VANILLA_100 }}
|
||||
>
|
||||
View logs
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GraphControlsPanel;
|
@ -19,13 +19,14 @@ import { useParams } from 'react-router-dom';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { Button } from '../styles';
|
||||
import { IServiceName } from '../types';
|
||||
import {
|
||||
handleNonInQueryRange,
|
||||
onViewTracePopupClick,
|
||||
useGetAPMToLogsQueries,
|
||||
useGetAPMToTracesQueries,
|
||||
} from '../util';
|
||||
import GraphControlsPanel from './GraphControlsPanel/GraphControlsPanel';
|
||||
|
||||
function ServiceOverview({
|
||||
onDragSelect,
|
||||
@ -75,21 +76,26 @@ function ServiceOverview({
|
||||
|
||||
const apmToTraceQuery = useGetAPMToTracesQueries({ servicename });
|
||||
|
||||
const apmToLogQuery = useGetAPMToLogsQueries({ servicename });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="default"
|
||||
size="small"
|
||||
<GraphControlsPanel
|
||||
id="Service_button"
|
||||
onClick={onViewTracePopupClick({
|
||||
onViewLogsClick={onViewTracePopupClick({
|
||||
servicename,
|
||||
selectedTraceTags,
|
||||
timestamp: selectedTimeStamp,
|
||||
apmToTraceQuery: apmToLogQuery,
|
||||
isViewLogsClicked: true,
|
||||
})}
|
||||
onViewTracesClick={onViewTracePopupClick({
|
||||
servicename,
|
||||
selectedTraceTags,
|
||||
timestamp: selectedTimeStamp,
|
||||
apmToTraceQuery,
|
||||
})}
|
||||
>
|
||||
View Traces
|
||||
</Button>
|
||||
/>
|
||||
<Card data-testid="service_latency">
|
||||
<GraphContainer>
|
||||
{topLevelOperationsIsLoading && (
|
||||
|
@ -5,6 +5,7 @@ import { routeConfig } from 'container/SideNav/config';
|
||||
import { getQueryString } from 'container/SideNav/helper';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import history from 'lib/history';
|
||||
import { prepareQueryWithDefaultTimestamp } from 'pages/LogsExplorer/utils';
|
||||
import { traceFilterKeys } from 'pages/TracesExplorer/Filter/filterUtils';
|
||||
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||
import {
|
||||
@ -31,12 +32,14 @@ interface OnViewTracePopupClickProps {
|
||||
selectedTraceTags: string;
|
||||
timestamp: number;
|
||||
apmToTraceQuery: Query;
|
||||
isViewLogsClicked?: boolean;
|
||||
}
|
||||
export function onViewTracePopupClick({
|
||||
selectedTraceTags,
|
||||
servicename,
|
||||
timestamp,
|
||||
apmToTraceQuery,
|
||||
isViewLogsClicked,
|
||||
}: OnViewTracePopupClickProps): VoidFunction {
|
||||
return (): void => {
|
||||
const currentTime = timestamp;
|
||||
@ -53,13 +56,14 @@ export function onViewTracePopupClick({
|
||||
JSON.stringify(apmToTraceQuery),
|
||||
);
|
||||
|
||||
const newTraceExplorerPath = `${
|
||||
ROUTES.TRACES_EXPLORER
|
||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${
|
||||
const basePath = isViewLogsClicked
|
||||
? ROUTES.LOGS_EXPLORER
|
||||
: ROUTES.TRACES_EXPLORER;
|
||||
const newPath = `${basePath}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${
|
||||
QueryParams.compositeQuery
|
||||
}=${JSONCompositeQuery}&${queryString.join('&')}`;
|
||||
|
||||
history.push(newTraceExplorerPath);
|
||||
history.push(newPath);
|
||||
};
|
||||
}
|
||||
|
||||
@ -79,7 +83,7 @@ export function onGraphClickHandler(
|
||||
|
||||
if (xValue) {
|
||||
if (buttonElement) {
|
||||
buttonElement.style.display = 'block';
|
||||
buttonElement.style.display = 'flex';
|
||||
buttonElement.style.left = `${mouseX}px`;
|
||||
buttonElement.style.top = `${mouseY}px`;
|
||||
setSelectedTimeStamp(xValue);
|
||||
@ -106,12 +110,13 @@ export function handleQueryChange(
|
||||
attributeKeys: BaseAutocompleteData,
|
||||
serviceAttribute: string,
|
||||
filters?: TagFilterItem[],
|
||||
logs?: boolean,
|
||||
): Query {
|
||||
const filterItem: TagFilterItem[] = [
|
||||
{
|
||||
id: uuid().slice(0, 8),
|
||||
key: attributeKeys,
|
||||
op: 'in',
|
||||
op: logs ? '=' : 'in',
|
||||
value: serviceAttribute,
|
||||
},
|
||||
];
|
||||
@ -130,6 +135,42 @@ export function handleQueryChange(
|
||||
};
|
||||
}
|
||||
|
||||
export function useGetAPMToLogsQueries({
|
||||
servicename,
|
||||
filters,
|
||||
}: {
|
||||
servicename: string;
|
||||
filters?: TagFilterItem[];
|
||||
}): Query {
|
||||
const finalFilters: TagFilterItem[] = [];
|
||||
const { updateAllQueriesOperators } = useQueryBuilder();
|
||||
let updatedQuery = updateAllQueriesOperators(
|
||||
initialQueriesMap.logs,
|
||||
PANEL_TYPES.LIST,
|
||||
DataSource.LOGS,
|
||||
);
|
||||
const serviceName = {
|
||||
id: 'service.name--string--resource--true',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
key: 'service.name',
|
||||
type: 'resource',
|
||||
isJSON: false,
|
||||
};
|
||||
|
||||
if (filters?.length) {
|
||||
finalFilters.push(...filters);
|
||||
}
|
||||
updatedQuery = prepareQueryWithDefaultTimestamp(updatedQuery);
|
||||
return handleQueryChange(
|
||||
updatedQuery,
|
||||
serviceName,
|
||||
servicename,
|
||||
finalFilters,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
export function useGetAPMToTracesQueries({
|
||||
servicename,
|
||||
isExternalCall,
|
||||
|
Loading…
x
Reference in New Issue
Block a user