feat: introduce search trace ID component (#1551)

* feat: searchTraceID checkpoint
* feat: filter spans using TraceID from trace filter page

Co-authored-by: palashgdev <palashgdev@gmail.com>
Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
This commit is contained in:
Vishal Sharma 2022-09-12 19:35:31 +05:30 committed by GitHub
parent 1ec9248975
commit eaadc3bb95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 170 additions and 7 deletions

View File

@ -0,0 +1,127 @@
import { Input, notification } from 'antd';
import getFilters from 'api/trace/getFilters';
import { AxiosError } from 'axios';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { getFilter, updateURL } from 'store/actions/trace/util';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { UPDATE_ALL_FILTERS } from 'types/actions/trace';
import { GlobalReducer } from 'types/reducer/globalTime';
import { TraceReducer } from 'types/reducer/trace';
const { Search } = Input;
function TraceID(): JSX.Element {
const {
selectedFilter,
filterToFetchData,
spansAggregate,
selectedTags,
userSelectedFilter,
isFilterExclude,
} = useSelector<AppState, TraceReducer>((state) => state.traces);
const dispatch = useDispatch<Dispatch<AppActions>>();
const globalTime = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const [isLoading, setIsLoading] = useState(false);
const [userEnteredValue, setUserEnteredValue] = useState<string>('');
useEffect(() => {
setUserEnteredValue(selectedFilter.get('traceID')?.[0] || '');
}, [selectedFilter]);
const onSearch = async (value: string): Promise<void> => {
try {
setIsLoading(true);
const preSelectedFilter = new Map(selectedFilter);
const preUserSelected = new Map(userSelectedFilter);
if (value !== '') {
preUserSelected.set('traceID', [value]);
preSelectedFilter.set('traceID', [value]);
} else {
preUserSelected.delete('traceID');
preSelectedFilter.delete('traceID');
}
const response = await getFilters({
other: Object.fromEntries(preSelectedFilter),
end: String(globalTime.maxTime),
start: String(globalTime.minTime),
getFilters: filterToFetchData,
isFilterExclude,
});
if (response.statusCode === 200) {
const preFilter = getFilter(response.payload);
preFilter.set('traceID', { traceID: value });
preFilter.forEach((value, key) => {
const values = Object.keys(value);
if (key !== 'duration' && values.length) {
preUserSelected.set(key, values);
}
});
dispatch({
type: UPDATE_ALL_FILTERS,
payload: {
current: spansAggregate.currentPage,
filter: preFilter,
filterToFetchData,
selectedFilter: preSelectedFilter,
selectedTags,
userSelected: preUserSelected,
isFilterExclude,
order: spansAggregate.order,
pageSize: spansAggregate.pageSize,
orderParam: spansAggregate.orderParam,
},
});
updateURL(
preSelectedFilter,
filterToFetchData,
spansAggregate.currentPage,
selectedTags,
isFilterExclude,
userSelectedFilter,
spansAggregate.order,
spansAggregate.pageSize,
spansAggregate.orderParam,
);
}
} catch (error) {
notification.error({
message: (error as AxiosError).toString() || 'Something went wrong',
});
} finally {
setIsLoading(false);
}
};
const onChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
setUserEnteredValue(e.target.value);
};
const onBlur = (): void => {
if (userEnteredValue !== selectedFilter.get('traceID')?.[0]) {
onSearch(userEnteredValue);
}
};
return (
<div>
<Search
placeholder="Filter by Trace ID"
onSearch={onSearch}
style={{
marginBottom: '5rem',
padding: '0 3%',
}}
loading={isLoading}
value={userEnteredValue}
onChange={onChange}
onBlur={onBlur}
/>
</div>
);
}
export default TraceID;

View File

@ -1,3 +1,4 @@
/* eslint-disable no-nested-ternary */
import { Card } from 'antd';
import Spinner from 'components/Spinner';
import React from 'react';
@ -7,6 +8,7 @@ import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
import CommonCheckBox from './CommonCheckBox';
import Duration from './Duration';
import TraceID from './SearchTraceID';
function PanelBody(props: PanelBodyProps): JSX.Element {
const { type } = props;
@ -22,12 +24,17 @@ function PanelBody(props: PanelBodyProps): JSX.Element {
</Card>
);
}
return (
<Card bordered={false}>
{type === 'duration' ? <Duration /> : <CommonCheckBox name={type} />}
</Card>
);
const renderBody = (type: TraceFilterEnum): JSX.Element => {
switch (type) {
case 'traceID':
return <TraceID />;
case 'duration':
return <Duration />;
default:
return <CommonCheckBox name={type} />;
}
};
return <Card bordered={false}>{renderBody(type)}</Card>;
}
interface PanelBodyProps {

View File

@ -16,6 +16,7 @@ export const AllTraceFilterEnum: TraceFilterEnum[] = [
'httpMethod',
'httpRoute',
'httpUrl',
'traceID',
];
function Filters(): JSX.Element {

View File

@ -68,6 +68,7 @@ const initialValue: TraceReducer = {
['responseStatusCode', INITIAL_FILTER_VALUE],
['serviceName', INITIAL_FILTER_VALUE],
['status', INITIAL_FILTER_VALUE],
['traceID', INITIAL_FILTER_VALUE],
]),
};

View File

@ -71,7 +71,8 @@ export type TraceFilterEnum =
| 'serviceName'
| 'status'
| 'responseStatusCode'
| 'rpcMethod';
| 'rpcMethod'
| 'traceID';
export const AllPanelHeading: {
key: TraceFilterEnum;
@ -125,4 +126,8 @@ export const AllPanelHeading: {
key: 'status',
displayValue: 'Status',
},
{
key: 'traceID',
displayValue: 'Trace ID',
},
];

View File

@ -936,6 +936,9 @@ func (r *ClickHouseReader) GetSpanFilters(ctx context.Context, queryParams *mode
}
args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))}
if len(queryParams.TraceID) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.TraceID, constants.TraceID, &query, args)
}
if len(queryParams.ServiceName) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
}
@ -995,6 +998,8 @@ func (r *ClickHouseReader) GetSpanFilters(ctx context.Context, queryParams *mode
for _, e := range queryParams.GetFilters {
switch e {
case constants.TraceID:
continue
case constants.ServiceName:
finalQuery := fmt.Sprintf("SELECT serviceName, count() as count FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", r.traceDB, r.indexTable)
finalQuery += query
@ -1271,6 +1276,9 @@ func (r *ClickHouseReader) GetFilteredSpans(ctx context.Context, queryParams *mo
var query string
args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))}
if len(queryParams.TraceID) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.TraceID, constants.TraceID, &query, args)
}
if len(queryParams.ServiceName) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
}
@ -1461,6 +1469,9 @@ func (r *ClickHouseReader) GetTagFilters(ctx context.Context, queryParams *model
var query string
args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))}
if len(queryParams.TraceID) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.TraceID, constants.TraceID, &query, args)
}
if len(queryParams.ServiceName) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
}
@ -1557,6 +1568,9 @@ func (r *ClickHouseReader) GetTagValues(ctx context.Context, queryParams *model.
var query string
args := []interface{}{clickhouse.Named("timestampL", strconv.FormatInt(queryParams.Start.UnixNano(), 10)), clickhouse.Named("timestampU", strconv.FormatInt(queryParams.End.UnixNano(), 10))}
if len(queryParams.TraceID) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.TraceID, constants.TraceID, &query, args)
}
if len(queryParams.ServiceName) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
}
@ -1864,6 +1878,9 @@ func (r *ClickHouseReader) GetFilteredSpansAggregates(ctx context.Context, query
query = fmt.Sprintf("SELECT toStartOfInterval(timestamp, INTERVAL %d minute) as time, %s FROM %s.%s WHERE timestamp >= @timestampL AND timestamp <= @timestampU", queryParams.StepSeconds/60, aggregation_query, r.traceDB, r.indexTable)
}
if len(queryParams.TraceID) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.TraceID, constants.TraceID, &query, args)
}
if len(queryParams.ServiceName) > 0 {
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
}

View File

@ -41,6 +41,7 @@ var AmChannelApiPath = GetOrDefaultEnv("ALERTMANAGER_API_CHANNEL_PATH", "v1/rout
var RELATIONAL_DATASOURCE_PATH = GetOrDefaultEnv("SIGNOZ_LOCAL_DB_PATH", "/var/lib/signoz/signoz.db")
const (
TraceID = "traceID"
ServiceName = "serviceName"
HttpRoute = "httpRoute"
HttpCode = "httpCode"

View File

@ -182,6 +182,7 @@ type TagQuery struct {
}
type GetFilteredSpansParams struct {
TraceID []string `json:"traceID"`
ServiceName []string `json:"serviceName"`
Operation []string `json:"operation"`
Kind string `json:"kind"`
@ -209,6 +210,7 @@ type GetFilteredSpansParams struct {
}
type GetFilteredSpanAggregatesParams struct {
TraceID []string `json:"traceID"`
ServiceName []string `json:"serviceName"`
Operation []string `json:"operation"`
Kind string `json:"kind"`
@ -237,6 +239,7 @@ type GetFilteredSpanAggregatesParams struct {
}
type SpanFilterParams struct {
TraceID []string `json:"traceID"`
Status []string `json:"status"`
ServiceName []string `json:"serviceName"`
HttpRoute []string `json:"httpRoute"`
@ -259,6 +262,7 @@ type SpanFilterParams struct {
}
type TagFilterParams struct {
TraceID []string `json:"traceID"`
Status []string `json:"status"`
ServiceName []string `json:"serviceName"`
HttpRoute []string `json:"httpRoute"`