diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index 83cbcb0fb..75393f177 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -86,13 +86,10 @@ export const PLOTLY_GAUGE_COLUMN_NUMBER = 4; export const APP_ANALYTICS_TAB_ID_REGEX = /application-analytics-tab.+/; export const DEFAULT_AVAILABILITY_QUERY = 'stats count() by span( timestamp, 1h )'; export const PPL_DEFAULT_PATTERN_REGEX_FILETER = '[a-zA-Z\\d]'; -export const PPL_PATTERNS_REGEX = /\|\s*patterns.+?\|\s*where\s+patterns_field\s*\=\s*'[^a-zA-Z0-9]+'/; // Greedily matches the longest substring for example (patterns referer | patterns pattern='[0-9]' message | where ...) used to modify the query for patterns table export const PATTERNS_REGEX = /\|\s*patterns.+?\|.*\s*where\s+patterns_field\s*\=\s*'[^a-zA-Z0-9]+'/; // Used to extract the initial pattern applied export const PATTERNS_EXTRACTOR_REGEX = /patterns\s+(?\S+)/; -// Used to extract the pattern that is being searched for (for highlighting pattern in pattern table) -export const SELECTED_PATTERN_REGEX = /\|\s*patterns.+?\|\s*where\s+patterns_field\s*\=\s*'(?[^a-zA-Z0-9]+)'/ export const ADD_BUTTON_TEXT = '+ Add color theme'; export const NUMBER_INPUT_MIN_LIMIT = 1; diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index 7fd4223a3..c1c6ccc07 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -288,7 +288,7 @@ export interface PatternTableData { count: number; pattern: string; sampleLog: string; - anomalyCount: number; + anomalyCount?: number; }; export interface ConfigListEntry { diff --git a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx index 4447457d0..bc8965ea6 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx @@ -30,20 +30,16 @@ import { cloneDeep, has, isEmpty, isEqual, reduce } from 'lodash'; import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { batch, useDispatch, useSelector } from 'react-redux'; import { - AGGREGATIONS, AVAILABLE_FIELDS, - CUSTOM_LABEL, DATE_PICKER_FORMAT, DEFAULT_AVAILABILITY_QUERY, EVENT_ANALYTICS_DOCUMENTATION_URL, FILTERED_PATTERN, - GROUPBY, NEW_TAB, PATTERNS_EXTRACTOR_REGEX, PATTERNS_REGEX, PATTERN_REGEX, PPL_DEFAULT_PATTERN_REGEX_FILETER, - PPL_PATTERNS_REGEX, RAW_QUERY, SAVED_OBJECT_ID, SAVED_OBJECT_TYPE, @@ -66,21 +62,8 @@ import { PPL_NEWLINE_REGEX, PPL_PATTERNS_DOCUMENTATION_URL, PPL_STATS_REGEX, - VIS_CHART_TYPES, } from '../../../../common/constants/shared'; -import { - getIndexPatternFromRawQuery, - preprocessQuery, - buildQuery, - composeFinalQuery, -} from '../../../../common/utils'; -import { formatError, getDefaultVisConfig } from '../utils'; -import { - statsChunk, - GroupByChunk, - GroupField, - StatsAggregationChunk, -} from '../../../../common/query_manager/ast/types'; +import { GroupByChunk } from '../../../../common/query_manager/ast/types'; import { IDefaultTimestampState, IExplorerProps, @@ -88,6 +71,11 @@ import { IQueryTab, IVisualizationContainerProps, } from '../../../../common/types/explorer'; +import { + buildQuery, + composeFinalQuery, + getIndexPatternFromRawQuery, +} from '../../../../common/utils'; import { sleep } from '../../common/live_tail/live_tail_button'; import { onItemSelect, parseGetSuggestions } from '../../common/search/autocomplete_logic'; import { Search } from '../../common/search/search'; @@ -106,6 +94,7 @@ import { change as updateVizConfig, selectVisualizationConfig, } from '../redux/slices/viualization_config_slice'; +import { formatError, getDefaultVisConfig } from '../utils'; import { DataGrid } from './events_views/data_grid'; import './explorer.scss'; import { HitsCounter } from './hits_counter/hits_counter'; @@ -193,9 +182,8 @@ export const Explorer = ({ const [liveTimestamp, setLiveTimestamp] = useState(DATE_PICKER_FORMAT); const [triggerAvailability, setTriggerAvailability] = useState(false); const [viewLogPatterns, setViewLogPatterns] = useState(false); - const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = useState( - false - ); + const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = + useState(false); const [spanValue, setSpanValue] = useState(false); const [subType, setSubType] = useState('visualization'); const [metricMeasure, setMetricMeasure] = useState(''); @@ -448,8 +436,8 @@ export const Explorer = ({ getCountVisualizations(minInterval); // to fetch patterns data on current query - if (!finalQuery.match(PPL_PATTERNS_REGEX)) { - getPatterns(minInterval, getErrorHandler('Error fetching patterns')); + if (!finalQuery.match(PATTERNS_REGEX)) { + getPatterns(minInterval); } } @@ -891,6 +879,11 @@ export const Explorer = ({ tabId={tabId} query={query} isPatternLoading={isPatternLoading} + totalHits={reduce( + countDistribution.data['count()'], + (sum, n) => sum + n, + 0 + )} /> diff --git a/dashboards-observability/public/components/event_analytics/explorer/log_patterns/patterns_table.tsx b/dashboards-observability/public/components/event_analytics/explorer/log_patterns/patterns_table.tsx index d670b85cb..aac53448b 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/log_patterns/patterns_table.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/log_patterns/patterns_table.tsx @@ -12,7 +12,7 @@ import { SortDirection, } from '@elastic/eui'; import { PatternTableData } from 'common/types/explorer'; -import { reduce, round } from 'lodash'; +import { round } from 'lodash'; import React from 'react'; import { useSelector } from 'react-redux'; import { FILTERED_PATTERN } from '../../../../../common/constants/explorer'; @@ -25,11 +25,13 @@ interface PatternsTableProps { tabId: string; query: any; isPatternLoading: boolean; + totalHits?: number; } export function PatternsTable(props: PatternsTableProps) { const { tableData, tabId, onPatternSelection, query } = props; const patternsData = useSelector(selectPatterns)[tabId]; + const totalHits = props.totalHits || tableData.reduce((p, v) => p + v.count, 0); const tableColumns = [ { @@ -47,16 +49,7 @@ export function PatternsTable(props: PatternsTableProps) { width: '6%', sortable: (row: PatternTableData) => row.count, render: (item: number) => { - const ratio = - (item / - reduce( - patternsData.total, - (sum, n) => { - return sum + n; - }, - 0 - )) * - 100; + const ratio = (item / totalHits) * 100; return {`${round(ratio, 2)}%`}; }, }, @@ -66,7 +59,7 @@ export function PatternsTable(props: PatternsTableProps) { width: '6%', sortable: (row: PatternTableData) => row.anomalyCount, render: (item: number) => { - return {item}; + return {item ?? 'N/A'}; }, }, { diff --git a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_events.ts b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_events.ts index ca19a4fe5..4bab88084 100644 --- a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_events.ts +++ b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_events.ts @@ -50,7 +50,10 @@ export const useFetchEvents = ({ pplService, requestParams }: IFetchEventsParams return pplService .fetch({ query, format }, errorHandler) .then((res: any) => handler(res)) - .catch((err: any) => console.error(err)) + .catch((err: any) => { + console.error(err); + throw err; + }) .finally(() => setIsEventsLoading(false)); }; diff --git a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_patterns.ts b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_patterns.ts index ebef0875f..dc26b57b3 100644 --- a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_patterns.ts +++ b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_patterns.ts @@ -36,7 +36,7 @@ export const useFetchPatterns = ({ pplService, requestParams }: IFetchPatternsPa const queriesRef = useRef(); queriesRef.current = queries; - const dispatchOnPatterns = (res: { patternTableData: PatternTableData[]; total: number[] }) => { + const dispatchOnPatterns = (res: { patternTableData: PatternTableData[] }) => { batch(() => { dispatch( resetPatterns({ @@ -76,7 +76,7 @@ export const useFetchPatterns = ({ pplService, requestParams }: IFetchPatternsPa const clearPatternCommands = (query: string) => query.replace(PATTERNS_REGEX, ''); - const getPatterns = (interval: string, errorHandler: (error: any) => void, query?: string) => { + const getPatterns = (interval: string, errorHandler?: (error: any) => void, query?: string) => { const cur = queriesRef.current; const rawQuery = cur![requestParams.tabId][FINAL_QUERY]; const searchQuery = isUndefined(query) ? clearPatternCommands(rawQuery) : query; @@ -92,31 +92,40 @@ export const useFetchPatterns = ({ pplService, requestParams }: IFetchPatternsPa interval ); // Fetch patterns data for the current query results - Promise.all([ - fetchEvents({ query: statsQuery }, 'jdbc', (res) => res, errorHandler), - fetchEvents({ query: anomaliesQuery }, 'jdbc', (res) => res, errorHandler), - fetchEvents({ query: `${searchQuery} | stats count()` }, 'jdbc', (res) => res, errorHandler), - ]).then((res) => { - const [statsRes, anomaliesRes, countRes] = res as IPPLEventsDataSource[]; - const anomaliesMap: { [x: string]: number } = {}; - anomaliesRes.datarows.forEach((row) => { - const pattern = row[2]; - const score = row[3]; - if (score > 0) { - anomaliesMap[pattern] = (anomaliesMap[pattern] || 0) + 1; + Promise.allSettled([ + fetchEvents({ query: statsQuery }, 'jdbc', (res) => res), + fetchEvents({ query: anomaliesQuery }, 'jdbc', (res) => res), + ]) + .then((res) => { + const [statsResp, anomaliesResp] = res as PromiseSettledResult[]; + if (statsResp.status === 'rejected') { + throw statsResp.reason; } - }); - const formatToTableData: PatternTableData[] = statsRes.datarows.map((row) => ({ - count: row[0], - pattern: row[2], - sampleLog: row[1][0], - anomalyCount: anomaliesMap[row[2]] || 0, - })); - dispatchOnPatterns({ - patternTableData: formatToTableData, - total: countRes.datarows[0], - }); - }); + + let anomaliesResultsAvailable = true; + const anomaliesMap: { [x: string]: number } = {}; + if (anomaliesResp.status === 'fulfilled') { + anomaliesResp.value.datarows.forEach((row) => { + const pattern = row[2]; + const score = row[3]; + if (score > 0) { + anomaliesMap[pattern] = (anomaliesMap[pattern] || 0) + 1; + } + }); + } else { + console.error('Error fetching anomalies in patterns'); + anomaliesResultsAvailable = false; + } + + const formatToTableData: PatternTableData[] = statsResp.value.datarows.map((row) => ({ + count: row[0], + pattern: row[2], + sampleLog: row[1][0], + anomalyCount: anomaliesResultsAvailable ? anomaliesMap[row[2]] || 0 : undefined, + })); + dispatchOnPatterns({ patternTableData: formatToTableData }); + }) + .catch(errorHandler); }; const setDefaultPatternsField = async ( diff --git a/dashboards-observability/public/services/requests/ppl.ts b/dashboards-observability/public/services/requests/ppl.ts index 4fe252f22..2cb5f8f7c 100644 --- a/dashboards-observability/public/services/requests/ppl.ts +++ b/dashboards-observability/public/services/requests/ppl.ts @@ -26,6 +26,7 @@ export default class PPLService { .catch((error) => { console.error('fetch error: ', error.body); if (errorHandler) errorHandler(error); + throw error; }); }; }