diff --git a/ee/anomaly/seasonal.go b/ee/anomaly/seasonal.go index abd455e0e005..6636188b6d04 100644 --- a/ee/anomaly/seasonal.go +++ b/ee/anomaly/seasonal.go @@ -50,19 +50,14 @@ func (p *BaseSeasonalProvider) getQueryParams(req *AnomaliesRequest) *anomalyQue func (p *BaseSeasonalProvider) toTSResults(ctx context.Context, resp *qbtypes.QueryRangeResponse) []*qbtypes.TimeSeriesData { - if resp == nil || resp.Data == nil { + tsData := []*qbtypes.TimeSeriesData{} + + if resp == nil { p.logger.InfoContext(ctx, "nil response from query range") + return tsData } - data, ok := resp.Data.(struct { - Results []any `json:"results"` - Warnings []string `json:"warnings"` - }) - if !ok { - return nil - } - tsData := []*qbtypes.TimeSeriesData{} - for _, item := range data.Results { + for _, item := range resp.Data.Results { if resultData, ok := item.(*qbtypes.TimeSeriesData); ok { tsData = append(tsData, resultData) } @@ -395,6 +390,11 @@ func (p *BaseSeasonalProvider) getAnomalies(ctx context.Context, orgID valuer.UU continue } + // no data; + if len(result.Aggregations) == 0 { + continue + } + aggOfInterest := result.Aggregations[0] for _, series := range aggOfInterest.Series { diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index 26f69087b164..c5fcbf64f473 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -113,6 +113,8 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) { // v5 router.HandleFunc("/api/v5/query_range", am.ViewAccess(ah.queryRangeV5)).Methods(http.MethodPost) + router.HandleFunc("/api/v5/substitute_vars", am.ViewAccess(ah.QuerierAPI.ReplaceVariables)).Methods(http.MethodPost) + // Gateway router.PathPrefix(gateway.RoutePrefix).HandlerFunc(am.EditAccess(ah.ServeGatewayHTTP)) diff --git a/ee/query-service/app/api/queryrange.go b/ee/query-service/app/api/queryrange.go index 85d60d4b6b43..698ac2d91c72 100644 --- a/ee/query-service/app/api/queryrange.go +++ b/ee/query-service/app/api/queryrange.go @@ -260,11 +260,9 @@ func (aH *APIHandler) queryRangeV5(rw http.ResponseWriter, req *http.Request) { finalResp := &qbtypes.QueryRangeResponse{ Type: queryRangeRequest.RequestType, Data: struct { - Results []any `json:"results"` - Warnings []string `json:"warnings"` + Results []any `json:"results"` }{ - Results: results, - Warnings: make([]string, 0), // TODO(srikanthccv): will there be any warnings here? + Results: results, }, Meta: struct { RowsScanned uint64 `json:"rowsScanned"` diff --git a/ee/query-service/rules/anomaly.go b/ee/query-service/rules/anomaly.go index 52226bd08b7f..3fbf1e32b1f2 100644 --- a/ee/query-service/rules/anomaly.go +++ b/ee/query-service/rules/anomaly.go @@ -211,7 +211,8 @@ func (r *AnomalyRule) prepareQueryRangeV5(ctx context.Context, ts time.Time) (*q }, NoCache: true, } - copy(r.Condition().CompositeQuery.Queries, req.CompositeQuery.Queries) + req.CompositeQuery.Queries = make([]qbtypes.QueryEnvelope, len(r.Condition().CompositeQuery.Queries)) + copy(req.CompositeQuery.Queries, r.Condition().CompositeQuery.Queries) return req, nil } diff --git a/frontend/src/constants/orgPreferences.ts b/frontend/src/constants/orgPreferences.ts index 83a4a1e3f341..f96aa4a014ed 100644 --- a/frontend/src/constants/orgPreferences.ts +++ b/frontend/src/constants/orgPreferences.ts @@ -7,8 +7,8 @@ export const ORG_PREFERENCES = { 'welcome_checklist_setup_alerts_skipped', WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED: 'welcome_checklist_setup_saved_view_skipped', - WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED: - 'welcome_checklist_send_infra_metrics_skipped', + WELCOME_CHECKLIST_SEND_METRICS_SKIPPED: + 'welcome_checklist_send_metrics_skipped', WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED: 'welcome_checklist_setup_dashboards_skipped', WELCOME_CHECKLIST_SETUP_WORKSPACE_SKIPPED: diff --git a/frontend/src/container/Home/Home.tsx b/frontend/src/container/Home/Home.tsx index 33e96374224d..d9133095b95d 100644 --- a/frontend/src/container/Home/Home.tsx +++ b/frontend/src/container/Home/Home.tsx @@ -4,21 +4,17 @@ import './Home.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Popover } from 'antd'; import logEvent from 'api/common/logEvent'; -import { HostListPayload } from 'api/infraMonitoring/getHostLists'; -import { K8sPodsListPayload } from 'api/infraMonitoring/getK8sPodsList'; import listUserPreferences from 'api/v1/user/preferences/list'; import updateUserPreferenceAPI from 'api/v1/user/preferences/name/update'; import Header from 'components/Header/Header'; import { ENTITY_VERSION_V5 } from 'constants/app'; -import { FeatureKeys } from 'constants/features'; import { LOCALSTORAGE } from 'constants/localStorage'; import { ORG_PREFERENCES } from 'constants/orgPreferences'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import ROUTES from 'constants/routes'; -import { getHostListsQuery } from 'container/InfraMonitoringHosts/utils'; -import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList'; -import { useGetK8sPodsList } from 'hooks/infraMonitoring/useGetK8sPodsList'; +import { getMetricsListQuery } from 'container/MetricsExplorer/Summary/utils'; +import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useGetTenantLicense } from 'hooks/useGetTenantLicense'; import history from 'lib/history'; @@ -132,9 +128,9 @@ export default function Home(): JSX.Element { }, ); - // Detect Infra Metrics - Hosts + // Detect Metrics const query = useMemo(() => { - const baseQuery = getHostListsQuery(); + const baseQuery = getMetricsListQuery(); let queryStartTime = startTime; let queryEndTime = endTime; @@ -161,26 +157,11 @@ export default function Home(): JSX.Element { }; }, [startTime, endTime]); - const { data: hostData } = useGetHostList(query as HostListPayload, { - queryKey: ['hostList', query], + const { data: metricsData } = useGetMetricsList(query, { enabled: !!query, + queryKey: ['metricsList', query], }); - const { featureFlags } = useAppContext(); - const dotMetricsEnabled = - featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED) - ?.active || false; - - const { data: k8sPodsData } = useGetK8sPodsList( - query as K8sPodsListPayload, - { - queryKey: ['K8sPodsList', query], - enabled: !!query, - }, - undefined, - dotMetricsEnabled, - ); - const [isLogsIngestionActive, setIsLogsIngestionActive] = useState(false); const [isTracesIngestionActive, setIsTracesIngestionActive] = useState(false); const [isMetricsIngestionActive, setIsMetricsIngestionActive] = useState( @@ -305,15 +286,14 @@ export default function Home(): JSX.Element { }, [tracesData, handleUpdateChecklistDoneItem]); useEffect(() => { - const hostDataTotal = hostData?.payload?.data?.total ?? 0; - const k8sPodsDataTotal = k8sPodsData?.payload?.data?.total ?? 0; + const metricsDataTotal = metricsData?.payload?.data?.total ?? 0; - if (hostDataTotal > 0 || k8sPodsDataTotal > 0) { + if (metricsDataTotal > 0) { setIsMetricsIngestionActive(true); handleUpdateChecklistDoneItem('ADD_DATA_SOURCE'); - handleUpdateChecklistDoneItem('SEND_INFRA_METRICS'); + handleUpdateChecklistDoneItem('SEND_METRICS'); } - }, [hostData, k8sPodsData, handleUpdateChecklistDoneItem]); + }, [metricsData, handleUpdateChecklistDoneItem]); useEffect(() => { logEvent('Homepage: Visited', {}); @@ -520,19 +500,19 @@ export default function Home(): JSX.Element { logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Metrics', }); - history.push(ROUTES.INFRASTRUCTURE_MONITORING_HOSTS); + history.push(ROUTES.METRICS_EXPLORER); }} onKeyDown={(e): void => { if (e.key === 'Enter') { logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Metrics', }); - history.push(ROUTES.INFRASTRUCTURE_MONITORING_HOSTS); + history.push(ROUTES.METRICS_EXPLORER); } }} > - Explore Infra Metrics + Explore Metrics @@ -593,6 +573,20 @@ export default function Home(): JSX.Element { > Open Traces Explorer + + diff --git a/frontend/src/container/Home/SavedViews/SavedViews.tsx b/frontend/src/container/Home/SavedViews/SavedViews.tsx index a89f898de55c..0e3272fc36e4 100644 --- a/frontend/src/container/Home/SavedViews/SavedViews.tsx +++ b/frontend/src/container/Home/SavedViews/SavedViews.tsx @@ -7,6 +7,7 @@ import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { ArrowRight, ArrowUpRight, + BarChart, CompassIcon, DraftingCompass, } from 'lucide-react'; @@ -42,6 +43,12 @@ export default function SavedViews({ isError: tracesViewsError, } = useGetAllViews(DataSource.TRACES); + const { + data: metricsViewsData, + isLoading: metricsViewsLoading, + isError: metricsViewsError, + } = useGetAllViews(DataSource.METRICS); + const logsViews = useMemo(() => [...(logsViewsData?.data.data || [])], [ logsViewsData, ]); @@ -50,14 +57,25 @@ export default function SavedViews({ tracesViewsData, ]); + const metricsViews = useMemo(() => [...(metricsViewsData?.data.data || [])], [ + metricsViewsData, + ]); + useEffect(() => { - setSelectedEntityViews(selectedEntity === 'logs' ? logsViews : tracesViews); - }, [selectedEntity, logsViews, tracesViews]); + if (selectedEntity === 'logs') { + setSelectedEntityViews(logsViews); + } else if (selectedEntity === 'traces') { + setSelectedEntityViews(tracesViews); + } else if (selectedEntity === 'metrics') { + setSelectedEntityViews(metricsViews); + } + }, [selectedEntity, logsViews, tracesViews, metricsViews]); const hasTracesViews = tracesViews.length > 0; const hasLogsViews = logsViews.length > 0; + const hasMetricsViews = metricsViews.length > 0; - const hasSavedViews = hasTracesViews || hasLogsViews; + const hasSavedViews = hasTracesViews || hasLogsViews || hasMetricsViews; const { handleExplorerTabChange } = useHandleExplorerTabChange(); @@ -68,10 +86,16 @@ export default function SavedViews({ entity: selectedEntity, }); - const currentViewDetails = getViewDetailsUsingViewKey( - view.id, - selectedEntity === 'logs' ? logsViews : tracesViews, - ); + let currentViews: ViewProps[] = []; + if (selectedEntity === 'logs') { + currentViews = logsViews; + } else if (selectedEntity === 'traces') { + currentViews = tracesViews; + } else if (selectedEntity === 'metrics') { + currentViews = metricsViews; + } + + const currentViewDetails = getViewDetailsUsingViewKey(view.id, currentViews); if (!currentViewDetails) return; const { query, name, id, panelType: currentPanelType } = currentViewDetails; @@ -94,6 +118,32 @@ export default function SavedViews({ } }, [hasSavedViews, onUpdateChecklistDoneItem, loadingUserPreferences]); + const footerLink = useMemo(() => { + if (selectedEntity === 'logs') { + return ROUTES.LOGS_SAVE_VIEWS; + } + if (selectedEntity === 'traces') { + return ROUTES.TRACES_SAVE_VIEWS; + } + if (selectedEntity === 'metrics') { + return ROUTES.METRICS_EXPLORER_VIEWS; + } + return ''; + }, [selectedEntity]); + + const getStartedLink = useMemo(() => { + if (selectedEntity === 'logs') { + return ROUTES.LOGS_EXPLORER; + } + if (selectedEntity === 'traces') { + return ROUTES.TRACES_EXPLORER; + } + if (selectedEntity === 'metrics') { + return ROUTES.METRICS_EXPLORER_EXPLORER; + } + return ''; + }, [selectedEntity]); + const emptyStateCard = (): JSX.Element => (
@@ -115,13 +165,7 @@ export default function SavedViews({ {user?.role !== USER_ROLES.VIEWER && (
- +
)} + + {selectedEntity === 'metrics' && metricsViewsError && ( +
+
+ Oops, something went wrong while loading your saved views. +
+
+ )}
); @@ -246,11 +298,19 @@ export default function SavedViews({ logEvent('Homepage: Saved views switched', { tab, }); - setSelectedEntityViews(tab === 'logs' ? logsViews : tracesViews); + let currentViews: ViewProps[] = []; + if (tab === 'logs') { + currentViews = logsViews; + } else if (tab === 'traces') { + currentViews = tracesViews; + } else if (tab === 'metrics') { + currentViews = metricsViews; + } + setSelectedEntityViews(currentViews); setSelectedEntity(tab); }; - if (logsViewsLoading || tracesViewsLoading) { + if (logsViewsLoading || tracesViewsLoading || metricsViewsLoading) { return ( @@ -260,7 +320,7 @@ export default function SavedViews({ ); } - if (logsViewsError || tracesViewsError) { + if (logsViewsError || tracesViewsError || metricsViewsError) { return ( @@ -299,6 +359,16 @@ export default function SavedViews({ > Traces + @@ -312,13 +382,7 @@ export default function SavedViews({ {selectedEntityViews.length > 0 && (
- +