This is an automated email from the ASF dual-hosted git repository.
zqr10159 pushed a commit to branch 2.0.0
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/2.0.0 by this push:
new 22549189ac Add trace span attribute filter actions
22549189ac is described below
commit 22549189ac9a5dc34c04cbd00863a94aac6db478
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 22:02:40 2026 +0800
Add trace span attribute filter actions
---
web-next/app/trace/manage/page.test.tsx | 9 ++++
web-next/app/trace/manage/trace-manage-page.tsx | 68 ++++++++++++++++++++++++-
2 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/web-next/app/trace/manage/page.test.tsx
b/web-next/app/trace/manage/page.test.tsx
index 55894d5aa1..725e4a8763 100644
--- a/web-next/app/trace/manage/page.test.tsx
+++ b/web-next/app/trace/manage/page.test.tsx
@@ -1766,13 +1766,22 @@ describe('trace manage page', () => {
expect(source).toContain('selectedSpanAttributeRows');
expect(source).toContain('selectedResourceAttributeRows');
expect(source).toContain('buildTraceResourceFilterExpression');
+ expect(source).toContain('buildTraceSpanAttributeFilterExpression');
expect(source).toContain('mergeTraceResourceFilterExpression');
expect(source).toContain('applyTraceResourceFilter');
expect(source).toContain('replaceTraceResourceFilter');
+ expect(source).toContain('applyTraceSpanAttributeFilter');
+ expect(source).toContain('replaceTraceSpanAttributeFilter');
expect(source).toContain('buildTraceResourceGroupBy');
expect(source).toContain('applyTraceResourceGroupBy');
expect(source).toContain('data-trace-manage-drawer-span-attributes="span-attributes"');
expect(source).toContain('data-trace-manage-drawer-span-attributes-owner="hertzbeat-ui-detail-rows"');
+
expect(source).toContain('data-trace-manage-drawer-span-attribute-filter-action="true"');
+
expect(source).toContain('data-trace-manage-drawer-span-attribute-filter-action-owner="hertzbeat-ui-button"');
+
expect(source).toContain('data-trace-manage-drawer-span-attribute-replace-action="true"');
+
expect(source).toContain('data-trace-manage-drawer-span-attribute-replace-action-owner="hertzbeat-ui-button"');
+ expect(source).toContain('attributeFilter:
mergeTraceResourceFilterExpression(draft.attributeFilter, expression)');
+ expect(source).toContain('attributeFilter: expression');
expect(source).toContain("heading={t('trace.manage.drawer.attributes.span.title')}");
expect(source).toContain("aria-label={t('trace.manage.drawer.attributes.span.aria')}");
expect(source).toContain('data-trace-manage-drawer-resource-attributes="resource-attributes"');
diff --git a/web-next/app/trace/manage/trace-manage-page.tsx
b/web-next/app/trace/manage/trace-manage-page.tsx
index bc8ace5d71..85f3ea1178 100644
--- a/web-next/app/trace/manage/trace-manage-page.tsx
+++ b/web-next/app/trace/manage/trace-manage-page.tsx
@@ -506,6 +506,10 @@ function buildTraceResourceFilterExpression(name:
React.ReactNode, value: React.
return `${key}=${filterValue}`;
}
+function buildTraceSpanAttributeFilterExpression(name: React.ReactNode, value:
React.ReactNode) {
+ return buildTraceResourceFilterExpression(name, value);
+}
+
function buildTraceResourceGroupBy(name: React.ReactNode) {
const key = String(name ?? '').trim();
if (!isSafeTraceResourceFilterKey(key)) {
@@ -745,6 +749,8 @@ function TraceWaterfallDrawer({
onSelectSpan,
onSelectEvent,
onApplyOperationFilter,
+ onApplySpanAttributeFilter,
+ onReplaceSpanAttributeFilter,
onApplyResourceFilter,
onReplaceResourceFilter,
onApplyResourceGroupBy
@@ -758,6 +764,8 @@ function TraceWaterfallDrawer({
onSelectSpan: (spanId: string) => void;
onSelectEvent: (eventKey: string | null, spanId?: string) => void;
onApplyOperationFilter: (operationName: string) => void;
+ onApplySpanAttributeFilter: (name: string, value: string) => void;
+ onReplaceSpanAttributeFilter: (name: string, value: string) => void;
onApplyResourceFilter: (name: string, value: string) => void;
onReplaceResourceFilter: (name: string, value: string) => void;
onApplyResourceGroupBy: (name: string) => void;
@@ -1193,7 +1201,41 @@ function TraceWaterfallDrawer({
key: `span-attribute-${row.title}`,
title: row.title,
copy: row.copy,
- meta: row.meta
+ meta: row.meta,
+ action: buildTraceSpanAttributeFilterExpression(row.title,
row.copy) ? (
+ <HzActionGroup
+
data-trace-manage-drawer-span-attribute-action-group="filter-group"
+
data-trace-manage-drawer-span-attribute-action-group-owner="hertzbeat-ui-action-group"
+ layout="end-wrap"
+ >
+ <HzButton
+
data-trace-manage-drawer-span-attribute-filter-action="true"
+
data-trace-manage-drawer-span-attribute-filter-action-owner="hertzbeat-ui-button"
+
data-trace-manage-drawer-span-attribute-filter-name={row.title}
+
data-trace-manage-drawer-span-attribute-filter-value={row.copy}
+ size="sm"
+ intent="secondary"
+ onClick={() => onApplySpanAttributeFilter(row.title,
row.copy)}
+
aria-label={t('trace.manage.drawer.attributes.filter-action.aria', { name:
row.title, value: row.copy })}
+ >
+ <HzButtonIcon icon={Filter}
data-trace-manage-drawer-span-attribute-filter-action-icon="filter"
data-trace-manage-drawer-span-attribute-filter-action-icon-owner="hertzbeat-ui-button-icon"
/>
+ {t('trace.manage.drawer.attributes.filter-action')}
+ </HzButton>
+ <HzButton
+
data-trace-manage-drawer-span-attribute-replace-action="true"
+
data-trace-manage-drawer-span-attribute-replace-action-owner="hertzbeat-ui-button"
+
data-trace-manage-drawer-span-attribute-filter-name={row.title}
+
data-trace-manage-drawer-span-attribute-filter-value={row.copy}
+ size="sm"
+ intent="secondary"
+ onClick={() =>
onReplaceSpanAttributeFilter(row.title, row.copy)}
+
aria-label={t('trace.manage.drawer.attributes.replace-action.aria', { name:
row.title, value: row.copy })}
+ >
+ <HzButtonIcon icon={Replace}
data-trace-manage-drawer-span-attribute-replace-action-icon="replace"
data-trace-manage-drawer-span-attribute-replace-action-icon-owner="hertzbeat-ui-button-icon"
/>
+ {t('trace.manage.drawer.attributes.replace-action')}
+ </HzButton>
+ </HzActionGroup>
+ ) : null
}))}
/>
<HzDetailRows
@@ -1757,6 +1799,28 @@ function TraceExplorer({
applyQuery(nextQuery);
}, [applyQuery, draft, setDraft]);
+ const applyTraceSpanAttributeFilter = useCallback((name: string, value:
string) => {
+ const expression = buildTraceSpanAttributeFilterExpression(name, value);
+ if (!expression) return;
+ const nextQuery: TraceQueryState = {
+ ...draft,
+ attributeFilter:
mergeTraceResourceFilterExpression(draft.attributeFilter, expression)
+ };
+ setDraft(nextQuery);
+ applyQuery(nextQuery);
+ }, [applyQuery, draft, setDraft]);
+
+ const replaceTraceSpanAttributeFilter = useCallback((name: string, value:
string) => {
+ const expression = buildTraceSpanAttributeFilterExpression(name, value);
+ if (!expression) return;
+ const nextQuery: TraceQueryState = {
+ ...draft,
+ attributeFilter: expression
+ };
+ setDraft(nextQuery);
+ applyQuery(nextQuery);
+ }, [applyQuery, draft, setDraft]);
+
const applyTraceResourceGroupBy = useCallback((name: string) => {
const groupBy = buildTraceResourceGroupBy(name);
if (!groupBy) return;
@@ -3289,6 +3353,8 @@ function TraceExplorer({
onSelectSpan={spanId => setTraceDetailDrawer(previous => ({
...previous, selectedSpanId: spanId, selectedEventKey: null }))}
onSelectEvent={(eventKey, spanId) => setTraceDetailDrawer(previous
=> ({ ...previous, selectedSpanId: spanId || previous.selectedSpanId,
selectedEventKey: eventKey }))}
onApplyOperationFilter={operationName =>
applyTraceQuickFilter('operationName', operationName)}
+ onApplySpanAttributeFilter={applyTraceSpanAttributeFilter}
+ onReplaceSpanAttributeFilter={replaceTraceSpanAttributeFilter}
onApplyResourceFilter={applyTraceResourceFilter}
onReplaceResourceFilter={replaceTraceResourceFilter}
onApplyResourceGroupBy={applyTraceResourceGroupBy}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]