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 b76ac05048 Add trace attribute exclude actions
b76ac05048 is described below
commit b76ac05048295f694d87fa58004e07010120b5d8
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 22:21:16 2026 +0800
Add trace attribute exclude actions
---
web-next/app/trace/manage/page.test.tsx | 9 ++++
web-next/app/trace/manage/trace-manage-page.tsx | 71 ++++++++++++++++++++++++-
web-next/lib/i18n-runtime-messages.ts | 4 ++
3 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/web-next/app/trace/manage/page.test.tsx
b/web-next/app/trace/manage/page.test.tsx
index b1030f9fa2..4af6d12017 100644
--- a/web-next/app/trace/manage/page.test.tsx
+++ b/web-next/app/trace/manage/page.test.tsx
@@ -1793,12 +1793,16 @@ describe('trace manage page', () => {
expect(source).toContain('selectedSpanAttributeRows');
expect(source).toContain('selectedResourceAttributeRows');
expect(source).toContain('buildTraceResourceFilterExpression');
+ expect(source).toContain('buildTraceResourceExcludeFilterExpression');
expect(source).toContain('buildTraceSpanAttributeFilterExpression');
+ expect(source).toContain('buildTraceSpanAttributeExcludeFilterExpression');
expect(source).toContain('buildTraceSpanAttributeGroupBy');
expect(source).toContain('mergeTraceResourceFilterExpression');
expect(source).toContain('applyTraceResourceFilter');
+ expect(source).toContain('excludeTraceResourceFilter');
expect(source).toContain('replaceTraceResourceFilter');
expect(source).toContain('applyTraceSpanAttributeFilter');
+ expect(source).toContain('excludeTraceSpanAttributeFilter');
expect(source).toContain('replaceTraceSpanAttributeFilter');
expect(source).toContain('applyTraceSpanAttributeGroupBy');
expect(source).toContain('buildTraceResourceGroupBy');
@@ -1807,6 +1811,8 @@ describe('trace manage page', () => {
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-filter-out-action="true"');
+
expect(source).toContain('data-trace-manage-drawer-span-attribute-filter-out-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('data-trace-manage-drawer-span-attribute-group-action="true"');
@@ -1820,6 +1826,9 @@ describe('trace manage page', () => {
expect(source).toContain('data-trace-manage-drawer-resource-filter-action="true"');
expect(source).toContain('data-trace-manage-drawer-resource-filter-action-owner="hertzbeat-ui-button"');
expect(source).toContain("t('trace.manage.drawer.attributes.filter-action')");
+
expect(source).toContain('data-trace-manage-drawer-resource-filter-out-action="true"');
+
expect(source).toContain('data-trace-manage-drawer-resource-filter-out-action-owner="hertzbeat-ui-button"');
+
expect(source).toContain("t('trace.manage.drawer.attributes.filter-out-action')");
expect(source).toContain('data-trace-manage-drawer-resource-replace-action="true"');
expect(source).toContain('data-trace-manage-drawer-resource-replace-action-owner="hertzbeat-ui-button"');
expect(source).toContain("t('trace.manage.drawer.attributes.replace-action')");
diff --git a/web-next/app/trace/manage/trace-manage-page.tsx
b/web-next/app/trace/manage/trace-manage-page.tsx
index 25c32ae4fd..23d8130095 100644
--- a/web-next/app/trace/manage/trace-manage-page.tsx
+++ b/web-next/app/trace/manage/trace-manage-page.tsx
@@ -506,10 +506,23 @@ function buildTraceResourceFilterExpression(name:
React.ReactNode, value: React.
return `${key}=${filterValue}`;
}
+function buildTraceResourceExcludeFilterExpression(name: React.ReactNode,
value: React.ReactNode) {
+ const key = String(name ?? '').trim();
+ const filterValue = String(value ?? '').trim();
+ if (!isSafeTraceResourceFilterKey(key) ||
!isSafeTraceResourceFilterValue(filterValue)) {
+ return null;
+ }
+ return `${key}!=${filterValue}`;
+}
+
function buildTraceSpanAttributeFilterExpression(name: React.ReactNode, value:
React.ReactNode) {
return buildTraceResourceFilterExpression(name, value);
}
+function buildTraceSpanAttributeExcludeFilterExpression(name: React.ReactNode,
value: React.ReactNode) {
+ return buildTraceResourceExcludeFilterExpression(name, value);
+}
+
function buildTraceSpanAttributeGroupBy(name: React.ReactNode) {
const key = String(name ?? '').trim();
if (!isSafeTraceResourceFilterKey(key)) {
@@ -763,9 +776,11 @@ function TraceWaterfallDrawer({
onSelectEvent,
onApplyOperationFilter,
onApplySpanAttributeFilter,
+ onExcludeSpanAttributeFilter,
onReplaceSpanAttributeFilter,
onApplySpanAttributeGroupBy,
onApplyResourceFilter,
+ onExcludeResourceFilter,
onReplaceResourceFilter,
onApplyResourceGroupBy
}: {
@@ -779,9 +794,11 @@ function TraceWaterfallDrawer({
onSelectEvent: (eventKey: string | null, spanId?: string) => void;
onApplyOperationFilter: (operationName: string) => void;
onApplySpanAttributeFilter: (name: string, value: string) => void;
+ onExcludeSpanAttributeFilter: (name: string, value: string) => void;
onReplaceSpanAttributeFilter: (name: string, value: string) => void;
onApplySpanAttributeGroupBy: (name: string) => void;
onApplyResourceFilter: (name: string, value: string) => void;
+ onExcludeResourceFilter: (name: string, value: string) => void;
onReplaceResourceFilter: (name: string, value: string) => void;
onApplyResourceGroupBy: (name: string) => void;
}) {
@@ -1217,7 +1234,7 @@ function TraceWaterfallDrawer({
title: row.title,
copy: row.copy,
meta: row.meta,
- action: buildTraceSpanAttributeFilterExpression(row.title,
row.copy) || buildTraceSpanAttributeGroupBy(row.title) ? (
+ action: buildTraceSpanAttributeFilterExpression(row.title,
row.copy) || buildTraceSpanAttributeExcludeFilterExpression(row.title,
row.copy) || buildTraceSpanAttributeGroupBy(row.title) ? (
<HzActionGroup
data-trace-manage-drawer-span-attribute-action-group="filter-group"
data-trace-manage-drawer-span-attribute-action-group-owner="hertzbeat-ui-action-group"
@@ -1238,6 +1255,19 @@ function TraceWaterfallDrawer({
<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-filter-out-action="true"
+
data-trace-manage-drawer-span-attribute-filter-out-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={() =>
onExcludeSpanAttributeFilter(row.title, row.copy)}
+
aria-label={t('trace.manage.drawer.attributes.filter-out-action.aria', { name:
row.title, value: row.copy })}
+ >
+ <HzButtonIcon icon={X}
data-trace-manage-drawer-span-attribute-filter-out-action-icon="exclude"
data-trace-manage-drawer-span-attribute-filter-out-action-icon-owner="hertzbeat-ui-button-icon"
/>
+
{t('trace.manage.drawer.attributes.filter-out-action')}
+ </HzButton>
<HzButton
data-trace-manage-drawer-span-attribute-replace-action="true"
data-trace-manage-drawer-span-attribute-replace-action-owner="hertzbeat-ui-button"
@@ -1281,7 +1311,7 @@ function TraceWaterfallDrawer({
title: row.title,
copy: row.copy,
meta: row.meta,
- action: buildTraceResourceFilterExpression(row.title,
row.copy) || buildTraceResourceGroupBy(row.title) ? (
+ action: buildTraceResourceFilterExpression(row.title,
row.copy) || buildTraceResourceExcludeFilterExpression(row.title, row.copy) ||
buildTraceResourceGroupBy(row.title) ? (
<HzActionGroup
data-trace-manage-drawer-resource-action-group="filter-group"
data-trace-manage-drawer-resource-action-group-owner="hertzbeat-ui-action-group"
@@ -1302,6 +1332,19 @@ function TraceWaterfallDrawer({
<HzButtonIcon icon={Filter}
data-trace-manage-drawer-resource-filter-action-icon="filter"
data-trace-manage-drawer-resource-filter-action-icon-owner="hertzbeat-ui-button-icon"
/>
{t('trace.manage.drawer.attributes.filter-action')}
</HzButton>
+ <HzButton
+
data-trace-manage-drawer-resource-filter-out-action="true"
+
data-trace-manage-drawer-resource-filter-out-action-owner="hertzbeat-ui-button"
+
data-trace-manage-drawer-resource-filter-name={row.title}
+
data-trace-manage-drawer-resource-filter-value={row.copy}
+ size="sm"
+ intent="secondary"
+ onClick={() => onExcludeResourceFilter(row.title,
row.copy)}
+
aria-label={t('trace.manage.drawer.attributes.filter-out-action.aria', { name:
row.title, value: row.copy })}
+ >
+ <HzButtonIcon icon={X}
data-trace-manage-drawer-resource-filter-out-action-icon="exclude"
data-trace-manage-drawer-resource-filter-out-action-icon-owner="hertzbeat-ui-button-icon"
/>
+
{t('trace.manage.drawer.attributes.filter-out-action')}
+ </HzButton>
<HzButton
data-trace-manage-drawer-resource-replace-action="true"
data-trace-manage-drawer-resource-replace-action-owner="hertzbeat-ui-button"
@@ -1821,6 +1864,17 @@ function TraceExplorer({
applyQuery(nextQuery);
}, [applyQuery, draft, setDraft]);
+ const excludeTraceResourceFilter = useCallback((name: string, value: string)
=> {
+ const expression = buildTraceResourceExcludeFilterExpression(name, value);
+ if (!expression) return;
+ const nextQuery: TraceQueryState = {
+ ...draft,
+ resourceFilter: mergeTraceResourceFilterExpression(draft.resourceFilter,
expression)
+ };
+ setDraft(nextQuery);
+ applyQuery(nextQuery);
+ }, [applyQuery, draft, setDraft]);
+
const replaceTraceResourceFilter = useCallback((name: string, value: string)
=> {
const expression = buildTraceResourceFilterExpression(name, value);
if (!expression) return;
@@ -1843,6 +1897,17 @@ function TraceExplorer({
applyQuery(nextQuery);
}, [applyQuery, draft, setDraft]);
+ const excludeTraceSpanAttributeFilter = useCallback((name: string, value:
string) => {
+ const expression = buildTraceSpanAttributeExcludeFilterExpression(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;
@@ -3400,9 +3465,11 @@ function TraceExplorer({
onSelectEvent={(eventKey, spanId) => setTraceDetailDrawer(previous
=> ({ ...previous, selectedSpanId: spanId || previous.selectedSpanId,
selectedEventKey: eventKey }))}
onApplyOperationFilter={operationName =>
applyTraceQuickFilter('operationName', operationName)}
onApplySpanAttributeFilter={applyTraceSpanAttributeFilter}
+ onExcludeSpanAttributeFilter={excludeTraceSpanAttributeFilter}
onReplaceSpanAttributeFilter={replaceTraceSpanAttributeFilter}
onApplySpanAttributeGroupBy={applyTraceSpanAttributeGroupBy}
onApplyResourceFilter={applyTraceResourceFilter}
+ onExcludeResourceFilter={excludeTraceResourceFilter}
onReplaceResourceFilter={replaceTraceResourceFilter}
onApplyResourceGroupBy={applyTraceResourceGroupBy}
/>
diff --git a/web-next/lib/i18n-runtime-messages.ts
b/web-next/lib/i18n-runtime-messages.ts
index d96900c35c..80cc7506a9 100644
--- a/web-next/lib/i18n-runtime-messages.ts
+++ b/web-next/lib/i18n-runtime-messages.ts
@@ -2737,6 +2737,8 @@ export const SUPPLEMENTAL_MESSAGES:
Partial<Record<LocaleCode, Messages>> = {
'trace.manage.drawer.attributes.resource.meta': 'resourceAttributes',
'trace.manage.drawer.attributes.filter-action': 'Filter',
'trace.manage.drawer.attributes.filter-action.aria': 'Filter {{name}}
equals {{value}}',
+ 'trace.manage.drawer.attributes.filter-out-action': 'Exclude',
+ 'trace.manage.drawer.attributes.filter-out-action.aria': 'Exclude traces
where {{name}} equals {{value}}',
'trace.manage.drawer.attributes.replace-action': 'Replace',
'trace.manage.drawer.attributes.replace-action.aria': 'Replace filters
with {{name}} equals {{value}}',
'trace.manage.drawer.attributes.empty.title': 'No attributes',
@@ -7195,6 +7197,8 @@ export const SUPPLEMENTAL_MESSAGES:
Partial<Record<LocaleCode, Messages>> = {
'trace.manage.drawer.attributes.resource.meta': 'resourceAttributes',
'trace.manage.drawer.attributes.filter-action': '过滤',
'trace.manage.drawer.attributes.filter-action.aria': '过滤 {{name}} 等于
{{value}}',
+ 'trace.manage.drawer.attributes.filter-out-action': '排除',
+ 'trace.manage.drawer.attributes.filter-out-action.aria': '排除 {{name}} 等于
{{value}} 的链路',
'trace.manage.drawer.attributes.replace-action': '替换',
'trace.manage.drawer.attributes.replace-action.aria': '替换为 {{name}} 等于
{{value}} 的过滤条件',
'trace.manage.drawer.attributes.empty.title': '无属性',
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]