mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-25 17:52:23 +00:00
Compare commits
6 Commits
ns/waterfa
...
fix/remove
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c77b5b80fd | ||
|
|
cb1a2a8a13 | ||
|
|
1a5d37b25a | ||
|
|
43509681fa | ||
|
|
ff5fcc0e98 | ||
|
|
122d88c4d2 |
@@ -308,3 +308,15 @@ export const PublicDashboardPage = Loadable(
|
||||
/* webpackChunkName: "Public Dashboard Page" */ 'pages/PublicDashboard'
|
||||
),
|
||||
);
|
||||
|
||||
export const AlertTypeSelectionPage = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "Alert Type Selection Page" */ 'pages/AlertTypeSelection'
|
||||
),
|
||||
);
|
||||
|
||||
export const MeterExplorerPage = Loadable(
|
||||
() =>
|
||||
import(/* webpackChunkName: "Meter Explorer Page" */ 'pages/MeterExplorer'),
|
||||
);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { RouteProps } from 'react-router-dom';
|
||||
import ROUTES from 'constants/routes';
|
||||
import AlertTypeSelectionPage from 'pages/AlertTypeSelection';
|
||||
import MessagingQueues from 'pages/MessagingQueues';
|
||||
import MeterExplorer from 'pages/MeterExplorer';
|
||||
|
||||
import {
|
||||
AlertHistory,
|
||||
AlertOverview,
|
||||
AlertTypeSelectionPage,
|
||||
AllAlertChannels,
|
||||
AllErrors,
|
||||
ApiMonitoring,
|
||||
@@ -29,6 +27,8 @@ import {
|
||||
LogsExplorer,
|
||||
LogsIndexToFields,
|
||||
LogsSaveViews,
|
||||
MessagingQueuesMainPage,
|
||||
MeterExplorerPage,
|
||||
MetricsExplorer,
|
||||
OldLogsExplorer,
|
||||
Onboarding,
|
||||
@@ -399,28 +399,28 @@ const routes: AppRoutes[] = [
|
||||
{
|
||||
path: ROUTES.MESSAGING_QUEUES_KAFKA,
|
||||
exact: true,
|
||||
component: MessagingQueues,
|
||||
component: MessagingQueuesMainPage,
|
||||
key: 'MESSAGING_QUEUES_KAFKA',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.MESSAGING_QUEUES_CELERY_TASK,
|
||||
exact: true,
|
||||
component: MessagingQueues,
|
||||
component: MessagingQueuesMainPage,
|
||||
key: 'MESSAGING_QUEUES_CELERY_TASK',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.MESSAGING_QUEUES_OVERVIEW,
|
||||
exact: true,
|
||||
component: MessagingQueues,
|
||||
component: MessagingQueuesMainPage,
|
||||
key: 'MESSAGING_QUEUES_OVERVIEW',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.MESSAGING_QUEUES_KAFKA_DETAIL,
|
||||
exact: true,
|
||||
component: MessagingQueues,
|
||||
component: MessagingQueuesMainPage,
|
||||
key: 'MESSAGING_QUEUES_KAFKA_DETAIL',
|
||||
isPrivate: true,
|
||||
},
|
||||
@@ -463,21 +463,21 @@ const routes: AppRoutes[] = [
|
||||
{
|
||||
path: ROUTES.METER,
|
||||
exact: true,
|
||||
component: MeterExplorer,
|
||||
component: MeterExplorerPage,
|
||||
key: 'METER',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.METER_EXPLORER,
|
||||
exact: true,
|
||||
component: MeterExplorer,
|
||||
component: MeterExplorerPage,
|
||||
key: 'METER_EXPLORER',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.METER_EXPLORER_VIEWS,
|
||||
exact: true,
|
||||
component: MeterExplorer,
|
||||
component: MeterExplorerPage,
|
||||
key: 'METER_EXPLORER_VIEWS',
|
||||
isPrivate: true,
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -103,9 +103,10 @@ function K8sClustersList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -105,9 +105,10 @@ function K8sDaemonSetsList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -106,9 +106,10 @@ function K8sDeploymentsList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -101,9 +101,10 @@ function K8sJobsList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { safeParseJSON } from './commonUtils';
|
||||
import { INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory } from './constants';
|
||||
import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel';
|
||||
import { IEntityColumn } from './utils';
|
||||
@@ -58,9 +59,10 @@ function K8sHeader({
|
||||
const urlFilters = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS);
|
||||
let { filters } = currentQuery.builder.queryData[0];
|
||||
if (urlFilters) {
|
||||
const decoded = decodeURIComponent(urlFilters);
|
||||
const parsed = JSON.parse(decoded);
|
||||
filters = parsed;
|
||||
const parsed = safeParseJSON<IBuilderQuery['filters']>(urlFilters);
|
||||
if (parsed) {
|
||||
filters = parsed;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...currentQuery,
|
||||
|
||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -104,9 +104,10 @@ function K8sNamespacesList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -99,9 +99,10 @@ function K8sNodesList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -92,9 +92,10 @@ function K8sPodsList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -105,9 +105,10 @@ function K8sStatefulSetsList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { getOrderByFromParams } from '../commonUtils';
|
||||
import { getOrderByFromParams, safeParseJSON } from '../commonUtils';
|
||||
import {
|
||||
GetK8sEntityToAggregateAttribute,
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS,
|
||||
@@ -105,9 +105,10 @@ function K8sVolumesList({
|
||||
const [groupBy, setGroupBy] = useState<IBuilderQuery['groupBy']>(() => {
|
||||
const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY);
|
||||
if (groupBy) {
|
||||
const decoded = decodeURIComponent(groupBy);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['groupBy'];
|
||||
const parsed = safeParseJSON<IBuilderQuery['groupBy']>(groupBy);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Table, Tooltip, Typography } from 'antd';
|
||||
import { Progress } from 'antd/lib';
|
||||
@@ -260,6 +261,19 @@ export const filterDuplicateFilters = (
|
||||
return uniqueFilters;
|
||||
};
|
||||
|
||||
export const safeParseJSON = <T,>(value: string): T | null => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value) as T;
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON from URL parameter:', e);
|
||||
// TODO: Should we capture this error in Sentry?
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getOrderByFromParams = (
|
||||
searchParams: URLSearchParams,
|
||||
returnNullAsDefault = false,
|
||||
@@ -271,9 +285,12 @@ export const getOrderByFromParams = (
|
||||
INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY,
|
||||
);
|
||||
if (orderByFromParams) {
|
||||
const decoded = decodeURIComponent(orderByFromParams);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as { columnName: string; order: 'asc' | 'desc' };
|
||||
const parsed = safeParseJSON<{ columnName: string; order: 'asc' | 'desc' }>(
|
||||
orderByFromParams,
|
||||
);
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
if (returnNullAsDefault) {
|
||||
return null;
|
||||
@@ -287,13 +304,7 @@ export const getFiltersFromParams = (
|
||||
): IBuilderQuery['filters'] | null => {
|
||||
const filtersFromParams = searchParams.get(queryKey);
|
||||
if (filtersFromParams) {
|
||||
try {
|
||||
const decoded = decodeURIComponent(filtersFromParams);
|
||||
const parsed = JSON.parse(decoded);
|
||||
return parsed as IBuilderQuery['filters'];
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
return safeParseJSON<IBuilderQuery['filters']>(filtersFromParams);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1046,19 +1046,7 @@ func (r *ClickHouseReader) GetWaterfallSpansForTraceWithMetadata(ctx context.Con
|
||||
}
|
||||
|
||||
processingPostCache := time.Now()
|
||||
limit := min(req.Limit, tracedetail.MaxLimitToSelectAllSpans)
|
||||
selectAllSpans := totalSpans <= uint64(limit)
|
||||
|
||||
var (
|
||||
selectedSpans []*model.Span
|
||||
uncollapsedSpans []string
|
||||
rootServiceName, rootServiceEntryPoint string
|
||||
)
|
||||
if selectAllSpans {
|
||||
selectedSpans, rootServiceName, rootServiceEntryPoint = tracedetail.GetAllSpans(traceRoots)
|
||||
} else {
|
||||
selectedSpans, uncollapsedSpans, rootServiceName, rootServiceEntryPoint = tracedetail.GetSelectedSpans(req.UncollapsedSpans, req.SelectedSpanID, traceRoots, spanIdToSpanNodeMap, req.IsSelectedSpanIDUnCollapsed)
|
||||
}
|
||||
selectedSpans, uncollapsedSpans, rootServiceName, rootServiceEntryPoint := tracedetail.GetSelectedSpans(req.UncollapsedSpans, req.SelectedSpanID, traceRoots, spanIdToSpanNodeMap, req.IsSelectedSpanIDUnCollapsed)
|
||||
zap.L().Info("getWaterfallSpansForTraceWithMetadata: processing post cache", zap.Duration("duration", time.Since(processingPostCache)), zap.String("traceID", traceID))
|
||||
|
||||
// convert start timestamp to millis because right now frontend is expecting it in millis
|
||||
@@ -1071,7 +1059,7 @@ func (r *ClickHouseReader) GetWaterfallSpansForTraceWithMetadata(ctx context.Con
|
||||
}
|
||||
|
||||
response.Spans = selectedSpans
|
||||
response.UncollapsedSpans = uncollapsedSpans // ignoring if all spans are returning
|
||||
response.UncollapsedSpans = uncollapsedSpans
|
||||
response.StartTimestampMillis = startTime / 1000000
|
||||
response.EndTimestampMillis = endTime / 1000000
|
||||
response.TotalSpansCount = totalSpans
|
||||
@@ -1080,7 +1068,6 @@ func (r *ClickHouseReader) GetWaterfallSpansForTraceWithMetadata(ctx context.Con
|
||||
response.RootServiceEntryPoint = rootServiceEntryPoint
|
||||
response.ServiceNameToTotalDurationMap = serviceNameToTotalDurationMap
|
||||
response.HasMissingSpans = hasMissingSpans
|
||||
response.HasMore = !selectAllSpans
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1212,7 +1199,7 @@ func (r *ClickHouseReader) GetFlamegraphSpansForTrace(ctx context.Context, orgID
|
||||
}
|
||||
}
|
||||
|
||||
selectedSpans = tracedetail.GetAllSpansForFlamegraph(traceRoots, spanIdToSpanNodeMap)
|
||||
selectedSpans = tracedetail.GetSelectedSpansForFlamegraph(traceRoots, spanIdToSpanNodeMap)
|
||||
traceCache := model.GetFlamegraphSpansForTraceCache{
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
@@ -1229,20 +1216,12 @@ func (r *ClickHouseReader) GetFlamegraphSpansForTrace(ctx context.Context, orgID
|
||||
}
|
||||
|
||||
processingPostCache := time.Now()
|
||||
selectedSpansForRequest := selectedSpans
|
||||
limit := min(req.Limit, tracedetail.MaxLimitWithoutSampling)
|
||||
totalSpanCount := tracedetail.GetTotalSpanCount(selectedSpans)
|
||||
if totalSpanCount > uint64(limit) {
|
||||
boundaryStart, boundaryEnd := utils.MilliToNano(req.BoundaryStartTS), utils.MilliToNano(req.BoundaryEndTS)
|
||||
selectedSpansForRequest = tracedetail.GetSelectedSpansForFlamegraphForRequest(req.SelectedSpanID, selectedSpans, boundaryStart, boundaryEnd)
|
||||
}
|
||||
zap.L().Info("getFlamegraphSpansForTrace: processing post cache", zap.Duration("duration", time.Since(processingPostCache)), zap.String("traceID", traceID),
|
||||
zap.Uint64("totalSpanCount", totalSpanCount))
|
||||
selectedSpansForRequest := tracedetail.GetSelectedSpansForFlamegraphForRequest(req.SelectedSpanID, selectedSpans, startTime, endTime)
|
||||
zap.L().Info("getFlamegraphSpansForTrace: processing post cache", zap.Duration("duration", time.Since(processingPostCache)), zap.String("traceID", traceID))
|
||||
|
||||
trace.Spans = selectedSpansForRequest
|
||||
trace.StartTimestampMillis = startTime / 1000000
|
||||
trace.EndTimestampMillis = endTime / 1000000
|
||||
trace.HasMore = totalSpanCount > uint64(limit)
|
||||
return trace, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
flamegraphSpanLevelLimit float64 = 50
|
||||
flamegraphSpanLimitPerLevel int = 1000
|
||||
flamegraphSamplingBucketCount int = 500
|
||||
|
||||
MaxLimitWithoutSampling uint = 120_000
|
||||
SPAN_LIMIT_PER_REQUEST_FOR_FLAMEGRAPH float64 = 50
|
||||
SPAN_LIMIT_PER_LEVEL int = 100
|
||||
TIMESTAMP_SAMPLING_BUCKET_COUNT int = 50
|
||||
)
|
||||
|
||||
func ContainsFlamegraphSpan(slice []*model.FlamegraphSpan, item *model.FlamegraphSpan) bool {
|
||||
@@ -54,8 +52,7 @@ func FindIndexForSelectedSpan(spans [][]*model.FlamegraphSpan, selectedSpanId st
|
||||
return selectedSpanLevel
|
||||
}
|
||||
|
||||
// GetAllSpansForFlamegraph groups all spans as per their level
|
||||
func GetAllSpansForFlamegraph(traceRoots []*model.FlamegraphSpan, spanIdToSpanNodeMap map[string]*model.FlamegraphSpan) [][]*model.FlamegraphSpan {
|
||||
func GetSelectedSpansForFlamegraph(traceRoots []*model.FlamegraphSpan, spanIdToSpanNodeMap map[string]*model.FlamegraphSpan) [][]*model.FlamegraphSpan {
|
||||
|
||||
var traceIdLevelledFlamegraph = map[string]map[int64][]*model.FlamegraphSpan{}
|
||||
selectedSpans := [][]*model.FlamegraphSpan{}
|
||||
@@ -103,7 +100,7 @@ func getLatencyAndTimestampBucketedSpans(spans []*model.FlamegraphSpan, selected
|
||||
})
|
||||
|
||||
// pick the top 5 latency spans
|
||||
for idx := range 100 {
|
||||
for idx := range 5 {
|
||||
sampledSpans = append(sampledSpans, spans[idx])
|
||||
}
|
||||
|
||||
@@ -113,7 +110,6 @@ func getLatencyAndTimestampBucketedSpans(spans []*model.FlamegraphSpan, selected
|
||||
for _idx, span := range spans {
|
||||
if span.SpanID == selectedSpanID {
|
||||
idx = _idx
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx != -1 {
|
||||
@@ -121,17 +117,17 @@ func getLatencyAndTimestampBucketedSpans(spans []*model.FlamegraphSpan, selected
|
||||
}
|
||||
}
|
||||
|
||||
bucketSize := (endTime - startTime) / uint64(flamegraphSamplingBucketCount)
|
||||
bucketSize := (endTime - startTime) / uint64(TIMESTAMP_SAMPLING_BUCKET_COUNT)
|
||||
if bucketSize == 0 {
|
||||
bucketSize = 1
|
||||
}
|
||||
|
||||
bucketedSpans := make([][]*model.FlamegraphSpan, flamegraphSamplingBucketCount)
|
||||
bucketedSpans := make([][]*model.FlamegraphSpan, 50)
|
||||
|
||||
for _, span := range spans {
|
||||
if span.TimeUnixNano >= startTime && span.TimeUnixNano <= endTime {
|
||||
bucketIndex := int((span.TimeUnixNano - startTime) / bucketSize)
|
||||
if bucketIndex >= 0 && bucketIndex < flamegraphSamplingBucketCount {
|
||||
if bucketIndex >= 0 && bucketIndex < 50 {
|
||||
bucketedSpans[bucketIndex] = append(bucketedSpans[bucketIndex], span)
|
||||
}
|
||||
}
|
||||
@@ -160,8 +156,8 @@ func GetSelectedSpansForFlamegraphForRequest(selectedSpanID string, selectedSpan
|
||||
selectedIndex = FindIndexForSelectedSpan(selectedSpans, selectedSpanID)
|
||||
}
|
||||
|
||||
lowerLimit := selectedIndex - int(flamegraphSpanLevelLimit*0.4)
|
||||
upperLimit := selectedIndex + int(flamegraphSpanLevelLimit*0.6)
|
||||
lowerLimit := selectedIndex - int(SPAN_LIMIT_PER_REQUEST_FOR_FLAMEGRAPH*0.4)
|
||||
upperLimit := selectedIndex + int(SPAN_LIMIT_PER_REQUEST_FOR_FLAMEGRAPH*0.6)
|
||||
|
||||
if lowerLimit < 0 {
|
||||
upperLimit = upperLimit - lowerLimit
|
||||
@@ -178,7 +174,7 @@ func GetSelectedSpansForFlamegraphForRequest(selectedSpanID string, selectedSpan
|
||||
}
|
||||
|
||||
for i := lowerLimit; i < upperLimit; i++ {
|
||||
if len(selectedSpans[i]) > flamegraphSpanLimitPerLevel {
|
||||
if len(selectedSpans[i]) > SPAN_LIMIT_PER_LEVEL {
|
||||
_spans := getLatencyAndTimestampBucketedSpans(selectedSpans[i], selectedSpanID, i == selectedIndex, startTime, endTime)
|
||||
selectedSpansForRequest = append(selectedSpansForRequest, _spans)
|
||||
} else {
|
||||
@@ -188,12 +184,3 @@ func GetSelectedSpansForFlamegraphForRequest(selectedSpanID string, selectedSpan
|
||||
|
||||
return selectedSpansForRequest
|
||||
}
|
||||
|
||||
func GetTotalSpanCount(spans [][]*model.FlamegraphSpan) uint64 {
|
||||
levelCount := len(spans)
|
||||
spanCount := uint64(0)
|
||||
for i := range levelCount {
|
||||
spanCount += uint64(len(spans[i]))
|
||||
}
|
||||
return spanCount
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@ import (
|
||||
|
||||
var (
|
||||
SPAN_LIMIT_PER_REQUEST_FOR_WATERFALL float64 = 500
|
||||
|
||||
maxDepthForSelectedSpanChildren int = 5
|
||||
MaxLimitToSelectAllSpans uint = 10_000
|
||||
)
|
||||
|
||||
type Interval struct {
|
||||
@@ -91,11 +88,8 @@ func getPathFromRootToSelectedSpanId(node *model.Span, selectedSpanId string, un
|
||||
return isPresentInSubtreeForTheNode, spansFromRootToNode
|
||||
}
|
||||
|
||||
func traverseTrace(span *model.Span, uncollapsedSpans []string, level uint64, isPartOfPreOrder bool, hasSibling bool, selectedSpanId string,
|
||||
depthFromSelectedSpan int, isSelectedSpanIDUnCollapsed bool, selectAllSpan bool) ([]*model.Span, []string) {
|
||||
|
||||
func traverseTrace(span *model.Span, uncollapsedSpans []string, level uint64, isPartOfPreOrder bool, hasSibling bool, selectedSpanId string) []*model.Span {
|
||||
preOrderTraversal := []*model.Span{}
|
||||
autoExpandedSpans := []string{}
|
||||
|
||||
// sort the children to maintain the order across requests
|
||||
sort.Slice(span.Children, func(i, j int) bool {
|
||||
@@ -132,40 +126,15 @@ func traverseTrace(span *model.Span, uncollapsedSpans []string, level uint64, is
|
||||
preOrderTraversal = append(preOrderTraversal, &nodeWithoutChildren)
|
||||
}
|
||||
|
||||
nextDepthFromSelectedSpan := -1
|
||||
if span.SpanID == selectedSpanId && isSelectedSpanIDUnCollapsed {
|
||||
nextDepthFromSelectedSpan = 1
|
||||
} else if depthFromSelectedSpan >= 1 && depthFromSelectedSpan < maxDepthForSelectedSpanChildren {
|
||||
nextDepthFromSelectedSpan = depthFromSelectedSpan + 1
|
||||
}
|
||||
|
||||
for index, child := range span.Children {
|
||||
// A child is included in the pre-order output if its parent is uncollapsed
|
||||
// OR if the child falls within MAX_DEPTH_FOR_SELECTED_SPAN_CHILDREN levels
|
||||
// below the selected span.
|
||||
isChildWithinMaxDepth := nextDepthFromSelectedSpan >= 1
|
||||
isAlreadyUncollapsed := slices.Contains(uncollapsedSpans, span.SpanID)
|
||||
childIsPartOfPreOrder := isPartOfPreOrder && (isAlreadyUncollapsed || isChildWithinMaxDepth)
|
||||
if selectAllSpan {
|
||||
childIsPartOfPreOrder = true
|
||||
}
|
||||
|
||||
if isPartOfPreOrder && isChildWithinMaxDepth && !isAlreadyUncollapsed {
|
||||
if !slices.Contains(autoExpandedSpans, span.SpanID) {
|
||||
autoExpandedSpans = append(autoExpandedSpans, span.SpanID)
|
||||
}
|
||||
}
|
||||
|
||||
_childTraversal, _autoExpanded := traverseTrace(child, uncollapsedSpans, level+1, childIsPartOfPreOrder, index != (len(span.Children)-1), selectedSpanId,
|
||||
nextDepthFromSelectedSpan, isSelectedSpanIDUnCollapsed, selectAllSpan)
|
||||
_childTraversal := traverseTrace(child, uncollapsedSpans, level+1, isPartOfPreOrder && slices.Contains(uncollapsedSpans, span.SpanID), index != (len(span.Children)-1), selectedSpanId)
|
||||
preOrderTraversal = append(preOrderTraversal, _childTraversal...)
|
||||
autoExpandedSpans = append(autoExpandedSpans, _autoExpanded...)
|
||||
nodeWithoutChildren.SubTreeNodeCount += child.SubTreeNodeCount + 1
|
||||
span.SubTreeNodeCount += child.SubTreeNodeCount + 1
|
||||
}
|
||||
|
||||
nodeWithoutChildren.SubTreeNodeCount += 1
|
||||
return preOrderTraversal, autoExpandedSpans
|
||||
return preOrderTraversal
|
||||
|
||||
}
|
||||
|
||||
@@ -199,13 +168,7 @@ func GetSelectedSpans(uncollapsedSpans []string, selectedSpanID string, traceRoo
|
||||
_, spansFromRootToNode := getPathFromRootToSelectedSpanId(rootNode, selectedSpanID, updatedUncollapsedSpans, isSelectedSpanIDUnCollapsed)
|
||||
updatedUncollapsedSpans = append(updatedUncollapsedSpans, spansFromRootToNode...)
|
||||
|
||||
_preOrderTraversal, _autoExpanded := traverseTrace(rootNode, updatedUncollapsedSpans, 0, true, false, selectedSpanID, -1, isSelectedSpanIDUnCollapsed, false)
|
||||
// Merge auto-expanded spans into updatedUncollapsedSpans for returning in response
|
||||
for _, spanID := range _autoExpanded {
|
||||
if !slices.Contains(updatedUncollapsedSpans, spanID) {
|
||||
updatedUncollapsedSpans = append(updatedUncollapsedSpans, spanID)
|
||||
}
|
||||
}
|
||||
_preOrderTraversal := traverseTrace(rootNode, updatedUncollapsedSpans, 0, true, false, selectedSpanID)
|
||||
_selectedSpanIndex := findIndexForSelectedSpanFromPreOrder(_preOrderTraversal, selectedSpanID)
|
||||
|
||||
if _selectedSpanIndex != -1 {
|
||||
@@ -249,17 +212,3 @@ func GetSelectedSpans(uncollapsedSpans []string, selectedSpanID string, traceRoo
|
||||
|
||||
return preOrderTraversal[startIndex:endIndex], updatedUncollapsedSpans, rootServiceName, rootServiceEntryPoint
|
||||
}
|
||||
|
||||
func GetAllSpans(traceRoots []*model.Span) (spans []*model.Span, rootServiceName, rootEntryPoint string) {
|
||||
for _, root := range traceRoots {
|
||||
childSpans, _ := traverseTrace(root, nil, 0, true, false, "", -1, false, true)
|
||||
spans = append(spans, childSpans...)
|
||||
if rootServiceName == "" {
|
||||
rootServiceName = root.ServiceName
|
||||
}
|
||||
if rootEntryPoint == "" {
|
||||
rootEntryPoint = root.Name
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -333,14 +333,10 @@ type GetWaterfallSpansForTraceWithMetadataParams struct {
|
||||
SelectedSpanID string `json:"selectedSpanId"`
|
||||
IsSelectedSpanIDUnCollapsed bool `json:"isSelectedSpanIDUnCollapsed"`
|
||||
UncollapsedSpans []string `json:"uncollapsedSpans"`
|
||||
Limit uint `json:"limit"`
|
||||
}
|
||||
|
||||
type GetFlamegraphSpansForTraceParams struct {
|
||||
SelectedSpanID string `json:"selectedSpanId"`
|
||||
Limit uint `json:"limit"`
|
||||
BoundaryStartTS uint64 `json:"boundaryStartTsMilli"`
|
||||
BoundaryEndTS uint64 `json:"boundarEndTsMilli"`
|
||||
SelectedSpanID string `json:"selectedSpanId"`
|
||||
}
|
||||
|
||||
type SpanFilterParams struct {
|
||||
|
||||
@@ -329,7 +329,6 @@ type GetWaterfallSpansForTraceWithMetadataResponse struct {
|
||||
HasMissingSpans bool `json:"hasMissingSpans"`
|
||||
// this is needed for frontend and query service sync
|
||||
UncollapsedSpans []string `json:"uncollapsedSpans"`
|
||||
HasMore bool `json:"hasMore"`
|
||||
}
|
||||
|
||||
type GetFlamegraphSpansForTraceResponse struct {
|
||||
@@ -337,7 +336,6 @@ type GetFlamegraphSpansForTraceResponse struct {
|
||||
EndTimestampMillis uint64 `json:"endTimestampMillis"`
|
||||
DurationNano uint64 `json:"durationNano"`
|
||||
Spans [][]*FlamegraphSpan `json:"spans"`
|
||||
HasMore bool `json:"hasMore"`
|
||||
}
|
||||
|
||||
type OtelSpanRef struct {
|
||||
|
||||
@@ -17,7 +17,3 @@ func Elapsed(funcName string, args map[string]interface{}) func() {
|
||||
zap.L().Info("Elapsed time", zapFields...)
|
||||
}
|
||||
}
|
||||
|
||||
func MilliToNano(milliTS uint64) uint64 {
|
||||
return milliTS * 1000_000
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user