diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 241395b23a..25f6f72a55 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -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 { /> - + onViewLogsClick={onErrorTrackHandler( + selectedTimeStamp, + logErrorQuery, + true, + )} + onViewTracesClick={onErrorTrackHandler( + selectedTimeStamp, + errorTrackQuery, + )} + /> void; + onViewTracesClick: () => void; +} + +function GraphControlsPanel({ + id, + onViewLogsClick, + onViewTracesClick, +}: GraphControlsPanelProps): JSX.Element { + return ( +
+ + +
+ ); +} + +export default GraphControlsPanel; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx index 9651e16d3a..0b7d86d660 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx @@ -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 ( <> - + /> {topLevelOperationsIsLoading && ( diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index 6832fe9d02..a9d04d361d 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -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,