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]

Reply via email to