Skip to content
This repository has been archived by the owner on Aug 9, 2022. It is now read-only.

Commit

Permalink
Allow filtering by traceGroup and service (#28)
Browse files Browse the repository at this point in the history
* Move service map to dashboard

* Add back filters

* Remove services table search bar

* Add service map filter

* Fix percentile

* Make unrelated services transparent

* Update jest tests

* Fix trace count in dashboard

* Fix ticks

* Fix parent span query

* Fix trace group filters for service map

* Revert plot query filters

* Use trace count in histogram

* Use trace count in error rate histogram

* Rename plots

* services view respecting filters

* Add special case for latency and errors filter

* Fix service map color calculation

* Fix error rate plot query

* Rename traceGroup field to traceGroup.name

* Update dashboard queries to calculate on parent span level

* Update filter fields to use parent span latency and errors

* Update traces queries to calculate on parent span level

* Update jest

* Fix percentile filter DSL

* UX changes

* Fix service map query targetResource to use traceGroup.name

* Update jest tests
  • Loading branch information
joshuali925 authored and zhongnansu committed Apr 6, 2021
1 parent 039b940 commit 16ac24f
Show file tree
Hide file tree
Showing 34 changed files with 1,974 additions and 1,498 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ exports[`Search bar components renders search bar 1`] = `
isLoading={false}
onChange={[Function]}
onSearch={[MockFunction]}
placeholder="Trace ID, trace group name"
placeholder="Trace ID, trace group name, service name"
value="test"
>
<EuiFormControlLayout
Expand All @@ -433,7 +433,7 @@ exports[`Search bar components renders search bar 1`] = `
data-test-subj="search-bar-input-box"
onChange={[Function]}
onKeyUp={[Function]}
placeholder="Trace ID, trace group name"
placeholder="Trace ID, trace group name, service name"
type="search"
value="test"
/>
Expand Down
10 changes: 5 additions & 5 deletions public/components/common/__tests__/helper_functions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ describe('Helper functions', () => {
);
expect(DSL).toEqual(
JSON.parse(
`{"field":"Latency percentile within trace group","operator":"","value":">= 95th","inverted":false,"disabled":false,"custom":{"query":{"bool":{"must":[],"filter":[],"should":[{"bool":{"must":[{"term":{"name":{"value":"order"}}},{"range":{"durationInNanos":{"gte":1000}}}]}}],"must_not":[],"minimum_should_match":1}}}}`
`{"field":"Latency percentile within trace group","operator":"","value":">= 95th","inverted":false,"disabled":false,"custom":{"query":{"bool":{"must":[],"filter":[],"should":[{"bool":{"must":[{"term":{"traceGroup.name":{"value":"order"}}},{"range":{"traceGroup.durationInNanos":{"gte":1000}}}]}}],"must_not":[],"minimum_should_match":1}}}}`
)
);
});

it('converts filters to DSL', () => {
const getTestFilters = (field = 'traceGroup', operator = 'exists') => [
const getTestFilters = (field = 'traceGroup.name', operator = 'exists') => [
[
{
field,
Expand All @@ -139,12 +139,12 @@ describe('Helper functions', () => {
];
const existsDSL = filtersToDsl(...getTestFilters());
expect(JSON.stringify(existsDSL)).toEqual(
`{"query":{"bool":{"must":[{"range":{"startTime":{"gte":"now-5m","lte":"now"}}},{"query_string":{"query":"order"}},{"exists":{"field":"traceGroup"}}],"filter":[],"should":[],"must_not":[]}},"custom":{"timeFilter":[{"range":{"startTime":{"gte":"now-5m","lte":"now"}}}],"serviceNames":[],"serviceNamesExclude":[],"traceGroup":[{"from":"100","to":"∞"}],"traceGroupExclude":[],"percentiles":{"query":{"bool":{"should":[]}}}}}`
"{\"query\":{\"bool\":{\"must\":[{\"range\":{\"startTime\":{\"gte\":\"now-5m\",\"lte\":\"now\"}}},{\"query_string\":{\"query\":\"order\"}},{\"exists\":{\"field\":\"traceGroup.name\"}}],\"filter\":[],\"should\":[],\"must_not\":[]}},\"custom\":{\"timeFilter\":[{\"range\":{\"startTime\":{\"gte\":\"now-5m\",\"lte\":\"now\"}}}],\"serviceNames\":[],\"serviceNamesExclude\":[],\"traceGroup\":[],\"traceGroupExclude\":[],\"percentiles\":{\"query\":{\"bool\":{\"should\":[]}}}}}"
);

const isDSL = filtersToDsl(...getTestFilters('traceGroup', 'is'));
const isDSL = filtersToDsl(...getTestFilters('traceGroup.name', 'is'));
expect(JSON.stringify(isDSL)).toEqual(
`{"query":{"bool":{"must":[{"range":{"startTime":{"gte":"now-5m","lte":"now"}}},{"query_string":{"query":"order"}},{"term":{"traceGroup":{"from":"100","to":"∞"}}}],"filter":[],"should":[],"must_not":[]}},"custom":{"timeFilter":[{"range":{"startTime":{"gte":"now-5m","lte":"now"}}}],"serviceNames":[],"serviceNamesExclude":[],"traceGroup":[{"from":"100","to":"∞"}],"traceGroupExclude":[],"percentiles":{"query":{"bool":{"should":[]}}}}}`
"{\"query\":{\"bool\":{\"must\":[{\"range\":{\"startTime\":{\"gte\":\"now-5m\",\"lte\":\"now\"}}},{\"query_string\":{\"query\":\"order\"}},{\"term\":{\"traceGroup.name\":{\"from\":\"100\",\"to\":\"∞\"}}}],\"filter\":[],\"should\":[],\"must_not\":[]}},\"custom\":{\"timeFilter\":[{\"range\":{\"startTime\":{\"gte\":\"now-5m\",\"lte\":\"now\"}}}],\"serviceNames\":[],\"serviceNamesExclude\":[],\"traceGroup\":[],\"traceGroupExclude\":[],\"percentiles\":{\"query\":{\"bool\":{\"should\":[]}}}}}"
);
const isBetweenDSL = filtersToDsl(...getTestFilters('durationInNanos', 'is between'));
expect(JSON.stringify(isBetweenDSL)).toEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ exports[`Filter popover component renders filter popover 1`] = `
filterFieldOptions={
Array [
Object {
"label": "traceGroup",
"label": "traceGroup.name",
},
Object {
"label": "status.code",
"label": "serviceName",
},
Object {
"label": "error",
},
Object {
"label": "status.message",
},
Object {
"label": "durationInNanos",
"label": "latency",
},
]
}
Expand Down Expand Up @@ -97,16 +100,19 @@ exports[`Filter popover component renders filter popover 1`] = `
options={
Array [
Object {
"label": "traceGroup",
"label": "traceGroup.name",
},
Object {
"label": "serviceName",
},
Object {
"label": "status.code",
"label": "error",
},
Object {
"label": "status.message",
},
Object {
"label": "durationInNanos",
"label": "latency",
},
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Filter popover component', () => {
wrapper
.find('input')
.at(0)
.simulate('change', [{ label: 'traceGroup' }]);
.simulate('change', [{ label: 'traceGroup.name' }]);
wrapper
.find('input')
.at(1)
Expand Down
35 changes: 22 additions & 13 deletions public/components/common/filters/__tests__/filter_helpers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
getFilterFields,
getInvertedOperator,
getOperatorOptions,
getType,
getValidFilterFields,
getValueComponent,
} from '../filter_helpers';
Expand All @@ -29,25 +28,33 @@ describe('Filter helper functions', () => {

it('returns fields by page', () => {
const fields = getFilterFields('dashboard');
expect(fields).toEqual(['traceGroup', 'status.code', 'status.message', 'durationInNanos']);
expect(fields).toEqual([
'traceGroup.name',
'serviceName',
'error',
'status.message',
'latency',
]);
});

it('returns valid fields by page', () => {
const dashboardFields = getValidFilterFields('dashboard');
const servicesFields = getValidFilterFields('services');
expect(dashboardFields).toEqual([
'traceGroup',
'status.code',
'traceGroup.name',
'serviceName',
'error',
'status.message',
'durationInNanos',
'latency',
'Latency percentile within trace group',
]);
expect(servicesFields).toEqual([]);
});

it('returns types by fields', () => {
const durationType = getType('durationInNanos');
expect(durationType).toEqual('long');
expect(servicesFields).toEqual([
'traceGroup.name',
'serviceName',
'error',
'status.message',
'latency',
]);
});

it('returns inverted operators', () => {
Expand Down Expand Up @@ -85,7 +92,7 @@ describe('Filter helper functions', () => {

it('renders textfield filter', () => {
const setValue = jest.fn((v) => {});
const wrapper = mount(getValueComponent('is', 0, setValue));
const wrapper = mount(getValueComponent('serviceName', 'is', 0, setValue));
expect(wrapper).toMatchSnapshot();

wrapper.find('input').simulate('change', { target: { value: '100' } });
Expand All @@ -94,7 +101,9 @@ describe('Filter helper functions', () => {

it('renders range field filter', () => {
const setValue = jest.fn((v) => {});
const wrapper = mount(getValueComponent('is not between', { from: '0', to: '100' }, setValue));
const wrapper = mount(
getValueComponent('latency', 'is not between', { from: '0', to: '100' }, setValue)
);
expect(wrapper).toMatchSnapshot();

wrapper
Expand Down
12 changes: 10 additions & 2 deletions public/components/common/filters/filter_edit_popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,19 @@ export function FilterEditPopover(props: {
</EuiFlexItem>
</EuiFlexGroup>
{selectedOperatorOptions.length > 0 &&
getValueComponent(selectedOperatorOptions[0].label, filterValue, setFilterValue)}
getValueComponent(
selectedFieldOptions[0].label,
selectedOperatorOptions[0].label,
filterValue,
setFilterValue
)}
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty data-test-subj="filter-popover-cancel-button" onClick={props.closePopover}>
<EuiButtonEmpty
data-test-subj="filter-popover-cancel-button"
onClick={props.closePopover}
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
Expand Down
60 changes: 47 additions & 13 deletions public/components/common/filters/filter_helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
* permissions and limitations under the License.
*/

import { EuiFieldText, EuiFormControlLayoutDelimited, EuiFormRow, EuiSpacer } from '@elastic/eui';
import {
EuiComboBox,
EuiFieldText,
EuiFormControlLayoutDelimited,
EuiFormRow,
EuiSpacer,
} from '@elastic/eui';
import _ from 'lodash';
import React from 'react';

const getFields = (page) =>
const getFields = (page: 'dashboard' | 'traces' | 'services') =>
({
dashboard: ['traceGroup', 'status.code', 'status.message', 'durationInNanos'],
traces: [
'traceId',
'traceGroup',
'status.code',
'status.message',
'durationInNanos',
],
services: [],
dashboard: ['traceGroup.name', 'serviceName', 'error', 'status.message', 'latency'],
traces: ['traceId', 'traceGroup.name', 'serviceName', 'error', 'status.message', 'latency'],
services: ['traceGroup.name', 'serviceName', 'error', 'status.message', 'latency'],
}[page]);
// filters will take effect and can be manually added
export const getFilterFields = (page: 'dashboard' | 'traces' | 'services') => getFields(page);
Expand All @@ -38,7 +38,7 @@ export const getValidFilterFields = (page: 'dashboard' | 'traces' | 'services')
return fields;
};

export const getType = (field: string): string => {
const getType = (field: string): string => {
const typeMapping = {
attributes: {
host: {
Expand All @@ -63,6 +63,7 @@ export const getType = (field: string): string => {
port: 'long',
},
durationInNanos: 'long',
latency: 'long',
endTime: 'date_nanos',
startTime: 'date_nanos',
};
Expand Down Expand Up @@ -121,7 +122,12 @@ export const getOperatorOptions = (field: string) => {
return operators;
};

export const getValueComponent = (operator: string, value: any, setValue: (v: any) => void) => {
export const getValueComponent = (
field: string,
operator: string,
value: any,
setValue: (v: any) => void
) => {
const textField = (
<>
<EuiSpacer size="s" />
Expand Down Expand Up @@ -183,6 +189,34 @@ export const getValueComponent = (operator: string, value: any, setValue: (v: an
);
};

const getComboBoxField = () => {
return (
<>
<EuiSpacer size="s" />
<EuiFormRow label={'Value'}>
<EuiComboBox
placeholder="Select a value"
options={[
{
label: 'true',
},
{
label: 'false',
},
]}
onChange={setValue}
selectedOptions={value || []}
singleSelection={true}
/>
</EuiFormRow>
</>
);
};

if (field === 'error' && (operator === 'is' || operator === 'is not')) {
return getComboBoxField();
}

const valueMapping = {
is: textField,
'is not': textField,
Expand Down
4 changes: 3 additions & 1 deletion public/components/common/filters/filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ export function Filters(props: FiltersOwnProps) {
const value =
typeof filter.value === 'string'
? filter.value
: `${filter.value.from} to ${filter.value.to}`;
: Array.isArray(filter.value) // combo box
? filter.value[0].label
: `${filter.value.from} to ${filter.value.to}`; // range selector
const filterLabel = filter.inverted ? (
<>
<EuiTextColor color={disabled ? 'default' : 'danger'}>{'NOT '}</EuiTextColor>
Expand Down
Loading

0 comments on commit 16ac24f

Please sign in to comment.