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 86a3f44391 Preserve attribute filters from signal evidence
86a3f44391 is described below
commit 86a3f443919734258077a941cef60a51da5c347a
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 19:42:49 2026 +0800
Preserve attribute filters from signal evidence
---
web-next/lib/signal-dashboards.test.ts | 9 +++++++--
web-next/lib/signal-dashboards.ts | 30 ++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/web-next/lib/signal-dashboards.test.ts
b/web-next/lib/signal-dashboards.test.ts
index 9d0a0cd629..6dc267afb3 100644
--- a/web-next/lib/signal-dashboards.test.ts
+++ b/web-next/lib/signal-dashboards.test.ts
@@ -1823,7 +1823,7 @@ describe('signal dashboards API client', () => {
tags: 'logs,traces,metrics',
layout: '[]',
widgets: JSON.stringify([
- { id: 'logs-panel', signal: 'logs', title: 'Logs', visualization:
'table', route: '/log/manage?view=table' },
+ { id: 'logs-panel', signal: 'logs', title: 'Logs', visualization:
'table', route: '/log/manage?view=table&attributeFilter=region%3Aus' },
{ id: 'trace-panel', signal: 'traces', title: 'Trace', visualization:
'table', route: '/trace/manage?view=table' },
{ id: 'metrics-panel', signal: 'metrics', title: 'Metrics',
visualization: 'time-series', route: '/ingestion/otlp/metrics?query=cpu' },
{ id: 'db-metrics-panel', signal: 'metrics', title: 'DB Metrics',
visualization: 'time-series', route:
'/ingestion/otlp/metrics?query=signoz_db_latency_count&serviceName=checkout&groupBy=db.system'
},
@@ -1976,6 +1976,7 @@ describe('signal dashboards API client', () => {
spanId: 'span-log',
serviceName: 'checkout',
serviceNamespace: 'payments',
+ attributeFilter: 'region:us',
breakoutAttributes: expect.arrayContaining([
expect.objectContaining({ name: 'resource:service.name', value:
'checkout' }),
expect.objectContaining({ name: 'resource:service.namespace',
value: 'payments' }),
@@ -2069,6 +2070,7 @@ describe('signal dashboards API client', () => {
{ name: 'service.name', type: 'query', value: '' },
{ name: 'service.namespace', type: 'query', value: '' },
{ name: 'deployment.environment.name', type: 'query', value: '' },
+ { name: 'attributeFilter', type: 'textbox', value: '' },
{ name: 'hertzbeat.entity_id', type: 'textbox', value: '' },
{ name: 'hertzbeat.entity_type', type: 'dynamic', value: '' },
{ name: 'hertzbeat.entity_name', type: 'query', value: '' },
@@ -2081,6 +2083,7 @@ describe('signal dashboards API client', () => {
expect.objectContaining({ variableName: 'service.name', value:
'checkout', source: 'service' }),
expect.objectContaining({ variableName: 'service.namespace', value:
'payments', source: 'serviceNamespace' }),
expect.objectContaining({ variableName: 'deployment.environment.name',
value: 'prod', source: 'environment' }),
+ expect.objectContaining({ variableName: 'attributeFilter', value:
'region:us', source: 'attributeFilter' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_id', value:
'4200', source: 'entityId' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_type', value:
'service', source: 'entityType' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_name', value:
'Checkout API', source: 'entityName' }),
@@ -2130,6 +2133,7 @@ describe('signal dashboards API client', () => {
expect.objectContaining({ variableName: 'service.name', value:
'checkout', source: 'service', variableType: 'query' }),
expect.objectContaining({ variableName: 'service.namespace', value:
'payments', source: 'serviceNamespace', variableType: 'query' }),
expect.objectContaining({ variableName: 'deployment.environment.name',
value: 'prod', source: 'environment', variableType: 'query' }),
+ expect.objectContaining({ variableName: 'attributeFilter', value:
'region:us', source: 'attributeFilter', variableType: 'textbox' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_id', value:
'4200', source: 'entityId', variableType: 'textbox' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_type', value:
'service', source: 'entityType', variableType: 'dynamic' }),
expect.objectContaining({ variableName: 'hertzbeat.entity_name', value:
'Checkout API', source: 'entityName', variableType: 'query' }),
@@ -2143,6 +2147,7 @@ describe('signal dashboards API client', () => {
{ name: 'service.name', type: 'query', value: '' },
{ name: 'service.namespace', type: 'query', value: '' },
{ name: 'deployment.environment.name', type: 'query', value: '' },
+ { name: 'attributeFilter', type: 'textbox', value: '' },
{ name: 'hertzbeat.entity_id', type: 'textbox', value: '' },
{ name: 'hertzbeat.entity_type', type: 'dynamic', value: '' },
{ name: 'hertzbeat.entity_name', type: 'query', value: '' },
@@ -2216,7 +2221,7 @@ describe('signal dashboards API client', () => {
expect(buildSignalDashboardRuntimeEvidenceSourceHandoff('/log/manage?view=table',
syncTooltip.rows[0], {
timeRange: { start: '1000', end: '3000' },
returnTo: '/dashboard?start=1000&end=3000'
-
})).toBe('/log/manage?view=table&traceId=trace-1&spanId=span-log&serviceName=checkout&serviceNamespace=payments&environment=prod&entityId=4200&entityType=service&entityName=Checkout+API&source=otlp&collector=collector-a&template=spring-boot&returnTo=%2Fdashboard%3Fstart%3D1000%26end%3D3000&start=1000&end=3000');
+
})).toBe('/log/manage?view=table&traceId=trace-1&spanId=span-log&serviceName=checkout&serviceNamespace=payments&attributeFilter=region%3Aus&environment=prod&entityId=4200&entityType=service&entityName=Checkout+API&source=otlp&collector=collector-a&template=spring-boot&returnTo=%2Fdashboard%3Fstart%3D1000%26end%3D3000&start=1000&end=3000');
expect(buildSignalDashboardRuntimeEvidenceSourceHandoff('/trace/manage?view=list',
syncTooltip.rows[4], {
timeRange: { start: '1000', end: '3000' },
returnTo: '/dashboard?start=1000&end=3000'
diff --git a/web-next/lib/signal-dashboards.ts
b/web-next/lib/signal-dashboards.ts
index 13fcc05c18..fc4d3a27a1 100644
--- a/web-next/lib/signal-dashboards.ts
+++ b/web-next/lib/signal-dashboards.ts
@@ -259,6 +259,7 @@ export type SignalDashboardRuntimeSyncTooltipRow = {
serviceName?: string;
serviceNamespace?: string;
resourceFilter?: string;
+ attributeFilter?: string;
operationName?: string;
relatedSignal?: 'logs' | 'traces';
relatedHandoffHref?: string;
@@ -311,6 +312,7 @@ export type SignalDashboardRuntimeEvidenceFilterSource =
| 'environment'
| 'operation'
| 'resourceFilter'
+ | 'attributeFilter'
| 'traceId'
| 'spanId'
| 'entityId'
@@ -3435,6 +3437,24 @@ function syncTooltipRelatedHref(path: string, params:
URLSearchParams, returnTo:
return `${path}?${params.toString()}`;
}
+function syncTooltipRouteDrilldownContext(route: string | undefined) {
+ const normalizedRoute = route?.trim();
+ if (!normalizedRoute) return {};
+ try {
+ const params = new URL(normalizedRoute,
'http://hertzbeat.local').searchParams;
+ const resourceFilter = syncTooltipIdentifier(params.get('resourceFilter')
|| undefined);
+ const attributeFilter =
syncTooltipIdentifier(params.get('attributeFilter') || undefined);
+ const operationName = syncTooltipIdentifier(params.get('operationName') ||
undefined);
+ return {
+ ...(resourceFilter ? { resourceFilter } : {}),
+ ...(attributeFilter ? { attributeFilter } : {}),
+ ...(operationName ? { operationName } : {})
+ };
+ } catch {
+ return {};
+ }
+}
+
function metricLabelValue(labels: Record<string, string>, names: string[]) {
for (const name of names) {
const value = syncTooltipIdentifier(labels[name]);
@@ -3613,6 +3633,7 @@ const SERVICE_NAMESPACE_VARIABLE_NAMES = new
Set(['service.namespace', 'servicen
const ENVIRONMENT_VARIABLE_NAMES = new Set(['deployment.environment.name',
'deployment_environment_name', 'environment']);
const OPERATION_VARIABLE_NAMES = new Set(['operation.name', 'operationname',
'operation_name', 'operation']);
const RESOURCE_FILTER_VARIABLE_NAMES = new Set(['resourcefilter',
'resource.filter', 'resource_filter']);
+const ATTRIBUTE_FILTER_VARIABLE_NAMES = new Set(['attributefilter',
'attribute.filter', 'attribute_filter']);
const TRACE_ID_VARIABLE_NAMES = new Set(['traceid', 'trace.id', 'trace_id']);
const SPAN_ID_VARIABLE_NAMES = new Set(['spanid', 'span.id', 'span_id']);
const ENTITY_ID_VARIABLE_NAMES = new Set(['hertzbeat.entity_id',
'hertzbeat.entity.id', 'entityid', 'entity.id', 'entity_id']);
@@ -3629,6 +3650,7 @@ function evidenceFilterSourceForVariableName(name:
string): SignalDashboardRunti
if (ENVIRONMENT_VARIABLE_NAMES.has(normalizedName)) return 'environment';
if (OPERATION_VARIABLE_NAMES.has(normalizedName)) return 'operation';
if (RESOURCE_FILTER_VARIABLE_NAMES.has(normalizedName)) return
'resourceFilter';
+ if (ATTRIBUTE_FILTER_VARIABLE_NAMES.has(normalizedName)) return
'attributeFilter';
if (TRACE_ID_VARIABLE_NAMES.has(normalizedName)) return 'traceId';
if (SPAN_ID_VARIABLE_NAMES.has(normalizedName)) return 'spanId';
if (ENTITY_ID_VARIABLE_NAMES.has(normalizedName)) return 'entityId';
@@ -3651,6 +3673,7 @@ function evidenceFilterValues(row:
SignalDashboardRuntimeSyncTooltipRow): Record
environment: breakoutValue(row, ['deployment.environment.name',
'environment']),
operation: syncTooltipIdentifier(row.operationName),
resourceFilter: syncTooltipIdentifier(row.resourceFilter),
+ attributeFilter: syncTooltipIdentifier(row.attributeFilter),
traceId: syncTooltipIdentifier(row.traceId),
spanId: syncTooltipIdentifier(row.spanId),
entityId: breakoutValue(row, ['hertzbeat.entity_id',
'hertzbeat.entity.id', 'entity.id', 'entity_id']),
@@ -3684,6 +3707,10 @@ export function
buildSignalDashboardRuntimeEvidenceSourceHandoff(
if (normalizedResourceFilter && (url.pathname === '/trace/manage' ||
url.pathname === '/log/manage') && !url.searchParams.get('resourceFilter')) {
url.searchParams.set('resourceFilter', normalizedResourceFilter);
}
+ const normalizedAttributeFilter =
syncTooltipIdentifier(row.attributeFilter);
+ if (normalizedAttributeFilter && url.pathname === '/log/manage' &&
!url.searchParams.get('attributeFilter')) {
+ url.searchParams.set('attributeFilter', normalizedAttributeFilter);
+ }
const normalizedOperationName = syncTooltipIdentifier(row.operationName);
if (normalizedOperationName && url.pathname === '/trace/manage' &&
!url.searchParams.get('operationName')) {
url.searchParams.set('operationName', normalizedOperationName);
@@ -3752,6 +3779,7 @@ export function
buildSignalDashboardRuntimeEvidenceFilterSuggestions(
{ source: 'environment', variableName: 'deployment.environment.name',
variableType: 'query' },
{ source: 'operation', variableName: 'operation.name', variableType:
'query' },
{ source: 'resourceFilter', variableName: 'resourceFilter', variableType:
'textbox' },
+ { source: 'attributeFilter', variableName: 'attributeFilter',
variableType: 'textbox' },
{ source: 'entityId', variableName: 'hertzbeat.entity_id', variableType:
'textbox' },
{ source: 'entityType', variableName: 'hertzbeat.entity_type',
variableType: 'dynamic' },
{ source: 'entityName', variableName: 'hertzbeat.entity_name',
variableType: 'query' },
@@ -3818,6 +3846,7 @@ export function buildSignalDashboardRuntimeSyncTooltip(
meta: row.traceId,
serviceName: row.service,
serviceNamespace: row.serviceNamespace,
+ ...syncTooltipRouteDrilldownContext(renderer.primaryUrl),
breakoutAttributes: row.breakoutAttributes,
...syncTooltipRelatedHandoff(renderer.signal, row.traceId, row.spanId,
options, {
serviceName: row.service,
@@ -3837,6 +3866,7 @@ export function buildSignalDashboardRuntimeSyncTooltip(
meta: row.duration,
serviceName: row.service,
serviceNamespace: row.serviceNamespace,
+ ...syncTooltipRouteDrilldownContext(renderer.primaryUrl),
breakoutAttributes: row.breakoutAttributes,
...syncTooltipRelatedHandoff(renderer.signal, row.traceId, row.spanId,
options, {
serviceName: row.service,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]