diff --git a/public/pages/Configuration/Configuration.tsx b/public/pages/Configuration/Configuration.tsx index d23436c..5903fe6 100644 --- a/public/pages/Configuration/Configuration.tsx +++ b/public/pages/Configuration/Configuration.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; import { EuiFlexItem, EuiPanel, @@ -20,19 +20,18 @@ import { CoreStart } from '../../../../../src/core/public'; import { QUERY_INSIGHTS, MetricSettings } from '../TopNQueries/TopNQueries'; const Configuration = ({ - latencySettings, + latencySettings, cpuSettings, memorySettings, configInfo, core, }: { - latencySettings: MetricSettings, - cpuSettings: MetricSettings, - memorySettings: MetricSettings, - configInfo: any + latencySettings: MetricSettings; + cpuSettings: MetricSettings; + memorySettings: MetricSettings; + configInfo: any; core: CoreStart; }) => { - const metricTypes = [ { value: 'latency', text: 'Latency' }, { value: 'cpu', text: 'CPU' }, @@ -60,23 +59,26 @@ const Configuration = ({ const [windowSize, setWindowSize] = useState(latencySettings.currWindowSize); const [time, setTime] = useState(latencySettings.currTimeUnit); - const metricSettingsMap: { [key: string]: MetricSettings } = { - latency: latencySettings, - cpu: cpuSettings, - memory: memorySettings, - }; + const metricSettingsMap = useMemo( + () => ({ + latency: latencySettings, + cpu: cpuSettings, + memory: memorySettings, + }), + [latencySettings, cpuSettings, memorySettings] + ); - const newOrReset = () => { + const newOrReset = useCallback(() => { const currMetric = metricSettingsMap[metric]; setTopNSize(currMetric.currTopN); setWindowSize(currMetric.currWindowSize); setTime(currMetric.currTimeUnit); setIsEnabled(currMetric.isEnabled); - }; + }, [metric, metricSettingsMap]); useEffect(() => { - newOrReset() - }, [metric]); + newOrReset(); + }, [newOrReset]); useEffect(() => { core.chrome.setBreadcrumbs([ @@ -136,14 +138,13 @@ const Configuration = ({ const WindowChoice = time === timeUnits[0].value ? MinutesBox : HoursBox; let changed = false; - if (isEnabled != metricSettingsMap[metric].isEnabled){ + if (isEnabled !== metricSettingsMap[metric].isEnabled) { changed = true; - } - else if (topNSize !== metricSettingsMap[metric].currTopN) { + } else if (topNSize !== metricSettingsMap[metric].currTopN) { changed = true; } else if (windowSize !== metricSettingsMap[metric].currWindowSize) { changed = true; - } else if (time !== metricSettingsMap[metric].currTimeUnit){ + } else if (time !== metricSettingsMap[metric].currTimeUnit) { changed = true; } @@ -184,9 +185,7 @@ const Configuration = ({ - + - - onEnabledChange(e)} - /> + + onEnabledChange(e)} /> {isEnabled ? ( @@ -222,8 +215,8 @@ const Configuration = ({

Value of N (count)

- Specify the value of N. N is the number of queries to be collected within the - window size. + Specify the value of N. N is the number of queries to be collected within + the window size. @@ -272,7 +265,7 @@ const Configuration = ({ - ): null} + ) : null} diff --git a/public/pages/QueryDetails/QueryDetails.tsx b/public/pages/QueryDetails/QueryDetails.tsx index 0f8ebb7..9efe886 100644 --- a/public/pages/QueryDetails/QueryDetails.tsx +++ b/public/pages/QueryDetails/QueryDetails.tsx @@ -15,7 +15,7 @@ import { import { useParams, useHistory, useLocation } from 'react-router-dom'; import { CoreStart } from '../../../../../src/core/public'; import QuerySummary from './Components/QuerySummary'; -import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries'; +import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries'; const QueryDetails = ({ queries, core }: { queries: any; core: CoreStart }) => { const { hashedQuery } = useParams<{ hashedQuery: string }>(); diff --git a/public/pages/QueryInsights/QueryInsights.tsx b/public/pages/QueryInsights/QueryInsights.tsx index 2d22959..93baa90 100644 --- a/public/pages/QueryInsights/QueryInsights.tsx +++ b/public/pages/QueryInsights/QueryInsights.tsx @@ -12,7 +12,7 @@ const INDICES_FIELD = 'indices'; const SEARCH_TYPE_FIELD = 'search_type'; const NODE_ID_FIELD = 'node_id'; const TOTAL_SHARDS_FIELD = 'total_shards'; -const METRIC_DEFAULT_MSG = "Not enabled"; +const METRIC_DEFAULT_MSG = 'Not enabled'; const QueryInsights = ({ queries, @@ -68,21 +68,23 @@ const QueryInsights = ({ { field: LATENCY_FIELD, name: 'Latency', - render: (latency: number) => (typeof latency !== "undefined") ? `${latency} ms`: `${METRIC_DEFAULT_MSG}`, + render: (latency: number) => + typeof latency !== 'undefined' ? `${latency} ms` : `${METRIC_DEFAULT_MSG}`, sortable: true, truncateText: true, }, { field: CPU_FIELD, name: 'CPU usage', - render: (cpu: number) => (typeof cpu !== "undefined") ? `${cpu} ns`: `${METRIC_DEFAULT_MSG}`, + render: (cpu: number) => (typeof cpu !== 'undefined' ? `${cpu} ns` : `${METRIC_DEFAULT_MSG}`), sortable: true, truncateText: true, }, { field: MEMORY_FIELD, name: 'Memory', - render: (memory: number) => (typeof memory !== "undefined") ? `${memory} B`: `${METRIC_DEFAULT_MSG}`, + render: (memory: number) => + typeof memory !== 'undefined' ? `${memory} B` : `${METRIC_DEFAULT_MSG}`, sortable: true, truncateText: true, }, diff --git a/public/pages/TopNQueries/TopNQueries.tsx b/public/pages/TopNQueries/TopNQueries.tsx index 253b1eb..e665c38 100644 --- a/public/pages/TopNQueries/TopNQueries.tsx +++ b/public/pages/TopNQueries/TopNQueries.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { MemoryRouter, Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom'; +import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom'; import { EuiTab, EuiTabs, EuiTitle } from '@elastic/eui'; import dateMath from '@elastic/datemath'; import QueryInsights from '../QueryInsights/QueryInsights'; @@ -11,11 +11,11 @@ export const QUERY_INSIGHTS = '/queryInsights'; export const CONFIGURATION = '/configuration'; export interface MetricSettings { - isEnabled: boolean, - currTopN: string, - currWindowSize: string, - currTimeUnit: string, -}; + isEnabled: boolean; + currTopN: string; + currWindowSize: string; + currTimeUnit: string; +} const TopNQueries = ({ core }: { core: CoreStart }) => { const history = useHistory(); @@ -26,33 +26,33 @@ const TopNQueries = ({ core }: { core: CoreStart }) => { isEnabled: false, currTopN: '', currWindowSize: '', - currTimeUnit: 'HOURS' + currTimeUnit: 'HOURS', }); const [cpuSettings, setCpuSettings] = useState({ isEnabled: false, currTopN: '', currWindowSize: '', - currTimeUnit: 'HOURS' + currTimeUnit: 'HOURS', }); const [memorySettings, setMemorySettings] = useState({ isEnabled: false, currTopN: '', currWindowSize: '', - currTimeUnit: 'HOURS' + currTimeUnit: 'HOURS', }); const setMetricSettings = (metricType: string, updates: Partial) => { - switch(metricType){ + switch (metricType) { case 'latency': - setLatencySettings(prevSettings => ({ ...prevSettings, ...updates })); + setLatencySettings((prevSettings) => ({ ...prevSettings, ...updates })); break; case 'cpu': - setCpuSettings(prevSettings => ({ ...prevSettings, ...updates })); + setCpuSettings((prevSettings) => ({ ...prevSettings, ...updates })); break; case 'memory': - setMemorySettings(prevSettings => ({ ...prevSettings, ...updates })); + setMemorySettings((prevSettings) => ({ ...prevSettings, ...updates })); break; default: console.error(`Unknown metric type: ${metricType}`); @@ -96,106 +96,119 @@ const TopNQueries = ({ core }: { core: CoreStart }) => { return date ? date.toDate().getTime() : new Date().getTime(); }; - const retrieveQueries = useCallback(async (start: string, end: string) => { - setLoading(true); - try { - const nullResponse = { response: { top_queries: [] } }; - console.log(latencySettings); - console.log(cpuSettings); - console.log(memorySettings); - const respLatency = latencySettings.isEnabled ? await core.http.get('/api/top_queries/latency'): nullResponse; - const respCpu = cpuSettings.isEnabled ? await core.http.get('/api/top_queries/cpu') : nullResponse; - const respMemory = memorySettings.isEnabled ? await core.http.get('/api/top_queries/memory'): nullResponse; - const newQueries = [ - ...respLatency.response.top_queries, - ...respCpu.response.top_queries, - ...respMemory.response.top_queries - ]; - const startTimestamp = parseDateString(start); - const endTimestamp = parseDateString(end); - const noDuplicates = newQueries.filter((array, index, self) => - index === self.findIndex((t) => (t.save === array.save && t.State === array.State))) - console.log(noDuplicates); - setQueries(noDuplicates.filter((item: any) => item.timestamp >= startTimestamp && item.timestamp <= endTimestamp)); - } catch (error) { - console.error('Failed to retrieve queries:', error); - } finally { - setLoading(false); - } - }, [latencySettings, cpuSettings, memorySettings, core]); - - const retrieveConfigInfo = async ( - get : boolean, - enabled: boolean = false, - metric: string = "", - newTopN: string = "", - newWindowSize: string = "", - newTimeUnit: string = "", - ) => { - if (get) { + const retrieveQueries = useCallback( + async (start: string, end: string) => { + setLoading(true); try { - const resp = await core.http.get('/api/settings'); - console.log(resp); - const settings = resp.response.persistent.search.insights.top_queries - const latency = settings.latency; - const cpu = settings.cpu; - const memory = settings.memory; - console.log(latency); - if (latency !== undefined && latency.enabled === "true") { - const [time, timeUnits] = latency.window_size.match(/\D+|\d+/g); - setMetricSettings('latency', { - isEnabled: true, - currTopN: latency.top_n_size, - currWindowSize: time, - currTimeUnit: timeUnits === 'm' ? 'MINUTES': 'HOURS', - }); + const nullResponse = { response: { top_queries: [] } }; + const respLatency = latencySettings.isEnabled + ? await core.http.get('/api/top_queries/latency') + : nullResponse; + const respCpu = cpuSettings.isEnabled + ? await core.http.get('/api/top_queries/cpu') + : nullResponse; + const respMemory = memorySettings.isEnabled + ? await core.http.get('/api/top_queries/memory') + : nullResponse; + const newQueries = [ + ...respLatency.response.top_queries, + ...respCpu.response.top_queries, + ...respMemory.response.top_queries, + ]; + const startTimestamp = parseDateString(start); + const endTimestamp = parseDateString(end); + const noDuplicates = newQueries.filter( + (array, index, self) => + index === self.findIndex((t) => t.save === array.save && t.State === array.State) + ); + setQueries( + noDuplicates.filter( + (item: any) => item.timestamp >= startTimestamp && item.timestamp <= endTimestamp + ) + ); + } catch (error) { + console.error('Failed to retrieve queries:', error); + } finally { + setLoading(false); + } + }, + [latencySettings, cpuSettings, memorySettings, core] + ); + + const retrieveConfigInfo = useCallback( + async ( + get: boolean, + enabled: boolean = false, + metric: string = '', + newTopN: string = '', + newWindowSize: string = '', + newTimeUnit: string = '' + ) => { + if (get) { + try { + const resp = await core.http.get('/api/settings'); + const settings = resp.response.persistent.search.insights.top_queries; + const latency = settings.latency; + const cpu = settings.cpu; + const memory = settings.memory; + if (latency !== undefined && latency.enabled === 'true') { + const [time, timeUnits] = latency.window_size.match(/\D+|\d+/g); + setMetricSettings('latency', { + isEnabled: true, + currTopN: latency.top_n_size, + currWindowSize: time, + currTimeUnit: timeUnits === 'm' ? 'MINUTES' : 'HOURS', + }); + } + if (cpu !== undefined && cpu.enabled === 'true') { + const [time, timeUnits] = cpu.window_size.match(/\D+|\d+/g); + setMetricSettings('cpu', { + isEnabled: true, + currTopN: cpu.top_n_size, + currWindowSize: time, + currTimeUnit: timeUnits === 'm' ? 'MINUTES' : 'HOURS', + }); + } + if (memory !== undefined && memory.enabled === 'true') { + const [time, timeUnits] = memory.window_size.match(/\D+|\d+/g); + setMetricSettings('memory', { + isEnabled: true, + currTopN: memory.top_n_size, + currWindowSize: time, + currTimeUnit: timeUnits === 'm' ? 'MINUTES' : 'HOURS', + }); + } + } catch (error) { + console.error('Failed to retrieve settings:', error); } - if (cpu !== undefined && cpu.enabled === "true") { - const [time, timeUnits] = cpu.window_size.match(/\D+|\d+/g); - setMetricSettings('cpu', { - isEnabled: true, - currTopN: cpu.top_n_size, - currWindowSize: time, - currTimeUnit: timeUnits === 'm' ? 'MINUTES': 'HOURS', + } else { + try { + setMetricSettings(metric, { + isEnabled: enabled, + currTopN: newTopN, + currWindowSize: newWindowSize, + currTimeUnit: newTimeUnit, }); - } - if (memory !== undefined && memory.enabled === "true") { - const [time, timeUnits] = memory.window_size.match(/\D+|\d+/g); - setMetricSettings('memory', { - isEnabled: true, - currTopN: memory.top_n_size, - currWindowSize: time, - currTimeUnit: timeUnits === 'm' ? 'MINUTES': 'HOURS', + const resp = await core.http.put('/api/update_settings', { + query: { + metric, + enabled, + top_n_size: newTopN, + window_size: `${newWindowSize}${newTimeUnit === 'MINUTES' ? 'm' : 'h'}`, + }, }); + } catch (error) { + console.error('Failed to set settings:', error); } - } catch (error) { - console.error('Failed to retrieve settings:', error); } - } else { - try { - setMetricSettings(metric, { - isEnabled: enabled, - currTopN: newTopN, - currWindowSize: newWindowSize, - currTimeUnit: newTimeUnit, - }); - const requestQuery = {metric: metric, enabled: enabled, top_n_size: newTopN, window_size: `${newWindowSize}${newTimeUnit == 'MINUTES' ? 'm': 'h'}`}; - const resp = await core.http.put('/api/update_settings', {query: requestQuery}); - console.log("Setting settings"); - console.log(resp); - } catch (error) { - console.error('Failed to set settings:', error); - } - } - // setTopN(newTopN); - // setWindowSize(newWindowSize); - // setTimeUnit(newTimeUnit); - }; + }, + [core] + ); - const onQueriesChange = (start : string, end : string) => { + const onQueriesChange = (start: string, end: string) => { retrieveQueries(start, end); retrieveConfigInfo(true); - } + }; useEffect(() => { retrieveQueries(defaultStart, 'now'); @@ -203,7 +216,7 @@ const TopNQueries = ({ core }: { core: CoreStart }) => { useEffect(() => { retrieveConfigInfo(true); - }, []) + }, [retrieveConfigInfo]); return (
diff --git a/server/clusters/queryInsightsPlugin.ts b/server/clusters/queryInsightsPlugin.ts index 0c4c355..53a87ba 100644 --- a/server/clusters/queryInsightsPlugin.ts +++ b/server/clusters/queryInsightsPlugin.ts @@ -3,51 +3,51 @@ * SPDX-License-Identifier: Apache-2.0 */ - export const QueryInsightsPlugin = function (Client, config, components) { - const ca = components.clientAction.factory; - Client.prototype.queryInsights = components.clientAction.namespaceFactory(); - const queryInsights = Client.prototype.queryInsights.prototype; +export const QueryInsightsPlugin = function (Client, config, components) { + const ca = components.clientAction.factory; + Client.prototype.queryInsights = components.clientAction.namespaceFactory(); + const queryInsights = Client.prototype.queryInsights.prototype; - queryInsights.getTopNQueries = ca({ - url: { - fmt: `/_insights/top_queries`, - }, - method: 'GET', - }); + queryInsights.getTopNQueries = ca({ + url: { + fmt: `/_insights/top_queries`, + }, + method: 'GET', + }); - queryInsights.getTopNQueriesLatency = ca({ - url: { - fmt: `/_insights/top_queries?type=latency`, - }, - method: 'GET', - }); + queryInsights.getTopNQueriesLatency = ca({ + url: { + fmt: `/_insights/top_queries?type=latency`, + }, + method: 'GET', + }); - queryInsights.getTopNQueriesCpu = ca({ - url: { - fmt: `/_insights/top_queries?type=cpu`, - }, - method: 'GET', - }); + queryInsights.getTopNQueriesCpu = ca({ + url: { + fmt: `/_insights/top_queries?type=cpu`, + }, + method: 'GET', + }); - queryInsights.getTopNQueriesMemory = ca({ - url: { - fmt: `/_insights/top_queries?type=memory`, - }, - method: 'GET', - }); + queryInsights.getTopNQueriesMemory = ca({ + url: { + fmt: `/_insights/top_queries?type=memory`, + }, + method: 'GET', + }); - queryInsights.getSettings = ca({ - url: { - fmt: `_cluster/settings?include_defaults=true`, - }, - method: 'GET', - }); + queryInsights.getSettings = ca({ + url: { + fmt: `_cluster/settings?include_defaults=true`, + }, + method: 'GET', + }); - queryInsights.setSettings = ca({ - url: { - fmt: `_cluster/settings`, - }, - method: 'PUT', - needBody: true, - }); - }; \ No newline at end of file + queryInsights.setSettings = ca({ + url: { + fmt: `_cluster/settings`, + }, + method: 'PUT', + needBody: true, + }); +}; diff --git a/server/plugin.ts b/server/plugin.ts index 302de5d..d48449f 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -23,7 +23,7 @@ export class QueryInsightsDashboardsPlugin this.logger.debug('query-insights-dashboards: Setup'); const router = core.http.createRouter(); const queryInsightsClient: ILegacyCustomClusterClient = core.opensearch.legacy.createClient( - 'opensearch_queryInsights', + 'opensearch_queryInsights', { plugins: [QueryInsightsPlugin], } @@ -32,11 +32,10 @@ export class QueryInsightsDashboardsPlugin core.http.registerRouteHandlerContext('queryInsights_plugin', (_context, _request) => { return { logger: this.logger, - queryInsightsClient: queryInsightsClient, + queryInsightsClient, }; }); - // Register server side APIs defineRoutes(router); diff --git a/server/routes/index.ts b/server/routes/index.ts index 933aa78..3deefed 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -1,3 +1,4 @@ +import { schema } from '@osd/config-schema'; import { IRouter } from '../../../../src/core/server'; export function defineRoutes(router: IRouter) { router.get( @@ -20,9 +21,9 @@ export function defineRoutes(router: IRouter) { }, async (context, request, response) => { try { - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const res = await client('queryInsights.getTopNQueries'); - console.log(res); return response.custom({ statusCode: 200, body: { @@ -31,12 +32,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to get top queries: ", error); + console.error('Unable to get top queries: ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } } @@ -49,9 +50,9 @@ export function defineRoutes(router: IRouter) { }, async (context, request, response) => { try { - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const res = await client('queryInsights.getTopNQueriesLatency'); - console.log(res); return response.custom({ statusCode: 200, body: { @@ -60,12 +61,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to get top queries (latency): ", error); + console.error('Unable to get top queries (latency): ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } } @@ -78,9 +79,9 @@ export function defineRoutes(router: IRouter) { }, async (context, request, response) => { try { - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const res = await client('queryInsights.getTopNQueriesCpu'); - console.log(res); return response.custom({ statusCode: 200, body: { @@ -89,12 +90,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to get top queries (cpu): ", error); + console.error('Unable to get top queries (cpu): ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } } @@ -107,9 +108,9 @@ export function defineRoutes(router: IRouter) { }, async (context, request, response) => { try { - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const res = await client('queryInsights.getTopNQueriesMemory'); - console.log(res); return response.custom({ statusCode: 200, body: { @@ -118,12 +119,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to get top queries (memory): ", error); + console.error('Unable to get top queries (memory): ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } } @@ -136,7 +137,8 @@ export function defineRoutes(router: IRouter) { }, async (context, request, response) => { try { - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const res = await client('queryInsights.getSettings'); return response.custom({ statusCode: 200, @@ -146,12 +148,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to get top queries: ", error); + console.error('Unable to get top queries: ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } } @@ -160,25 +162,29 @@ export function defineRoutes(router: IRouter) { router.put( { path: '/api/update_settings', - validate: false, + validate: { + query: schema.object({ + metric: schema.maybe(schema.string({ defaultValue: '' })), + enabled: schema.maybe(schema.boolean({ defaultValue: false })), + top_n_size: schema.maybe(schema.string({ defaultValue: '' })), + window_size: schema.maybe(schema.string({ defaultValue: '' })), + }), + }, }, async (context, request, response) => { try { - // console.log("context is: ", context); - // console.log("response is: ", response); const query = request.query; - console.log("----------------request is: ", query); - const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request).callAsCurrentUser; + const client = context.queryInsights_plugin.queryInsightsClient.asScoped(request) + .callAsCurrentUser; const params = { - "body": { - "persistent": { + body: { + persistent: { [`search.insights.top_queries.${query.metric}.enabled`]: query.enabled, [`search.insights.top_queries.${query.metric}.top_n_size`]: query.top_n_size, - [`search.insights.top_queries.${query.metric}.window_size`] : query.window_size, - } - } + [`search.insights.top_queries.${query.metric}.window_size`]: query.window_size, + }, + }, }; - console.log(params); const res = await client('queryInsights.setSettings', params); return response.custom({ statusCode: 200, @@ -188,12 +194,12 @@ export function defineRoutes(router: IRouter) { }, }); } catch (error) { - console.error("Unable to set settings: ", error); + console.error('Unable to set settings: ', error); return response.ok({ body: { ok: false, response: error.message, - } + }, }); } }