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 65b0dbacca Scope log queries by entity id
65b0dbacca is described below

commit 65b0dbacca2f5703f3387aeed8369ffa156f1fdf
Author: Logic <[email protected]>
AuthorDate: Tue Jun 9 20:32:51 2026 +0800

    Scope log queries by entity id
---
 .../logs/controller/LogQueryController.java        | 35 ++++++++++++++--------
 .../logs/controller/LogQueryControllerTest.java    | 24 +++++++++++----
 2 files changed, 40 insertions(+), 19 deletions(-)

diff --git 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/controller/LogQueryController.java
 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/controller/LogQueryController.java
index c6a7b72266..9fa606eee7 100644
--- 
a/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/controller/LogQueryController.java
+++ 
b/hertzbeat-observability/src/main/java/org/apache/hertzbeat/observability/logs/controller/LogQueryController.java
@@ -89,7 +89,7 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         Page<LogEntry> result = logQueryService.list(entityId, start, end, 
traceId, spanId, severityNumber, severityText, search,
                 serviceName, serviceNamespace, environment, 
scopedResourceFilter, attributeFilter,
                 pageIndex, pageSize, hideInternal, hideNoise);
@@ -130,7 +130,7 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         return ResponseEntity.ok(Message.success(logQueryService.context(
                 entityId, logTimeUnixNano, start, end, serviceName, 
serviceNamespace, environment,
                 scopedResourceFilter, attributeFilter, limit, direction, 
cursorLogTimeUnixNano, hideInternal, hideNoise)));
@@ -172,7 +172,7 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         return ResponseEntity.ok(Message.success(logQueryService.overviewStats(
                 entityId, start, end, traceId, spanId, severityNumber, 
severityText, search,
                 serviceName, serviceNamespace, environment, 
scopedResourceFilter, attributeFilter,
@@ -215,7 +215,7 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         return 
ResponseEntity.ok(Message.success(logQueryService.traceCoverageStats(
                 entityId, start, end, traceId, spanId, severityNumber, 
severityText, search,
                 serviceName, serviceNamespace, environment, 
scopedResourceFilter, attributeFilter,
@@ -258,7 +258,7 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         return ResponseEntity.ok(Message.success(logQueryService.trendStats(
                 entityId, start, end, traceId, spanId, severityNumber, 
severityText, search,
                 serviceName, serviceNamespace, environment, 
scopedResourceFilter, attributeFilter,
@@ -309,26 +309,35 @@ public class LogQueryController {
             @RequestParam(value = "hideInternal", required = false, 
defaultValue = "false") boolean hideInternal,
             @Parameter(description = "Hide demo infrastructure noise logs such 
as kafka/load-generator when focusing on business requests", example = "true")
             @RequestParam(value = "hideNoise", required = false, defaultValue 
= "false") boolean hideNoise) {
-        String scopedResourceFilter = 
mergeEntityTypeResourceFilter(entityType, resourceFilter);
+        String scopedResourceFilter = 
mergeEntityContextResourceFilter(entityId, entityType, resourceFilter);
         return ResponseEntity.ok(Message.success(logQueryService.groupByStats(
                 entityId, start, end, traceId, spanId, severityNumber, 
severityText, search,
                 serviceName, serviceNamespace, environment, 
scopedResourceFilter, attributeFilter, groupBy,
                 limit, orderBy, minCount, hideInternal, hideNoise)));
     }
 
-    private String mergeEntityTypeResourceFilter(String entityType, String 
resourceFilter) {
+    private String mergeEntityContextResourceFilter(Long entityId, String 
entityType, String resourceFilter) {
         String normalizedResourceFilter = 
StringUtils.trimWhitespace(resourceFilter);
+        String scopedResourceFilter = normalizedResourceFilter;
+        if (entityId != null && entityId > 0 && 
(StringUtils.hasText(scopedResourceFilter)
+                ? 
!scopedResourceFilter.contains(OtlpResourceSemanticAttributes.HERTZBEAT_ENTITY_ID)
+                : true)) {
+            String entityIdFilter = 
OtlpResourceSemanticAttributes.HERTZBEAT_ENTITY_ID + "=\"" + entityId + "\"";
+            scopedResourceFilter = StringUtils.hasText(scopedResourceFilter)
+                    ? scopedResourceFilter + " and " + entityIdFilter
+                    : entityIdFilter;
+        }
         String normalizedEntityType = StringUtils.trimWhitespace(entityType);
         if (!StringUtils.hasText(normalizedEntityType) || 
!normalizedEntityType.matches("[A-Za-z0-9_.:-]+")) {
-            return normalizedResourceFilter;
+            return scopedResourceFilter;
         }
-        if (StringUtils.hasText(normalizedResourceFilter)
-                && 
normalizedResourceFilter.contains(OtlpResourceSemanticAttributes.HERTZBEAT_ENTITY_TYPE))
 {
-            return normalizedResourceFilter;
+        if (StringUtils.hasText(scopedResourceFilter)
+                && 
scopedResourceFilter.contains(OtlpResourceSemanticAttributes.HERTZBEAT_ENTITY_TYPE))
 {
+            return scopedResourceFilter;
         }
         String entityTypeFilter = 
OtlpResourceSemanticAttributes.HERTZBEAT_ENTITY_TYPE + "=\"" + 
normalizedEntityType + "\"";
-        return StringUtils.hasText(normalizedResourceFilter)
-                ? normalizedResourceFilter + " and " + entityTypeFilter
+        return StringUtils.hasText(scopedResourceFilter)
+                ? scopedResourceFilter + " and " + entityTypeFilter
                 : entityTypeFilter;
     }
 }
diff --git 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
index 66abfd8e21..cede67c0f2 100644
--- 
a/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
+++ 
b/hertzbeat-observability/src/test/java/org/apache/hertzbeat/observability/logs/controller/LogQueryControllerTest.java
@@ -300,7 +300,8 @@ class LogQueryControllerTest {
                 .resource(new HashMap<>(Map.of(
                         "service.name", "checkout",
                         "service.namespace", "payments",
-                        "deployment.environment.name", "prod")))
+                        "deployment.environment.name", "prod",
+                        "hertzbeat.entity_id", "42")))
                 .build();
         LogEntry billingStagingLog = LogEntry.builder()
                 .timeUnixNano(1734005477640000000L)
@@ -311,11 +312,14 @@ class LogQueryControllerTest {
                         "service.namespace", "wrong-namespace",
                         "deployment.environment.name", "staging")))
                 .build();
+        Map<String, String> expectedResourceFilters = 
Map.of("hertzbeat.entity_id", "42");
         when(historyDataReader.countLogsByMultipleConditions(any(), any(), 
any(), any(), any(), any(), any(),
-                anySet(), eq(false), eq(null), eq("checkout"), eq("payments"), 
eq("prod")))
+                anySet(), eq(false), eq(null), eq("checkout"), eq("payments"), 
eq("prod"),
+                eq(expectedResourceFilters), eq(Map.of())))
                 .thenReturn(2L);
         
when(historyDataReader.queryLogsByMultipleConditionsWithPagination(any(), 
any(), any(), any(), any(), any(),
-                any(), eq(0), eq(20), anySet(), eq(false), eq(null), 
eq("checkout"), eq("payments"), eq("prod")))
+                any(), eq(0), eq(20), anySet(), eq(false), eq(null), 
eq("checkout"), eq("payments"), eq("prod"),
+                eq(expectedResourceFilters), eq(Map.of())))
                 .thenReturn(List.of(checkoutProdLog, billingStagingLog));
 
         mockMvc.perform(MockMvcRequestBuilders.get("/api/logs/list")
@@ -363,7 +367,9 @@ class LogQueryControllerTest {
                         .priority(20)
                         .build()
         ));
-        Map<String, String> expectedResourceFilters = 
Map.of("service.version", "1.2.3");
+        Map<String, String> expectedResourceFilters = Map.of(
+                "service.version", "1.2.3",
+                "hertzbeat.entity_id", "42");
         Map<String, String> expectedAttributeFilters = Map.of("http.route", 
"/checkout");
         when(historyDataReader.countLogsBySeverityBuckets(any(), any(), any(), 
any(), any(), any(), any(),
                 anySet(), eq(false), org.mockito.ArgumentMatchers.isNull(), 
eq("checkout"), eq("payments"), eq("prod"),
@@ -439,10 +445,13 @@ class LogQueryControllerTest {
                         "service.name", "checkout",
                         "service.namespace", "payments",
                         "deployment.environment.name", "prod",
-                        "service.version", "1.2.3")))
+                        "service.version", "1.2.3",
+                        "hertzbeat.entity_id", "42")))
                 .attributes(new HashMap<>(Map.of("http.route", "/checkout")))
                 .build();
-        Map<String, String> expectedResourceFilters = 
Map.of("service.version", "1.2.3");
+        Map<String, String> expectedResourceFilters = Map.of(
+                "service.version", "1.2.3",
+                "hertzbeat.entity_id", "42");
         Map<String, String> expectedAttributeFilters = Map.of("http.route", 
"/checkout");
         when(historyDataReader.queryLogsByMultipleConditions(any(), any(), 
any(), any(), any(), any(), any(),
                 anySet(), eq(false), org.mockito.ArgumentMatchers.isNull(), 
eq("checkout"), eq("payments"), eq("prod"),
@@ -632,6 +641,7 @@ class LogQueryControllerTest {
                 .resource(new HashMap<>(java.util.Map.of(
                         "service.name", "checkout",
                         "service.version", "1.2.3",
+                        "hertzbeat.entity_id", "42",
                         "hertzbeat.entity_type", "service",
                         "hertzbeat.workspace_id", "team-a")))
                 .attributes(new HashMap<>(java.util.Map.of("http.route", 
"/checkout")))
@@ -639,6 +649,7 @@ class LogQueryControllerTest {
 
         Map<String, String> expectedResourceFilters = Map.of(
                 "service.version", "1.2.3",
+                "hertzbeat.entity_id", "42",
                 "hertzbeat.entity_type", "service"
         );
         AuthTokenRequestContext.bindWorkspaceId("team-a");
@@ -652,6 +663,7 @@ class LogQueryControllerTest {
                 .thenReturn(List.of(filteredLog));
 
         mockMvc.perform(MockMvcRequestBuilders.get("/api/logs/list")
+                        .param("entityId", "42")
                         .param("entityType", "service")
                         .param("resourceFilter", "service.version=1.2.3")
                         .param("attributeFilter", "http.route:/checkout"))


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

Reply via email to