Skip to content

Commit fdcd37d

Browse files
authored
feat(otlp): Allow searching trace waterfall by span name (#97712)
Allow queries like `name:select` and `span.name:select`, which will search on `span.name` attribute, if possible. This is technically only relevant for OTel folks right now, but feature-flagging this seemed like it's not needed, since `span.name` isn't in autocomplete, or anywhere a user might accidentally spot it.
1 parent 60d675a commit fdcd37d

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

static/app/views/performance/newTraceDetails/traceModels/traceTreeTestUtils.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export function makeEAPSpan(
7777
event_id: overrides.event_id ?? uuid4(),
7878
op: 'span.op',
7979
description: 'span.description',
80+
name: 'span.name',
8081
start_timestamp: 0,
8182
end_timestamp: 10,
8283
is_transaction: false,

static/app/views/performance/newTraceDetails/traceSearch/traceSearchEvaluator.spec.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,60 @@ describe('TraceSearchEvaluator', () => {
590590
expect(cb.mock.calls[0][0][0]).toEqual([{index: 1, value: tree.list[1]}]);
591591
expect(cb.mock.calls[0][0][2]).toBeNull();
592592
});
593+
594+
it('name filter', async () => {
595+
const tree = TraceTree.FromTrace(
596+
[makeEAPSpan({name: 'authentication'}), makeEAPSpan({name: 'database'})],
597+
{
598+
meta: null,
599+
replay: null,
600+
}
601+
);
602+
603+
const cb = jest.fn();
604+
search('name:authentication', tree, cb);
605+
await waitFor(() => expect(cb).toHaveBeenCalled());
606+
expect(cb.mock.calls[0][0][1].size).toBe(1);
607+
expect(cb.mock.calls[0][0][0]).toEqual([{index: 1, value: tree.list[1]}]);
608+
expect(cb.mock.calls[0][0][2]).toBeNull();
609+
});
610+
611+
it('name filter with prefix', async () => {
612+
const tree = TraceTree.FromTrace(
613+
[makeEAPSpan({name: 'authentication'}), makeEAPSpan({name: 'database'})],
614+
{
615+
meta: null,
616+
replay: null,
617+
}
618+
);
619+
620+
const cb = jest.fn();
621+
search('span.name:authentication', tree, cb);
622+
await waitFor(() => expect(cb).toHaveBeenCalled());
623+
expect(cb.mock.calls[0][0][1].size).toBe(1);
624+
expect(cb.mock.calls[0][0][0]).toEqual([{index: 1, value: tree.list[1]}]);
625+
expect(cb.mock.calls[0][0][2]).toBeNull();
626+
});
627+
628+
it('name free text search', async () => {
629+
const tree = TraceTree.FromTrace(
630+
[
631+
makeEAPSpan({name: 'user_authentication_service', op: 'http'}),
632+
makeEAPSpan({name: 'database_query', op: 'db'}),
633+
],
634+
{
635+
meta: null,
636+
replay: null,
637+
}
638+
);
639+
640+
const cb = jest.fn();
641+
search('authentication', tree, cb);
642+
await waitFor(() => expect(cb).toHaveBeenCalled());
643+
expect(cb.mock.calls[0][0][1].size).toBe(1);
644+
expect(cb.mock.calls[0][0][0]).toEqual([{index: 1, value: tree.list[1]}]);
645+
expect(cb.mock.calls[0][0][2]).toBeNull();
646+
});
593647
});
594648

595649
describe('synthetic keys', () => {

static/app/views/performance/newTraceDetails/traceSearch/traceSearchEvaluator.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,13 @@ function evaluateNodeFreeText(
604604
if (node.value.description?.includes(query)) {
605605
return true;
606606
}
607+
if (
608+
'name' in node.value &&
609+
typeof node.value.name === 'string' &&
610+
node.value.name.includes(query)
611+
) {
612+
return true;
613+
}
607614

608615
const spanId = 'span_id' in node.value ? node.value.span_id : node.value.event_id;
609616
if (spanId && spanId === query) {
@@ -630,6 +637,13 @@ function evaluateNodeFreeText(
630637
if (node.value.description?.includes(query)) {
631638
return true;
632639
}
640+
if (
641+
'name' in node.value &&
642+
typeof node.value.name === 'string' &&
643+
node.value.name.includes(query)
644+
) {
645+
return true;
646+
}
633647
}
634648

635649
if (isTraceErrorNode(node) || isEAPErrorNode(node)) {

static/app/views/performance/newTraceDetails/traceSearch/traceTokenConverter.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ const SPAN_DURATION_SYNTHETIC_KEYS: SpanKey[] = [
7676
'self_time',
7777
];
7878

79+
// New span keys that are available on Span-First SDK and OTLP SDK spans for
80+
// now. In the future we'll backfill the `name` key for all spans, and this can
81+
// be moved into `SPAN_TEXT_KEYS`
82+
const SPAN_FIRST_TEXT_KEYS = ['name'];
83+
7984
// @TODO the current date parsing does not support timestamps, so we
8085
// exclude these keys for now and parse them as numeric keys
8186
const SPAN_DATE_KEYS: SpanKey[] = [
@@ -98,6 +103,7 @@ const TEXT_KEYS = new Set([
98103
...SYNTHETIC_KEYS,
99104
...withPrefixedPermutation('transaction', TRANSACTION_TEXT_KEYS),
100105
...withPrefixedPermutation('span', SPAN_TEXT_KEYS),
106+
...withPrefixedPermutation('span', SPAN_FIRST_TEXT_KEYS),
101107
]);
102108
const NUMERIC_KEYS = new Set([
103109
...withPrefixedPermutation('transaction', TRANSACTION_NUMERIC_KEYS),

0 commit comments

Comments
 (0)