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 6756e4a071 Add operation filters to log trace handoffs
6756e4a071 is described below

commit 6756e4a071569df799f154389aa1283285d07ba7
Author: Logic <[email protected]>
AuthorDate: Wed Jun 10 09:18:10 2026 +0800

    Add operation filters to log trace handoffs
---
 web-next/lib/log-manage/view-model.test.ts | 31 ++++++++++++++++++++++++++++++
 web-next/lib/log-manage/view-model.ts      | 19 ++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/web-next/lib/log-manage/view-model.test.ts 
b/web-next/lib/log-manage/view-model.test.ts
index 1f9651a3f1..cb08703f3d 100644
--- a/web-next/lib/log-manage/view-model.test.ts
+++ b/web-next/lib/log-manage/view-model.test.ts
@@ -745,6 +745,7 @@ describe('log view model', () => {
 
     const traceParams = new URL(result.traceHref, 
'https://example.com').searchParams;
     expect(traceParams.get('operationName')).toBe('POST /checkout');
+    expect(traceParams.get('attributeFilter')).toBeNull();
 
     const metricsParams = new URL(result.metricsHref, 
'https://example.com').searchParams;
     expect(metricsParams.get('operationName')).toBe('POST /checkout');
@@ -761,6 +762,36 @@ describe('log view model', () => {
     expect(alertRuleParams.get('operationName')).toBe('POST /checkout');
   });
 
+  it('adds an executable trace attribute filter for operation-level log 
handoffs', () => {
+    const result = buildLogHandoffLinks(
+      {
+        traceId: '',
+        spanId: '',
+        timeUnixNano: 1_710_000_000_000_000_000,
+        resource: {
+          'service.name': 'checkout',
+          'service.namespace': 'payments'
+        },
+        attributes: {
+          'http.route': '/checkout/:id'
+        }
+      } as any,
+      {
+        entityId: '7',
+        entityType: 'service',
+        entityName: 'Checkout API'
+      }
+    );
+
+    const traceParams = new URL(result.traceHref, 
'https://example.com').searchParams;
+    expect(traceParams.get('traceId')).toBeNull();
+    expect(traceParams.get('spanId')).toBeNull();
+    expect(traceParams.get('serviceName')).toBe('checkout');
+    expect(traceParams.get('serviceNamespace')).toBe('payments');
+    expect(traceParams.get('operationName')).toBe('/checkout/:id');
+    
expect(traceParams.get('attributeFilter')).toBe('http.route="/checkout/:id"');
+  });
+
   it('can override trace and metrics return paths with the current log 
workspace route', () => {
     const currentLogReturnTo =
       
`/log/manage?traceId=trace-1&spanId=span-1&view=stream&start=1709999100000&end=1710000060000&returnTo=%2Foverview&returnLabel=${encodeURIComponent(t('menu.log.manage'))}`;
diff --git a/web-next/lib/log-manage/view-model.ts 
b/web-next/lib/log-manage/view-model.ts
index ef2536804f..6aa4a872e4 100644
--- a/web-next/lib/log-manage/view-model.ts
+++ b/web-next/lib/log-manage/view-model.ts
@@ -340,6 +340,23 @@ function buildMetricFilterExpression(name: string, value: 
string | undefined) {
   return `${trimmedName}="${escapeMetricFilterValue(trimmedValue)}"`;
 }
 
+function buildTraceAttributeFilterExpression(name: string, value: string | 
undefined) {
+  const trimmedName = name.trim();
+  const trimmedValue = value?.trim();
+  if (!/^[A-Za-z0-9_.:-]+$/.test(trimmedName) || !trimmedValue || trimmedValue 
=== '-') return undefined;
+  return `${trimmedName}="${escapeMetricFilterValue(trimmedValue)}"`;
+}
+
+function buildLogTraceOperationAttributeFilter(selectedLog: LogEntry | null, 
traceId: string | undefined, spanId: string | undefined) {
+  if (traceId || spanId) return undefined;
+  const httpRoute = firstText(
+    readAttribute(selectedLog?.attributes, 'http.route'),
+    readAttribute(selectedLog?.attributes, 'http_route')
+  );
+  if (httpRoute) return buildTraceAttributeFilterExpression('http.route', 
httpRoute);
+  return buildTraceAttributeFilterExpression('span.name', 
readAttribute(selectedLog?.attributes, 'span.name'));
+}
+
 function buildLogMetricsResourceFilter(selectedLog: LogEntry | null) {
   const expressions = [
     buildMetricFilterExpression('k8s.namespace.name', firstText(
@@ -750,6 +767,8 @@ export function buildLogHandoffLinks(
   if (spanId) traceParams.set('spanId', spanId);
   if (traceContext.serviceName) traceParams.set('serviceName', 
traceContext.serviceName);
   appendSignalRouteContext(traceParams, traceContext);
+  const traceOperationAttributeFilter = 
buildLogTraceOperationAttributeFilter(selectedLog, traceId, spanId);
+  if (traceOperationAttributeFilter) traceParams.set('attributeFilter', 
traceOperationAttributeFilter);
   const traceHref = traceParams.toString() ? 
`/trace/manage?${traceParams.toString()}` : '/trace/manage';
 
   const metricsParams = new URLSearchParams();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to