Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support in-page-search in trace view on key=value pairs #1391

Merged
merged 5 commits into from
Jun 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote the tooltip to make the explanation a bit more clear and to remove the styling applied to some key words which was difficult to read on the dark background.

);
};

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