Skip to content

Commit

Permalink
Support in-page-search in trace view on key=value pairs (#1391)
Browse files Browse the repository at this point in the history
Previously one could only filter on `key` or `value`. This wasn't useful
when one needed to find spans with `result=false` in a trace view.

With this change one can use `key=value` as a search term, for example:

* `http.method=GET`

Before (no matches):

<img width="1231" alt="image"
src="https://user-images.githubusercontent.com/89186/236012512-f97a67e8-8bf8-4372-b698-d0f34383b441.png">

After (a successful match):

<img width="1231" alt="image"
src="https://user-images.githubusercontent.com/89186/236012540-7c011a81-ca63-41d2-9051-cad4c5eab02e.png">

---------

Signed-off-by: Ivan Babrou <[email protected]>
Signed-off-by: Yuri Shkuro <[email protected]>
Co-authored-by: Yuri Shkuro <[email protected]>
  • Loading branch information
bobrik and yurishkuro authored Jun 4, 2023
1 parent 9924af6 commit 77203ce
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import * as React from 'react';
import { Button, Input } from 'antd';
import { Button, Input, Tooltip } from 'antd';
import cx from 'classnames';
import IoAndroidLocate from 'react-icons/lib/io/android-locate';

Expand Down Expand Up @@ -56,49 +56,79 @@ export function TracePageSearchBarFn(props: TracePageSearchBarProps & { forwarde
suffix: count,
};

const renderTooltip = () => {
return (
<div style={{ wordBreak: 'normal' }}>
<p>
This is an in-page search. Enter the query as a list of space-separated string terms. Each term is
used in a substring match against any of the following data elements: service name, operation name,
span ID, and key-value pairs in tags and logs. The spans that match any of the search terms will be
highlighted.
</p>
<p>
When matching key-value pairs, the substring search is applied separately against the key, the
value, and the concatenated <code>&quot;key=value&quot;</code> string. The latter allows searching
for exact matches like <code>http.status_code=200</code>.
</p>
<p>
To preclude certain key-value pairs from participating in the matching, prefix the key with the
minus <code>&apos;-&apos;</code> sign, e.g., <code>-http.status_code</code>.
</p>
</div>
);
};

return (
<div className="TracePageSearchBar">
{/* style inline because compact overwrites the display */}
<Input.Group className="ub-justify-end" compact style={{ display: 'flex' }}>
<UiFindInput
inputProps={uiFindInputInputProps}
forwardedRef={forwardedRef}
trackFindFunction={trackFilter}
/>
{navigable && (
<>
<Button
className={cx(btnClass, 'TracePageSearchBar--locateBtn')}
disabled={!textFilter}
htmlType="button"
onClick={focusUiFindMatches}
>
<IoAndroidLocate />
</Button>
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="up"
onClick={prevResult}
/>
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="down"
onClick={nextResult}
/>
</>
)}
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="close"
onClick={clearSearch}
/>
</Input.Group>
<Tooltip
arrowPointAtCenter
mouseLeaveDelay={0.5}
placement="bottom"
overlayStyle={{ maxWidth: '600px' }} // This is a large tooltip and the default is too narrow.
title={renderTooltip()}
>
{/* style inline because compact overwrites the display */}
<Input.Group className="ub-justify-end" compact style={{ display: 'flex' }}>
<UiFindInput
inputProps={uiFindInputInputProps}
forwardedRef={forwardedRef}
trackFindFunction={trackFilter}
/>
{navigable && (
<>
<Button
className={cx(btnClass, 'TracePageSearchBar--locateBtn')}
disabled={!textFilter}
htmlType="button"
onClick={focusUiFindMatches}
>
<IoAndroidLocate />
</Button>
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="up"
onClick={prevResult}
/>
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="down"
onClick={nextResult}
/>
</>
)}
<Button
className={btnClass}
disabled={!textFilter}
htmlType="button"
icon="close"
onClick={clearSearch}
/>
</Input.Group>
</Tooltip>
</div>
);
}
Expand Down
18 changes: 18 additions & 0 deletions packages/jaeger-ui/src/utils/filter-spans.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ describe('filterSpans', () => {
expect(filterSpans('tagValue2', spans)).toEqual(new Set([spanID2]));
});

it("should return spans whose tags' kv.key=kv.value match a filter", () => {
expect(filterSpans('tagKey1=tagValue1', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('tagKey0=tagValue0', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('tagKey2=tagValue1', spans)).toEqual(new Set([spanID2]));
});

it("should exclude span whose tags' kv.value or kv.key match a filter if the key matches an excludeKey", () => {
expect(filterSpans('tagValue1 -tagKey2', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('tagValue1 -tagKey1', spans)).toEqual(new Set([spanID2]));
Expand All @@ -177,6 +183,12 @@ describe('filterSpans', () => {
expect(filterSpans('logFieldValue2', spans)).toEqual(new Set([spanID2]));
});

it('should return spans whose logs have a field whose kv.key=kv.value match a filter', () => {
expect(filterSpans('logFieldKey1=logFieldValue1', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('logFieldKey0=logFieldValue0', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('logFieldKey2=logFieldValue1', spans)).toEqual(new Set([spanID2]));
});

it('should exclude span whose logs have a field whose kv.value or kv.key match a filter if the key matches an excludeKey', () => {
expect(filterSpans('logFieldValue1 -logFieldKey2', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('logFieldValue1 -logFieldKey1', spans)).toEqual(new Set([spanID2]));
Expand All @@ -199,6 +211,12 @@ describe('filterSpans', () => {
expect(filterSpans('processTagValue2', spans)).toEqual(new Set([spanID2]));
});

it("should return spans whose process.processTags' kv.keykv.value match a filter", () => {
expect(filterSpans('processTagKey1=processTagValue1', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('processTagKey0=processTagValue0', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('processTagKey2=processTagValue1', spans)).toEqual(new Set([spanID2]));
});

it("should exclude span whose process.processTags' kv.value or kv.key match a filter if the key matches an excludeKey", () => {
expect(filterSpans('processTagValue1 -processTagKey2', spans)).toEqual(new Set([spanID0]));
expect(filterSpans('processTagValue1 -processTagKey1', spans)).toEqual(new Set([spanID2]));
Expand Down
6 changes: 4 additions & 2 deletions packages/jaeger-ui/src/utils/filter-spans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ export default function filterSpans(textFilter: string, spans: Span[] | TNil) {
? kvs.some(kv => {
// ignore checking key and value for a match if key is in excludeKeys
if (isTextInFilters(excludeKeys, kv.key)) return false;
// match if key or value matches an item in includeFilters
// match if key, value or key=value string matches an item in includeFilters
return (
isTextInFilters(includeFilters, kv.key) || isTextInFilters(includeFilters, kv.value.toString())
isTextInFilters(includeFilters, kv.key) ||
isTextInFilters(includeFilters, kv.value.toString()) ||
isTextInFilters(includeFilters, `${kv.key}=${kv.value.toString()}`)
);
})
: false;
Expand Down
2 changes: 1 addition & 1 deletion scripts/check-license.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

licRes=$(
for file in $(find scripts typings packages/*/src packages/*/test packages/plexus/demo -type f -iregex '.*\.[cjt]ss*x*$' \! -name 'layout.worker.bundled.js'); do
for file in $(find scripts packages/*/src packages/*/test packages/plexus/demo -type f -iregex '.*\.[cjt]ss*x*$' \! -name 'layout.worker.bundled.js'); do
head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" || echo " ${file}"
done;)
if [ -n "${licRes}" ]; then
Expand Down

0 comments on commit 77203ce

Please sign in to comment.